Skip to content

Commit ae9eca3

Browse files
nextlevelshitMichael Czechowski
authored andcommitted
fix(privacy): hide email addresses behind click-to-reveal (#189)
Co-authored-by: Michael Czechowski <mail@dailysh.it> Co-committed-by: Michael Czechowski <mail@dailysh.it>
1 parent a34814a commit ae9eca3

4 files changed

Lines changed: 53 additions & 15 deletions

File tree

src/app.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3432,6 +3432,26 @@ function init() {
34323432
});
34333433
});
34343434

3435+
// Email-reveal: spam-resistant pattern. Email parts (user, domain) live
3436+
// in data-attrs only — never concatenated in static HTML. On click, we
3437+
// build the address + replace the button with a real mailto link the
3438+
// user can click again to launch their MUA. Crawlers see "mail" and
3439+
// "codecrispi.es" as separate strings, never the joined address.
3440+
document.querySelectorAll(".email-reveal").forEach((btn) => {
3441+
btn.addEventListener("click", () => {
3442+
const u = btn.dataset.u || "";
3443+
const d = btn.dataset.d || "";
3444+
if (!u || !d) return;
3445+
const addr = `${u}@${d}`; // @ = @, defer concatenation a beat
3446+
const a = document.createElement("a");
3447+
a.href = `mailto:${addr}`;
3448+
a.textContent = addr;
3449+
a.className = "email-revealed";
3450+
btn.replaceWith(a);
3451+
track("email_reveal");
3452+
});
3453+
});
3454+
34353455
document.querySelector(".privacy-dialog-close")?.addEventListener("click", () => {
34363456
privacyDialog?.close();
34373457
});

