From 1d59d18870784222a447c0727e54e6d23f83310a Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Fri, 16 Jan 2026 11:06:42 +0100 Subject: [PATCH] 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 --- src/app.js | 13 +++++++++ src/i18n.js | 66 ++++++++++++++++++++++++++++++++++++--------- src/index.html | 29 +++++++++++++++----- src/main.css | 72 +++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 161 insertions(+), 19 deletions(-) diff --git a/src/app.js b/src/app.js index 0145ea9..ade1085 100644 --- a/src/app.js +++ b/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 diff --git a/src/i18n.js b/src/i18n.js index ff443dc..7945ab2 100644 --- a/src/i18n.js +++ b/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: "Best on desktop or tablet (landscape). 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: "Am besten auf Desktop oder Tablet (Querformat). 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: "Najlepiej na komputerze lub tablecie (poziomo). 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: "Mejor en escritorio o tablet (horizontal). 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: "أفضل على الكمبيوتر أو الجهاز اللوحي (أفقي). يعمل على الجوال، لكن الشاشات الأكبر تسهّل البرمجة.", @@ -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: "Найкраще на комп'ютері або планшеті (горизонтально). На телефоні теж працює, але більший екран полегшує програмування.", diff --git a/src/index.html b/src/index.html index a1c18f1..d644a59 100644 --- a/src/index.html +++ b/src/index.html @@ -173,37 +173,54 @@

Coming Soon

- 🔄 + + +

Cloud Sync

Sync your progress across all devices. Start on desktop, continue on tablet.

- 🏆 + + +

Achievements

Earn badges as you master new skills. Track your learning milestones.

- + + +

JavaScript

Interactive JavaScript lessons with live code execution and DOM manipulation.

- 🧩 + + +

Frameworks

React, Vue, and Svelte basics. Build real components step by step.

+
+

Want to know when new features launch?

+ + + +
-

+

Best on desktop or tablet (landscape). Mobile works, but larger screens make coding easier.

Start Learning Today

- Begin Your Journey + Let's get crispy!

Free and open source. No account required. Progress saved locally.

diff --git a/src/main.css b/src/main.css index 7423163..a4348ca 100644 --- a/src/main.css +++ b/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);