feat(component): add email obfuscation component

This commit is contained in:
DeCentN2Madness committed 2026-07-02 13:31:04 -07:00
1 parent b73e92a8a4
commit fd8e12f925
29 files changed
+147 -42

No files matched your search

+58
View File
@@ -0,0 +1,58 @@
---
interface Props {
email?: string;
phone?: string;
hrefValue?: string;
linkText?: string;
obfuscatedText?: string;
class?: string;
id?: string;
}
const {
email,
phone,
hrefValue,
linkText,
obfuscatedText,
class: className,
id
} = Astro.props;
if ((email ? 1 : 0) + (phone ? 1 : 0) !== 1) {
throw new Error('EmailObfuscation requires exactly one of email or phone.');
}
const contactType = email ? 'email' : 'phone';
const sourceValue = email ?? phone ?? '';
const linkValue = linkText ?? sourceValue;
const fallbackText = obfuscatedText ?? sourceValue;
const protocol = email ? 'mailto' : 'tel';
const telHrefValue = hrefValue ?? sourceValue.replace(/[^\d+]/g, '');
const targetValue = email ? (hrefValue ?? sourceValue) : telHrefValue;
const encode = (value: string) => Array.from(value, (character) => character.charCodeAt(0));
const encodedTarget = encode(targetValue);
const encodedText = encode(linkValue);
const spanId = id ?? `${contactType}-obfuscation-${Math.random().toString(36).slice(2, 10)}`;
---
<span id={spanId} class={className}>{fallbackText}</span><script type="text/javascript" define:vars={{ encodedTarget, encodedText, protocol, spanId }}>
(() => {
const contactSpan = document.getElementById(spanId);
if (!contactSpan) return;
const decode = (encodedValue) => encodedValue
.map((characterCode) => String.fromCharCode(characterCode))
.join('');
const target = decode(encodedTarget);
const text = decode(encodedText);
const link = document.createElement('a');
link.href = `${protocol}:${target}`;
link.textContent = text;
contactSpan.textContent = '';
contactSpan.appendChild(link);
})();
</script>
+3 -2
View File
@@ -1,4 +1,5 @@
---
import EmailObfuscation from './EmailObfuscation.astro';
import site from '../data/site.json';
const year = new Date().getFullYear();
const { lang = 'en' } = Astro.props;
@@ -31,11 +32,11 @@ const labels = lang === 'es'
<div class="footer-contact">
<div class="footer-contact-item">
<img src="/assets/images/phone-blue.svg" alt="" aria-hidden="true" width="24" height="24" />
<a href={`tel:${site.phone.replace(/\D/g, '')}`}>{site.phone}</a>
<EmailObfuscation phone={site.phone} hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
</div>
<div class="footer-contact-item">
<img src="/assets/images/envelope-blue.svg" alt="" aria-hidden="true" width="24" height="24" />
<a href={`mailto:${site.email}`}>{site.email}</a>
<EmailObfuscation email={site.email} obfuscatedText="moc.msitua4etutitsniza@ofni" />
</div>
<div class="footer-contact-item">
<img src="/assets/images/location-blue.svg" alt="" aria-hidden="true" width="24" height="24" />
+2 -1
View File
@@ -1,5 +1,6 @@
---
import Button from './Button.astro';
import EmailObfuscation from './EmailObfuscation.astro';
const { title = 'Schedule Your Free Consultation', showBackground = false } = Astro.props;
---
<section class:list={['form-section', { 'form-section-background': showBackground }]} aria-labelledby="form-title">
@@ -14,7 +15,7 @@ const { title = 'Schedule Your Free Consultation', showBackground = false } = As
<label>Service of interest<select name="service"><option>ABA Therapy</option><option>Autism Evaluation</option><option>Learner Social Club</option><option>Other</option></select></label>
<label>How can we help?<textarea name="message" rows="5"></textarea></label>
<Button type="submit" disabled aria-describedby="form-note">Submit</Button>
<p id="form-note" class="form-note">Online submission is not yet connected. Please call (480) 687-7099.</p>
<p id="form-note" class="form-note">Online submission is not yet connected. Please call <EmailObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />.</p>
</form>
</div>
</section>
+3 -1
View File
@@ -1,5 +1,6 @@
---
import Button from '../Button.astro';
import EmailObfuscation from '../EmailObfuscation.astro';
const groups = [
{
title: 'Board Certified Behavioral Analysts & Psychologists',
@@ -68,7 +69,7 @@ const groups = [
<p class="eyebrow">Join the AIA Team!</p>
<h2>Employment Opportunities</h2>
<p>Apply online to join the AIA team! At the Arizona Institute for Autism (AIA) we are on a mission to improve special education and strengthen communities throughout Arizona.</p>
<p>Our talented and passionate employees are a critical piece to the high-quality ABA therapy we provide along with our commitment to behavioral health, school solutions, and community outreach. For questions regarding open positions, please contact us at <a href="mailto:hr@azinstitute4autism.com">hr@azinstitute4autism.com</a>.</p>
<p>Our talented and passionate employees are a critical piece to the high-quality ABA therapy we provide along with our commitment to behavioral health, school solutions, and community outreach. For questions regarding open positions, please contact us at <EmailObfuscation email="hr@azinstitute4autism.com" obfuscatedText="hr [at] abaclinicaz [dot] com" class="team-email" />.</p>
<Button href="/careers">View Open Positions</Button>
</div>
</section>
@@ -89,6 +90,7 @@ const groups = [
.team-member p { font-size: .85rem; margin: 0; }
.team-careers { padding-block: 5rem; }
.team-careers :global(.content-button) { margin-left: 50%; margin-top: 4rem; translate: -50%; }
.team-email { font-weight: 700; }
@media (max-width: 950px) {
.team-member { flex-basis: min(100%, 227px); }
}