feat: add welcome lesson, DVD bounce animation, mobile layout reorder
Some checks failed
Deploy static content to Pages / deploy (push) Has been cancelled

- Add Welcome module with intro lessons (EN/DE)
- Success message now bounces like DVD screensaver (10s duration)
- Mobile: nav bar at top, preview before editor
- Logo: CODE with purple background pill
This commit is contained in:
2025-12-30 21:44:37 +01:00
parent 54719f0df7
commit 130b0dbdc7
5 changed files with 270 additions and 26 deletions

111
lessons/00-welcome.json Normal file
View File

@@ -0,0 +1,111 @@
{
"$schema": "../schemas/code-crispies-module-schema.json",
"id": "welcome",
"title": "Welcome",
"description": "Learn how to use Code Crispies and get started with interactive web development lessons",
"mode": "html",
"difficulty": "beginner",
"lessons": [
{
"id": "what-is-code-crispies",
"title": "What is Code Crispies?",
"description": "<strong>Code Crispies</strong> is a free, open-source platform for learning web development through hands-on exercises.<br><br>You'll learn:<br>- <strong>HTML</strong> - Semantic markup and native elements<br>- <strong>CSS</strong> - Styling and layout techniques<br>- <strong>Tailwind</strong> - Utility-first CSS framework<br><br>No account required - just start coding!",
"task": "Try it out! Type <kbd>&lt;h1&gt;Hello World&lt;/h1&gt;</kbd> in the editor below and watch the preview update.",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }",
"sandboxCSS": "",
"initialCode": "",
"solution": "<h1>Hello World</h1>",
"previewContainer": "preview-area",
"validations": [
{
"type": "element_exists",
"value": "h1",
"message": "Add an <kbd>&lt;h1&gt;</kbd> heading element"
},
{
"type": "contains",
"value": "Hello",
"message": "Add the text 'Hello' inside your heading"
}
]
},
{
"id": "how-lessons-work",
"title": "How Lessons Work",
"description": "Each lesson has:<br><br><strong>1. Instructions</strong> - Read the task on the left<br><strong>2. Editor</strong> - Write your code below the instructions<br><strong>3. Preview</strong> - See results instantly on the right<br><strong>4. Hints</strong> - Get feedback when you run your code<br><br>When all validations pass, you'll see a success message!",
"task": "Create a paragraph with the text 'I am learning!' using <kbd>&lt;p&gt;</kbd> tags.",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; } p { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 8px; text-align: center; font-size: 1.2em; }",
"sandboxCSS": "",
"initialCode": "",
"solution": "<p>I am learning!</p>",
"previewContainer": "preview-area",
"validations": [
{
"type": "element_exists",
"value": "p",
"message": "Add a <kbd>&lt;p&gt;</kbd> paragraph element"
},
{
"type": "contains",
"value": "learning",
"message": "Include the word 'learning' in your paragraph"
}
]
},
{
"id": "navigation-tips",
"title": "Navigation Tips",
"description": "Use these controls to navigate:<br><br><strong>Menu button (top left)</strong> - Browse all lesson modules<br><strong>Previous / Next buttons</strong> - Move between lessons<br><strong>Show Expected</strong> - See the target result<br><strong>Reset button</strong> - Start the lesson over<br><br><strong>Keyboard shortcuts:</strong><br><kbd>Ctrl+Enter</kbd> - Run your code<br><kbd>Ctrl+Z</kbd> - Undo<br><kbd>Ctrl+Shift+Z</kbd> - Redo",
"task": "Create a button element with the text 'Click me!' using <kbd>&lt;button&gt;</kbd> tags.",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; display: flex; justify-content: center; align-items: center; min-height: 80px; } button { background: #6366f1; color: white; border: none; padding: 12px 24px; border-radius: 6px; font-size: 1em; cursor: pointer; transition: transform 0.2s; } button:hover { transform: scale(1.05); }",
"sandboxCSS": "",
"initialCode": "",
"solution": "<button>Click me!</button>",
"previewContainer": "preview-area",
"validations": [
{
"type": "element_exists",
"value": "button",
"message": "Add a <kbd>&lt;button&gt;</kbd> element"
},
{
"type": "contains",
"value": "Click",
"message": "Add the text 'Click me!' inside the button"
}
]
},
{
"id": "ready-to-learn",
"title": "Ready to Learn!",
"description": "You're all set! Here's what to explore next:<br><br><strong>HTML Modules</strong> - Learn semantic elements, forms, tables<br><strong>CSS Modules</strong> - Master selectors, box model, flexbox<br><strong>Advanced Topics</strong> - Animations, responsive design<br><br>Open the menu to browse all available modules. Your progress is saved automatically!",
"task": "Create a simple card with a heading and paragraph:<br>1. Add <kbd>&lt;h2&gt;</kbd> with 'Ready!'<br>2. Add <kbd>&lt;p&gt;</kbd> with 'Let's start learning.'",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; } h2, p { background: white; margin: 0; } h2 { color: #6366f1; padding: 20px 20px 10px; border-radius: 8px 8px 0 0; border: 2px solid #e5e7eb; border-bottom: none; } p { padding: 10px 20px 20px; border-radius: 0 0 8px 8px; border: 2px solid #e5e7eb; border-top: none; color: #4b5563; }",
"sandboxCSS": "",
"initialCode": "",
"solution": "<h2>Ready!</h2>\n<p>Let's start learning.</p>",
"previewContainer": "preview-area",
"validations": [
{
"type": "element_exists",
"value": "h2",
"message": "Add an <kbd>&lt;h2&gt;</kbd> heading"
},
{
"type": "element_exists",
"value": "p",
"message": "Add a <kbd>&lt;p&gt;</kbd> paragraph"
},
{
"type": "contains",
"value": "Ready",
"message": "Include 'Ready!' in your heading"
}
]
}
]
}