src/i18n.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ const translations = {
167167
newsletterPlaceholder: "your@email.com",
168168
newsletterButton: "Notify Me",
169169
newsletterThanks: "Thanks! We'll keep you posted.",
170-
newsletterDisclaimer: "Max once a week. Unsubscribe anytime via mail@codecrispi.es",
170+
newsletterDisclaimer: "Max once a week. Unsubscribe anytime — see Imprint for contact.",
171171

172172
// Device Notice
173173
deviceNotice: "<strong>Best on desktop or tablet (landscape).</strong> Mobile works, but larger screens make coding easier.",
@@ -195,7 +195,7 @@ const translations = {
195195
privacyNoTrackingTitle: "No Tracking",
196196
privacyNoTrackingText: "We do not use cookies for tracking, analytics, or advertising. We do not share your data with third parties.",
197197
privacyRightsTitle: "Your Rights (GDPR)",
198-
privacyRightsText: "You can delete your account and all associated data at any time from the sidebar menu. For questions or data requests, contact us at mail@codecrispi.es",
198+
privacyRightsText: "You can delete your account and all associated data at any time from the sidebar menu. For questions or data requests, see the contact link in the Imprint.",
199199
privacyUpdated: "Last updated: January 2025",
200200

201201
// Imprint
@@ -401,7 +401,7 @@ const translations = {
401401
newsletterPlaceholder: "deine@email.de",
402402
newsletterButton: "Benachrichtigen",
403403
newsletterThanks: "Danke! Wir halten dich auf dem Laufenden.",
404-
newsletterDisclaimer: "Max. einmal pro Woche. Jederzeit abmelden über mail@codecrispi.es",
404+
newsletterDisclaimer: "Max. einmal pro Woche. Abmelden jederzeit — Kontakt im Impressum.",
405405

406406
// Device Notice
407407
deviceNotice: "<strong>Am besten auf Desktop oder Tablet (Querformat).</strong> Mobil funktioniert, aber größere Bildschirme machen das Coden einfacher.",
@@ -427,7 +427,7 @@ const translations = {
427427
privacyNoTrackingTitle: "Kein Tracking",
428428
privacyNoTrackingText: "Wir verwenden keine Cookies für Tracking, Analytik oder Werbung. Wir teilen deine Daten nicht mit Dritten.",
429429
privacyRightsTitle: "Deine Rechte (DSGVO)",
430-
privacyRightsText: "Du kannst dein Konto und alle zugehörigen Daten jederzeit über das Seitenmenü löschen. Bei Fragen: mail@codecrispi.es",
430+
privacyRightsText: "Du kannst dein Konto und alle zugehörigen Daten jederzeit über das Seitenmenü löschen. Bei Fragen oder Datenanfragen siehe Kontakt im Impressum.",
431431
privacyUpdated: "Zuletzt aktualisiert: Januar 2025",
432432
imprintTitle: "Impressum",
433433
imprintResponsibleTitle: "Verantwortlich für den Inhalt",
@@ -631,7 +631,7 @@ const translations = {
631631
newsletterPlaceholder: "twoj@email.pl",
632632
newsletterButton: "Powiadom mnie",
633633
newsletterThanks: "Dzięki! Będziemy informować.",
634-
newsletterDisclaimer: "Maks. raz w tygodniu. Wypisz się w dowolnym momencie przez mail@codecrispi.es",
634+
newsletterDisclaimer: "Maks. raz w tygodniu. Wypisz się w dowolnym momencie — kontakt w Stopce redakcyjnej.",
635635

636636
// Device Notice
637637
deviceNotice: "<strong>Najlepiej na komputerze lub tablecie (poziomo).</strong> Na telefonie też działa, ale większy ekran ułatwia kodowanie.",
@@ -657,7 +657,7 @@ const translations = {
657657
privacyNoTrackingTitle: "Brak śledzenia",
658658
privacyNoTrackingText: "Nie używamy plików cookie do śledzenia, analityki ani reklam. Nie udostępniamy danych osobom trzecim.",
659659
privacyRightsTitle: "Twoje prawa (RODO)",
660-
privacyRightsText: "Możesz usunąć swoje konto i wszystkie powiązane dane w dowolnym momencie z menu bocznego. Pytania: mail@codecrispi.es",
660+
privacyRightsText: "Możesz usunąć swoje konto i wszystkie powiązane dane w dowolnym momencie z menu bocznego. W sprawie pytań — kontakt w Stopce redakcyjnej.",
661661
privacyUpdated: "Ostatnia aktualizacja: styczeń 2025",
662662
imprintTitle: "Informacje prawne",
663663
imprintResponsibleTitle: "Odpowiedzialny za treść",
@@ -863,7 +863,7 @@ const translations = {
863863
newsletterPlaceholder: "tu@email.com",
864864
newsletterButton: "Notificarme",
865865
newsletterThanks: "¡Gracias! Te mantendremos informado.",
866-
newsletterDisclaimer: "Máximo una vez por semana. Cancela cuando quieras vía mail@codecrispi.es",
866+
newsletterDisclaimer: "Máximo una vez por semana. Cancela cuando quieras — contacto en el Aviso legal.",
867867

868868
// Device Notice
869869
deviceNotice: "<strong>Mejor en escritorio o tablet (horizontal).</strong> Funciona en móvil, pero pantallas más grandes facilitan la programación.",
@@ -889,7 +889,7 @@ const translations = {
889889
privacyNoTrackingTitle: "Sin rastreo",
890890
privacyNoTrackingText: "No usamos cookies para rastreo, analíticas o publicidad. No compartimos tus datos con terceros.",
891891
privacyRightsTitle: "Tus derechos (RGPD)",
892-
privacyRightsText: "Puedes eliminar tu cuenta y todos los datos asociados en cualquier momento desde el menú lateral. Contacto: mail@codecrispi.es",
892+
privacyRightsText: "Puedes eliminar tu cuenta y todos los datos asociados en cualquier momento desde el menú lateral. Para consultas, ver contacto en el Aviso legal.",
893893
privacyUpdated: "Última actualización: enero 2025",
894894
imprintTitle: "Aviso legal",
895895
imprintResponsibleTitle: "Responsable del contenido",
@@ -1090,7 +1090,7 @@ const translations = {
10901090
newsletterPlaceholder: "بريدك@email.com",
10911091
newsletterButton: "أبلغني",
10921092
newsletterThanks: "شكراً! سنبقيك على اطلاع.",
1093-
newsletterDisclaimer: "مرة واحدة أسبوعياً كحد أقصى. إلغاء الاشتراك في أي وقت عبر mail@codecrispi.es",
1093+
newsletterDisclaimer: "مرة واحدة أسبوعياً كحد أقصى. إلغاء الاشتراك في أي وقت — جهة الاتصال في بيان الناشر.",
10941094

10951095
// Device Notice
10961096
deviceNotice: "<strong>أفضل على الكمبيوتر أو الجهاز اللوحي (أفقي).</strong> يعمل على الجوال، لكن الشاشات الأكبر تسهّل البرمجة.",
@@ -1116,7 +1116,7 @@ const translations = {
11161116
privacyNoTrackingTitle: "بدون تتبع",
11171117
privacyNoTrackingText: "لا نستخدم ملفات تعريف الارتباط للتتبع أو التحليلات أو الإعلانات. لا نشارك بياناتك مع أطراف ثالثة.",
11181118
privacyRightsTitle: "حقوقك (GDPR)",
1119-
privacyRightsText: "يمكنك حذف حسابك وجميع البيانات المرتبطة في أي وقت من القائمة الجانبية. للاستفسارات: mail@codecrispi.es",
1119+
privacyRightsText: "يمكنك حذف حسابك وجميع البيانات المرتبطة في أي وقت من القائمة الجانبية. للاستفسارات: انظر جهة الاتصال في بيان الناشر.",
11201120
privacyUpdated: "آخر تحديث: يناير 2025",
11211121
imprintTitle: "البيانات القانونية",
11221122
imprintResponsibleTitle: "المسؤول عن المحتوى",
@@ -1319,7 +1319,7 @@ const translations = {
13191319
newsletterPlaceholder: "ваш@email.com",
13201320
newsletterButton: "Повідомити мене",
13211321
newsletterThanks: "Дякуємо! Ми будемо тримати вас в курсі.",
1322-
newsletterDisclaimer: "Максимум раз на тиждень. Відписатися можна будь-коли через mail@codecrispi.es",
1322+
newsletterDisclaimer: "Максимум раз на тиждень. Відписатися можна будь-коли — контакт у вихідних даних.",
13231323

13241324
// Device Notice
13251325
deviceNotice: "<strong>Найкраще на комп'ютері або планшеті (горизонтально).</strong> На телефоні теж працює, але більший екран полегшує програмування.",
@@ -1345,7 +1345,7 @@ const translations = {
13451345
privacyNoTrackingTitle: "Без відстеження",
13461346
privacyNoTrackingText: "Ми не використовуємо файли cookie для відстеження, аналітики чи реклами. Ми не ділимося твоїми даними з третіми сторонами.",
13471347
privacyRightsTitle: "Твої права (GDPR)",
1348-
privacyRightsText: "Ти можеш видалити свій акаунт і всі пов'язані дані в будь-який час з бічного меню. Питання: mail@codecrispi.es",
1348+
privacyRightsText: "Ти можеш видалити свій акаунт і всі пов'язані дані в будь-який час з бічного меню. Питання — контакт у вихідних даних.",
13491349
privacyUpdated: "Останнє оновлення: січень 2025",
13501350
imprintTitle: "Правова інформація",
13511351
imprintResponsibleTitle: "Відповідальний за вміст",

src/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ <h3 data-i18n="comingSoonChallengesTitle">Code Challenges</h3>
284284
<input type="email" id="newsletter-email" required data-i18n-placeholder="newsletterPlaceholder" placeholder="your@email.com">
285285
<button type="submit" class="btn btn-outline" data-i18n="newsletterButton">Notify Me</button>
286286
</form>
287-
<p class="newsletter-disclaimer" data-i18n="newsletterDisclaimer">Max once a week. Unsubscribe anytime via mail@codecrispi.es</p>
287+
<p class="newsletter-disclaimer" data-i18n="newsletterDisclaimer">Max once a week. Unsubscribe anytime — see Imprint for contact.</p>
288288
<p id="newsletter-thanks" class="newsletter-thanks hidden" data-i18n="newsletterThanks">Thanks! We'll keep you posted.</p>
289289
</div>
290290
</section>
@@ -887,7 +887,7 @@ <h4 data-i18n="privacyNoTrackingTitle">No Tracking</h4>
887887
<p data-i18n="privacyNoTrackingText">We do not use cookies for tracking, analytics, or advertising. We do not share your data with third parties.</p>
888888

889889
<h4 data-i18n="privacyRightsTitle">Your Rights (GDPR)</h4>
890-
<p data-i18n="privacyRightsText">You can delete your account and all associated data at any time from the sidebar menu. For questions or data requests, contact us at mail@codecrispi.es</p>
890+
<p data-i18n="privacyRightsText">You can delete your account and all associated data at any time from the sidebar menu. For questions or data requests, see the contact link in the Imprint.</p>
891891

892892
<p class="legal-updated" data-i18n="privacyUpdated">Last updated: January 2025</p>
893893
</div>
@@ -910,7 +910,7 @@ <h4 data-i18n="imprintResponsibleTitle">Responsible for content</h4>
910910

911911
<h4 data-i18n="imprintContactTitle">Contact</h4>
912912
<p>
913-
Email: mail@codecrispi.es<br>
913+
<button type="button" class="email-reveal" data-u="mail" data-d="codecrispi.es" aria-label="Reveal email address">Email: <span class="email-reveal-text">click to reveal</span></button><br>
914914
Website: <a href="https://librete.ch" target="_blank">librete.ch</a>
915915
</p>
916916

src/main.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4433,6 +4433,24 @@ body[data-section="javascript"] #lesson-title {
44334433
.search-toggle:hover { border-color: var(--primary-color); color: var(--primary-color); }
44344434
.search-toggle:focus-visible { outline: 2px solid var(--primary-color); outline-offset: 2px; }
44354435

4436+
/* ================= EMAIL REVEAL =================
4437+
Spam-resistant click-to-reveal pattern used inside imprint dialog.
4438+
Email parts live in data-attrs only; concatenation happens on click. */
4439+
.email-reveal {
4440+
background: transparent;
4441+
border: 0;
4442+
padding: 0;
4443+
font: inherit;
4444+
color: var(--primary-color);
4445+
cursor: pointer;
4446+
text-decoration: underline;
4447+
text-underline-offset: 2px;
4448+
}
4449+
.email-reveal:hover { color: var(--primary-dark); }
4450+
.email-reveal:focus-visible { outline: 2px solid var(--primary-color); outline-offset: 2px; border-radius: 2px; }
4451+
.email-reveal-text { font-style: italic; opacity: 0.85; }
4452+
.email-revealed { color: var(--primary-color); }
4453+
44364454
/* ================= SYNC INDICATOR =================
44374455
Tiny status dot in header. Hover for tooltip; aria-live announces. */
44384456
.sync-indicator {

0 commit comments

Comments
 (0)