feat: obfuscate email with click-to-reveal across all pages

Replace all bare info@ occurrences with a click-to-reveal pattern:
- New EmailLink React component (base64 decode on click, never in DOM pre-click)
- privacy, terms, refund pages use EmailLink
- contact/index.html uses a vanilla JS button with the same pattern
- PaymentForm mailto builder uses atob() to keep email out of source literals

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chris 2026-05-08 10:23:50 -04:00
parent 3330c47af2
commit 57cc5840b9
6 changed files with 63 additions and 10 deletions

View File

@ -1,4 +1,5 @@
import type { Metadata } from 'next' import type { Metadata } from 'next'
import EmailLink from '@/components/EmailLink'
export const metadata: Metadata = { title: 'Privacy Policy' } export const metadata: Metadata = { title: 'Privacy Policy' }
@ -75,13 +76,12 @@ export default function PrivacyPage() {
<h2>8. Your rights</h2> <h2>8. Your rights</h2>
<p> <p>
You may request access to, correction of, or deletion of your personal data at any You may request access to, correction of, or deletion of your personal data at any
time by emailing <a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a>. time by emailing <EmailLink label="us directly" />.
</p> </p>
<h2>9. Contact</h2> <h2>9. Contact</h2>
<p> <p>
Questions about this policy? Email us at{' '} Questions about this policy? <EmailLink label="Email us" /> or call{' '}
<a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a> or call{' '}
<a href="tel:+12035551234">(203) 555-1234</a>. <a href="tel:+12035551234">(203) 555-1234</a>.
</p> </p>
</div> </div>

View File

@ -1,4 +1,5 @@
import type { Metadata } from 'next' import type { Metadata } from 'next'
import EmailLink from '@/components/EmailLink'
export const metadata: Metadata = { title: 'Refund & Cancellation Policy' } export const metadata: Metadata = { title: 'Refund & Cancellation Policy' }
@ -19,8 +20,7 @@ export default function RefundPage() {
<strong>75% store credit</strong>. <strong>75% store credit</strong>.
</p> </p>
<p> <p>
To cancel, please contact us as soon as possible at{' '} To cancel, please contact us as soon as possible <EmailLink label="email us" />.
<a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a>.
</p> </p>
<h2>After Delivery or Pickup</h2> <h2>After Delivery or Pickup</h2>
@ -45,7 +45,7 @@ export default function RefundPage() {
<h2>Questions?</h2> <h2>Questions?</h2>
<p> <p>
Email <a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a> <EmailLink label="Email us" />
we&rsquo;re a small local business and we&rsquo;ll always do our best to make things right. we&rsquo;re a small local business and we&rsquo;ll always do our best to make things right.
</p> </p>
</div> </div>

View File

@ -1,4 +1,5 @@
import type { Metadata } from 'next' import type { Metadata } from 'next'
import EmailLink from '@/components/EmailLink'
export const metadata: Metadata = { title: 'Terms & Conditions' } export const metadata: Metadata = { title: 'Terms & Conditions' }
@ -32,8 +33,7 @@ export default function TermsPage() {
be resolved in the courts of New Haven County, Connecticut. be resolved in the courts of New Haven County, Connecticut.
</p> </p>
<p> <p>
Questions or concerns? Email us at{' '} Questions or concerns? <EmailLink label="Email us" />.
<a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a>.
</p> </p>
<hr /> <hr />

View File

@ -0,0 +1,42 @@
'use client'
import { useState } from 'react'
const ENCODED = 'aW5mb0BiZWFjaHBhcnR5YmFsbG9vbnMuY29t'
interface Props {
label?: string
subject?: string
body?: string
}
export default function EmailLink({ label = 'click to reveal', subject, body }: Props) {
const [addr, setAddr] = useState<string | null>(null)
if (!addr) {
return (
<button
type="button"
onClick={() => setAddr(atob(ENCODED))}
style={{
background: 'none',
border: 'none',
padding: 0,
color: 'inherit',
cursor: 'pointer',
textDecoration: 'underline',
font: 'inherit',
}}
>
{label}
</button>
)
}
const params: string[] = []
if (subject) params.push(`subject=${encodeURIComponent(subject)}`)
if (body) params.push(`body=${encodeURIComponent(body)}`)
const href = `mailto:${addr}${params.length ? '?' + params.join('&') : ''}`
return <a href={href}>{addr}</a>
}

View File

@ -90,7 +90,8 @@ function buildMailtoLink(payload: CheckoutPayload): string {
`Total: ${fmtCents(payload.grandTotal)}`, `Total: ${fmtCents(payload.grandTotal)}`,
].join('\n') ].join('\n')
return `mailto:info@beachpartyballoons.com?subject=${encodeURIComponent('Online Order — Invoice Request')}&body=${encodeURIComponent(body)}` const addr = atob('aW5mb0BiZWFjaHBhcnR5YmFsbG9vbnMuY29t')
return `mailto:${addr}?subject=${encodeURIComponent('Online Order — Invoice Request')}&body=${encodeURIComponent(body)}`
} }
export default function PaymentForm({ payload, onSuccess, onError, active }: Props) { export default function PaymentForm({ payload, onSuccess, onError, active }: Props) {

View File

@ -51,7 +51,17 @@
<div class="is-flex-direction-column is-dark"> <div class="is-flex-direction-column is-dark">
<h2 class="is-size-4" style="text-align: center;"> <a target="_blank" href="https://maps.app.goo.gl/gRk6NztgMRUsSVJf9">554 Boston Post Road, Milford, CT 06460</a> </h2> <h2 class="is-size-4" style="text-align: center;"> <a target="_blank" href="https://maps.app.goo.gl/gRk6NztgMRUsSVJf9">554 Boston Post Road, Milford, CT 06460</a> </h2>
<h2 class="is-size-4" style="text-align: center;" ><a href="tel:203.283.5626">203.283.5626</a> </h2> <h2 class="is-size-4" style="text-align: center;" ><a href="tel:203.283.5626">203.283.5626</a> </h2>
<h2 class="is-size-4" style="text-align: center;" ><a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a> </h2> <h2 class="is-size-4" style="text-align: center;" id="bpb-email-h2">
<button id="bpb-email-btn" style="background:none;border:none;cursor:pointer;font:inherit;color:inherit;text-decoration:underline;padding:0;">
Click to show email address
</button>
</h2>
<script>
document.getElementById('bpb-email-btn').addEventListener('click', function () {
var a = atob('aW5mb0BiZWFjaHBhcnR5YmFsbG9vbnMuY29t');
document.getElementById('bpb-email-h2').innerHTML = '<a href="mailto:' + a + '">' + a + '<\/a>';
});
</script>
</div> </div>
</div> </div>