111
lessons/de/00-welcome.json Normal file
View File

@@ -0,0 +1,111 @@
{
"$schema": "../../schemas/code-crispies-module-schema.json",
"id": "welcome",
"title": "Willkommen",
"description": "Lerne, wie du Code Crispies nutzt und starte mit interaktiven Webentwicklungs-Lektionen",
"mode": "html",
"difficulty": "beginner",
"lessons": [
{
"id": "what-is-code-crispies",
"title": "Was ist Code Crispies?",
"description": "<strong>Code Crispies</strong> ist eine kostenlose Open-Source-Plattform zum Erlernen von Webentwicklung durch praktische Übungen.<br><br>Du lernst:<br>- <strong>HTML</strong> - Semantisches Markup und native Elemente<br>- <strong>CSS</strong> - Styling und Layout-Techniken<br>- <strong>Tailwind</strong> - Utility-First CSS Framework<br><br>Kein Konto erforderlich - einfach loslegen!",
"task": "Probiere es aus! Tippe <kbd>&lt;h1&gt;Hallo Welt&lt;/h1&gt;</kbd> in den Editor und beobachte die Vorschau.",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }",
"sandboxCSS": "",
"initialCode": "",
"solution": "<h1>Hallo Welt</h1>",
"previewContainer": "preview-area",
"validations": [
{
"type": "element_exists",
"value": "h1",
"message": "Füge ein <kbd>&lt;h1&gt;</kbd> Überschrift-Element hinzu"
},
{
"type": "contains",
"value": "Hallo",
"message": "Füge den Text 'Hallo' in deine Überschrift ein"
}
]
},
{
"id": "how-lessons-work",
"title": "Wie Lektionen funktionieren",
"description": "Jede Lektion hat:<br><br><strong>1. Anweisungen</strong> - Lies die Aufgabe links<br><strong>2. Editor</strong> - Schreibe deinen Code unter den Anweisungen<br><strong>3. Vorschau</strong> - Sieh Ergebnisse sofort rechts<br><strong>4. Hinweise</strong> - Bekomme Feedback beim Ausführen<br><br>Wenn alle Validierungen bestanden sind, siehst du eine Erfolgsmeldung!",
"task": "Erstelle einen Absatz mit dem Text 'Ich lerne!' mit <kbd>&lt;p&gt;</kbd> Tags.",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; } p { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 8px; text-align: center; font-size: 1.2em; }",
"sandboxCSS": "",
"initialCode": "",
"solution": "<p>Ich lerne!</p>",
"previewContainer": "preview-area",
"validations": [
{
"type": "element_exists",
"value": "p",
"message": "Füge ein <kbd>&lt;p&gt;</kbd> Absatz-Element hinzu"
},
{
"type": "contains",
"value": "lerne",
"message": "Füge das Wort 'lerne' in deinen Absatz ein"
}
]
},
{
"id": "navigation-tips",
"title": "Navigations-Tipps",
"description": "Nutze diese Steuerungen zur Navigation:<br><br><strong>Menü-Button (oben links)</strong> - Alle Lektionsmodule durchsuchen<br><strong>Zurück / Weiter Buttons</strong> - Zwischen Lektionen wechseln<br><strong>Lösung zeigen</strong> - Das Zielergebnis anzeigen<br><strong>Zurücksetzen</strong> - Lektion neu starten<br><br><strong>Tastenkürzel:</strong><br><kbd>Strg+Enter</kbd> - Code ausführen<br><kbd>Strg+Z</kbd> - Rückgängig<br><kbd>Strg+Umschalt+Z</kbd> - Wiederholen",
"task": "Erstelle ein Button-Element mit dem Text 'Klick mich!' mit <kbd>&lt;button&gt;</kbd> Tags.",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; display: flex; justify-content: center; align-items: center; min-height: 80px; } button { background: #6366f1; color: white; border: none; padding: 12px 24px; border-radius: 6px; font-size: 1em; cursor: pointer; transition: transform 0.2s; } button:hover { transform: scale(1.05); }",
"sandboxCSS": "",
"initialCode": "",
"solution": "<button>Klick mich!</button>",
"previewContainer": "preview-area",
"validations": [
{
"type": "element_exists",
"value": "button",
"message": "Füge ein <kbd>&lt;button&gt;</kbd> Element hinzu"
},
{
"type": "contains",
"value": "Klick",
"message": "Füge den Text 'Klick mich!' in den Button ein"
}
]
},
{
"id": "ready-to-learn",
"title": "Bereit zum Lernen!",
"description": "Du bist startklar! Hier ist, was du als nächstes erkunden kannst:<br><br><strong>HTML Module</strong> - Lerne semantische Elemente, Formulare, Tabellen<br><strong>CSS Module</strong> - Meistere Selektoren, Box-Model, Flexbox<br><strong>Fortgeschrittene Themen</strong> - Animationen, Responsives Design<br><br>Öffne das Menü, um alle verfügbaren Module zu durchsuchen. Dein Fortschritt wird automatisch gespeichert!",
"task": "Erstelle eine einfache Karte mit Überschrift und Absatz:<br>1. Füge <kbd>&lt;h2&gt;</kbd> mit 'Fertig!' hinzu<br>2. Füge <kbd>&lt;p&gt;</kbd> mit 'Los geht's!' hinzu",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; } h2, p { background: white; margin: 0; } h2 { color: #6366f1; padding: 20px 20px 10px; border-radius: 8px 8px 0 0; border: 2px solid #e5e7eb; border-bottom: none; } p { padding: 10px 20px 20px; border-radius: 0 0 8px 8px; border: 2px solid #e5e7eb; border-top: none; color: #4b5563; }",
"sandboxCSS": "",
"initialCode": "",
"solution": "<h2>Fertig!</h2>\n<p>Los geht's!</p>",
"previewContainer": "preview-area",
"validations": [
{
"type": "element_exists",
"value": "h2",
"message": "Füge eine <kbd>&lt;h2&gt;</kbd> Überschrift hinzu"
},
{
"type": "element_exists",
"value": "p",
"message": "Füge einen <kbd>&lt;p&gt;</kbd> Absatz hinzu"
},
{
"type": "contains",
"value": "Fertig",
"message": "Füge 'Fertig!' in deine Überschrift ein"
}
]
}
]
}

