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" });
|
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
|
// Start the application
|
||||||
|
|||||||
66
src/i18n.js
66
src/i18n.js
@@ -118,7 +118,7 @@ const translations = {
|
|||||||
|
|
||||||
// Landing page
|
// Landing page
|
||||||
landingHeroTitle: "Learn Web Development",
|
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.",
|
landingHeroSubtitle: "Master HTML, CSS, and Tailwind through hands-on exercises with instant feedback. Free and open source.",
|
||||||
landingCtaStart: "Start Learning NOW",
|
landingCtaStart: "Start Learning NOW",
|
||||||
landingWhyTitle: "Why CODE CRISPIES Works",
|
landingWhyTitle: "Why CODE CRISPIES Works",
|
||||||
@@ -137,7 +137,7 @@ const translations = {
|
|||||||
comingSoon: "Coming Soon",
|
comingSoon: "Coming Soon",
|
||||||
landingCtaTitle: "Start Learning Today",
|
landingCtaTitle: "Start Learning Today",
|
||||||
landingCtaSub: "Free and open source. No account required. Progress saved locally.",
|
landingCtaSub: "Free and open source. No account required. Progress saved locally.",
|
||||||
landingCtaButton: "Begin Your Journey",
|
landingCtaButton: "Let's get crispy!",
|
||||||
|
|
||||||
// Coming Soon
|
// Coming Soon
|
||||||
landingComingSoonTitle: "Coming Soon",
|
landingComingSoonTitle: "Coming Soon",
|
||||||
@@ -150,6 +150,13 @@ const translations = {
|
|||||||
comingSoonFrameworksTitle: "Frameworks",
|
comingSoonFrameworksTitle: "Frameworks",
|
||||||
comingSoonFrameworksText: "React, Vue, and Svelte basics. Build real components step by step.",
|
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
|
// Device Notice
|
||||||
deviceNotice: "<strong>Best on desktop or tablet (landscape).</strong> Mobile works, but larger screens make coding easier.",
|
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
|
// Landing page
|
||||||
landingHeroTitle: "Web Programmierung",
|
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.",
|
landingHeroSubtitle: "Meistere HTML, CSS und Tailwind durch praktische Übungen mit sofortigem Feedback. Kostenlos und Open Source.",
|
||||||
landingCtaStart: "Jetzt starten",
|
landingCtaStart: "Jetzt starten",
|
||||||
landingWhyTitle: "Warum CODE CRISPIES funktioniert",
|
landingWhyTitle: "Warum CODE CRISPIES funktioniert",
|
||||||
@@ -304,7 +311,7 @@ const translations = {
|
|||||||
comingSoon: "Bald verfügbar",
|
comingSoon: "Bald verfügbar",
|
||||||
landingCtaTitle: "Heute noch anfangen",
|
landingCtaTitle: "Heute noch anfangen",
|
||||||
landingCtaSub: "Kostenlos und Open Source. Kein Konto erforderlich. Fortschritt wird lokal gespeichert.",
|
landingCtaSub: "Kostenlos und Open Source. Kein Konto erforderlich. Fortschritt wird lokal gespeichert.",
|
||||||
landingCtaButton: "Jetzt erste Schritte machen",
|
landingCtaButton: "Let's get crispy!",
|
||||||
|
|
||||||
// Coming Soon
|
// Coming Soon
|
||||||
landingComingSoonTitle: "Demnächst",
|
landingComingSoonTitle: "Demnächst",
|
||||||
@@ -317,6 +324,13 @@ const translations = {
|
|||||||
comingSoonFrameworksTitle: "Frameworks",
|
comingSoonFrameworksTitle: "Frameworks",
|
||||||
comingSoonFrameworksText: "React, Vue und Svelte Grundlagen. Baue echte Komponenten Schritt für Schritt.",
|
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
|
// Device Notice
|
||||||
deviceNotice: "<strong>Am besten auf Desktop oder Tablet (Querformat).</strong> Mobil funktioniert, aber größere Bildschirme machen das Coden einfacher.",
|
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
|
// Landing page
|
||||||
landingHeroTitle: "Naucz się tworzenia stron",
|
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.",
|
landingHeroSubtitle: "Opanuj HTML, CSS i Tailwind poprzez praktyczne ćwiczenia z natychmiastową informacją zwrotną. Darmowe i open source.",
|
||||||
landingCtaStart: "Zacznij TERAZ",
|
landingCtaStart: "Zacznij TERAZ",
|
||||||
landingWhyTitle: "Dlaczego CODE CRISPIES działa",
|
landingWhyTitle: "Dlaczego CODE CRISPIES działa",
|
||||||
@@ -471,7 +485,7 @@ const translations = {
|
|||||||
comingSoon: "Wkrótce",
|
comingSoon: "Wkrótce",
|
||||||
landingCtaTitle: "Zacznij naukę już dziś",
|
landingCtaTitle: "Zacznij naukę już dziś",
|
||||||
landingCtaSub: "Darmowe i open source. Bez konta. Postęp zapisywany lokalnie.",
|
landingCtaSub: "Darmowe i open source. Bez konta. Postęp zapisywany lokalnie.",
|
||||||
landingCtaButton: "Rozpocznij swoją podróż",
|
landingCtaButton: "Let's get crispy!",
|
||||||
|
|
||||||
// Coming Soon
|
// Coming Soon
|
||||||
landingComingSoonTitle: "Wkrótce",
|
landingComingSoonTitle: "Wkrótce",
|
||||||
@@ -484,6 +498,13 @@ const translations = {
|
|||||||
comingSoonFrameworksTitle: "Frameworki",
|
comingSoonFrameworksTitle: "Frameworki",
|
||||||
comingSoonFrameworksText: "Podstawy React, Vue i Svelte. Buduj prawdziwe komponenty krok po kroku.",
|
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
|
// Device Notice
|
||||||
deviceNotice: "<strong>Najlepiej na komputerze lub tablecie (poziomo).</strong> Na telefonie też działa, ale większy ekran ułatwia kodowanie.",
|
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
|
// Landing page
|
||||||
landingHeroTitle: "Aprende desarrollo web",
|
landingHeroTitle: "Aprende desarrollo web",
|
||||||
landingHeroHighlight: "Escribiendo código real",
|
landingHeroHighlight: "Crispy Code",
|
||||||
landingHeroSubtitle:
|
landingHeroSubtitle:
|
||||||
"Domina HTML, CSS y Tailwind a través de ejercicios prácticos con retroalimentación instantánea. Gratis y de código abierto.",
|
"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",
|
landingCtaStart: "Empieza AHORA",
|
||||||
@@ -640,7 +661,7 @@ const translations = {
|
|||||||
comingSoon: "Próximamente",
|
comingSoon: "Próximamente",
|
||||||
landingCtaTitle: "Empieza a aprender hoy",
|
landingCtaTitle: "Empieza a aprender hoy",
|
||||||
landingCtaSub: "Gratis y de código abierto. Sin cuenta requerida. Progreso guardado localmente.",
|
landingCtaSub: "Gratis y de código abierto. Sin cuenta requerida. Progreso guardado localmente.",
|
||||||
landingCtaButton: "Comienza tu viaje",
|
landingCtaButton: "Let's get crispy!",
|
||||||
|
|
||||||
// Coming Soon
|
// Coming Soon
|
||||||
landingComingSoonTitle: "Próximamente",
|
landingComingSoonTitle: "Próximamente",
|
||||||
@@ -653,6 +674,13 @@ const translations = {
|
|||||||
comingSoonFrameworksTitle: "Frameworks",
|
comingSoonFrameworksTitle: "Frameworks",
|
||||||
comingSoonFrameworksText: "Fundamentos de React, Vue y Svelte. Construye componentes reales paso a paso.",
|
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
|
// Device Notice
|
||||||
deviceNotice: "<strong>Mejor en escritorio o tablet (horizontal).</strong> Funciona en móvil, pero pantallas más grandes facilitan la programación.",
|
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
|
// Landing page
|
||||||
landingHeroTitle: "تعلم تطوير الويب",
|
landingHeroTitle: "تعلم تطوير الويب",
|
||||||
landingHeroHighlight: "بكتابة كود حقيقي",
|
landingHeroHighlight: "Crispy Code",
|
||||||
landingHeroSubtitle: "أتقن HTML و CSS و Tailwind من خلال تمارين عملية مع ملاحظات فورية. مجاني ومفتوح المصدر.",
|
landingHeroSubtitle: "أتقن HTML و CSS و Tailwind من خلال تمارين عملية مع ملاحظات فورية. مجاني ومفتوح المصدر.",
|
||||||
landingCtaStart: "ابدأ الآن",
|
landingCtaStart: "ابدأ الآن",
|
||||||
landingWhyTitle: "لماذا CODE CRISPIES فعال",
|
landingWhyTitle: "لماذا CODE CRISPIES فعال",
|
||||||
@@ -804,7 +832,7 @@ const translations = {
|
|||||||
comingSoon: "قريباً",
|
comingSoon: "قريباً",
|
||||||
landingCtaTitle: "ابدأ التعلم اليوم",
|
landingCtaTitle: "ابدأ التعلم اليوم",
|
||||||
landingCtaSub: "مجاني ومفتوح المصدر. لا حاجة لحساب. يُحفظ التقدم محليًا.",
|
landingCtaSub: "مجاني ومفتوح المصدر. لا حاجة لحساب. يُحفظ التقدم محليًا.",
|
||||||
landingCtaButton: "ابدأ رحلتك",
|
landingCtaButton: "Let's get crispy!",
|
||||||
|
|
||||||
// Coming Soon
|
// Coming Soon
|
||||||
landingComingSoonTitle: "قريباً",
|
landingComingSoonTitle: "قريباً",
|
||||||
@@ -817,6 +845,13 @@ const translations = {
|
|||||||
comingSoonFrameworksTitle: "أطر العمل",
|
comingSoonFrameworksTitle: "أطر العمل",
|
||||||
comingSoonFrameworksText: "أساسيات React وVue وSvelte. ابنِ مكونات حقيقية خطوة بخطوة.",
|
comingSoonFrameworksText: "أساسيات React وVue وSvelte. ابنِ مكونات حقيقية خطوة بخطوة.",
|
||||||
|
|
||||||
|
// Newsletter
|
||||||
|
newsletterText: "هل تريد معرفة متى تُطلق ميزات جديدة؟",
|
||||||
|
newsletterPlaceholder: "بريدك@email.com",
|
||||||
|
newsletterButton: "أبلغني",
|
||||||
|
newsletterThanks: "شكراً! سنبقيك على اطلاع.",
|
||||||
|
newsletterDisclaimer: "مرة واحدة أسبوعياً كحد أقصى. إلغاء الاشتراك في أي وقت عبر mail@codecrispi.es",
|
||||||
|
|
||||||
// Device Notice
|
// Device Notice
|
||||||
deviceNotice: "<strong>أفضل على الكمبيوتر أو الجهاز اللوحي (أفقي).</strong> يعمل على الجوال، لكن الشاشات الأكبر تسهّل البرمجة.",
|
deviceNotice: "<strong>أفضل على الكمبيوتر أو الجهاز اللوحي (أفقي).</strong> يعمل على الجوال، لكن الشاشات الأكبر تسهّل البرمجة.",
|
||||||
|
|
||||||
@@ -950,7 +985,7 @@ const translations = {
|
|||||||
|
|
||||||
// Landing page
|
// Landing page
|
||||||
landingHeroTitle: "Вивчай веб-розробку",
|
landingHeroTitle: "Вивчай веб-розробку",
|
||||||
landingHeroHighlight: "Пишучи справжній код",
|
landingHeroHighlight: "Crispy Code",
|
||||||
landingHeroSubtitle: "Опануй HTML, CSS та Tailwind через практичні вправи з миттєвим зворотним зв'язком. Безкоштовно та з відкритим кодом.",
|
landingHeroSubtitle: "Опануй HTML, CSS та Tailwind через практичні вправи з миттєвим зворотним зв'язком. Безкоштовно та з відкритим кодом.",
|
||||||
landingCtaStart: "Почни ЗАРАЗ",
|
landingCtaStart: "Почни ЗАРАЗ",
|
||||||
landingWhyTitle: "Чому CODE CRISPIES працює",
|
landingWhyTitle: "Чому CODE CRISPIES працює",
|
||||||
@@ -970,7 +1005,7 @@ const translations = {
|
|||||||
comingSoon: "Незабаром",
|
comingSoon: "Незабаром",
|
||||||
landingCtaTitle: "Почни вчитися сьогодні",
|
landingCtaTitle: "Почни вчитися сьогодні",
|
||||||
landingCtaSub: "Безкоштовно та з відкритим кодом. Без реєстрації. Прогрес зберігається локально.",
|
landingCtaSub: "Безкоштовно та з відкритим кодом. Без реєстрації. Прогрес зберігається локально.",
|
||||||
landingCtaButton: "Розпочни свою подорож",
|
landingCtaButton: "Let's get crispy!",
|
||||||
|
|
||||||
// Coming Soon
|
// Coming Soon
|
||||||
landingComingSoonTitle: "Незабаром",
|
landingComingSoonTitle: "Незабаром",
|
||||||
@@ -983,6 +1018,13 @@ const translations = {
|
|||||||
comingSoonFrameworksTitle: "Фреймворки",
|
comingSoonFrameworksTitle: "Фреймворки",
|
||||||
comingSoonFrameworksText: "Основи React, Vue та Svelte. Створюй справжні компоненти крок за кроком.",
|
comingSoonFrameworksText: "Основи React, Vue та Svelte. Створюй справжні компоненти крок за кроком.",
|
||||||
|
|
||||||
|
// Newsletter
|
||||||
|
newsletterText: "Хочете дізнатися, коли з'являться нові функції?",
|
||||||
|
newsletterPlaceholder: "ваш@email.com",
|
||||||
|
newsletterButton: "Повідомити мене",
|
||||||
|
newsletterThanks: "Дякуємо! Ми будемо тримати вас в курсі.",
|
||||||
|
newsletterDisclaimer: "Максимум раз на тиждень. Відписатися можна будь-коли через mail@codecrispi.es",
|
||||||
|
|
||||||
// Device Notice
|
// Device Notice
|
||||||
deviceNotice: "<strong>Найкраще на комп'ютері або планшеті (горизонтально).</strong> На телефоні теж працює, але більший екран полегшує програмування.",
|
deviceNotice: "<strong>Найкраще на комп'ютері або планшеті (горизонтально).</strong> На телефоні теж працює, але більший екран полегшує програмування.",
|
||||||
|
|
||||||
|
|||||||
@@ -173,37 +173,54 @@
|
|||||||
<h2 data-i18n="landingComingSoonTitle">Coming Soon</h2>
|
<h2 data-i18n="landingComingSoonTitle">Coming Soon</h2>
|
||||||
<div class="coming-soon-grid">
|
<div class="coming-soon-grid">
|
||||||
<article class="coming-soon-card">
|
<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>
|
<h3 data-i18n="comingSoonSyncTitle">Cloud Sync</h3>
|
||||||
<p data-i18n="comingSoonSyncText">Sync your progress across all devices. Start on desktop, continue on tablet.</p>
|
<p data-i18n="comingSoonSyncText">Sync your progress across all devices. Start on desktop, continue on tablet.</p>
|
||||||
</article>
|
</article>
|
||||||
<article class="coming-soon-card">
|
<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>
|
<h3 data-i18n="comingSoonAchievementsTitle">Achievements</h3>
|
||||||
<p data-i18n="comingSoonAchievementsText">Earn badges as you master new skills. Track your learning milestones.</p>
|
<p data-i18n="comingSoonAchievementsText">Earn badges as you master new skills. Track your learning milestones.</p>
|
||||||
</article>
|
</article>
|
||||||
<article class="coming-soon-card">
|
<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>
|
<h3 data-i18n="comingSoonJsTitle">JavaScript</h3>
|
||||||
<p data-i18n="comingSoonJsText">Interactive JavaScript lessons with live code execution and DOM manipulation.</p>
|
<p data-i18n="comingSoonJsText">Interactive JavaScript lessons with live code execution and DOM manipulation.</p>
|
||||||
</article>
|
</article>
|
||||||
<article class="coming-soon-card">
|
<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>
|
<h3 data-i18n="comingSoonFrameworksTitle">Frameworks</h3>
|
||||||
<p data-i18n="comingSoonFrameworksText">React, Vue, and Svelte basics. Build real components step by step.</p>
|
<p data-i18n="comingSoonFrameworksText">React, Vue, and Svelte basics. Build real components step by step.</p>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<section class="device-notice">
|
<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.
|
<strong>Best on desktop or tablet (landscape).</strong> Mobile works, but larger screens make coding easier.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="landing-cta">
|
<section class="landing-cta">
|
||||||
<h2 data-i18n="landingCtaTitle">Start Learning Today</h2>
|
<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>
|
<p class="cta-sub" data-i18n="landingCtaSub">Free and open source. No account required. Progress saved locally.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
72
src/main.css
72
src/main.css
@@ -1924,11 +1924,16 @@ input:checked + .toggle-slider::before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.coming-soon-icon {
|
.coming-soon-icon {
|
||||||
font-size: 2rem;
|
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.coming-soon-icon svg {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
stroke: var(--section-color);
|
||||||
|
}
|
||||||
|
|
||||||
.coming-soon-card h3 {
|
.coming-soon-card h3 {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
margin-bottom: 0.5rem;
|
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 */
|
||||||
.device-notice {
|
.device-notice {
|
||||||
margin-top: var(--spacing-lg);
|
margin-top: var(--spacing-lg);
|
||||||
|
|||||||
Reference in New Issue
Block a user