feat: add newsletter signup with email field and Umami tracking
- Add email input field to newsletter signup form - Add disclaimer about max frequency and unsubscribe option - Add newsletter translations for all 6 languages (en, de, pl, es, ar, uk) - Update hero highlight to "Crispy Code" - Update CTA button to "Let's get crispy!" - Add Umami tracking for newsletter submissions - Style newsletter form without white background
This commit is contained in:
13
src/app.js
13
src/app.js
@@ -2560,6 +2560,19 @@ function init() {
|
||||
track("support_click", { location: "landing" });
|
||||
}
|
||||
});
|
||||
|
||||
// Newsletter form submission
|
||||
const newsletterForm = document.getElementById("newsletter-form");
|
||||
const newsletterThanks = document.getElementById("newsletter-thanks");
|
||||
newsletterForm?.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
const email = document.getElementById("newsletter-email")?.value;
|
||||
if (email) {
|
||||
track("newsletter_signup", { email });
|
||||
newsletterForm.classList.add("hidden");
|
||||
newsletterThanks?.classList.remove("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Start the application
|
||||
|
||||
66
src/i18n.js
66
src/i18n.js
@@ -118,7 +118,7 @@ const translations = {
|
||||
|
||||
// Landing page
|
||||
landingHeroTitle: "Learn Web Development",
|
||||
landingHeroHighlight: "By Writing Real Code",
|
||||
landingHeroHighlight: "Crispy Code",
|
||||
landingHeroSubtitle: "Master HTML, CSS, and Tailwind through hands-on exercises with instant feedback. Free and open source.",
|
||||
landingCtaStart: "Start Learning NOW",
|
||||
landingWhyTitle: "Why CODE CRISPIES Works",
|
||||
@@ -137,7 +137,7 @@ const translations = {
|
||||
comingSoon: "Coming Soon",
|
||||
landingCtaTitle: "Start Learning Today",
|
||||
landingCtaSub: "Free and open source. No account required. Progress saved locally.",
|
||||
landingCtaButton: "Begin Your Journey",
|
||||
landingCtaButton: "Let's get crispy!",
|
||||
|
||||
// Coming Soon
|
||||
landingComingSoonTitle: "Coming Soon",
|
||||
@@ -150,6 +150,13 @@ const translations = {
|
||||
comingSoonFrameworksTitle: "Frameworks",
|
||||
comingSoonFrameworksText: "React, Vue, and Svelte basics. Build real components step by step.",
|
||||
|
||||
// Newsletter
|
||||
newsletterText: "Want to know when new features launch?",
|
||||
newsletterPlaceholder: "your@email.com",
|
||||
newsletterButton: "Notify Me",
|
||||
newsletterThanks: "Thanks! We'll keep you posted.",
|
||||
newsletterDisclaimer: "Max once a week. Unsubscribe anytime via mail@codecrispi.es",
|
||||
|
||||
// Device Notice
|
||||
deviceNotice: "<strong>Best on desktop or tablet (landscape).</strong> Mobile works, but larger screens make coding easier.",
|
||||
|
||||
@@ -283,7 +290,7 @@ const translations = {
|
||||
|
||||
// Landing page
|
||||
landingHeroTitle: "Web Programmierung",
|
||||
landingHeroHighlight: "Selbstständig lernen",
|
||||
landingHeroHighlight: "Crispy Code",
|
||||
landingHeroSubtitle: "Meistere HTML, CSS und Tailwind durch praktische Übungen mit sofortigem Feedback. Kostenlos und Open Source.",
|
||||
landingCtaStart: "Jetzt starten",
|
||||
landingWhyTitle: "Warum CODE CRISPIES funktioniert",
|
||||
@@ -304,7 +311,7 @@ const translations = {
|
||||
comingSoon: "Bald verfügbar",
|
||||
landingCtaTitle: "Heute noch anfangen",
|
||||
landingCtaSub: "Kostenlos und Open Source. Kein Konto erforderlich. Fortschritt wird lokal gespeichert.",
|
||||
landingCtaButton: "Jetzt erste Schritte machen",
|
||||
landingCtaButton: "Let's get crispy!",
|
||||
|
||||
// Coming Soon
|
||||
landingComingSoonTitle: "Demnächst",
|
||||
@@ -317,6 +324,13 @@ const translations = {
|
||||
comingSoonFrameworksTitle: "Frameworks",
|
||||
comingSoonFrameworksText: "React, Vue und Svelte Grundlagen. Baue echte Komponenten Schritt für Schritt.",
|
||||
|
||||
// Newsletter
|
||||
newsletterText: "Möchtest du erfahren, wenn neue Funktionen erscheinen?",
|
||||
newsletterPlaceholder: "deine@email.de",
|
||||
newsletterButton: "Benachrichtigen",
|
||||
newsletterThanks: "Danke! Wir halten dich auf dem Laufenden.",
|
||||
newsletterDisclaimer: "Max. einmal pro Woche. Jederzeit abmelden über mail@codecrispi.es",
|
||||
|
||||
// Device Notice
|
||||
deviceNotice: "<strong>Am besten auf Desktop oder Tablet (Querformat).</strong> Mobil funktioniert, aber größere Bildschirme machen das Coden einfacher.",
|
||||
|
||||
@@ -450,7 +464,7 @@ const translations = {
|
||||
|
||||
// Landing page
|
||||
landingHeroTitle: "Naucz się tworzenia stron",
|
||||
landingHeroHighlight: "Pisząc prawdziwy kod",
|
||||
landingHeroHighlight: "Crispy Code",
|
||||
landingHeroSubtitle: "Opanuj HTML, CSS i Tailwind poprzez praktyczne ćwiczenia z natychmiastową informacją zwrotną. Darmowe i open source.",
|
||||
landingCtaStart: "Zacznij TERAZ",
|
||||
landingWhyTitle: "Dlaczego CODE CRISPIES działa",
|
||||
@@ -471,7 +485,7 @@ const translations = {
|
||||
comingSoon: "Wkrótce",
|
||||
landingCtaTitle: "Zacznij naukę już dziś",
|
||||
landingCtaSub: "Darmowe i open source. Bez konta. Postęp zapisywany lokalnie.",
|
||||
landingCtaButton: "Rozpocznij swoją podróż",
|
||||
landingCtaButton: "Let's get crispy!",
|
||||
|
||||
// Coming Soon
|
||||
landingComingSoonTitle: "Wkrótce",
|
||||
@@ -484,6 +498,13 @@ const translations = {
|
||||
comingSoonFrameworksTitle: "Frameworki",
|
||||
comingSoonFrameworksText: "Podstawy React, Vue i Svelte. Buduj prawdziwe komponenty krok po kroku.",
|
||||
|
||||
// Newsletter
|
||||
newsletterText: "Chcesz wiedzieć, kiedy pojawią się nowe funkcje?",
|
||||
newsletterPlaceholder: "twoj@email.pl",
|
||||
newsletterButton: "Powiadom mnie",
|
||||
newsletterThanks: "Dzięki! Będziemy informować.",
|
||||
newsletterDisclaimer: "Maks. raz w tygodniu. Wypisz się w dowolnym momencie przez mail@codecrispi.es",
|
||||
|
||||
// Device Notice
|
||||
deviceNotice: "<strong>Najlepiej na komputerze lub tablecie (poziomo).</strong> Na telefonie też działa, ale większy ekran ułatwia kodowanie.",
|
||||
|
||||
@@ -618,7 +639,7 @@ const translations = {
|
||||
|
||||
// Landing page
|
||||
landingHeroTitle: "Aprende desarrollo web",
|
||||
landingHeroHighlight: "Escribiendo código real",
|
||||
landingHeroHighlight: "Crispy Code",
|
||||
landingHeroSubtitle:
|
||||
"Domina HTML, CSS y Tailwind a través de ejercicios prácticos con retroalimentación instantánea. Gratis y de código abierto.",
|
||||
landingCtaStart: "Empieza AHORA",
|
||||
@@ -640,7 +661,7 @@ const translations = {
|
||||
comingSoon: "Próximamente",
|
||||
landingCtaTitle: "Empieza a aprender hoy",
|
||||
landingCtaSub: "Gratis y de código abierto. Sin cuenta requerida. Progreso guardado localmente.",
|
||||
landingCtaButton: "Comienza tu viaje",
|
||||
landingCtaButton: "Let's get crispy!",
|
||||
|
||||
// Coming Soon
|
||||
landingComingSoonTitle: "Próximamente",
|
||||
@@ -653,6 +674,13 @@ const translations = {
|
||||
comingSoonFrameworksTitle: "Frameworks",
|
||||
comingSoonFrameworksText: "Fundamentos de React, Vue y Svelte. Construye componentes reales paso a paso.",
|
||||
|
||||
// Newsletter
|
||||
newsletterText: "¿Quieres saber cuando se lancen nuevas funciones?",
|
||||
newsletterPlaceholder: "tu@email.com",
|
||||
newsletterButton: "Notificarme",
|
||||
newsletterThanks: "¡Gracias! Te mantendremos informado.",
|
||||
newsletterDisclaimer: "Máximo una vez por semana. Cancela cuando quieras vía mail@codecrispi.es",
|
||||
|
||||
// Device Notice
|
||||
deviceNotice: "<strong>Mejor en escritorio o tablet (horizontal).</strong> Funciona en móvil, pero pantallas más grandes facilitan la programación.",
|
||||
|
||||
@@ -785,7 +813,7 @@ const translations = {
|
||||
|
||||
// Landing page
|
||||
landingHeroTitle: "تعلم تطوير الويب",
|
||||
landingHeroHighlight: "بكتابة كود حقيقي",
|
||||
landingHeroHighlight: "Crispy Code",
|
||||
landingHeroSubtitle: "أتقن HTML و CSS و Tailwind من خلال تمارين عملية مع ملاحظات فورية. مجاني ومفتوح المصدر.",
|
||||
landingCtaStart: "ابدأ الآن",
|
||||
landingWhyTitle: "لماذا CODE CRISPIES فعال",
|
||||
@@ -804,7 +832,7 @@ const translations = {
|
||||
comingSoon: "قريباً",
|
||||
landingCtaTitle: "ابدأ التعلم اليوم",
|
||||
landingCtaSub: "مجاني ومفتوح المصدر. لا حاجة لحساب. يُحفظ التقدم محليًا.",
|
||||
landingCtaButton: "ابدأ رحلتك",
|
||||
landingCtaButton: "Let's get crispy!",
|
||||
|
||||
// Coming Soon
|
||||
landingComingSoonTitle: "قريباً",
|
||||
@@ -817,6 +845,13 @@ const translations = {
|
||||
comingSoonFrameworksTitle: "أطر العمل",
|
||||
comingSoonFrameworksText: "أساسيات React وVue وSvelte. ابنِ مكونات حقيقية خطوة بخطوة.",
|
||||
|
||||
// Newsletter
|
||||
newsletterText: "هل تريد معرفة متى تُطلق ميزات جديدة؟",
|
||||
newsletterPlaceholder: "بريدك@email.com",
|
||||
newsletterButton: "أبلغني",
|
||||
newsletterThanks: "شكراً! سنبقيك على اطلاع.",
|
||||
newsletterDisclaimer: "مرة واحدة أسبوعياً كحد أقصى. إلغاء الاشتراك في أي وقت عبر mail@codecrispi.es",
|
||||
|
||||
// Device Notice
|
||||
deviceNotice: "<strong>أفضل على الكمبيوتر أو الجهاز اللوحي (أفقي).</strong> يعمل على الجوال، لكن الشاشات الأكبر تسهّل البرمجة.",
|
||||
|
||||
@@ -950,7 +985,7 @@ const translations = {
|
||||
|
||||
// Landing page
|
||||
landingHeroTitle: "Вивчай веб-розробку",
|
||||
landingHeroHighlight: "Пишучи справжній код",
|
||||
landingHeroHighlight: "Crispy Code",
|
||||
landingHeroSubtitle: "Опануй HTML, CSS та Tailwind через практичні вправи з миттєвим зворотним зв'язком. Безкоштовно та з відкритим кодом.",
|
||||
landingCtaStart: "Почни ЗАРАЗ",
|
||||
landingWhyTitle: "Чому CODE CRISPIES працює",
|
||||
@@ -970,7 +1005,7 @@ const translations = {
|
||||
comingSoon: "Незабаром",
|
||||
landingCtaTitle: "Почни вчитися сьогодні",
|
||||
landingCtaSub: "Безкоштовно та з відкритим кодом. Без реєстрації. Прогрес зберігається локально.",
|
||||
landingCtaButton: "Розпочни свою подорож",
|
||||
landingCtaButton: "Let's get crispy!",
|
||||
|
||||
// Coming Soon
|
||||
landingComingSoonTitle: "Незабаром",
|
||||
@@ -983,6 +1018,13 @@ const translations = {
|
||||
comingSoonFrameworksTitle: "Фреймворки",
|
||||
comingSoonFrameworksText: "Основи React, Vue та Svelte. Створюй справжні компоненти крок за кроком.",
|
||||
|
||||
// Newsletter
|
||||
newsletterText: "Хочете дізнатися, коли з'являться нові функції?",
|
||||
newsletterPlaceholder: "ваш@email.com",
|
||||
newsletterButton: "Повідомити мене",
|
||||
newsletterThanks: "Дякуємо! Ми будемо тримати вас в курсі.",
|
||||
newsletterDisclaimer: "Максимум раз на тиждень. Відписатися можна будь-коли через mail@codecrispi.es",
|
||||
|
||||
// Device Notice
|
||||
deviceNotice: "<strong>Найкраще на комп'ютері або планшеті (горизонтально).</strong> На телефоні теж працює, але більший екран полегшує програмування.",
|
||||
|
||||
|
||||
@@ -173,37 +173,54 @@
|
||||
<h2 data-i18n="landingComingSoonTitle">Coming Soon</h2>
|
||||
<div class="coming-soon-grid">
|
||||
<article class="coming-soon-card">
|
||||
<span class="coming-soon-icon">🔄</span>
|
||||
<span class="coming-soon-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 0 1-9 9m9-9a9 9 0 0 0-9-9m9 9H3m9 9a9 9 0 0 1-9-9m9 9c1.66 0 3-4.03 3-9s-1.34-9-3-9m0 18c-1.66 0-3-4.03-3-9s1.34-9 3-9m-9 9a9 9 0 0 1 9-9"/></svg>
|
||||
</span>
|
||||
<h3 data-i18n="comingSoonSyncTitle">Cloud Sync</h3>
|
||||
<p data-i18n="comingSoonSyncText">Sync your progress across all devices. Start on desktop, continue on tablet.</p>
|
||||
</article>
|
||||
<article class="coming-soon-card">
|
||||
<span class="coming-soon-icon">🏆</span>
|
||||
<span class="coming-soon-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="8" r="6"/><path d="M15.477 12.89 17 22l-5-3-5 3 1.523-9.11"/></svg>
|
||||
</span>
|
||||
<h3 data-i18n="comingSoonAchievementsTitle">Achievements</h3>
|
||||
<p data-i18n="comingSoonAchievementsText">Earn badges as you master new skills. Track your learning milestones.</p>
|
||||
</article>
|
||||
<article class="coming-soon-card">
|
||||
<span class="coming-soon-icon">⚡</span>
|
||||
<span class="coming-soon-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/></svg>
|
||||
</span>
|
||||
<h3 data-i18n="comingSoonJsTitle">JavaScript</h3>
|
||||
<p data-i18n="comingSoonJsText">Interactive JavaScript lessons with live code execution and DOM manipulation.</p>
|
||||
</article>
|
||||
<article class="coming-soon-card">
|
||||
<span class="coming-soon-icon">🧩</span>
|
||||
<span class="coming-soon-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
||||
</span>
|
||||
<h3 data-i18n="comingSoonFrameworksTitle">Frameworks</h3>
|
||||
<p data-i18n="comingSoonFrameworksText">React, Vue, and Svelte basics. Build real components step by step.</p>
|
||||
</article>
|
||||
</div>
|
||||
<div class="newsletter-signup">
|
||||
<p data-i18n="newsletterText">Want to know when new features launch?</p>
|
||||
<form id="newsletter-form" class="newsletter-form">
|
||||
<input type="email" id="newsletter-email" required data-i18n-placeholder="newsletterPlaceholder" placeholder="your@email.com">
|
||||
<button type="submit" class="btn btn-outline" data-i18n="newsletterButton">Notify Me</button>
|
||||
</form>
|
||||
<p class="newsletter-disclaimer" data-i18n="newsletterDisclaimer">Max once a week. Unsubscribe anytime via mail@codecrispi.es</p>
|
||||
<p id="newsletter-thanks" class="newsletter-thanks hidden" data-i18n="newsletterThanks">Thanks! We'll keep you posted.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="device-notice">
|
||||
<p data-i18n="deviceNotice">
|
||||
<p data-i18n-html="deviceNotice">
|
||||
<strong>Best on desktop or tablet (landscape).</strong> Mobile works, but larger screens make coding easier.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="landing-cta">
|
||||
<h2 data-i18n="landingCtaTitle">Start Learning Today</h2>
|
||||
<a href="#welcome/0" class="cta-button" data-i18n="landingCtaButton">Begin Your Journey</a>
|
||||
<a href="#welcome/0" class="cta-button" data-i18n="landingCtaButton">Let's get crispy!</a>
|
||||
<p class="cta-sub" data-i18n="landingCtaSub">Free and open source. No account required. Progress saved locally.</p>
|
||||
</section>
|
||||
|
||||
|
||||
72
src/main.css
72
src/main.css
@@ -1924,11 +1924,16 @@ input:checked + .toggle-slider::before {
|
||||
}
|
||||
|
||||
.coming-soon-icon {
|
||||
font-size: 2rem;
|
||||
display: block;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.coming-soon-icon svg {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
stroke: var(--section-color);
|
||||
}
|
||||
|
||||
.coming-soon-card h3 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
@@ -1954,6 +1959,71 @@ input:checked + .toggle-slider::before {
|
||||
}
|
||||
}
|
||||
|
||||
/* Newsletter Signup */
|
||||
.newsletter-signup {
|
||||
margin-top: var(--spacing-lg);
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.newsletter-signup p {
|
||||
margin: 0;
|
||||
color: var(--light-text);
|
||||
}
|
||||
|
||||
.newsletter-form {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.newsletter-form input[type="email"] {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: var(--border-radius-sm);
|
||||
background: var(--panel-bg);
|
||||
color: var(--text);
|
||||
font-size: 1rem;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.newsletter-form input[type="email"]:focus {
|
||||
outline: none;
|
||||
border-color: var(--section-color);
|
||||
}
|
||||
|
||||
.newsletter-signup .btn-outline {
|
||||
border: 2px solid var(--section-color);
|
||||
color: var(--section-color);
|
||||
background: transparent;
|
||||
padding: 0.5rem 1.5rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.newsletter-signup .btn-outline:hover {
|
||||
background: var(--section-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.newsletter-disclaimer {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.newsletter-thanks {
|
||||
color: var(--success);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.newsletter-thanks.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Device Notice */
|
||||
.device-notice {
|
||||
margin-top: var(--spacing-lg);
|
||||
|
||||
Reference in New Issue
Block a user