View File

@@ -599,11 +599,11 @@ function runCode() {
elements.nextBtn.classList.add("success");
elements.taskInstruction.classList.add("success-instruction");
// Show match animation
// Show match animation (DVD-style bouncing)
elements.previewWrapper?.classList.add("matched");
setTimeout(() => {
elements.previewWrapper?.classList.remove("matched");
}, 2500);
}, 10000);
updateNavigationButtons();
updateProgressDisplay();

View File

@@ -4,6 +4,7 @@
*/
// English lesson imports
import welcomeEN from "../../lessons/00-welcome.json";
import basicSelectorsEN from "../../lessons/00-basic-selectors.json";
import boxModelEN from "../../lessons/01-box-model.json";
import unitsVariablesEN from "../../lessons/05-units-variables.json";
@@ -20,6 +21,7 @@ import htmlSvgEN from "../../lessons/32-html-svg.json";
import flexboxEN from "../../lessons/flexbox.json";
// German lesson imports
import welcomeDE from "../../lessons/de/00-welcome.json";
import basicSelectorsDE from "../../lessons/de/00-basic-selectors.json";
import boxModelDE from "../../lessons/de/01-box-model.json";
import unitsVariablesDE from "../../lessons/de/05-units-variables.json";
@@ -37,6 +39,8 @@ import flexboxDE from "../../lessons/de/flexbox.json";
// English module store - ordered by learning path
const moduleStoreEN = [
// Welcome
welcomeEN,
// HTML Grundlagen
htmlElementsEN,
htmlFormsBasicEN,
@@ -61,6 +65,8 @@ const moduleStoreEN = [
// German module store - ordered by learning path
const moduleStoreDE = [
// Welcome
welcomeDE,
// HTML Grundlagen
htmlElementsDE,
htmlFormsBasicDE,

View File

@@ -589,27 +589,42 @@ code, kbd {
.preview-wrapper.matched::after {
content: "CRISPY! ٩(◕‿◕)۶";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--success-color);
color: white;
padding: var(--spacing-sm) var(--spacing-lg);
border-radius: var(--border-radius-lg);
font-weight: bold;
font-size: 1.1rem;
animation: pop-in 0.4s ease-out;
animation: dvd-bounce 8s ease-in-out infinite;
z-index: 10;
white-space: nowrap;
}
@keyframes pop-in {
@keyframes dvd-bounce {
0% {
transform: translate(-50%, -50%) scale(0.8);
opacity: 0;
top: 10%;
left: 10%;
transform: scale(1);
}
25% {
top: 70%;
left: 80%;
transform: scale(1.1);
}
50% {
top: 20%;
left: 70%;
transform: scale(0.95);
}
75% {
top: 60%;
left: 15%;
transform: scale(1.05);
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
top: 10%;
left: 10%;
transform: scale(1);
}
}
@@ -1111,40 +1126,41 @@ input:checked + .toggle-slider::before {
width: 100%;
flex-shrink: 0;
border-right: none;
display: contents;
}
.left-panel {
min-height: 80vh;
border-bottom: 1px solid var(--border-color);
}
.right-panel {
min-height: 50vh;
/* Mobile order: nav -> instructions -> preview -> editor */
.game-controls {
order: 1;
padding: var(--spacing-sm);
}
.instructions {
order: 2;
max-height: none;
overflow-y: visible;
}
.preview-section {
order: 3;
}
.editor-section {
order: 4;
flex: 1;
min-height: 50vh;
}
.preview-wrapper {
margin: var(--spacing-sm);
min-height: 40vh;
}
.editor-content {
flex: 1;
min-height: 45vh;
}
.preview-wrapper {
margin: var(--spacing-sm);
}
.game-controls {
padding: var(--spacing-sm);
}
.module-pill {
flex: 1;
margin: 0 var(--spacing-sm);