From 0bc6e933905a62ecc9d3da5a7532d6009d2ec72d Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Tue, 6 Jan 2026 15:50:11 +0100 Subject: [PATCH] fix(lessons): use code tags for quoted text, add syntax examples - Fixed validation in welcome lesson (Hello World instead of Hello) - Replaced 'quoted text' with quoted text in all task descriptions - Added syntax examples to Transitions and Keyframes lessons - Updated all language versions (en, de, pl, es, ar, uk) --- lessons/00-welcome.json | 6 +- lessons/06-transitions-animations.json | 4 +- lessons/20-html-elements.json | 2 +- lessons/21-html-forms-basic.json | 6 +- lessons/23-html-details-summary.json | 4 +- lessons/24-html-progress-meter.json | 6 +- lessons/25-html-datalist.json | 4 +- lessons/27-html-dialog.json | 4 +- lessons/28-html-forms-fieldset.json | 6 +- lessons/30-html-tables.json | 6 +- lessons/31-html-marquee.json | 2 +- lessons/ar/00-welcome.json | 8 +- lessons/ar/06-transitions-animations.json | 4 +- lessons/ar/20-html-elements.json | 2 +- lessons/ar/21-html-forms-basic.json | 6 +- lessons/ar/23-html-details-summary.json | 4 +- lessons/ar/24-html-progress-meter.json | 6 +- lessons/ar/25-html-datalist.json | 78 +++++++++++++ lessons/ar/27-html-dialog.json | 83 ++++++++++++++ lessons/ar/28-html-forms-fieldset.json | 127 ++++++++++++++++++++++ lessons/ar/30-html-tables.json | 6 +- lessons/ar/31-html-marquee.json | 2 +- lessons/de/00-welcome.json | 6 +- lessons/de/06-transitions-animations.json | 4 +- lessons/de/20-html-elements.json | 2 +- lessons/de/21-html-forms-basic.json | 8 +- lessons/de/23-html-details-summary.json | 4 +- lessons/de/24-html-progress-meter.json | 6 +- lessons/de/25-html-datalist.json | 4 +- lessons/de/27-html-dialog.json | 4 +- lessons/de/28-html-forms-fieldset.json | 6 +- lessons/de/30-html-tables.json | 6 +- lessons/de/31-html-marquee.json | 2 +- lessons/es/00-welcome.json | 8 +- lessons/es/06-transitions-animations.json | 4 +- lessons/es/20-html-elements.json | 2 +- lessons/es/21-html-forms-basic.json | 6 +- lessons/es/23-html-details-summary.json | 4 +- lessons/es/24-html-progress-meter.json | 6 +- lessons/es/25-html-datalist.json | 78 +++++++++++++ lessons/es/27-html-dialog.json | 83 ++++++++++++++ lessons/es/28-html-forms-fieldset.json | 127 ++++++++++++++++++++++ lessons/es/30-html-tables.json | 6 +- lessons/es/31-html-marquee.json | 2 +- lessons/pl/00-welcome.json | 8 +- lessons/pl/06-transitions-animations.json | 4 +- lessons/pl/20-html-elements.json | 2 +- lessons/pl/21-html-forms-basic.json | 6 +- lessons/pl/23-html-details-summary.json | 4 +- lessons/pl/24-html-progress-meter.json | 6 +- lessons/pl/25-html-datalist.json | 78 +++++++++++++ lessons/pl/27-html-dialog.json | 83 ++++++++++++++ lessons/pl/28-html-forms-fieldset.json | 127 ++++++++++++++++++++++ lessons/pl/30-html-tables.json | 6 +- lessons/pl/31-html-marquee.json | 2 +- lessons/uk/00-welcome.json | 8 +- lessons/uk/06-transitions-animations.json | 4 +- lessons/uk/20-html-elements.json | 2 +- lessons/uk/21-html-forms-basic.json | 6 +- lessons/uk/23-html-details-summary.json | 4 +- lessons/uk/24-html-progress-meter.json | 6 +- lessons/uk/25-html-datalist.json | 78 +++++++++++++ lessons/uk/27-html-dialog.json | 83 ++++++++++++++ lessons/uk/28-html-forms-fieldset.json | 127 ++++++++++++++++++++++ lessons/uk/30-html-tables.json | 6 +- lessons/uk/31-html-marquee.json | 2 +- 66 files changed, 1279 insertions(+), 127 deletions(-) create mode 100644 lessons/ar/25-html-datalist.json create mode 100644 lessons/ar/27-html-dialog.json create mode 100644 lessons/ar/28-html-forms-fieldset.json create mode 100644 lessons/es/25-html-datalist.json create mode 100644 lessons/es/27-html-dialog.json create mode 100644 lessons/es/28-html-forms-fieldset.json create mode 100644 lessons/pl/25-html-datalist.json create mode 100644 lessons/pl/27-html-dialog.json create mode 100644 lessons/pl/28-html-forms-fieldset.json create mode 100644 lessons/uk/25-html-datalist.json create mode 100644 lessons/uk/27-html-dialog.json create mode 100644 lessons/uk/28-html-forms-fieldset.json diff --git a/lessons/00-welcome.json b/lessons/00-welcome.json index 0a0ba73..76a48f5 100644 --- a/lessons/00-welcome.json +++ b/lessons/00-welcome.json @@ -10,7 +10,7 @@ "id": "get-started", "title": "Get Started", "description": "Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required!

What you'll learn:
HTML - Semantic elements, forms, tables, SVG (HTML Block & Inline, HTML Forms, HTML Tables)
CSS - Selectors, box model, flexbox, animations (CSS Selectors, CSS Box Model, CSS Flexbox)
Responsive Design - Media queries and mobile-first layouts

How it works:
1. Read the task in the left panel
2. Write code in the editor
3. See live results in the preview
4. Get instant feedback with hints

Keyboard shortcuts: Ctrl+Z to undo, Ctrl+Shift+Z to redo

More resources:
HTML over JS - Native HTML vs JavaScript solutions
Web Engineering Mandala - JavaScript technology roadmap", - "task": "Hello World", + "task": "Write Hello World", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }", "sandboxCSS": "", @@ -20,8 +20,8 @@ "validations": [ { "type": "contains", - "value": "Hello", - "message": "Write 'Hello World'" + "value": "Hello World", + "message": "Write Hello World" } ] }, diff --git a/lessons/06-transitions-animations.json b/lessons/06-transitions-animations.json index 468e21a..2014250 100644 --- a/lessons/06-transitions-animations.json +++ b/lessons/06-transitions-animations.json @@ -8,7 +8,7 @@ { "id": "transitions-1", "title": "Transitions", - "description": "Learn how to apply transition to properties for smooth changes on state changes.", + "description": "Learn how to apply transition to properties for smooth changes on state changes.

.element {\n  transition: property duration;\n  /* e.g. transition: background-color 0.3s; */\n}
", "task": "Add transition: background-color 0.3s to .btn so the color fades smoothly on hover.", "previewHTML": "", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .btn { background: black; color: white; padding: 0.5rem 1rem; border: none; cursor: pointer; } .btn:hover { background: white; color: black; }", @@ -63,7 +63,7 @@ { "id": "transitions-3", "title": "Keyframes", - "description": "Create named animations using @keyframes and apply them via the animation shorthand.", + "description": "Create named animations using @keyframes and apply them via the animation shorthand.

@keyframes name {\n  0%   { transform: scale(1); }\n  50%  { transform: scale(1.2); }\n  100% { transform: scale(1); }\n}\n.element {\n  animation: name 1s infinite;\n}
", "task": "Define a keyframe at 50% with transform: translateY(-20px) and apply animation: bounce 1s infinite to .ball.", "previewHTML": "
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }", diff --git a/lessons/20-html-elements.json b/lessons/20-html-elements.json index 5afbf44..cf5a67f 100644 --- a/lessons/20-html-elements.json +++ b/lessons/20-html-elements.json @@ -34,7 +34,7 @@ "id": "semantic-containers", "title": "Semantic Tags", "description": "Modern HTML uses semantic containers that describe their content:

<header> - Page or section header
<nav> - Navigation links
<main> - Main content area
<section> - Thematic grouping
<article> - Self-contained content
<footer> - Page or section footer", - "task": "Create a basic page structure:
1. Add a <header> with an <h1> containing the text 'My Website'
2. Add a <main> element with a paragraph saying 'Welcome to my site!'
3. Add a <footer> with a paragraph saying 'Copyright 2025'", + "task": "Create a basic page structure:
1. Add a <header> with an <h1> containing the text My Website
2. Add a <main> element with a paragraph saying Welcome to my site!
3. Add a <footer> with a paragraph saying Copyright 2025", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; margin: 0; } header { background: #1976d2; color: white; padding: 15px; } main { padding: 20px; min-height: 100px; } footer { background: #424242; color: white; padding: 10px; text-align: center; }", "sandboxCSS": "", diff --git a/lessons/21-html-forms-basic.json b/lessons/21-html-forms-basic.json index ea57a5f..2c80946 100644 --- a/lessons/21-html-forms-basic.json +++ b/lessons/21-html-forms-basic.json @@ -10,7 +10,7 @@ "id": "form-structure", "title": "Form Structure", "description": "Every form needs a <form> wrapper. Inside, use <label> to describe inputs and <input> for user data entry.

The for attribute on labels should match the id on inputs for accessibility.", - "task": "Create a form with:
1. A <label> with the text 'Name:' and for=\"name\" attribute
2. A text <input> with id=\"name\" and name=\"name\" attributes", + "task": "Create a form with:
1. A <label> with the text Name: and for=\"name\" attribute
2. A text <input> with id=\"name\" and name=\"name\" attributes", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-bottom: 5px; font-weight: 500; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }", "sandboxCSS": "", @@ -77,8 +77,8 @@ { "id": "submit-button", "title": "Submit Button", - "description": "Forms need a way to submit data. Use:

<button type=\"submit\"> - Preferred, flexible content
<input type=\"submit\"> - Simple text-only button

The button text should be action-oriented (e.g., 'Sign In', 'Register', 'Send').", - "task": "Add a submit button to the form with the text 'Sign In'.", + "description": "Forms need a way to submit data. Use:

<button type=\"submit\"> - Preferred, flexible content
<input type=\"submit\"> - Simple text-only button

The button text should be action-oriented (e.g., Sign In, 'Register', 'Send').", + "task": "Add a submit button to the form with the text Sign In.", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-top: 15px; margin-bottom: 5px; } label:first-child { margin-top: 0; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } button { width: 100%; margin-top: 20px; padding: 10px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background: #1565c0; }", "sandboxCSS": "", diff --git a/lessons/23-html-details-summary.json b/lessons/23-html-details-summary.json index 532eb82..efa6170 100644 --- a/lessons/23-html-details-summary.json +++ b/lessons/23-html-details-summary.json @@ -10,7 +10,7 @@ "id": "details-summary-basic", "title": "First Widget", "description": "The <details> element creates a collapsible section. The <summary> provides the clickable label.

Click the summary to toggle the hidden content - no JavaScript needed!", - "task": "Create a <details> element with:
1. A <summary> saying 'Click to reveal'
2. A <p> with the text 'This content was hidden!'", + "task": "Create a <details> element with:
1. A <summary> saying Click to reveal
2. A <p> with the text This content was hidden!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } details { border: 1px solid #ddd; border-radius: 8px; padding: 15px; } summary { font-weight: 600; cursor: pointer; } summary:hover { color: #1976d2; } details p { margin-top: 15px; color: #666; }", "sandboxCSS": "", @@ -63,7 +63,7 @@ "id": "faq-accordion", "title": "FAQ Accordion", "description": "Multiple <details> elements create an accordion-style FAQ. Each question can be expanded independently.

Pro tip: Type details*3>summary+p and press Tab for Emmet expansion. *3 creates 3 elements, > nests inside, + adds siblings.", - "task": "Create an FAQ section with:
1. An <h1> saying 'Frequently Asked Questions'
2. Three <details> elements, each with a question in <summary> and an answer in <p>", + "task": "Create an FAQ section with:
1. An <h1> saying Frequently Asked Questions
2. Three <details> elements, each with a question in <summary> and an answer in <p>", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; min-height: 100vh; background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); display: flex; flex-direction: column; justify-content: center; padding: 40px; margin: 0; box-sizing: border-box; } h1 { font-size: 2.5rem; color: #4a4a4a; text-align: center; margin: 0 0 30px 0; } details { background: white; border-radius: 12px; margin-bottom: 15px; padding: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } summary { font-size: 1.3rem; font-weight: 600; color: #5c5c5c; cursor: pointer; list-style: none; } summary::before { content: '▸ '; color: #fcb69f; } details[open] summary::before { content: '▾ '; } details p { margin: 15px 0 0 0; color: #666; line-height: 1.6; }", "sandboxCSS": "", diff --git a/lessons/24-html-progress-meter.json b/lessons/24-html-progress-meter.json index d1a7d48..1bf278f 100644 --- a/lessons/24-html-progress-meter.json +++ b/lessons/24-html-progress-meter.json @@ -10,7 +10,7 @@ "id": "progress-basic", "title": "Progress Bars", "description": "The <progress> element shows task completion. Use value for current progress and max for the total.

Note: This is not a self-closing tag! Write <progress>...</progress> with fallback text inside for older browsers.", - "task": "Create a progress bar showing 70% completion:
1. Add a <label> saying 'Download:'
2. Add a <progress> with value=\"70\" and max=\"100\"", + "task": "Create a progress bar showing 70% completion:
1. Add a <label> saying Download:
2. Add a <progress> with value=\"70\" and max=\"100\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } progress { width: 100%; height: 20px; border-radius: 10px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 10px; } progress::-webkit-progress-value { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; } progress::-moz-progress-bar { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "progress-indeterminate", "title": "Indeterminate Progress", "description": "When progress is unknown (like loading), omit the value attribute. This creates an animated indeterminate state.

Useful for network requests or processes with unknown duration.", - "task": "Create a loading indicator:
1. Add a <p> saying 'Loading...'
2. Add a <progress> without a value attribute", + "task": "Create a loading indicator:
1. Add a <p> saying Loading...
2. Add a <progress> without a value attribute", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } p { margin-bottom: 10px; color: #666; } progress { width: 100%; height: 8px; border-radius: 4px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 4px; }", "sandboxCSS": "", @@ -68,7 +68,7 @@ "id": "meter-gauge", "title": "Meter Gauges", "description": "The <meter> element displays a scalar value within a range. Use it for measurements like disk space, battery, or ratings.

Set low, high, and optimum to define good/bad ranges - the browser colors it accordingly!", - "task": "Create a battery level meter:
1. Add a <label> saying 'Battery:'
2. Add a <meter> with:
- value=\"0.8\"
- min=\"0\" and max=\"1\"
- low=\"0.2\" and high=\"0.8\"
- optimum=\"1\"", + "task": "Create a battery level meter:
1. Add a <label> saying Battery:
2. Add a <meter> with:
- value=\"0.8\"
- min=\"0\" and max=\"1\"
- low=\"0.2\" and high=\"0.8\"
- optimum=\"1\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }", "sandboxCSS": "", diff --git a/lessons/25-html-datalist.json b/lessons/25-html-datalist.json index 9dda987..10b71c6 100644 --- a/lessons/25-html-datalist.json +++ b/lessons/25-html-datalist.json @@ -10,7 +10,7 @@ "id": "datalist-basic", "title": "Input with Suggestions", "description": "The <datalist> element provides autocomplete suggestions for inputs. Connect it using the list attribute on the input matching the datalist's id.

Users can still type freely - suggestions are just helpers!", - "task": "Create a browser selector:
1. Add a <label> saying 'Browser:'
2. Add an <input> with list=\"browsers\"
3. Add a <datalist id=\"browsers\"> with options for Chrome, Firefox, and Safari", + "task": "Create a browser selector:
1. Add a <label> saying Browser:
2. Add an <input> with list=\"browsers\"
3. Add a <datalist id=\"browsers\"> with options for Chrome, Firefox, and Safari", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 6px; font-size: 16px; } input:focus { outline: 2px solid #1976d2; border-color: transparent; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "datalist-countries", "title": "Country Selector", "description": "Datalists work great for long lists like countries. Users can type to filter suggestions instantly.

The value attribute is what gets entered, and you can add display text after it.", - "task": "Create a country input:
1. Add a <label> saying 'Country:'
2. Add an <input> with list=\"countries\"
3. Add a <datalist id=\"countries\"> with at least 4 country options", + "task": "Create a country input:
1. Add a <label> saying Country:
2. Add an <input> with list=\"countries\"
3. Add a <datalist id=\"countries\"> with at least 4 country options", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 30px; background: linear-gradient(135deg, #e0f7fa 0%, #b2ebf2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } label { display: block; margin-bottom: 10px; font-weight: 600; color: #00695c; } input { width: 100%; padding: 12px 15px; border: 2px solid #26a69a; border-radius: 8px; font-size: 16px; background: white; } input:focus { outline: none; border-color: #00695c; box-shadow: 0 0 0 3px rgba(38,166,154,0.2); }", "sandboxCSS": "", diff --git a/lessons/27-html-dialog.json b/lessons/27-html-dialog.json index 28b3501..f31ddd0 100644 --- a/lessons/27-html-dialog.json +++ b/lessons/27-html-dialog.json @@ -10,7 +10,7 @@ "id": "dialog-basic", "title": "Open Dialog", "description": "The <dialog> element creates a native modal. Add the open attribute to show it.

Use <form method=\"dialog\"> inside to close it when the form submits - no JavaScript needed!", - "task": "Create a dialog with:
1. The open attribute to show it
2. An <h2> saying 'Welcome!'
3. A <p> with a greeting message
4. A <form method=\"dialog\"> with a close button", + "task": "Create a dialog with:
1. The open attribute to show it
2. An <h2> saying Welcome!
3. A <p> with a greeting message
4. A <form method=\"dialog\"> with a close button", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: #f5f5f5; } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 400px; } dialog::backdrop { background: rgba(0,0,0,0.5); } dialog h2 { margin: 0 0 15px 0; color: #1976d2; } dialog p { color: #666; margin: 0 0 20px 0; } dialog button { background: #1976d2; color: white; border: none; padding: 10px 25px; border-radius: 6px; cursor: pointer; font-size: 16px; } dialog button:hover { background: #1565c0; }", "sandboxCSS": "", @@ -49,7 +49,7 @@ "id": "dialog-form", "title": "Dialog + Form", "description": "Dialogs can contain full forms. The method=\"dialog\" makes the form close the dialog on submit instead of sending data.

This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.", - "task": "Create a confirmation dialog:
1. Add open to show it
2. An <h2> saying 'Confirm Delete'
3. A <p> asking 'Are you sure?'
4. A <form method=\"dialog\"> with Cancel and Delete buttons", + "task": "Create a confirmation dialog:
1. Add open to show it
2. An <h2> saying Confirm Delete
3. A <p> asking Are you sure?
4. A <form method=\"dialog\"> with Cancel and Delete buttons", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: linear-gradient(135deg, #fce4ec 0%, #f8bbd9 100%); } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 350px; text-align: center; } dialog h2 { margin: 0 0 10px 0; color: #c62828; } dialog p { color: #666; margin: 0 0 20px 0; } dialog form { display: flex; gap: 10px; justify-content: center; } dialog button { padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px; border: none; } dialog button:first-child { background: #e0e0e0; color: #333; } dialog button:last-child { background: #c62828; color: white; } dialog button:hover { opacity: 0.9; }", "sandboxCSS": "", diff --git a/lessons/28-html-forms-fieldset.json b/lessons/28-html-forms-fieldset.json index 6cb24ea..24d528b 100644 --- a/lessons/28-html-forms-fieldset.json +++ b/lessons/28-html-forms-fieldset.json @@ -10,7 +10,7 @@ "id": "fieldset-basic", "title": "Grouping with Fieldset", "description": "The <fieldset> element groups related form controls together. Add a <legend> as the first child to give the group a title.

This helps with accessibility and visual organization of complex forms.", - "task": "Create a form with a fieldset:
1. A <form> element
2. A <fieldset> inside
3. A <legend> saying 'Personal Info'
4. Two labeled inputs for name and email", + "task": "Create a form with a fieldset:
1. A <form> element
2. A <fieldset> inside
3. A <legend> saying Personal Info
4. Two labeled inputs for name and email", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f0f4f8; } form { max-width: 400px; } fieldset { border: 2px solid #3498db; border-radius: 10px; padding: 20px; background: white; } legend { color: #3498db; font-weight: 600; padding: 0 10px; font-size: 1.1rem; } label { display: block; margin: 15px 0 5px; color: #333; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; } input:focus { outline: 2px solid #3498db; border-color: transparent; }", "sandboxCSS": "", @@ -49,7 +49,7 @@ "id": "fieldset-textarea", "title": "Adding Textarea", "description": "The <textarea> element creates a multi-line text input, perfect for longer content like messages or descriptions.

Use rows and cols attributes to set default size.", - "task": "Create a contact form:
1. A <fieldset> with <legend> 'Contact Us'
2. A labeled <input> for email
3. A labeled <textarea> for the message
4. A submit <button>", + "task": "Create a contact form:
1. A <fieldset> with <legend> Contact Us
2. A labeled <input> for email
3. A labeled <textarea> for the message
4. A submit <button>", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } form { max-width: 450px; margin: 0 auto; } fieldset { border: none; border-radius: 15px; padding: 30px; background: white; box-shadow: 0 10px 40px rgba(0,0,0,0.2); } legend { color: #667eea; font-weight: 700; padding: 0; font-size: 1.5rem; margin-bottom: 10px; } label { display: block; margin: 20px 0 8px; color: #333; font-weight: 500; } input, textarea { width: 100%; padding: 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: none; border-color: #667eea; } textarea { resize: vertical; min-height: 100px; } button { margin-top: 20px; width: 100%; padding: 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; } button:hover { opacity: 0.9; }", "sandboxCSS": "", @@ -88,7 +88,7 @@ "id": "fieldset-multiple", "title": "Multiple Fieldsets", "description": "Complex forms can use multiple <fieldset> elements to organize different sections.

This improves usability for long forms like registration or checkout pages.", - "task": "Create a registration form with 2 fieldsets:
1. 'Account Info' with username and password inputs
2. 'Preferences' with a textarea for bio
3. A submit button outside the fieldsets", + "task": "Create a registration form with 2 fieldsets:
1. Account Info with username and password inputs
2. Preferences with a textarea for bio
3. A submit button outside the fieldsets", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } form { max-width: 500px; } fieldset { border: 1px solid #ddd; border-radius: 10px; padding: 20px; margin-bottom: 20px; background: white; } legend { color: #2c3e50; font-weight: 600; padding: 0 10px; } label { display: block; margin: 15px 0 5px; color: #555; } input, textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: 2px solid #3498db; border-color: transparent; } textarea { resize: vertical; min-height: 80px; } button { width: 100%; padding: 14px; background: #2c3e50; color: white; border: none; border-radius: 8px; font-size: 16px; cursor: pointer; } button:hover { background: #34495e; }", "sandboxCSS": "", diff --git a/lessons/30-html-tables.json b/lessons/30-html-tables.json index a60d32c..2d0c6d0 100644 --- a/lessons/30-html-tables.json +++ b/lessons/30-html-tables.json @@ -10,7 +10,7 @@ "id": "table-basic", "title": "Basic Table Structure", "description": "Tables use <table> with <tr> for rows. Inside rows, use <th> for headers and <td> for data cells.

The <caption> element provides an accessible title for the table.", - "task": "Create a simple table with:
1. A <caption> saying 'Fruit Prices'
2. A header row with 'Fruit' and 'Price' columns
3. At least 2 data rows", + "task": "Create a simple table with:
1. A <caption> saying Fruit Prices
2. A header row with Fruit and Price columns
3. At least 2 data rows", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f5f5f5; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px; font-weight: 600; font-size: 1.2rem; color: #333; background: #f8f9fa; } th, td { padding: 12px 20px; text-align: left; border-bottom: 1px solid #eee; } th { background: #3498db; color: white; font-weight: 500; } tr:hover { background: #f8f9fa; } tr:last-child td { border-bottom: none; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "table-thead-tbody", "title": "Table Head & Body", "description": "Use <thead> to group header rows and <tbody> to group data rows. This helps browsers and assistive technology understand the table structure.

You can also use <tfoot> for footer rows like totals.", - "task": "Create a structured table:
1. A <caption> with 'Monthly Sales'
2. A <thead> with Month and Revenue headers
3. A <tbody> with at least 2 data rows", + "task": "Create a structured table:
1. A <caption> with Monthly Sales
2. A <thead> with Month and Revenue headers
3. A <tbody> with at least 2 data rows", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } table { border-collapse: collapse; width: 100%; max-width: 450px; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.2); } caption { padding: 20px; font-weight: 700; font-size: 1.3rem; color: white; background: transparent; text-shadow: 0 2px 4px rgba(0,0,0,0.2); caption-side: top; } thead { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } th { padding: 15px 20px; text-align: left; color: white; font-weight: 500; } tbody tr { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } td { padding: 15px 20px; color: #333; } tbody tr:last-child { border-bottom: none; }", "sandboxCSS": "", @@ -83,7 +83,7 @@ "id": "table-complete", "title": "Complete Table with Footer", "description": "Add <tfoot> to create a footer section for totals or summary data. The footer stays at the bottom even if tbody has many rows.

Combine all sections for a fully structured, accessible table.", - "task": "Create a complete table:
1. A <caption> with 'Order Summary'
2. A <thead> with Item and Price headers
3. A <tbody> with 2 items
4. A <tfoot> with a Total row", + "task": "Create a complete table:
1. A <caption> with Order Summary
2. A <thead> with Item and Price headers
3. A <tbody> with 2 items
4. A <tfoot> with a Total row", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px 20px; font-weight: 600; font-size: 1.1rem; color: #333; text-align: left; background: #f8f9fa; border-bottom: 2px solid #eee; } th, td { padding: 12px 20px; text-align: left; } thead th { background: #2c3e50; color: white; } tbody td { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } tfoot { background: #ecf0f1; font-weight: 600; } tfoot td { border-top: 2px solid #2c3e50; color: #2c3e50; }", "sandboxCSS": "", diff --git a/lessons/31-html-marquee.json b/lessons/31-html-marquee.json index 78538ea..29c0d99 100644 --- a/lessons/31-html-marquee.json +++ b/lessons/31-html-marquee.json @@ -10,7 +10,7 @@ "id": "marquee-basic", "title": "Scrolling Text", "description": "The <marquee> element creates scrolling text - a classic from the early web! While deprecated, it still works in most browsers.

Note: For production, use CSS animations instead. But for learning and fun, marquee is great!", - "task": "Create a simple marquee:
1. Add a <marquee> element
2. Put some text inside like 'Welcome to my website!'", + "task": "Create a simple marquee:
1. Add a <marquee> element
2. Put some text inside like Welcome to my website!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%); min-height: 150px; display: flex; align-items: center; } marquee { font-size: 2rem; color: #00ff00; text-shadow: 0 0 10px #00ff00, 0 0 20px #00ff00; font-family: 'Courier New', monospace; }", "sandboxCSS": "", diff --git a/lessons/ar/00-welcome.json b/lessons/ar/00-welcome.json index 4ed3039..c3f0db8 100644 --- a/lessons/ar/00-welcome.json +++ b/lessons/ar/00-welcome.json @@ -10,7 +10,7 @@ "id": "get-started", "title": "Get Started", "description": "Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required!

What you'll learn:
HTML - Semantic elements, forms, tables, SVG (HTML Block & Inline, HTML Forms, HTML Tables)
CSS - Selectors, box model, flexbox, animations (CSS Selectors, CSS Box Model, CSS Flexbox)
Responsive Design - Media queries and mobile-first layouts

How it works:
1. Read the task in the left panel
2. Write code in the editor
3. See live results in the preview
4. Get instant feedback with hints

Keyboard shortcuts: Ctrl+Z to undo, Ctrl+Shift+Z to redo

More resources:
HTML over JS - Native HTML vs JavaScript solutions
Web Engineering Mandala - JavaScript technology roadmap", - "task": "Hello World", + "task": "Write Hello World", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }", "sandboxCSS": "", @@ -20,8 +20,8 @@ "validations": [ { "type": "contains", - "value": "Hello", - "message": "Write 'Hello World'" + "value": "Hello World", + "message": "Write Hello World" } ] }, @@ -39,7 +39,7 @@ "validations": [ { "type": "contains", - "value": "Hello", + "value": "Hello World", "message": "Click Next to continue" } ] diff --git a/lessons/ar/06-transitions-animations.json b/lessons/ar/06-transitions-animations.json index 2697fd7..2c10321 100644 --- a/lessons/ar/06-transitions-animations.json +++ b/lessons/ar/06-transitions-animations.json @@ -8,7 +8,7 @@ { "id": "transitions-1", "title": "Transitions", - "description": "Learn how to apply transition to properties for smooth changes on state changes.", + "description": "Learn how to apply transition to properties for smooth changes on state changes.

.element {\n  transition: property duration;\n  /* e.g. transition: background-color 0.3s; */\n}
", "task": "Add transition: background-color 0.3s to .btn so the color fades smoothly on hover.", "previewHTML": "", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .btn { background: black; color: white; padding: 0.5rem 1rem; border: none; cursor: pointer; } .btn:hover { background: white; color: black; }", @@ -63,7 +63,7 @@ { "id": "transitions-3", "title": "Keyframes", - "description": "Create named animations using @keyframes and apply them via the animation shorthand.", + "description": "Create named animations using @keyframes and apply them via the animation shorthand.

@keyframes name {\n  0%   { transform: scale(1); }\n  50%  { transform: scale(1.2); }\n  100% { transform: scale(1); }\n}\n.element {\n  animation: name 1s infinite;\n}
", "task": "Define a keyframe at 50% with transform: translateY(-20px) and apply animation: bounce 1s infinite to .ball.", "previewHTML": "
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }", diff --git a/lessons/ar/20-html-elements.json b/lessons/ar/20-html-elements.json index 93f9214..97c569d 100644 --- a/lessons/ar/20-html-elements.json +++ b/lessons/ar/20-html-elements.json @@ -34,7 +34,7 @@ "id": "semantic-containers", "title": "Semantic Tags", "description": "Modern HTML uses semantic containers that describe their content:

<header> - Page or section header
<nav> - Navigation links
<main> - Main content area
<section> - Thematic grouping
<article> - Self-contained content
<footer> - Page or section footer", - "task": "Create a basic page structure:
1. Add a <header> with an <h1> containing the text 'My Website'
2. Add a <main> element with a paragraph saying 'Welcome to my site!'
3. Add a <footer> with a paragraph saying 'Copyright 2025'", + "task": "Create a basic page structure:
1. Add a <header> with an <h1> containing the text My Website
2. Add a <main> element with a paragraph saying Welcome to my site!
3. Add a <footer> with a paragraph saying Copyright 2025", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; margin: 0; } header { background: #1976d2; color: white; padding: 15px; } main { padding: 20px; min-height: 100px; } footer { background: #424242; color: white; padding: 10px; text-align: center; }", "sandboxCSS": "", diff --git a/lessons/ar/21-html-forms-basic.json b/lessons/ar/21-html-forms-basic.json index d971cbf..e92962f 100644 --- a/lessons/ar/21-html-forms-basic.json +++ b/lessons/ar/21-html-forms-basic.json @@ -10,7 +10,7 @@ "id": "form-structure", "title": "Form Structure", "description": "Every form needs a <form> wrapper. Inside, use <label> to describe inputs and <input> for user data entry.

The for attribute on labels should match the id on inputs for accessibility.", - "task": "Create a form with:
1. A <label> with the text 'Name:' and for=\"name\" attribute
2. A text <input> with id=\"name\" and name=\"name\" attributes", + "task": "Create a form with:
1. A <label> with the text Name: and for=\"name\" attribute
2. A text <input> with id=\"name\" and name=\"name\" attributes", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-bottom: 5px; font-weight: 500; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }", "sandboxCSS": "", @@ -77,8 +77,8 @@ { "id": "submit-button", "title": "Submit Button", - "description": "Forms need a way to submit data. Use:

<button type=\"submit\"> - Preferred, flexible content
<input type=\"submit\"> - Simple text-only button

The button text should be action-oriented (e.g., 'Sign In', 'Register', 'Send').", - "task": "Add a submit button to the form with the text 'Sign In'.", + "description": "Forms need a way to submit data. Use:

<button type=\"submit\"> - Preferred, flexible content
<input type=\"submit\"> - Simple text-only button

The button text should be action-oriented (e.g., Sign In, 'Register', 'Send').", + "task": "Add a submit button to the form with the text Sign In.", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-top: 15px; margin-bottom: 5px; } label:first-child { margin-top: 0; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } button { width: 100%; margin-top: 20px; padding: 10px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background: #1565c0; }", "sandboxCSS": "", diff --git a/lessons/ar/23-html-details-summary.json b/lessons/ar/23-html-details-summary.json index 3c60ad0..ff83fc3 100644 --- a/lessons/ar/23-html-details-summary.json +++ b/lessons/ar/23-html-details-summary.json @@ -10,7 +10,7 @@ "id": "details-summary-basic", "title": "First Widget", "description": "The <details> element creates a collapsible section. The <summary> provides the clickable label.

Click the summary to toggle the hidden content - no JavaScript needed!", - "task": "Create a <details> element with:
1. A <summary> saying 'Click to reveal'
2. A <p> with the text 'This content was hidden!'", + "task": "Create a <details> element with:
1. A <summary> saying Click to reveal
2. A <p> with the text This content was hidden!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } details { border: 1px solid #ddd; border-radius: 8px; padding: 15px; } summary { font-weight: 600; cursor: pointer; } summary:hover { color: #1976d2; } details p { margin-top: 15px; color: #666; }", "sandboxCSS": "", @@ -63,7 +63,7 @@ "id": "faq-accordion", "title": "FAQ Accordion", "description": "Multiple <details> elements create an accordion-style FAQ. Each question can be expanded independently.

Pro tip: Type details*3>summary+p and press Tab for Emmet expansion. *3 creates 3 elements, > nests inside, + adds siblings.", - "task": "Create an FAQ section with:
1. An <h1> saying 'Frequently Asked Questions'
2. Three <details> elements, each with a question in <summary> and an answer in <p>", + "task": "Create an FAQ section with:
1. An <h1> saying Frequently Asked Questions
2. Three <details> elements, each with a question in <summary> and an answer in <p>", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; min-height: 100vh; background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); display: flex; flex-direction: column; justify-content: center; padding: 40px; margin: 0; box-sizing: border-box; } h1 { font-size: 2.5rem; color: #4a4a4a; text-align: center; margin: 0 0 30px 0; } details { background: white; border-radius: 12px; margin-bottom: 15px; padding: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } summary { font-size: 1.3rem; font-weight: 600; color: #5c5c5c; cursor: pointer; list-style: none; } summary::before { content: '▸ '; color: #fcb69f; } details[open] summary::before { content: '▾ '; } details p { margin: 15px 0 0 0; color: #666; line-height: 1.6; }", "sandboxCSS": "", diff --git a/lessons/ar/24-html-progress-meter.json b/lessons/ar/24-html-progress-meter.json index 28b3bf3..3a89bc3 100644 --- a/lessons/ar/24-html-progress-meter.json +++ b/lessons/ar/24-html-progress-meter.json @@ -10,7 +10,7 @@ "id": "progress-basic", "title": "Progress Bars", "description": "The <progress> element shows task completion. Use value for current progress and max for the total.

Note: This is not a self-closing tag! Write <progress>...</progress> with fallback text inside for older browsers.", - "task": "Create a progress bar showing 70% completion:
1. Add a <label> saying 'Download:'
2. Add a <progress> with value=\"70\" and max=\"100\"", + "task": "Create a progress bar showing 70% completion:
1. Add a <label> saying Download:
2. Add a <progress> with value=\"70\" and max=\"100\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } progress { width: 100%; height: 20px; border-radius: 10px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 10px; } progress::-webkit-progress-value { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; } progress::-moz-progress-bar { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "progress-indeterminate", "title": "Indeterminate Progress", "description": "When progress is unknown (like loading), omit the value attribute. This creates an animated indeterminate state.

Useful for network requests or processes with unknown duration.", - "task": "Create a loading indicator:
1. Add a <p> saying 'Loading...'
2. Add a <progress> without a value attribute", + "task": "Create a loading indicator:
1. Add a <p> saying Loading...
2. Add a <progress> without a value attribute", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } p { margin-bottom: 10px; color: #666; } progress { width: 100%; height: 8px; border-radius: 4px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 4px; }", "sandboxCSS": "", @@ -68,7 +68,7 @@ "id": "meter-gauge", "title": "Meter Gauges", "description": "The <meter> element displays a scalar value within a range. Use it for measurements like disk space, battery, or ratings.

Set low, high, and optimum to define good/bad ranges - the browser colors it accordingly!", - "task": "Create a battery level meter:
1. Add a <label> saying 'Battery:'
2. Add a <meter> with:
- value=\"0.8\"
- min=\"0\" and max=\"1\"
- low=\"0.2\" and high=\"0.8\"
- optimum=\"1\"", + "task": "Create a battery level meter:
1. Add a <label> saying Battery:
2. Add a <meter> with:
- value=\"0.8\"
- min=\"0\" and max=\"1\"
- low=\"0.2\" and high=\"0.8\"
- optimum=\"1\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }", "sandboxCSS": "", diff --git a/lessons/ar/25-html-datalist.json b/lessons/ar/25-html-datalist.json new file mode 100644 index 0000000..8d33f10 --- /dev/null +++ b/lessons/ar/25-html-datalist.json @@ -0,0 +1,78 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-datalist", + "title": "Datalist", + "description": "Provide suggestions for text inputs without JavaScript", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "datalist-basic", + "title": "Input with Suggestions", + "description": "The <datalist> element provides autocomplete suggestions for inputs. Connect it using the list attribute on the input matching the datalist's id.

Users can still type freely - suggestions are just helpers!", + "task": "Create a browser selector:
1. Add a <label> saying Browser:
2. Add an <input> with list=\"browsers\"
3. Add a <datalist id=\"browsers\"> with options for Chrome, Firefox, and Safari", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 6px; font-size: 16px; } input:focus { outline: 2px solid #1976d2; border-color: transparent; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n\n\n ", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "datalist", + "message": "Add a <datalist> element" + }, + { + "type": "attribute_value", + "value": { "selector": "input", "attr": "list", "value": "browsers" }, + "message": "Connect the input to datalist using list=\"browsers\"" + }, + { + "type": "element_count", + "value": { "selector": "option", "min": 3 }, + "message": "Add at least 3 <option> elements inside <datalist>" + }, + { + "type": "element_exists", + "value": "label", + "message": "Add a <label> for the input" + } + ] + }, + { + "id": "datalist-countries", + "title": "Country Selector", + "description": "Datalists work great for long lists like countries. Users can type to filter suggestions instantly.

The value attribute is what gets entered, and you can add display text after it.", + "task": "Create a country input:
1. Add a <label> saying Country:
2. Add an <input> with list=\"countries\"
3. Add a <datalist id=\"countries\"> with at least 4 country options", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 30px; background: linear-gradient(135deg, #e0f7fa 0%, #b2ebf2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } label { display: block; margin-bottom: 10px; font-weight: 600; color: #00695c; } input { width: 100%; padding: 12px 15px; border: 2px solid #26a69a; border-radius: 8px; font-size: 16px; background: white; } input:focus { outline: none; border-color: #00695c; box-shadow: 0 0 0 3px rgba(38,166,154,0.2); }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n\n\n ", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "datalist", + "message": "Add a <datalist> element" + }, + { + "type": "attribute_value", + "value": { "selector": "datalist", "attr": "id", "value": "countries" }, + "message": "Set id=\"countries\" on the datalist" + }, + { + "type": "attribute_value", + "value": { "selector": "input", "attr": "list", "value": "countries" }, + "message": "Connect the input using list=\"countries\"" + }, + { + "type": "element_count", + "value": { "selector": "option", "min": 4 }, + "message": "Add at least 4 country options" + } + ] + } + ] +} diff --git a/lessons/ar/27-html-dialog.json b/lessons/ar/27-html-dialog.json new file mode 100644 index 0000000..4b721c7 --- /dev/null +++ b/lessons/ar/27-html-dialog.json @@ -0,0 +1,83 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-dialog", + "title": "Dialogs", + "description": "Create modal dialogs without JavaScript libraries", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "dialog-basic", + "title": "Open Dialog", + "description": "The <dialog> element creates a native modal. Add the open attribute to show it.

Use <form method=\"dialog\"> inside to close it when the form submits - no JavaScript needed!", + "task": "Create a dialog with:
1. The open attribute to show it
2. An <h2> saying Welcome!
3. A <p> with a greeting message
4. A <form method=\"dialog\"> with a close button", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: #f5f5f5; } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 400px; } dialog::backdrop { background: rgba(0,0,0,0.5); } dialog h2 { margin: 0 0 15px 0; color: #1976d2; } dialog p { color: #666; margin: 0 0 20px 0; } dialog button { background: #1976d2; color: white; border: none; padding: 10px 25px; border-radius: 6px; cursor: pointer; font-size: 16px; } dialog button:hover { background: #1565c0; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n

Welcome!

\n

This is a native HTML dialog element.

\n
\n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "dialog", + "message": "Add a <dialog> element" + }, + { + "type": "attribute_value", + "value": { "selector": "dialog", "attr": "open", "value": true }, + "message": "Add the open attribute to show the dialog" + }, + { + "type": "element_exists", + "value": "dialog h2", + "message": "Add an <h2> heading inside the dialog" + }, + { + "type": "element_exists", + "value": "form[method='dialog']", + "message": "Add a <form method=\"dialog\"> for closing" + }, + { + "type": "element_exists", + "value": "dialog button", + "message": "Add a close button inside the form" + } + ] + }, + { + "id": "dialog-form", + "title": "Dialog + Form", + "description": "Dialogs can contain full forms. The method=\"dialog\" makes the form close the dialog on submit instead of sending data.

This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.", + "task": "Create a confirmation dialog:
1. Add open to show it
2. An <h2> saying Confirm Delete
3. A <p> asking Are you sure?
4. A <form method=\"dialog\"> with Cancel and Delete buttons", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: linear-gradient(135deg, #fce4ec 0%, #f8bbd9 100%); } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 350px; text-align: center; } dialog h2 { margin: 0 0 10px 0; color: #c62828; } dialog p { color: #666; margin: 0 0 20px 0; } dialog form { display: flex; gap: 10px; justify-content: center; } dialog button { padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px; border: none; } dialog button:first-child { background: #e0e0e0; color: #333; } dialog button:last-child { background: #c62828; color: white; } dialog button:hover { opacity: 0.9; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n

Confirm Delete

\n

Are you sure you want to delete this item?

\n
\n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "dialog[open]", + "message": "Add a <dialog> with the open attribute" + }, + { + "type": "element_exists", + "value": "dialog h2", + "message": "Add a heading to the dialog" + }, + { + "type": "element_exists", + "value": "form[method='dialog']", + "message": "Add a <form method=\"dialog\"> for the buttons" + }, + { + "type": "element_count", + "value": { "selector": "dialog button", "min": 2 }, + "message": "Add at least 2 buttons (Cancel and Confirm)" + } + ] + } + ] +} diff --git a/lessons/ar/28-html-forms-fieldset.json b/lessons/ar/28-html-forms-fieldset.json new file mode 100644 index 0000000..add7ef9 --- /dev/null +++ b/lessons/ar/28-html-forms-fieldset.json @@ -0,0 +1,127 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-forms-fieldset", + "title": "Fieldsets", + "description": "Group form controls with fieldset and legend elements", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "fieldset-basic", + "title": "Grouping with Fieldset", + "description": "The <fieldset> element groups related form controls together. Add a <legend> as the first child to give the group a title.

This helps with accessibility and visual organization of complex forms.", + "task": "Create a form with a fieldset:
1. A <form> element
2. A <fieldset> inside
3. A <legend> saying Personal Info
4. Two labeled inputs for name and email", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f0f4f8; } form { max-width: 400px; } fieldset { border: 2px solid #3498db; border-radius: 10px; padding: 20px; background: white; } legend { color: #3498db; font-weight: 600; padding: 0 10px; font-size: 1.1rem; } label { display: block; margin: 15px 0 5px; color: #333; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; } input:focus { outline: 2px solid #3498db; border-color: transparent; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Personal Info\n \n \n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "form", + "message": "Add a <form> element" + }, + { + "type": "element_exists", + "value": "fieldset", + "message": "Add a <fieldset> inside the form" + }, + { + "type": "element_exists", + "value": "legend", + "message": "Add a <legend> to title your fieldset" + }, + { + "type": "element_count", + "value": { "selector": "label", "min": 2 }, + "message": "Add at least 2 labels" + }, + { + "type": "element_count", + "value": { "selector": "input", "min": 2 }, + "message": "Add at least 2 input fields" + } + ] + }, + { + "id": "fieldset-textarea", + "title": "Adding Textarea", + "description": "The <textarea> element creates a multi-line text input, perfect for longer content like messages or descriptions.

Use rows and cols attributes to set default size.", + "task": "Create a contact form:
1. A <fieldset> with <legend> Contact Us
2. A labeled <input> for email
3. A labeled <textarea> for the message
4. A submit <button>", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } form { max-width: 450px; margin: 0 auto; } fieldset { border: none; border-radius: 15px; padding: 30px; background: white; box-shadow: 0 10px 40px rgba(0,0,0,0.2); } legend { color: #667eea; font-weight: 700; padding: 0; font-size: 1.5rem; margin-bottom: 10px; } label { display: block; margin: 20px 0 8px; color: #333; font-weight: 500; } input, textarea { width: 100%; padding: 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: none; border-color: #667eea; } textarea { resize: vertical; min-height: 100px; } button { margin-top: 20px; width: 100%; padding: 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; } button:hover { opacity: 0.9; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Contact Us\n \n \n \n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "fieldset", + "message": "Add a <fieldset> element" + }, + { + "type": "element_exists", + "value": "legend", + "message": "Add a <legend> element" + }, + { + "type": "element_exists", + "value": "textarea", + "message": "Add a <textarea> for the message" + }, + { + "type": "element_exists", + "value": "button", + "message": "Add a submit button" + }, + { + "type": "element_exists", + "value": "input", + "message": "Add an input field for email" + } + ] + }, + { + "id": "fieldset-multiple", + "title": "Multiple Fieldsets", + "description": "Complex forms can use multiple <fieldset> elements to organize different sections.

This improves usability for long forms like registration or checkout pages.", + "task": "Create a registration form with 2 fieldsets:
1. Account Info with username and password inputs
2. Preferences with a textarea for bio
3. A submit button outside the fieldsets", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } form { max-width: 500px; } fieldset { border: 1px solid #ddd; border-radius: 10px; padding: 20px; margin-bottom: 20px; background: white; } legend { color: #2c3e50; font-weight: 600; padding: 0 10px; } label { display: block; margin: 15px 0 5px; color: #555; } input, textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: 2px solid #3498db; border-color: transparent; } textarea { resize: vertical; min-height: 80px; } button { width: 100%; padding: 14px; background: #2c3e50; color: white; border: none; border-radius: 8px; font-size: 16px; cursor: pointer; } button:hover { background: #34495e; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Account Info\n \n \n \n \n
\n
\n Preferences\n \n \n
\n \n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_count", + "value": { "selector": "fieldset", "min": 2 }, + "message": "Create at least 2 fieldsets" + }, + { + "type": "element_count", + "value": { "selector": "legend", "min": 2 }, + "message": "Add a legend to each fieldset" + }, + { + "type": "element_exists", + "value": "textarea", + "message": "Add a textarea for the bio" + }, + { + "type": "element_exists", + "value": "button", + "message": "Add a submit button" + }, + { + "type": "element_count", + "value": { "selector": "input", "min": 2 }, + "message": "Add at least 2 input fields" + } + ] + } + ] +} diff --git a/lessons/ar/30-html-tables.json b/lessons/ar/30-html-tables.json index 1bdfaeb..f4d29cf 100644 --- a/lessons/ar/30-html-tables.json +++ b/lessons/ar/30-html-tables.json @@ -10,7 +10,7 @@ "id": "table-basic", "title": "Basic Table Structure", "description": "Tables use <table> with <tr> for rows. Inside rows, use <th> for headers and <td> for data cells.

The <caption> element provides an accessible title for the table.", - "task": "Create a simple table with:
1. A <caption> saying 'Fruit Prices'
2. A header row with 'Fruit' and 'Price' columns
3. At least 2 data rows", + "task": "Create a simple table with:
1. A <caption> saying Fruit Prices
2. A header row with Fruit and Price columns
3. At least 2 data rows", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f5f5f5; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px; font-weight: 600; font-size: 1.2rem; color: #333; background: #f8f9fa; } th, td { padding: 12px 20px; text-align: left; border-bottom: 1px solid #eee; } th { background: #3498db; color: white; font-weight: 500; } tr:hover { background: #f8f9fa; } tr:last-child td { border-bottom: none; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "table-thead-tbody", "title": "Table Head & Body", "description": "Use <thead> to group header rows and <tbody> to group data rows. This helps browsers and assistive technology understand the table structure.

You can also use <tfoot> for footer rows like totals.", - "task": "Create a structured table:
1. A <caption> with 'Monthly Sales'
2. A <thead> with Month and Revenue headers
3. A <tbody> with at least 2 data rows", + "task": "Create a structured table:
1. A <caption> with Monthly Sales
2. A <thead> with Month and Revenue headers
3. A <tbody> with at least 2 data rows", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } table { border-collapse: collapse; width: 100%; max-width: 450px; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.2); } caption { padding: 20px; font-weight: 700; font-size: 1.3rem; color: white; background: transparent; text-shadow: 0 2px 4px rgba(0,0,0,0.2); caption-side: top; } thead { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } th { padding: 15px 20px; text-align: left; color: white; font-weight: 500; } tbody tr { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } td { padding: 15px 20px; color: #333; } tbody tr:last-child { border-bottom: none; }", "sandboxCSS": "", @@ -83,7 +83,7 @@ "id": "table-complete", "title": "Complete Table with Footer", "description": "Add <tfoot> to create a footer section for totals or summary data. The footer stays at the bottom even if tbody has many rows.

Combine all sections for a fully structured, accessible table.", - "task": "Create a complete table:
1. A <caption> with 'Order Summary'
2. A <thead> with Item and Price headers
3. A <tbody> with 2 items
4. A <tfoot> with a Total row", + "task": "Create a complete table:
1. A <caption> with Order Summary
2. A <thead> with Item and Price headers
3. A <tbody> with 2 items
4. A <tfoot> with a Total row", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px 20px; font-weight: 600; font-size: 1.1rem; color: #333; text-align: left; background: #f8f9fa; border-bottom: 2px solid #eee; } th, td { padding: 12px 20px; text-align: left; } thead th { background: #2c3e50; color: white; } tbody td { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } tfoot { background: #ecf0f1; font-weight: 600; } tfoot td { border-top: 2px solid #2c3e50; color: #2c3e50; }", "sandboxCSS": "", diff --git a/lessons/ar/31-html-marquee.json b/lessons/ar/31-html-marquee.json index 7c2939a..ec3a486 100644 --- a/lessons/ar/31-html-marquee.json +++ b/lessons/ar/31-html-marquee.json @@ -10,7 +10,7 @@ "id": "marquee-basic", "title": "Scrolling Text", "description": "The <marquee> element creates scrolling text - a classic from the early web! While deprecated, it still works in most browsers.

Note: For production, use CSS animations instead. But for learning and fun, marquee is great!", - "task": "Create a simple marquee:
1. Add a <marquee> element
2. Put some text inside like 'Welcome to my website!'", + "task": "Create a simple marquee:
1. Add a <marquee> element
2. Put some text inside like Welcome to my website!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%); min-height: 150px; display: flex; align-items: center; } marquee { font-size: 2rem; color: #00ff00; text-shadow: 0 0 10px #00ff00, 0 0 20px #00ff00; font-family: 'Courier New', monospace; }", "sandboxCSS": "", diff --git a/lessons/de/00-welcome.json b/lessons/de/00-welcome.json index a9d4376..5c43d29 100644 --- a/lessons/de/00-welcome.json +++ b/lessons/de/00-welcome.json @@ -10,7 +10,7 @@ "id": "get-started", "title": "Los geht's", "description": "Code Crispies ist eine kostenlose Open-Source-Plattform zum Erlernen von Webentwicklung durch praktische Übungen. Kein Konto erforderlich!

Was du lernst:
HTML - Semantische Elemente, Formulare, Tabellen, SVG (HTML Block & Inline, HTML Formulare, HTML Tabellen)
CSS - Selektoren, Box-Model, Flexbox, Animationen (CSS Selektoren, CSS Box-Model, CSS Flexbox)
Responsive Design - Media Queries und Mobile-First Layouts

So funktioniert's:
1. Lies die Aufgabe im linken Bereich
2. Schreibe Code im Editor
3. Sieh Live-Ergebnisse in der Vorschau
4. Bekomme sofortiges Feedback mit Hinweisen

Tastenkürzel: Strg+Z rückgängig, Strg+Umschalt+Z wiederholen

Weitere Ressourcen:
HTML over JS - Native HTML vs JavaScript-Lösungen
Web Engineering Mandala - JavaScript Technologie-Roadmap", - "task": "Hello World", + "task": "Schreibe Hello World", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }", "sandboxCSS": "", @@ -20,8 +20,8 @@ "validations": [ { "type": "contains", - "value": "Hello", - "message": "Schreibe 'Hello World'" + "value": "Hello World", + "message": "Schreibe Hello World" } ] }, diff --git a/lessons/de/06-transitions-animations.json b/lessons/de/06-transitions-animations.json index 8f4b78b..ef8bd5e 100644 --- a/lessons/de/06-transitions-animations.json +++ b/lessons/de/06-transitions-animations.json @@ -8,7 +8,7 @@ { "id": "transitions-1", "title": "Einfache Transitions", - "description": "Lerne, wie du transition auf Eigenschaften anwendest für sanfte Änderungen bei Zustandswechseln.", + "description": "Lerne, wie du transition auf Eigenschaften anwendest für sanfte Änderungen bei Zustandswechseln.

.element {\n  transition: property duration;\n  /* z.B. transition: background-color 0.3s; */\n}
", "task": "Füge transition: background-color 0.3s zu .btn hinzu, damit die Farbe beim Hover sanft überblendet.", "previewHTML": "", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .btn { background: #6200ee; color: white; padding: 0.5rem 1rem; border: none; } .btn:hover { background: #3700b3; }", @@ -63,7 +63,7 @@ { "id": "transitions-3", "title": "Keyframe-Animationen Grundlagen", - "description": "Erstelle benannte Animationen mit @keyframes und wende sie mit der animation Kurzschreibweise an.", + "description": "Erstelle benannte Animationen mit @keyframes und wende sie mit der animation Kurzschreibweise an.

@keyframes name {\n  0%   { transform: scale(1); }\n  50%  { transform: scale(1.2); }\n  100% { transform: scale(1); }\n}\n.element {\n  animation: name 1s infinite;\n}
", "task": "Definiere bei 50% ein transform: translateY(-20px) und wende animation: bounce 1s infinite auf .ball an.", "previewHTML": "
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: #ff0266; border-radius: 50%; margin: 2rem auto; }", diff --git a/lessons/de/20-html-elements.json b/lessons/de/20-html-elements.json index 7a53d02..4c60803 100644 --- a/lessons/de/20-html-elements.json +++ b/lessons/de/20-html-elements.json @@ -34,7 +34,7 @@ "id": "semantic-containers", "title": "Semantische Container-Elemente", "description": "Modernes HTML verwendet semantische Container, die ihren Inhalt beschreiben:

<header> - Kopfbereich der Seite oder eines Abschnitts
<nav> - Navigationslinks
<main> - Hauptinhalt
<section> - Thematische Gruppierung
<article> - Eigenständiger Inhalt
<footer> - Fußbereich der Seite oder eines Abschnitts", - "task": "Erstelle eine einfache Seitenstruktur:
1. Füge ein <header> mit einem <h1> hinzu, das den Text 'Meine Webseite' enthält
2. Füge ein <main>-Element mit einem Absatz hinzu, der 'Willkommen auf meiner Seite!' sagt
3. Füge ein <footer> mit einem Absatz hinzu, der 'Copyright 2025' sagt", + "task": "Erstelle eine einfache Seitenstruktur:
1. Füge ein <header> mit einem <h1> hinzu, das den Text Meine Webseite enthält
2. Füge ein <main>-Element mit einem Absatz hinzu, der Willkommen auf meiner Seite! sagt
3. Füge ein <footer> mit einem Absatz hinzu, der Copyright 2025 sagt", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; margin: 0; } header { background: #1976d2; color: white; padding: 15px; } main { padding: 20px; min-height: 100px; } footer { background: #424242; color: white; padding: 10px; text-align: center; }", "sandboxCSS": "", diff --git a/lessons/de/21-html-forms-basic.json b/lessons/de/21-html-forms-basic.json index df53167..0e32d64 100644 --- a/lessons/de/21-html-forms-basic.json +++ b/lessons/de/21-html-forms-basic.json @@ -10,7 +10,7 @@ "id": "form-structure", "title": "Formularstruktur", "description": "Jedes Formular benötigt einen <form>-Wrapper. Innerhalb verwendest du <label> zur Beschreibung der Eingaben und <input> für die Dateneingabe.

Das for-Attribut bei Labels sollte mit der id der Eingaben übereinstimmen für bessere Zugänglichkeit.", - "task": "Erstelle ein Formular mit:
1. Einem <label> mit dem Text 'Name:' und dem for=\"name\"-Attribut
2. Einem Text-<input> mit id=\"name\" und name=\"name\"-Attributen", + "task": "Erstelle ein Formular mit:
1. Einem <label> mit dem Text Name: und dem for=\"name\"-Attribut
2. Einem Text-<input> mit id=\"name\" und name=\"name\"-Attributen", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-bottom: 5px; font-weight: 500; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }", "sandboxCSS": "", @@ -77,8 +77,8 @@ { "id": "submit-button", "title": "Absende-Button", - "description": "Formulare benötigen eine Möglichkeit zum Absenden. Verwende:

<button type=\"submit\"> - Bevorzugt, flexibler Inhalt
<input type=\"submit\"> - Einfacher Text-Button

Der Button-Text sollte handlungsorientiert sein (z.B. 'Anmelden', 'Registrieren', 'Senden').", - "task": "Füge dem Formular einen Absende-Button mit dem Text 'Anmelden' hinzu.", + "description": "Formulare benötigen eine Möglichkeit zum Absenden. Verwende:

<button type=\"submit\"> - Bevorzugt, flexibler Inhalt
<input type=\"submit\"> - Einfacher Text-Button

Der Button-Text sollte handlungsorientiert sein (z.B. Anmelden, 'Registrieren', 'Senden').", + "task": "Füge dem Formular einen Absende-Button mit dem Text Anmelden hinzu.", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-top: 15px; margin-bottom: 5px; } label:first-child { margin-top: 0; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } button { width: 100%; margin-top: 20px; padding: 10px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background: #1565c0; }", "sandboxCSS": "", @@ -94,7 +94,7 @@ { "type": "element_text", "value": { "selector": "button", "text": "Anmelden" }, - "message": "Der Button sollte 'Anmelden' anzeigen" + "message": "Der Button sollte Anmelden anzeigen" } ] } diff --git a/lessons/de/23-html-details-summary.json b/lessons/de/23-html-details-summary.json index e8aaed6..f2c7ee4 100644 --- a/lessons/de/23-html-details-summary.json +++ b/lessons/de/23-html-details-summary.json @@ -10,7 +10,7 @@ "id": "details-summary-basic", "title": "Dein erstes Aufklapp-Element", "description": "Das <details>-Element erstellt einen aufklappbaren Bereich. Das <summary> bietet die anklickbare Beschriftung.

Klicke auf die Zusammenfassung, um den versteckten Inhalt anzuzeigen - kein JavaScript nötig!", - "task": "Erstelle ein <details>-Element mit:
1. Einem <summary> mit dem Text 'Klicken zum Aufklappen'
2. Einem <p> mit dem Text 'Dieser Inhalt war versteckt!'", + "task": "Erstelle ein <details>-Element mit:
1. Einem <summary> mit dem Text Klicken zum Aufklappen
2. Einem <p> mit dem Text Dieser Inhalt war versteckt!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } details { border: 1px solid #ddd; border-radius: 8px; padding: 15px; } summary { font-weight: 600; cursor: pointer; } summary:hover { color: #1976d2; } details p { margin-top: 15px; color: #666; }", "sandboxCSS": "", @@ -63,7 +63,7 @@ "id": "faq-accordion", "title": "FAQ-Akkordeon", "description": "Mehrere <details>-Elemente erstellen ein Akkordeon-artiges FAQ. Jede Frage kann unabhängig aufgeklappt werden.

Pro-Tipp: Tippe details*3>summary+p und drücke Tab für Emmet-Expansion. Das *3 erstellt 3 Elemente, > geht eine Ebene tiefer, + fügt Geschwister hinzu.", - "task": "Erstelle einen FAQ-Bereich mit:
1. Einer <h1> mit dem Text 'Häufig gestellte Fragen'
2. Drei <details>-Elementen, jeweils mit einer Frage im <summary> und einer Antwort im <p>", + "task": "Erstelle einen FAQ-Bereich mit:
1. Einer <h1> mit dem Text Häufig gestellte Fragen
2. Drei <details>-Elementen, jeweils mit einer Frage im <summary> und einer Antwort im <p>", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; min-height: 100vh; background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); display: flex; flex-direction: column; justify-content: center; padding: 40px; margin: 0; box-sizing: border-box; } h1 { font-size: 2.5rem; color: #4a4a4a; text-align: center; margin: 0 0 30px 0; } details { background: white; border-radius: 12px; margin-bottom: 15px; padding: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } summary { font-size: 1.3rem; font-weight: 600; color: #5c5c5c; cursor: pointer; list-style: none; } summary::before { content: '▸ '; color: #fcb69f; } details[open] summary::before { content: '▾ '; } details p { margin: 15px 0 0 0; color: #666; line-height: 1.6; }", "sandboxCSS": "", diff --git a/lessons/de/24-html-progress-meter.json b/lessons/de/24-html-progress-meter.json index a5f781e..bb0fe69 100644 --- a/lessons/de/24-html-progress-meter.json +++ b/lessons/de/24-html-progress-meter.json @@ -10,7 +10,7 @@ "id": "progress-basic", "title": "Fortschrittsbalken", "description": "Das <progress>-Element zeigt den Aufgabenfortschritt. Verwende value für den aktuellen Stand und max für das Maximum.

Wichtig: Dies ist kein selbstschließendes Tag! Schreibe <progress>...</progress> mit einem Fallback-Text dazwischen für ältere Browser.", - "task": "Erstelle einen Fortschrittsbalken mit 70% Fortschritt:
1. Füge ein <label> mit 'Download:' hinzu
2. Füge ein <progress> mit value=\"70\" und max=\"100\" hinzu", + "task": "Erstelle einen Fortschrittsbalken mit 70% Fortschritt:
1. Füge ein <label> mit Download: hinzu
2. Füge ein <progress> mit value=\"70\" und max=\"100\" hinzu", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } progress { width: 100%; height: 20px; border-radius: 10px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 10px; } progress::-webkit-progress-value { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; } progress::-moz-progress-bar { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "progress-indeterminate", "title": "Unbestimmter Fortschritt", "description": "Wenn der Fortschritt unbekannt ist (wie beim Laden), lasse das value-Attribut weg. Dies erstellt einen animierten unbestimmten Zustand.

Nützlich für Netzwerkanfragen oder Prozesse mit unbekannter Dauer.", - "task": "Erstelle eine Ladeanzeige:
1. Füge ein <p> mit 'Lädt...' hinzu
2. Füge ein <progress> ohne value-Attribut hinzu", + "task": "Erstelle eine Ladeanzeige:
1. Füge ein <p> mit Lädt... hinzu
2. Füge ein <progress> ohne value-Attribut hinzu", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } p { margin-bottom: 10px; color: #666; } progress { width: 100%; height: 8px; border-radius: 4px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 4px; }", "sandboxCSS": "", @@ -68,7 +68,7 @@ "id": "meter-gauge", "title": "Meter-Anzeigen", "description": "Das <meter>-Element zeigt einen Skalarwert innerhalb eines Bereichs. Verwende es für Messungen wie Speicherplatz, Akku oder Bewertungen.

Setze low, high und optimum, um gute/schlechte Bereiche zu definieren - der Browser färbt es entsprechend ein!", - "task": "Erstelle eine Akku-Anzeige:
1. Füge ein <label> mit 'Akku:' hinzu
2. Füge ein <meter> hinzu mit:
- value=\"0.8\"
- min=\"0\" und max=\"1\"
- low=\"0.2\" und high=\"0.8\"
- optimum=\"1\"", + "task": "Erstelle eine Akku-Anzeige:
1. Füge ein <label> mit Akku: hinzu
2. Füge ein <meter> hinzu mit:
- value=\"0.8\"
- min=\"0\" und max=\"1\"
- low=\"0.2\" und high=\"0.8\"
- optimum=\"1\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }", "sandboxCSS": "", diff --git a/lessons/de/25-html-datalist.json b/lessons/de/25-html-datalist.json index 8fc2da1..185c7af 100644 --- a/lessons/de/25-html-datalist.json +++ b/lessons/de/25-html-datalist.json @@ -10,7 +10,7 @@ "id": "datalist-basic", "title": "Eingabe mit Vorschlägen", "description": "Das <datalist>-Element bietet Autovervollständigungs-Vorschläge für Eingabefelder. Verbinde es mit dem list-Attribut am Input, das zur id der Datalist passt.

Benutzer können trotzdem frei tippen - Vorschläge sind nur Hilfen!", - "task": "Erstelle eine Browser-Auswahl:
1. Füge ein <label> mit 'Browser:' hinzu
2. Füge ein <input> mit list=\"browsers\" hinzu
3. Füge eine <datalist id=\"browsers\"> mit Optionen für Chrome, Firefox und Safari hinzu", + "task": "Erstelle eine Browser-Auswahl:
1. Füge ein <label> mit Browser: hinzu
2. Füge ein <input> mit list=\"browsers\" hinzu
3. Füge eine <datalist id=\"browsers\"> mit Optionen für Chrome, Firefox und Safari hinzu", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 6px; font-size: 16px; } input:focus { outline: 2px solid #1976d2; border-color: transparent; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "datalist-countries", "title": "Länderauswahl", "description": "Datalists funktionieren super für lange Listen wie Länder. Benutzer können tippen, um Vorschläge sofort zu filtern.

Das value-Attribut ist das, was eingegeben wird, und du kannst Anzeigetext dahinter hinzufügen.", - "task": "Erstelle eine Länder-Eingabe:
1. Füge ein <label> mit 'Land:' hinzu
2. Füge ein <input> mit list=\"countries\" hinzu
3. Füge eine <datalist id=\"countries\"> mit mindestens 4 Länder-Optionen hinzu", + "task": "Erstelle eine Länder-Eingabe:
1. Füge ein <label> mit Land: hinzu
2. Füge ein <input> mit list=\"countries\" hinzu
3. Füge eine <datalist id=\"countries\"> mit mindestens 4 Länder-Optionen hinzu", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 30px; background: linear-gradient(135deg, #e0f7fa 0%, #b2ebf2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } label { display: block; margin-bottom: 10px; font-weight: 600; color: #00695c; } input { width: 100%; padding: 12px 15px; border: 2px solid #26a69a; border-radius: 8px; font-size: 16px; background: white; } input:focus { outline: none; border-color: #00695c; box-shadow: 0 0 0 3px rgba(38,166,154,0.2); }", "sandboxCSS": "", diff --git a/lessons/de/27-html-dialog.json b/lessons/de/27-html-dialog.json index e459b4f..3af172d 100644 --- a/lessons/de/27-html-dialog.json +++ b/lessons/de/27-html-dialog.json @@ -10,7 +10,7 @@ "id": "dialog-basic", "title": "Dialog öffnen", "description": "Das <dialog>-Element erstellt ein natives Modal. Füge das open-Attribut hinzu, um es anzuzeigen.

Verwende <form method=\"dialog\"> darin, um es beim Absenden des Formulars zu schließen - kein JavaScript nötig!", - "task": "Erstelle einen Dialog mit:
1. Dem open-Attribut, um ihn anzuzeigen
2. Einem <h2> mit 'Willkommen!'
3. Einem <p> mit einer Begrüßungsnachricht
4. Einem <form method=\"dialog\"> mit einem Schließen-Button", + "task": "Erstelle einen Dialog mit:
1. Dem open-Attribut, um ihn anzuzeigen
2. Einem <h2> mit Willkommen!
3. Einem <p> mit einer Begrüßungsnachricht
4. Einem <form method=\"dialog\"> mit einem Schließen-Button", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: #f5f5f5; } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 400px; } dialog::backdrop { background: rgba(0,0,0,0.5); } dialog h2 { margin: 0 0 15px 0; color: #1976d2; } dialog p { color: #666; margin: 0 0 20px 0; } dialog button { background: #1976d2; color: white; border: none; padding: 10px 25px; border-radius: 6px; cursor: pointer; font-size: 16px; } dialog button:hover { background: #1565c0; }", "sandboxCSS": "", @@ -49,7 +49,7 @@ "id": "dialog-form", "title": "Dialog mit Formular", "description": "Dialoge können vollständige Formulare enthalten. Das method=\"dialog\" lässt das Formular den Dialog beim Absenden schließen, anstatt Daten zu senden.

Dieses Muster ist perfekt für Bestätigungsdialoge, schnelle Eingaben oder Einstellungspanels.", - "task": "Erstelle einen Bestätigungsdialog:
1. Füge open hinzu, um ihn anzuzeigen
2. Ein <h2> mit 'Löschen bestätigen'
3. Ein <p> mit 'Bist du sicher?'
4. Ein <form method=\"dialog\"> mit Abbrechen- und Löschen-Buttons", + "task": "Erstelle einen Bestätigungsdialog:
1. Füge open hinzu, um ihn anzuzeigen
2. Ein <h2> mit Löschen bestätigen
3. Ein <p> mit Bist du sicher?
4. Ein <form method=\"dialog\"> mit Abbrechen- und Löschen-Buttons", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: linear-gradient(135deg, #fce4ec 0%, #f8bbd9 100%); } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 350px; text-align: center; } dialog h2 { margin: 0 0 10px 0; color: #c62828; } dialog p { color: #666; margin: 0 0 20px 0; } dialog form { display: flex; gap: 10px; justify-content: center; } dialog button { padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px; border: none; } dialog button:first-child { background: #e0e0e0; color: #333; } dialog button:last-child { background: #c62828; color: white; } dialog button:hover { opacity: 0.9; }", "sandboxCSS": "", diff --git a/lessons/de/28-html-forms-fieldset.json b/lessons/de/28-html-forms-fieldset.json index 83ffcea..b9e6222 100644 --- a/lessons/de/28-html-forms-fieldset.json +++ b/lessons/de/28-html-forms-fieldset.json @@ -10,7 +10,7 @@ "id": "fieldset-basic", "title": "Gruppieren mit Fieldset", "description": "Das <fieldset>-Element gruppiert zusammengehörige Formularfelder. Füge ein <legend> als erstes Kind hinzu, um der Gruppe einen Titel zu geben.

Das verbessert die Zugänglichkeit und visuelle Organisation komplexer Formulare.", - "task": "Erstelle ein Formular mit einem Fieldset:
1. Ein <form>-Element
2. Ein <fieldset> darin
3. Ein <legend> mit 'Persönliche Daten'
4. Zwei beschriftete Eingabefelder für Name und E-Mail", + "task": "Erstelle ein Formular mit einem Fieldset:
1. Ein <form>-Element
2. Ein <fieldset> darin
3. Ein <legend> mit Persönliche Daten
4. Zwei beschriftete Eingabefelder für Name und E-Mail", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f0f4f8; } form { max-width: 400px; } fieldset { border: 2px solid #3498db; border-radius: 10px; padding: 20px; background: white; } legend { color: #3498db; font-weight: 600; padding: 0 10px; font-size: 1.1rem; } label { display: block; margin: 15px 0 5px; color: #333; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; } input:focus { outline: 2px solid #3498db; border-color: transparent; }", "sandboxCSS": "", @@ -49,7 +49,7 @@ "id": "fieldset-textarea", "title": "Textarea hinzufügen", "description": "Das <textarea>-Element erstellt ein mehrzeiliges Textfeld, perfekt für längere Inhalte wie Nachrichten oder Beschreibungen.

Verwende rows und cols Attribute, um die Standardgröße festzulegen.", - "task": "Erstelle ein Kontaktformular:
1. Ein <fieldset> mit <legend> 'Kontaktiere uns'
2. Ein beschriftetes <input> für E-Mail
3. Eine beschriftete <textarea> für die Nachricht
4. Einen Absende-<button>", + "task": "Erstelle ein Kontaktformular:
1. Ein <fieldset> mit <legend> Kontaktiere uns
2. Ein beschriftetes <input> für E-Mail
3. Eine beschriftete <textarea> für die Nachricht
4. Einen Absende-<button>", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } form { max-width: 450px; margin: 0 auto; } fieldset { border: none; border-radius: 15px; padding: 30px; background: white; box-shadow: 0 10px 40px rgba(0,0,0,0.2); } legend { color: #667eea; font-weight: 700; padding: 0; font-size: 1.5rem; margin-bottom: 10px; } label { display: block; margin: 20px 0 8px; color: #333; font-weight: 500; } input, textarea { width: 100%; padding: 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: none; border-color: #667eea; } textarea { resize: vertical; min-height: 100px; } button { margin-top: 20px; width: 100%; padding: 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; } button:hover { opacity: 0.9; }", "sandboxCSS": "", @@ -88,7 +88,7 @@ "id": "fieldset-multiple", "title": "Mehrere Fieldsets", "description": "Komplexe Formulare können mehrere <fieldset>-Elemente verwenden, um verschiedene Abschnitte zu organisieren.

Das verbessert die Benutzerfreundlichkeit bei langen Formularen wie Registrierung oder Checkout.", - "task": "Erstelle ein Registrierungsformular mit 2 Fieldsets:
1. 'Kontodaten' mit Benutzername und Passwort
2. 'Einstellungen' mit einer Textarea für Bio
3. Einen Absende-Button außerhalb der Fieldsets", + "task": "Erstelle ein Registrierungsformular mit 2 Fieldsets:
1. Kontodaten mit Benutzername und Passwort
2. Einstellungen mit einer Textarea für Bio
3. Einen Absende-Button außerhalb der Fieldsets", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } form { max-width: 500px; } fieldset { border: 1px solid #ddd; border-radius: 10px; padding: 20px; margin-bottom: 20px; background: white; } legend { color: #2c3e50; font-weight: 600; padding: 0 10px; } label { display: block; margin: 15px 0 5px; color: #555; } input, textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: 2px solid #3498db; border-color: transparent; } textarea { resize: vertical; min-height: 80px; } button { width: 100%; padding: 14px; background: #2c3e50; color: white; border: none; border-radius: 8px; font-size: 16px; cursor: pointer; } button:hover { background: #34495e; }", "sandboxCSS": "", diff --git a/lessons/de/30-html-tables.json b/lessons/de/30-html-tables.json index 5134298..d10c687 100644 --- a/lessons/de/30-html-tables.json +++ b/lessons/de/30-html-tables.json @@ -10,7 +10,7 @@ "id": "table-basic", "title": "Grundlegende Tabellenstruktur", "description": "Tabellen verwenden <table> mit <tr> für Zeilen. In Zeilen nutze <th> für Überschriften und <td> für Datenzellen.

Das <caption>-Element bietet einen zugänglichen Titel für die Tabelle.", - "task": "Erstelle eine einfache Tabelle mit:
1. Einer <caption> mit 'Obstpreise'
2. Einer Kopfzeile mit 'Obst' und 'Preis' Spalten
3. Mindestens 2 Datenzeilen", + "task": "Erstelle eine einfache Tabelle mit:
1. Einer <caption> mit Obstpreise
2. Einer Kopfzeile mit Obst und Preis Spalten
3. Mindestens 2 Datenzeilen", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f5f5f5; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px; font-weight: 600; font-size: 1.2rem; color: #333; background: #f8f9fa; } th, td { padding: 12px 20px; text-align: left; border-bottom: 1px solid #eee; } th { background: #3498db; color: white; font-weight: 500; } tr:hover { background: #f8f9fa; } tr:last-child td { border-bottom: none; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "table-thead-tbody", "title": "Tabellenkopf & -körper", "description": "Verwende <thead> zum Gruppieren von Kopfzeilen und <tbody> zum Gruppieren von Datenzeilen. Das hilft Browsern und Hilfstechnologien, die Tabellenstruktur zu verstehen.

Du kannst auch <tfoot> für Fußzeilen wie Summen verwenden.", - "task": "Erstelle eine strukturierte Tabelle:
1. Eine <caption> mit 'Monatliche Verkäufe'
2. Ein <thead> mit Monat und Umsatz Überschriften
3. Ein <tbody> mit mindestens 2 Datenzeilen", + "task": "Erstelle eine strukturierte Tabelle:
1. Eine <caption> mit Monatliche Verkäufe
2. Ein <thead> mit Monat und Umsatz Überschriften
3. Ein <tbody> mit mindestens 2 Datenzeilen", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } table { border-collapse: collapse; width: 100%; max-width: 450px; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.2); } caption { padding: 20px; font-weight: 700; font-size: 1.3rem; color: white; background: transparent; text-shadow: 0 2px 4px rgba(0,0,0,0.2); caption-side: top; } thead { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } th { padding: 15px 20px; text-align: left; color: white; font-weight: 500; } tbody tr { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } td { padding: 15px 20px; color: #333; } tbody tr:last-child { border-bottom: none; }", "sandboxCSS": "", @@ -83,7 +83,7 @@ "id": "table-complete", "title": "Vollständige Tabelle mit Fuß", "description": "Füge <tfoot> hinzu, um einen Fußbereich für Summen oder Zusammenfassungen zu erstellen. Der Fuß bleibt unten, auch wenn tbody viele Zeilen hat.

Kombiniere alle Abschnitte für eine vollständig strukturierte, zugängliche Tabelle.", - "task": "Erstelle eine vollständige Tabelle:
1. Eine <caption> mit 'Bestellübersicht'
2. Ein <thead> mit Artikel und Preis Überschriften
3. Ein <tbody> mit 2 Artikeln
4. Ein <tfoot> mit einer Summenzeile", + "task": "Erstelle eine vollständige Tabelle:
1. Eine <caption> mit Bestellübersicht
2. Ein <thead> mit Artikel und Preis Überschriften
3. Ein <tbody> mit 2 Artikeln
4. Ein <tfoot> mit einer Summenzeile", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px 20px; font-weight: 600; font-size: 1.1rem; color: #333; text-align: left; background: #f8f9fa; border-bottom: 2px solid #eee; } th, td { padding: 12px 20px; text-align: left; } thead th { background: #2c3e50; color: white; } tbody td { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } tfoot { background: #ecf0f1; font-weight: 600; } tfoot td { border-top: 2px solid #2c3e50; color: #2c3e50; }", "sandboxCSS": "", diff --git a/lessons/de/31-html-marquee.json b/lessons/de/31-html-marquee.json index 62985f7..1f07d55 100644 --- a/lessons/de/31-html-marquee.json +++ b/lessons/de/31-html-marquee.json @@ -10,7 +10,7 @@ "id": "marquee-basic", "title": "Lauftext", "description": "Das <marquee>-Element erstellt Lauftext - ein Klassiker aus dem frühen Web! Obwohl veraltet, funktioniert es noch in den meisten Browsern.

Hinweis: Für produktive Seiten nutze CSS-Animationen. Aber zum Lernen und Spaß haben ist Marquee super!", - "task": "Erstelle ein einfaches Marquee:
1. Füge ein <marquee>-Element hinzu
2. Schreibe Text hinein wie 'Willkommen auf meiner Website!'", + "task": "Erstelle ein einfaches Marquee:
1. Füge ein <marquee>-Element hinzu
2. Schreibe Text hinein wie Willkommen auf meiner Website!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%); min-height: 150px; display: flex; align-items: center; } marquee { font-size: 2rem; color: #00ff00; text-shadow: 0 0 10px #00ff00, 0 0 20px #00ff00; font-family: 'Courier New', monospace; }", "sandboxCSS": "", diff --git a/lessons/es/00-welcome.json b/lessons/es/00-welcome.json index 4ed3039..c3f0db8 100644 --- a/lessons/es/00-welcome.json +++ b/lessons/es/00-welcome.json @@ -10,7 +10,7 @@ "id": "get-started", "title": "Get Started", "description": "Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required!

What you'll learn:
HTML - Semantic elements, forms, tables, SVG (HTML Block & Inline, HTML Forms, HTML Tables)
CSS - Selectors, box model, flexbox, animations (CSS Selectors, CSS Box Model, CSS Flexbox)
Responsive Design - Media queries and mobile-first layouts

How it works:
1. Read the task in the left panel
2. Write code in the editor
3. See live results in the preview
4. Get instant feedback with hints

Keyboard shortcuts: Ctrl+Z to undo, Ctrl+Shift+Z to redo

More resources:
HTML over JS - Native HTML vs JavaScript solutions
Web Engineering Mandala - JavaScript technology roadmap", - "task": "Hello World", + "task": "Write Hello World", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }", "sandboxCSS": "", @@ -20,8 +20,8 @@ "validations": [ { "type": "contains", - "value": "Hello", - "message": "Write 'Hello World'" + "value": "Hello World", + "message": "Write Hello World" } ] }, @@ -39,7 +39,7 @@ "validations": [ { "type": "contains", - "value": "Hello", + "value": "Hello World", "message": "Click Next to continue" } ] diff --git a/lessons/es/06-transitions-animations.json b/lessons/es/06-transitions-animations.json index 2697fd7..2c10321 100644 --- a/lessons/es/06-transitions-animations.json +++ b/lessons/es/06-transitions-animations.json @@ -8,7 +8,7 @@ { "id": "transitions-1", "title": "Transitions", - "description": "Learn how to apply transition to properties for smooth changes on state changes.", + "description": "Learn how to apply transition to properties for smooth changes on state changes.

.element {\n  transition: property duration;\n  /* e.g. transition: background-color 0.3s; */\n}
", "task": "Add transition: background-color 0.3s to .btn so the color fades smoothly on hover.", "previewHTML": "", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .btn { background: black; color: white; padding: 0.5rem 1rem; border: none; cursor: pointer; } .btn:hover { background: white; color: black; }", @@ -63,7 +63,7 @@ { "id": "transitions-3", "title": "Keyframes", - "description": "Create named animations using @keyframes and apply them via the animation shorthand.", + "description": "Create named animations using @keyframes and apply them via the animation shorthand.

@keyframes name {\n  0%   { transform: scale(1); }\n  50%  { transform: scale(1.2); }\n  100% { transform: scale(1); }\n}\n.element {\n  animation: name 1s infinite;\n}
", "task": "Define a keyframe at 50% with transform: translateY(-20px) and apply animation: bounce 1s infinite to .ball.", "previewHTML": "
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }", diff --git a/lessons/es/20-html-elements.json b/lessons/es/20-html-elements.json index 93f9214..97c569d 100644 --- a/lessons/es/20-html-elements.json +++ b/lessons/es/20-html-elements.json @@ -34,7 +34,7 @@ "id": "semantic-containers", "title": "Semantic Tags", "description": "Modern HTML uses semantic containers that describe their content:

<header> - Page or section header
<nav> - Navigation links
<main> - Main content area
<section> - Thematic grouping
<article> - Self-contained content
<footer> - Page or section footer", - "task": "Create a basic page structure:
1. Add a <header> with an <h1> containing the text 'My Website'
2. Add a <main> element with a paragraph saying 'Welcome to my site!'
3. Add a <footer> with a paragraph saying 'Copyright 2025'", + "task": "Create a basic page structure:
1. Add a <header> with an <h1> containing the text My Website
2. Add a <main> element with a paragraph saying Welcome to my site!
3. Add a <footer> with a paragraph saying Copyright 2025", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; margin: 0; } header { background: #1976d2; color: white; padding: 15px; } main { padding: 20px; min-height: 100px; } footer { background: #424242; color: white; padding: 10px; text-align: center; }", "sandboxCSS": "", diff --git a/lessons/es/21-html-forms-basic.json b/lessons/es/21-html-forms-basic.json index d971cbf..e92962f 100644 --- a/lessons/es/21-html-forms-basic.json +++ b/lessons/es/21-html-forms-basic.json @@ -10,7 +10,7 @@ "id": "form-structure", "title": "Form Structure", "description": "Every form needs a <form> wrapper. Inside, use <label> to describe inputs and <input> for user data entry.

The for attribute on labels should match the id on inputs for accessibility.", - "task": "Create a form with:
1. A <label> with the text 'Name:' and for=\"name\" attribute
2. A text <input> with id=\"name\" and name=\"name\" attributes", + "task": "Create a form with:
1. A <label> with the text Name: and for=\"name\" attribute
2. A text <input> with id=\"name\" and name=\"name\" attributes", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-bottom: 5px; font-weight: 500; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }", "sandboxCSS": "", @@ -77,8 +77,8 @@ { "id": "submit-button", "title": "Submit Button", - "description": "Forms need a way to submit data. Use:

<button type=\"submit\"> - Preferred, flexible content
<input type=\"submit\"> - Simple text-only button

The button text should be action-oriented (e.g., 'Sign In', 'Register', 'Send').", - "task": "Add a submit button to the form with the text 'Sign In'.", + "description": "Forms need a way to submit data. Use:

<button type=\"submit\"> - Preferred, flexible content
<input type=\"submit\"> - Simple text-only button

The button text should be action-oriented (e.g., Sign In, 'Register', 'Send').", + "task": "Add a submit button to the form with the text Sign In.", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-top: 15px; margin-bottom: 5px; } label:first-child { margin-top: 0; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } button { width: 100%; margin-top: 20px; padding: 10px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background: #1565c0; }", "sandboxCSS": "", diff --git a/lessons/es/23-html-details-summary.json b/lessons/es/23-html-details-summary.json index 3c60ad0..ff83fc3 100644 --- a/lessons/es/23-html-details-summary.json +++ b/lessons/es/23-html-details-summary.json @@ -10,7 +10,7 @@ "id": "details-summary-basic", "title": "First Widget", "description": "The <details> element creates a collapsible section. The <summary> provides the clickable label.

Click the summary to toggle the hidden content - no JavaScript needed!", - "task": "Create a <details> element with:
1. A <summary> saying 'Click to reveal'
2. A <p> with the text 'This content was hidden!'", + "task": "Create a <details> element with:
1. A <summary> saying Click to reveal
2. A <p> with the text This content was hidden!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } details { border: 1px solid #ddd; border-radius: 8px; padding: 15px; } summary { font-weight: 600; cursor: pointer; } summary:hover { color: #1976d2; } details p { margin-top: 15px; color: #666; }", "sandboxCSS": "", @@ -63,7 +63,7 @@ "id": "faq-accordion", "title": "FAQ Accordion", "description": "Multiple <details> elements create an accordion-style FAQ. Each question can be expanded independently.

Pro tip: Type details*3>summary+p and press Tab for Emmet expansion. *3 creates 3 elements, > nests inside, + adds siblings.", - "task": "Create an FAQ section with:
1. An <h1> saying 'Frequently Asked Questions'
2. Three <details> elements, each with a question in <summary> and an answer in <p>", + "task": "Create an FAQ section with:
1. An <h1> saying Frequently Asked Questions
2. Three <details> elements, each with a question in <summary> and an answer in <p>", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; min-height: 100vh; background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); display: flex; flex-direction: column; justify-content: center; padding: 40px; margin: 0; box-sizing: border-box; } h1 { font-size: 2.5rem; color: #4a4a4a; text-align: center; margin: 0 0 30px 0; } details { background: white; border-radius: 12px; margin-bottom: 15px; padding: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } summary { font-size: 1.3rem; font-weight: 600; color: #5c5c5c; cursor: pointer; list-style: none; } summary::before { content: '▸ '; color: #fcb69f; } details[open] summary::before { content: '▾ '; } details p { margin: 15px 0 0 0; color: #666; line-height: 1.6; }", "sandboxCSS": "", diff --git a/lessons/es/24-html-progress-meter.json b/lessons/es/24-html-progress-meter.json index 28b3bf3..3a89bc3 100644 --- a/lessons/es/24-html-progress-meter.json +++ b/lessons/es/24-html-progress-meter.json @@ -10,7 +10,7 @@ "id": "progress-basic", "title": "Progress Bars", "description": "The <progress> element shows task completion. Use value for current progress and max for the total.

Note: This is not a self-closing tag! Write <progress>...</progress> with fallback text inside for older browsers.", - "task": "Create a progress bar showing 70% completion:
1. Add a <label> saying 'Download:'
2. Add a <progress> with value=\"70\" and max=\"100\"", + "task": "Create a progress bar showing 70% completion:
1. Add a <label> saying Download:
2. Add a <progress> with value=\"70\" and max=\"100\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } progress { width: 100%; height: 20px; border-radius: 10px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 10px; } progress::-webkit-progress-value { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; } progress::-moz-progress-bar { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "progress-indeterminate", "title": "Indeterminate Progress", "description": "When progress is unknown (like loading), omit the value attribute. This creates an animated indeterminate state.

Useful for network requests or processes with unknown duration.", - "task": "Create a loading indicator:
1. Add a <p> saying 'Loading...'
2. Add a <progress> without a value attribute", + "task": "Create a loading indicator:
1. Add a <p> saying Loading...
2. Add a <progress> without a value attribute", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } p { margin-bottom: 10px; color: #666; } progress { width: 100%; height: 8px; border-radius: 4px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 4px; }", "sandboxCSS": "", @@ -68,7 +68,7 @@ "id": "meter-gauge", "title": "Meter Gauges", "description": "The <meter> element displays a scalar value within a range. Use it for measurements like disk space, battery, or ratings.

Set low, high, and optimum to define good/bad ranges - the browser colors it accordingly!", - "task": "Create a battery level meter:
1. Add a <label> saying 'Battery:'
2. Add a <meter> with:
- value=\"0.8\"
- min=\"0\" and max=\"1\"
- low=\"0.2\" and high=\"0.8\"
- optimum=\"1\"", + "task": "Create a battery level meter:
1. Add a <label> saying Battery:
2. Add a <meter> with:
- value=\"0.8\"
- min=\"0\" and max=\"1\"
- low=\"0.2\" and high=\"0.8\"
- optimum=\"1\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }", "sandboxCSS": "", diff --git a/lessons/es/25-html-datalist.json b/lessons/es/25-html-datalist.json new file mode 100644 index 0000000..8d33f10 --- /dev/null +++ b/lessons/es/25-html-datalist.json @@ -0,0 +1,78 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-datalist", + "title": "Datalist", + "description": "Provide suggestions for text inputs without JavaScript", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "datalist-basic", + "title": "Input with Suggestions", + "description": "The <datalist> element provides autocomplete suggestions for inputs. Connect it using the list attribute on the input matching the datalist's id.

Users can still type freely - suggestions are just helpers!", + "task": "Create a browser selector:
1. Add a <label> saying Browser:
2. Add an <input> with list=\"browsers\"
3. Add a <datalist id=\"browsers\"> with options for Chrome, Firefox, and Safari", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 6px; font-size: 16px; } input:focus { outline: 2px solid #1976d2; border-color: transparent; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n\n\n ", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "datalist", + "message": "Add a <datalist> element" + }, + { + "type": "attribute_value", + "value": { "selector": "input", "attr": "list", "value": "browsers" }, + "message": "Connect the input to datalist using list=\"browsers\"" + }, + { + "type": "element_count", + "value": { "selector": "option", "min": 3 }, + "message": "Add at least 3 <option> elements inside <datalist>" + }, + { + "type": "element_exists", + "value": "label", + "message": "Add a <label> for the input" + } + ] + }, + { + "id": "datalist-countries", + "title": "Country Selector", + "description": "Datalists work great for long lists like countries. Users can type to filter suggestions instantly.

The value attribute is what gets entered, and you can add display text after it.", + "task": "Create a country input:
1. Add a <label> saying Country:
2. Add an <input> with list=\"countries\"
3. Add a <datalist id=\"countries\"> with at least 4 country options", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 30px; background: linear-gradient(135deg, #e0f7fa 0%, #b2ebf2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } label { display: block; margin-bottom: 10px; font-weight: 600; color: #00695c; } input { width: 100%; padding: 12px 15px; border: 2px solid #26a69a; border-radius: 8px; font-size: 16px; background: white; } input:focus { outline: none; border-color: #00695c; box-shadow: 0 0 0 3px rgba(38,166,154,0.2); }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n\n\n ", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "datalist", + "message": "Add a <datalist> element" + }, + { + "type": "attribute_value", + "value": { "selector": "datalist", "attr": "id", "value": "countries" }, + "message": "Set id=\"countries\" on the datalist" + }, + { + "type": "attribute_value", + "value": { "selector": "input", "attr": "list", "value": "countries" }, + "message": "Connect the input using list=\"countries\"" + }, + { + "type": "element_count", + "value": { "selector": "option", "min": 4 }, + "message": "Add at least 4 country options" + } + ] + } + ] +} diff --git a/lessons/es/27-html-dialog.json b/lessons/es/27-html-dialog.json new file mode 100644 index 0000000..4b721c7 --- /dev/null +++ b/lessons/es/27-html-dialog.json @@ -0,0 +1,83 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-dialog", + "title": "Dialogs", + "description": "Create modal dialogs without JavaScript libraries", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "dialog-basic", + "title": "Open Dialog", + "description": "The <dialog> element creates a native modal. Add the open attribute to show it.

Use <form method=\"dialog\"> inside to close it when the form submits - no JavaScript needed!", + "task": "Create a dialog with:
1. The open attribute to show it
2. An <h2> saying Welcome!
3. A <p> with a greeting message
4. A <form method=\"dialog\"> with a close button", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: #f5f5f5; } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 400px; } dialog::backdrop { background: rgba(0,0,0,0.5); } dialog h2 { margin: 0 0 15px 0; color: #1976d2; } dialog p { color: #666; margin: 0 0 20px 0; } dialog button { background: #1976d2; color: white; border: none; padding: 10px 25px; border-radius: 6px; cursor: pointer; font-size: 16px; } dialog button:hover { background: #1565c0; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n

Welcome!

\n

This is a native HTML dialog element.

\n
\n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "dialog", + "message": "Add a <dialog> element" + }, + { + "type": "attribute_value", + "value": { "selector": "dialog", "attr": "open", "value": true }, + "message": "Add the open attribute to show the dialog" + }, + { + "type": "element_exists", + "value": "dialog h2", + "message": "Add an <h2> heading inside the dialog" + }, + { + "type": "element_exists", + "value": "form[method='dialog']", + "message": "Add a <form method=\"dialog\"> for closing" + }, + { + "type": "element_exists", + "value": "dialog button", + "message": "Add a close button inside the form" + } + ] + }, + { + "id": "dialog-form", + "title": "Dialog + Form", + "description": "Dialogs can contain full forms. The method=\"dialog\" makes the form close the dialog on submit instead of sending data.

This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.", + "task": "Create a confirmation dialog:
1. Add open to show it
2. An <h2> saying Confirm Delete
3. A <p> asking Are you sure?
4. A <form method=\"dialog\"> with Cancel and Delete buttons", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: linear-gradient(135deg, #fce4ec 0%, #f8bbd9 100%); } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 350px; text-align: center; } dialog h2 { margin: 0 0 10px 0; color: #c62828; } dialog p { color: #666; margin: 0 0 20px 0; } dialog form { display: flex; gap: 10px; justify-content: center; } dialog button { padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px; border: none; } dialog button:first-child { background: #e0e0e0; color: #333; } dialog button:last-child { background: #c62828; color: white; } dialog button:hover { opacity: 0.9; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n

Confirm Delete

\n

Are you sure you want to delete this item?

\n
\n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "dialog[open]", + "message": "Add a <dialog> with the open attribute" + }, + { + "type": "element_exists", + "value": "dialog h2", + "message": "Add a heading to the dialog" + }, + { + "type": "element_exists", + "value": "form[method='dialog']", + "message": "Add a <form method=\"dialog\"> for the buttons" + }, + { + "type": "element_count", + "value": { "selector": "dialog button", "min": 2 }, + "message": "Add at least 2 buttons (Cancel and Confirm)" + } + ] + } + ] +} diff --git a/lessons/es/28-html-forms-fieldset.json b/lessons/es/28-html-forms-fieldset.json new file mode 100644 index 0000000..add7ef9 --- /dev/null +++ b/lessons/es/28-html-forms-fieldset.json @@ -0,0 +1,127 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-forms-fieldset", + "title": "Fieldsets", + "description": "Group form controls with fieldset and legend elements", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "fieldset-basic", + "title": "Grouping with Fieldset", + "description": "The <fieldset> element groups related form controls together. Add a <legend> as the first child to give the group a title.

This helps with accessibility and visual organization of complex forms.", + "task": "Create a form with a fieldset:
1. A <form> element
2. A <fieldset> inside
3. A <legend> saying Personal Info
4. Two labeled inputs for name and email", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f0f4f8; } form { max-width: 400px; } fieldset { border: 2px solid #3498db; border-radius: 10px; padding: 20px; background: white; } legend { color: #3498db; font-weight: 600; padding: 0 10px; font-size: 1.1rem; } label { display: block; margin: 15px 0 5px; color: #333; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; } input:focus { outline: 2px solid #3498db; border-color: transparent; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Personal Info\n \n \n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "form", + "message": "Add a <form> element" + }, + { + "type": "element_exists", + "value": "fieldset", + "message": "Add a <fieldset> inside the form" + }, + { + "type": "element_exists", + "value": "legend", + "message": "Add a <legend> to title your fieldset" + }, + { + "type": "element_count", + "value": { "selector": "label", "min": 2 }, + "message": "Add at least 2 labels" + }, + { + "type": "element_count", + "value": { "selector": "input", "min": 2 }, + "message": "Add at least 2 input fields" + } + ] + }, + { + "id": "fieldset-textarea", + "title": "Adding Textarea", + "description": "The <textarea> element creates a multi-line text input, perfect for longer content like messages or descriptions.

Use rows and cols attributes to set default size.", + "task": "Create a contact form:
1. A <fieldset> with <legend> Contact Us
2. A labeled <input> for email
3. A labeled <textarea> for the message
4. A submit <button>", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } form { max-width: 450px; margin: 0 auto; } fieldset { border: none; border-radius: 15px; padding: 30px; background: white; box-shadow: 0 10px 40px rgba(0,0,0,0.2); } legend { color: #667eea; font-weight: 700; padding: 0; font-size: 1.5rem; margin-bottom: 10px; } label { display: block; margin: 20px 0 8px; color: #333; font-weight: 500; } input, textarea { width: 100%; padding: 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: none; border-color: #667eea; } textarea { resize: vertical; min-height: 100px; } button { margin-top: 20px; width: 100%; padding: 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; } button:hover { opacity: 0.9; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Contact Us\n \n \n \n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "fieldset", + "message": "Add a <fieldset> element" + }, + { + "type": "element_exists", + "value": "legend", + "message": "Add a <legend> element" + }, + { + "type": "element_exists", + "value": "textarea", + "message": "Add a <textarea> for the message" + }, + { + "type": "element_exists", + "value": "button", + "message": "Add a submit button" + }, + { + "type": "element_exists", + "value": "input", + "message": "Add an input field for email" + } + ] + }, + { + "id": "fieldset-multiple", + "title": "Multiple Fieldsets", + "description": "Complex forms can use multiple <fieldset> elements to organize different sections.

This improves usability for long forms like registration or checkout pages.", + "task": "Create a registration form with 2 fieldsets:
1. Account Info with username and password inputs
2. Preferences with a textarea for bio
3. A submit button outside the fieldsets", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } form { max-width: 500px; } fieldset { border: 1px solid #ddd; border-radius: 10px; padding: 20px; margin-bottom: 20px; background: white; } legend { color: #2c3e50; font-weight: 600; padding: 0 10px; } label { display: block; margin: 15px 0 5px; color: #555; } input, textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: 2px solid #3498db; border-color: transparent; } textarea { resize: vertical; min-height: 80px; } button { width: 100%; padding: 14px; background: #2c3e50; color: white; border: none; border-radius: 8px; font-size: 16px; cursor: pointer; } button:hover { background: #34495e; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Account Info\n \n \n \n \n
\n
\n Preferences\n \n \n
\n \n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_count", + "value": { "selector": "fieldset", "min": 2 }, + "message": "Create at least 2 fieldsets" + }, + { + "type": "element_count", + "value": { "selector": "legend", "min": 2 }, + "message": "Add a legend to each fieldset" + }, + { + "type": "element_exists", + "value": "textarea", + "message": "Add a textarea for the bio" + }, + { + "type": "element_exists", + "value": "button", + "message": "Add a submit button" + }, + { + "type": "element_count", + "value": { "selector": "input", "min": 2 }, + "message": "Add at least 2 input fields" + } + ] + } + ] +} diff --git a/lessons/es/30-html-tables.json b/lessons/es/30-html-tables.json index 1bdfaeb..f4d29cf 100644 --- a/lessons/es/30-html-tables.json +++ b/lessons/es/30-html-tables.json @@ -10,7 +10,7 @@ "id": "table-basic", "title": "Basic Table Structure", "description": "Tables use <table> with <tr> for rows. Inside rows, use <th> for headers and <td> for data cells.

The <caption> element provides an accessible title for the table.", - "task": "Create a simple table with:
1. A <caption> saying 'Fruit Prices'
2. A header row with 'Fruit' and 'Price' columns
3. At least 2 data rows", + "task": "Create a simple table with:
1. A <caption> saying Fruit Prices
2. A header row with Fruit and Price columns
3. At least 2 data rows", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f5f5f5; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px; font-weight: 600; font-size: 1.2rem; color: #333; background: #f8f9fa; } th, td { padding: 12px 20px; text-align: left; border-bottom: 1px solid #eee; } th { background: #3498db; color: white; font-weight: 500; } tr:hover { background: #f8f9fa; } tr:last-child td { border-bottom: none; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "table-thead-tbody", "title": "Table Head & Body", "description": "Use <thead> to group header rows and <tbody> to group data rows. This helps browsers and assistive technology understand the table structure.

You can also use <tfoot> for footer rows like totals.", - "task": "Create a structured table:
1. A <caption> with 'Monthly Sales'
2. A <thead> with Month and Revenue headers
3. A <tbody> with at least 2 data rows", + "task": "Create a structured table:
1. A <caption> with Monthly Sales
2. A <thead> with Month and Revenue headers
3. A <tbody> with at least 2 data rows", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } table { border-collapse: collapse; width: 100%; max-width: 450px; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.2); } caption { padding: 20px; font-weight: 700; font-size: 1.3rem; color: white; background: transparent; text-shadow: 0 2px 4px rgba(0,0,0,0.2); caption-side: top; } thead { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } th { padding: 15px 20px; text-align: left; color: white; font-weight: 500; } tbody tr { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } td { padding: 15px 20px; color: #333; } tbody tr:last-child { border-bottom: none; }", "sandboxCSS": "", @@ -83,7 +83,7 @@ "id": "table-complete", "title": "Complete Table with Footer", "description": "Add <tfoot> to create a footer section for totals or summary data. The footer stays at the bottom even if tbody has many rows.

Combine all sections for a fully structured, accessible table.", - "task": "Create a complete table:
1. A <caption> with 'Order Summary'
2. A <thead> with Item and Price headers
3. A <tbody> with 2 items
4. A <tfoot> with a Total row", + "task": "Create a complete table:
1. A <caption> with Order Summary
2. A <thead> with Item and Price headers
3. A <tbody> with 2 items
4. A <tfoot> with a Total row", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px 20px; font-weight: 600; font-size: 1.1rem; color: #333; text-align: left; background: #f8f9fa; border-bottom: 2px solid #eee; } th, td { padding: 12px 20px; text-align: left; } thead th { background: #2c3e50; color: white; } tbody td { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } tfoot { background: #ecf0f1; font-weight: 600; } tfoot td { border-top: 2px solid #2c3e50; color: #2c3e50; }", "sandboxCSS": "", diff --git a/lessons/es/31-html-marquee.json b/lessons/es/31-html-marquee.json index 7c2939a..ec3a486 100644 --- a/lessons/es/31-html-marquee.json +++ b/lessons/es/31-html-marquee.json @@ -10,7 +10,7 @@ "id": "marquee-basic", "title": "Scrolling Text", "description": "The <marquee> element creates scrolling text - a classic from the early web! While deprecated, it still works in most browsers.

Note: For production, use CSS animations instead. But for learning and fun, marquee is great!", - "task": "Create a simple marquee:
1. Add a <marquee> element
2. Put some text inside like 'Welcome to my website!'", + "task": "Create a simple marquee:
1. Add a <marquee> element
2. Put some text inside like Welcome to my website!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%); min-height: 150px; display: flex; align-items: center; } marquee { font-size: 2rem; color: #00ff00; text-shadow: 0 0 10px #00ff00, 0 0 20px #00ff00; font-family: 'Courier New', monospace; }", "sandboxCSS": "", diff --git a/lessons/pl/00-welcome.json b/lessons/pl/00-welcome.json index 4ed3039..c3f0db8 100644 --- a/lessons/pl/00-welcome.json +++ b/lessons/pl/00-welcome.json @@ -10,7 +10,7 @@ "id": "get-started", "title": "Get Started", "description": "Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required!

What you'll learn:
HTML - Semantic elements, forms, tables, SVG (HTML Block & Inline, HTML Forms, HTML Tables)
CSS - Selectors, box model, flexbox, animations (CSS Selectors, CSS Box Model, CSS Flexbox)
Responsive Design - Media queries and mobile-first layouts

How it works:
1. Read the task in the left panel
2. Write code in the editor
3. See live results in the preview
4. Get instant feedback with hints

Keyboard shortcuts: Ctrl+Z to undo, Ctrl+Shift+Z to redo

More resources:
HTML over JS - Native HTML vs JavaScript solutions
Web Engineering Mandala - JavaScript technology roadmap", - "task": "Hello World", + "task": "Write Hello World", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }", "sandboxCSS": "", @@ -20,8 +20,8 @@ "validations": [ { "type": "contains", - "value": "Hello", - "message": "Write 'Hello World'" + "value": "Hello World", + "message": "Write Hello World" } ] }, @@ -39,7 +39,7 @@ "validations": [ { "type": "contains", - "value": "Hello", + "value": "Hello World", "message": "Click Next to continue" } ] diff --git a/lessons/pl/06-transitions-animations.json b/lessons/pl/06-transitions-animations.json index 2697fd7..2c10321 100644 --- a/lessons/pl/06-transitions-animations.json +++ b/lessons/pl/06-transitions-animations.json @@ -8,7 +8,7 @@ { "id": "transitions-1", "title": "Transitions", - "description": "Learn how to apply transition to properties for smooth changes on state changes.", + "description": "Learn how to apply transition to properties for smooth changes on state changes.

.element {\n  transition: property duration;\n  /* e.g. transition: background-color 0.3s; */\n}
", "task": "Add transition: background-color 0.3s to .btn so the color fades smoothly on hover.", "previewHTML": "", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .btn { background: black; color: white; padding: 0.5rem 1rem; border: none; cursor: pointer; } .btn:hover { background: white; color: black; }", @@ -63,7 +63,7 @@ { "id": "transitions-3", "title": "Keyframes", - "description": "Create named animations using @keyframes and apply them via the animation shorthand.", + "description": "Create named animations using @keyframes and apply them via the animation shorthand.

@keyframes name {\n  0%   { transform: scale(1); }\n  50%  { transform: scale(1.2); }\n  100% { transform: scale(1); }\n}\n.element {\n  animation: name 1s infinite;\n}
", "task": "Define a keyframe at 50% with transform: translateY(-20px) and apply animation: bounce 1s infinite to .ball.", "previewHTML": "
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }", diff --git a/lessons/pl/20-html-elements.json b/lessons/pl/20-html-elements.json index 93f9214..97c569d 100644 --- a/lessons/pl/20-html-elements.json +++ b/lessons/pl/20-html-elements.json @@ -34,7 +34,7 @@ "id": "semantic-containers", "title": "Semantic Tags", "description": "Modern HTML uses semantic containers that describe their content:

<header> - Page or section header
<nav> - Navigation links
<main> - Main content area
<section> - Thematic grouping
<article> - Self-contained content
<footer> - Page or section footer", - "task": "Create a basic page structure:
1. Add a <header> with an <h1> containing the text 'My Website'
2. Add a <main> element with a paragraph saying 'Welcome to my site!'
3. Add a <footer> with a paragraph saying 'Copyright 2025'", + "task": "Create a basic page structure:
1. Add a <header> with an <h1> containing the text My Website
2. Add a <main> element with a paragraph saying Welcome to my site!
3. Add a <footer> with a paragraph saying Copyright 2025", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; margin: 0; } header { background: #1976d2; color: white; padding: 15px; } main { padding: 20px; min-height: 100px; } footer { background: #424242; color: white; padding: 10px; text-align: center; }", "sandboxCSS": "", diff --git a/lessons/pl/21-html-forms-basic.json b/lessons/pl/21-html-forms-basic.json index d971cbf..e92962f 100644 --- a/lessons/pl/21-html-forms-basic.json +++ b/lessons/pl/21-html-forms-basic.json @@ -10,7 +10,7 @@ "id": "form-structure", "title": "Form Structure", "description": "Every form needs a <form> wrapper. Inside, use <label> to describe inputs and <input> for user data entry.

The for attribute on labels should match the id on inputs for accessibility.", - "task": "Create a form with:
1. A <label> with the text 'Name:' and for=\"name\" attribute
2. A text <input> with id=\"name\" and name=\"name\" attributes", + "task": "Create a form with:
1. A <label> with the text Name: and for=\"name\" attribute
2. A text <input> with id=\"name\" and name=\"name\" attributes", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-bottom: 5px; font-weight: 500; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }", "sandboxCSS": "", @@ -77,8 +77,8 @@ { "id": "submit-button", "title": "Submit Button", - "description": "Forms need a way to submit data. Use:

<button type=\"submit\"> - Preferred, flexible content
<input type=\"submit\"> - Simple text-only button

The button text should be action-oriented (e.g., 'Sign In', 'Register', 'Send').", - "task": "Add a submit button to the form with the text 'Sign In'.", + "description": "Forms need a way to submit data. Use:

<button type=\"submit\"> - Preferred, flexible content
<input type=\"submit\"> - Simple text-only button

The button text should be action-oriented (e.g., Sign In, 'Register', 'Send').", + "task": "Add a submit button to the form with the text Sign In.", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-top: 15px; margin-bottom: 5px; } label:first-child { margin-top: 0; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } button { width: 100%; margin-top: 20px; padding: 10px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background: #1565c0; }", "sandboxCSS": "", diff --git a/lessons/pl/23-html-details-summary.json b/lessons/pl/23-html-details-summary.json index 3c60ad0..ff83fc3 100644 --- a/lessons/pl/23-html-details-summary.json +++ b/lessons/pl/23-html-details-summary.json @@ -10,7 +10,7 @@ "id": "details-summary-basic", "title": "First Widget", "description": "The <details> element creates a collapsible section. The <summary> provides the clickable label.

Click the summary to toggle the hidden content - no JavaScript needed!", - "task": "Create a <details> element with:
1. A <summary> saying 'Click to reveal'
2. A <p> with the text 'This content was hidden!'", + "task": "Create a <details> element with:
1. A <summary> saying Click to reveal
2. A <p> with the text This content was hidden!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } details { border: 1px solid #ddd; border-radius: 8px; padding: 15px; } summary { font-weight: 600; cursor: pointer; } summary:hover { color: #1976d2; } details p { margin-top: 15px; color: #666; }", "sandboxCSS": "", @@ -63,7 +63,7 @@ "id": "faq-accordion", "title": "FAQ Accordion", "description": "Multiple <details> elements create an accordion-style FAQ. Each question can be expanded independently.

Pro tip: Type details*3>summary+p and press Tab for Emmet expansion. *3 creates 3 elements, > nests inside, + adds siblings.", - "task": "Create an FAQ section with:
1. An <h1> saying 'Frequently Asked Questions'
2. Three <details> elements, each with a question in <summary> and an answer in <p>", + "task": "Create an FAQ section with:
1. An <h1> saying Frequently Asked Questions
2. Three <details> elements, each with a question in <summary> and an answer in <p>", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; min-height: 100vh; background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); display: flex; flex-direction: column; justify-content: center; padding: 40px; margin: 0; box-sizing: border-box; } h1 { font-size: 2.5rem; color: #4a4a4a; text-align: center; margin: 0 0 30px 0; } details { background: white; border-radius: 12px; margin-bottom: 15px; padding: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } summary { font-size: 1.3rem; font-weight: 600; color: #5c5c5c; cursor: pointer; list-style: none; } summary::before { content: '▸ '; color: #fcb69f; } details[open] summary::before { content: '▾ '; } details p { margin: 15px 0 0 0; color: #666; line-height: 1.6; }", "sandboxCSS": "", diff --git a/lessons/pl/24-html-progress-meter.json b/lessons/pl/24-html-progress-meter.json index 28b3bf3..3a89bc3 100644 --- a/lessons/pl/24-html-progress-meter.json +++ b/lessons/pl/24-html-progress-meter.json @@ -10,7 +10,7 @@ "id": "progress-basic", "title": "Progress Bars", "description": "The <progress> element shows task completion. Use value for current progress and max for the total.

Note: This is not a self-closing tag! Write <progress>...</progress> with fallback text inside for older browsers.", - "task": "Create a progress bar showing 70% completion:
1. Add a <label> saying 'Download:'
2. Add a <progress> with value=\"70\" and max=\"100\"", + "task": "Create a progress bar showing 70% completion:
1. Add a <label> saying Download:
2. Add a <progress> with value=\"70\" and max=\"100\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } progress { width: 100%; height: 20px; border-radius: 10px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 10px; } progress::-webkit-progress-value { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; } progress::-moz-progress-bar { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "progress-indeterminate", "title": "Indeterminate Progress", "description": "When progress is unknown (like loading), omit the value attribute. This creates an animated indeterminate state.

Useful for network requests or processes with unknown duration.", - "task": "Create a loading indicator:
1. Add a <p> saying 'Loading...'
2. Add a <progress> without a value attribute", + "task": "Create a loading indicator:
1. Add a <p> saying Loading...
2. Add a <progress> without a value attribute", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } p { margin-bottom: 10px; color: #666; } progress { width: 100%; height: 8px; border-radius: 4px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 4px; }", "sandboxCSS": "", @@ -68,7 +68,7 @@ "id": "meter-gauge", "title": "Meter Gauges", "description": "The <meter> element displays a scalar value within a range. Use it for measurements like disk space, battery, or ratings.

Set low, high, and optimum to define good/bad ranges - the browser colors it accordingly!", - "task": "Create a battery level meter:
1. Add a <label> saying 'Battery:'
2. Add a <meter> with:
- value=\"0.8\"
- min=\"0\" and max=\"1\"
- low=\"0.2\" and high=\"0.8\"
- optimum=\"1\"", + "task": "Create a battery level meter:
1. Add a <label> saying Battery:
2. Add a <meter> with:
- value=\"0.8\"
- min=\"0\" and max=\"1\"
- low=\"0.2\" and high=\"0.8\"
- optimum=\"1\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }", "sandboxCSS": "", diff --git a/lessons/pl/25-html-datalist.json b/lessons/pl/25-html-datalist.json new file mode 100644 index 0000000..8d33f10 --- /dev/null +++ b/lessons/pl/25-html-datalist.json @@ -0,0 +1,78 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-datalist", + "title": "Datalist", + "description": "Provide suggestions for text inputs without JavaScript", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "datalist-basic", + "title": "Input with Suggestions", + "description": "The <datalist> element provides autocomplete suggestions for inputs. Connect it using the list attribute on the input matching the datalist's id.

Users can still type freely - suggestions are just helpers!", + "task": "Create a browser selector:
1. Add a <label> saying Browser:
2. Add an <input> with list=\"browsers\"
3. Add a <datalist id=\"browsers\"> with options for Chrome, Firefox, and Safari", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 6px; font-size: 16px; } input:focus { outline: 2px solid #1976d2; border-color: transparent; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n\n\n ", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "datalist", + "message": "Add a <datalist> element" + }, + { + "type": "attribute_value", + "value": { "selector": "input", "attr": "list", "value": "browsers" }, + "message": "Connect the input to datalist using list=\"browsers\"" + }, + { + "type": "element_count", + "value": { "selector": "option", "min": 3 }, + "message": "Add at least 3 <option> elements inside <datalist>" + }, + { + "type": "element_exists", + "value": "label", + "message": "Add a <label> for the input" + } + ] + }, + { + "id": "datalist-countries", + "title": "Country Selector", + "description": "Datalists work great for long lists like countries. Users can type to filter suggestions instantly.

The value attribute is what gets entered, and you can add display text after it.", + "task": "Create a country input:
1. Add a <label> saying Country:
2. Add an <input> with list=\"countries\"
3. Add a <datalist id=\"countries\"> with at least 4 country options", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 30px; background: linear-gradient(135deg, #e0f7fa 0%, #b2ebf2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } label { display: block; margin-bottom: 10px; font-weight: 600; color: #00695c; } input { width: 100%; padding: 12px 15px; border: 2px solid #26a69a; border-radius: 8px; font-size: 16px; background: white; } input:focus { outline: none; border-color: #00695c; box-shadow: 0 0 0 3px rgba(38,166,154,0.2); }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n\n\n ", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "datalist", + "message": "Add a <datalist> element" + }, + { + "type": "attribute_value", + "value": { "selector": "datalist", "attr": "id", "value": "countries" }, + "message": "Set id=\"countries\" on the datalist" + }, + { + "type": "attribute_value", + "value": { "selector": "input", "attr": "list", "value": "countries" }, + "message": "Connect the input using list=\"countries\"" + }, + { + "type": "element_count", + "value": { "selector": "option", "min": 4 }, + "message": "Add at least 4 country options" + } + ] + } + ] +} diff --git a/lessons/pl/27-html-dialog.json b/lessons/pl/27-html-dialog.json new file mode 100644 index 0000000..4b721c7 --- /dev/null +++ b/lessons/pl/27-html-dialog.json @@ -0,0 +1,83 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-dialog", + "title": "Dialogs", + "description": "Create modal dialogs without JavaScript libraries", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "dialog-basic", + "title": "Open Dialog", + "description": "The <dialog> element creates a native modal. Add the open attribute to show it.

Use <form method=\"dialog\"> inside to close it when the form submits - no JavaScript needed!", + "task": "Create a dialog with:
1. The open attribute to show it
2. An <h2> saying Welcome!
3. A <p> with a greeting message
4. A <form method=\"dialog\"> with a close button", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: #f5f5f5; } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 400px; } dialog::backdrop { background: rgba(0,0,0,0.5); } dialog h2 { margin: 0 0 15px 0; color: #1976d2; } dialog p { color: #666; margin: 0 0 20px 0; } dialog button { background: #1976d2; color: white; border: none; padding: 10px 25px; border-radius: 6px; cursor: pointer; font-size: 16px; } dialog button:hover { background: #1565c0; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n

Welcome!

\n

This is a native HTML dialog element.

\n
\n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "dialog", + "message": "Add a <dialog> element" + }, + { + "type": "attribute_value", + "value": { "selector": "dialog", "attr": "open", "value": true }, + "message": "Add the open attribute to show the dialog" + }, + { + "type": "element_exists", + "value": "dialog h2", + "message": "Add an <h2> heading inside the dialog" + }, + { + "type": "element_exists", + "value": "form[method='dialog']", + "message": "Add a <form method=\"dialog\"> for closing" + }, + { + "type": "element_exists", + "value": "dialog button", + "message": "Add a close button inside the form" + } + ] + }, + { + "id": "dialog-form", + "title": "Dialog + Form", + "description": "Dialogs can contain full forms. The method=\"dialog\" makes the form close the dialog on submit instead of sending data.

This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.", + "task": "Create a confirmation dialog:
1. Add open to show it
2. An <h2> saying Confirm Delete
3. A <p> asking Are you sure?
4. A <form method=\"dialog\"> with Cancel and Delete buttons", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: linear-gradient(135deg, #fce4ec 0%, #f8bbd9 100%); } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 350px; text-align: center; } dialog h2 { margin: 0 0 10px 0; color: #c62828; } dialog p { color: #666; margin: 0 0 20px 0; } dialog form { display: flex; gap: 10px; justify-content: center; } dialog button { padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px; border: none; } dialog button:first-child { background: #e0e0e0; color: #333; } dialog button:last-child { background: #c62828; color: white; } dialog button:hover { opacity: 0.9; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n

Confirm Delete

\n

Are you sure you want to delete this item?

\n
\n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "dialog[open]", + "message": "Add a <dialog> with the open attribute" + }, + { + "type": "element_exists", + "value": "dialog h2", + "message": "Add a heading to the dialog" + }, + { + "type": "element_exists", + "value": "form[method='dialog']", + "message": "Add a <form method=\"dialog\"> for the buttons" + }, + { + "type": "element_count", + "value": { "selector": "dialog button", "min": 2 }, + "message": "Add at least 2 buttons (Cancel and Confirm)" + } + ] + } + ] +} diff --git a/lessons/pl/28-html-forms-fieldset.json b/lessons/pl/28-html-forms-fieldset.json new file mode 100644 index 0000000..add7ef9 --- /dev/null +++ b/lessons/pl/28-html-forms-fieldset.json @@ -0,0 +1,127 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-forms-fieldset", + "title": "Fieldsets", + "description": "Group form controls with fieldset and legend elements", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "fieldset-basic", + "title": "Grouping with Fieldset", + "description": "The <fieldset> element groups related form controls together. Add a <legend> as the first child to give the group a title.

This helps with accessibility and visual organization of complex forms.", + "task": "Create a form with a fieldset:
1. A <form> element
2. A <fieldset> inside
3. A <legend> saying Personal Info
4. Two labeled inputs for name and email", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f0f4f8; } form { max-width: 400px; } fieldset { border: 2px solid #3498db; border-radius: 10px; padding: 20px; background: white; } legend { color: #3498db; font-weight: 600; padding: 0 10px; font-size: 1.1rem; } label { display: block; margin: 15px 0 5px; color: #333; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; } input:focus { outline: 2px solid #3498db; border-color: transparent; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Personal Info\n \n \n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "form", + "message": "Add a <form> element" + }, + { + "type": "element_exists", + "value": "fieldset", + "message": "Add a <fieldset> inside the form" + }, + { + "type": "element_exists", + "value": "legend", + "message": "Add a <legend> to title your fieldset" + }, + { + "type": "element_count", + "value": { "selector": "label", "min": 2 }, + "message": "Add at least 2 labels" + }, + { + "type": "element_count", + "value": { "selector": "input", "min": 2 }, + "message": "Add at least 2 input fields" + } + ] + }, + { + "id": "fieldset-textarea", + "title": "Adding Textarea", + "description": "The <textarea> element creates a multi-line text input, perfect for longer content like messages or descriptions.

Use rows and cols attributes to set default size.", + "task": "Create a contact form:
1. A <fieldset> with <legend> Contact Us
2. A labeled <input> for email
3. A labeled <textarea> for the message
4. A submit <button>", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } form { max-width: 450px; margin: 0 auto; } fieldset { border: none; border-radius: 15px; padding: 30px; background: white; box-shadow: 0 10px 40px rgba(0,0,0,0.2); } legend { color: #667eea; font-weight: 700; padding: 0; font-size: 1.5rem; margin-bottom: 10px; } label { display: block; margin: 20px 0 8px; color: #333; font-weight: 500; } input, textarea { width: 100%; padding: 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: none; border-color: #667eea; } textarea { resize: vertical; min-height: 100px; } button { margin-top: 20px; width: 100%; padding: 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; } button:hover { opacity: 0.9; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Contact Us\n \n \n \n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "fieldset", + "message": "Add a <fieldset> element" + }, + { + "type": "element_exists", + "value": "legend", + "message": "Add a <legend> element" + }, + { + "type": "element_exists", + "value": "textarea", + "message": "Add a <textarea> for the message" + }, + { + "type": "element_exists", + "value": "button", + "message": "Add a submit button" + }, + { + "type": "element_exists", + "value": "input", + "message": "Add an input field for email" + } + ] + }, + { + "id": "fieldset-multiple", + "title": "Multiple Fieldsets", + "description": "Complex forms can use multiple <fieldset> elements to organize different sections.

This improves usability for long forms like registration or checkout pages.", + "task": "Create a registration form with 2 fieldsets:
1. Account Info with username and password inputs
2. Preferences with a textarea for bio
3. A submit button outside the fieldsets", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } form { max-width: 500px; } fieldset { border: 1px solid #ddd; border-radius: 10px; padding: 20px; margin-bottom: 20px; background: white; } legend { color: #2c3e50; font-weight: 600; padding: 0 10px; } label { display: block; margin: 15px 0 5px; color: #555; } input, textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: 2px solid #3498db; border-color: transparent; } textarea { resize: vertical; min-height: 80px; } button { width: 100%; padding: 14px; background: #2c3e50; color: white; border: none; border-radius: 8px; font-size: 16px; cursor: pointer; } button:hover { background: #34495e; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Account Info\n \n \n \n \n
\n
\n Preferences\n \n \n
\n \n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_count", + "value": { "selector": "fieldset", "min": 2 }, + "message": "Create at least 2 fieldsets" + }, + { + "type": "element_count", + "value": { "selector": "legend", "min": 2 }, + "message": "Add a legend to each fieldset" + }, + { + "type": "element_exists", + "value": "textarea", + "message": "Add a textarea for the bio" + }, + { + "type": "element_exists", + "value": "button", + "message": "Add a submit button" + }, + { + "type": "element_count", + "value": { "selector": "input", "min": 2 }, + "message": "Add at least 2 input fields" + } + ] + } + ] +} diff --git a/lessons/pl/30-html-tables.json b/lessons/pl/30-html-tables.json index 1bdfaeb..f4d29cf 100644 --- a/lessons/pl/30-html-tables.json +++ b/lessons/pl/30-html-tables.json @@ -10,7 +10,7 @@ "id": "table-basic", "title": "Basic Table Structure", "description": "Tables use <table> with <tr> for rows. Inside rows, use <th> for headers and <td> for data cells.

The <caption> element provides an accessible title for the table.", - "task": "Create a simple table with:
1. A <caption> saying 'Fruit Prices'
2. A header row with 'Fruit' and 'Price' columns
3. At least 2 data rows", + "task": "Create a simple table with:
1. A <caption> saying Fruit Prices
2. A header row with Fruit and Price columns
3. At least 2 data rows", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f5f5f5; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px; font-weight: 600; font-size: 1.2rem; color: #333; background: #f8f9fa; } th, td { padding: 12px 20px; text-align: left; border-bottom: 1px solid #eee; } th { background: #3498db; color: white; font-weight: 500; } tr:hover { background: #f8f9fa; } tr:last-child td { border-bottom: none; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "table-thead-tbody", "title": "Table Head & Body", "description": "Use <thead> to group header rows and <tbody> to group data rows. This helps browsers and assistive technology understand the table structure.

You can also use <tfoot> for footer rows like totals.", - "task": "Create a structured table:
1. A <caption> with 'Monthly Sales'
2. A <thead> with Month and Revenue headers
3. A <tbody> with at least 2 data rows", + "task": "Create a structured table:
1. A <caption> with Monthly Sales
2. A <thead> with Month and Revenue headers
3. A <tbody> with at least 2 data rows", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } table { border-collapse: collapse; width: 100%; max-width: 450px; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.2); } caption { padding: 20px; font-weight: 700; font-size: 1.3rem; color: white; background: transparent; text-shadow: 0 2px 4px rgba(0,0,0,0.2); caption-side: top; } thead { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } th { padding: 15px 20px; text-align: left; color: white; font-weight: 500; } tbody tr { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } td { padding: 15px 20px; color: #333; } tbody tr:last-child { border-bottom: none; }", "sandboxCSS": "", @@ -83,7 +83,7 @@ "id": "table-complete", "title": "Complete Table with Footer", "description": "Add <tfoot> to create a footer section for totals or summary data. The footer stays at the bottom even if tbody has many rows.

Combine all sections for a fully structured, accessible table.", - "task": "Create a complete table:
1. A <caption> with 'Order Summary'
2. A <thead> with Item and Price headers
3. A <tbody> with 2 items
4. A <tfoot> with a Total row", + "task": "Create a complete table:
1. A <caption> with Order Summary
2. A <thead> with Item and Price headers
3. A <tbody> with 2 items
4. A <tfoot> with a Total row", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px 20px; font-weight: 600; font-size: 1.1rem; color: #333; text-align: left; background: #f8f9fa; border-bottom: 2px solid #eee; } th, td { padding: 12px 20px; text-align: left; } thead th { background: #2c3e50; color: white; } tbody td { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } tfoot { background: #ecf0f1; font-weight: 600; } tfoot td { border-top: 2px solid #2c3e50; color: #2c3e50; }", "sandboxCSS": "", diff --git a/lessons/pl/31-html-marquee.json b/lessons/pl/31-html-marquee.json index 7c2939a..ec3a486 100644 --- a/lessons/pl/31-html-marquee.json +++ b/lessons/pl/31-html-marquee.json @@ -10,7 +10,7 @@ "id": "marquee-basic", "title": "Scrolling Text", "description": "The <marquee> element creates scrolling text - a classic from the early web! While deprecated, it still works in most browsers.

Note: For production, use CSS animations instead. But for learning and fun, marquee is great!", - "task": "Create a simple marquee:
1. Add a <marquee> element
2. Put some text inside like 'Welcome to my website!'", + "task": "Create a simple marquee:
1. Add a <marquee> element
2. Put some text inside like Welcome to my website!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%); min-height: 150px; display: flex; align-items: center; } marquee { font-size: 2rem; color: #00ff00; text-shadow: 0 0 10px #00ff00, 0 0 20px #00ff00; font-family: 'Courier New', monospace; }", "sandboxCSS": "", diff --git a/lessons/uk/00-welcome.json b/lessons/uk/00-welcome.json index 4ed3039..c3f0db8 100644 --- a/lessons/uk/00-welcome.json +++ b/lessons/uk/00-welcome.json @@ -10,7 +10,7 @@ "id": "get-started", "title": "Get Started", "description": "Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required!

What you'll learn:
HTML - Semantic elements, forms, tables, SVG (HTML Block & Inline, HTML Forms, HTML Tables)
CSS - Selectors, box model, flexbox, animations (CSS Selectors, CSS Box Model, CSS Flexbox)
Responsive Design - Media queries and mobile-first layouts

How it works:
1. Read the task in the left panel
2. Write code in the editor
3. See live results in the preview
4. Get instant feedback with hints

Keyboard shortcuts: Ctrl+Z to undo, Ctrl+Shift+Z to redo

More resources:
HTML over JS - Native HTML vs JavaScript solutions
Web Engineering Mandala - JavaScript technology roadmap", - "task": "Hello World", + "task": "Write Hello World", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }", "sandboxCSS": "", @@ -20,8 +20,8 @@ "validations": [ { "type": "contains", - "value": "Hello", - "message": "Write 'Hello World'" + "value": "Hello World", + "message": "Write Hello World" } ] }, @@ -39,7 +39,7 @@ "validations": [ { "type": "contains", - "value": "Hello", + "value": "Hello World", "message": "Click Next to continue" } ] diff --git a/lessons/uk/06-transitions-animations.json b/lessons/uk/06-transitions-animations.json index 2697fd7..2c10321 100644 --- a/lessons/uk/06-transitions-animations.json +++ b/lessons/uk/06-transitions-animations.json @@ -8,7 +8,7 @@ { "id": "transitions-1", "title": "Transitions", - "description": "Learn how to apply transition to properties for smooth changes on state changes.", + "description": "Learn how to apply transition to properties for smooth changes on state changes.

.element {\n  transition: property duration;\n  /* e.g. transition: background-color 0.3s; */\n}
", "task": "Add transition: background-color 0.3s to .btn so the color fades smoothly on hover.", "previewHTML": "", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .btn { background: black; color: white; padding: 0.5rem 1rem; border: none; cursor: pointer; } .btn:hover { background: white; color: black; }", @@ -63,7 +63,7 @@ { "id": "transitions-3", "title": "Keyframes", - "description": "Create named animations using @keyframes and apply them via the animation shorthand.", + "description": "Create named animations using @keyframes and apply them via the animation shorthand.

@keyframes name {\n  0%   { transform: scale(1); }\n  50%  { transform: scale(1.2); }\n  100% { transform: scale(1); }\n}\n.element {\n  animation: name 1s infinite;\n}
", "task": "Define a keyframe at 50% with transform: translateY(-20px) and apply animation: bounce 1s infinite to .ball.", "previewHTML": "
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }", diff --git a/lessons/uk/20-html-elements.json b/lessons/uk/20-html-elements.json index 93f9214..97c569d 100644 --- a/lessons/uk/20-html-elements.json +++ b/lessons/uk/20-html-elements.json @@ -34,7 +34,7 @@ "id": "semantic-containers", "title": "Semantic Tags", "description": "Modern HTML uses semantic containers that describe their content:

<header> - Page or section header
<nav> - Navigation links
<main> - Main content area
<section> - Thematic grouping
<article> - Self-contained content
<footer> - Page or section footer", - "task": "Create a basic page structure:
1. Add a <header> with an <h1> containing the text 'My Website'
2. Add a <main> element with a paragraph saying 'Welcome to my site!'
3. Add a <footer> with a paragraph saying 'Copyright 2025'", + "task": "Create a basic page structure:
1. Add a <header> with an <h1> containing the text My Website
2. Add a <main> element with a paragraph saying Welcome to my site!
3. Add a <footer> with a paragraph saying Copyright 2025", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; margin: 0; } header { background: #1976d2; color: white; padding: 15px; } main { padding: 20px; min-height: 100px; } footer { background: #424242; color: white; padding: 10px; text-align: center; }", "sandboxCSS": "", diff --git a/lessons/uk/21-html-forms-basic.json b/lessons/uk/21-html-forms-basic.json index d971cbf..e92962f 100644 --- a/lessons/uk/21-html-forms-basic.json +++ b/lessons/uk/21-html-forms-basic.json @@ -10,7 +10,7 @@ "id": "form-structure", "title": "Form Structure", "description": "Every form needs a <form> wrapper. Inside, use <label> to describe inputs and <input> for user data entry.

The for attribute on labels should match the id on inputs for accessibility.", - "task": "Create a form with:
1. A <label> with the text 'Name:' and for=\"name\" attribute
2. A text <input> with id=\"name\" and name=\"name\" attributes", + "task": "Create a form with:
1. A <label> with the text Name: and for=\"name\" attribute
2. A text <input> with id=\"name\" and name=\"name\" attributes", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-bottom: 5px; font-weight: 500; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }", "sandboxCSS": "", @@ -77,8 +77,8 @@ { "id": "submit-button", "title": "Submit Button", - "description": "Forms need a way to submit data. Use:

<button type=\"submit\"> - Preferred, flexible content
<input type=\"submit\"> - Simple text-only button

The button text should be action-oriented (e.g., 'Sign In', 'Register', 'Send').", - "task": "Add a submit button to the form with the text 'Sign In'.", + "description": "Forms need a way to submit data. Use:

<button type=\"submit\"> - Preferred, flexible content
<input type=\"submit\"> - Simple text-only button

The button text should be action-oriented (e.g., Sign In, 'Register', 'Send').", + "task": "Add a submit button to the form with the text Sign In.", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 300px; } label { display: block; margin-top: 15px; margin-bottom: 5px; } label:first-child { margin-top: 0; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } button { width: 100%; margin-top: 20px; padding: 10px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background: #1565c0; }", "sandboxCSS": "", diff --git a/lessons/uk/23-html-details-summary.json b/lessons/uk/23-html-details-summary.json index 3c60ad0..ff83fc3 100644 --- a/lessons/uk/23-html-details-summary.json +++ b/lessons/uk/23-html-details-summary.json @@ -10,7 +10,7 @@ "id": "details-summary-basic", "title": "First Widget", "description": "The <details> element creates a collapsible section. The <summary> provides the clickable label.

Click the summary to toggle the hidden content - no JavaScript needed!", - "task": "Create a <details> element with:
1. A <summary> saying 'Click to reveal'
2. A <p> with the text 'This content was hidden!'", + "task": "Create a <details> element with:
1. A <summary> saying Click to reveal
2. A <p> with the text This content was hidden!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } details { border: 1px solid #ddd; border-radius: 8px; padding: 15px; } summary { font-weight: 600; cursor: pointer; } summary:hover { color: #1976d2; } details p { margin-top: 15px; color: #666; }", "sandboxCSS": "", @@ -63,7 +63,7 @@ "id": "faq-accordion", "title": "FAQ Accordion", "description": "Multiple <details> elements create an accordion-style FAQ. Each question can be expanded independently.

Pro tip: Type details*3>summary+p and press Tab for Emmet expansion. *3 creates 3 elements, > nests inside, + adds siblings.", - "task": "Create an FAQ section with:
1. An <h1> saying 'Frequently Asked Questions'
2. Three <details> elements, each with a question in <summary> and an answer in <p>", + "task": "Create an FAQ section with:
1. An <h1> saying Frequently Asked Questions
2. Three <details> elements, each with a question in <summary> and an answer in <p>", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; min-height: 100vh; background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); display: flex; flex-direction: column; justify-content: center; padding: 40px; margin: 0; box-sizing: border-box; } h1 { font-size: 2.5rem; color: #4a4a4a; text-align: center; margin: 0 0 30px 0; } details { background: white; border-radius: 12px; margin-bottom: 15px; padding: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } summary { font-size: 1.3rem; font-weight: 600; color: #5c5c5c; cursor: pointer; list-style: none; } summary::before { content: '▸ '; color: #fcb69f; } details[open] summary::before { content: '▾ '; } details p { margin: 15px 0 0 0; color: #666; line-height: 1.6; }", "sandboxCSS": "", diff --git a/lessons/uk/24-html-progress-meter.json b/lessons/uk/24-html-progress-meter.json index 28b3bf3..3a89bc3 100644 --- a/lessons/uk/24-html-progress-meter.json +++ b/lessons/uk/24-html-progress-meter.json @@ -10,7 +10,7 @@ "id": "progress-basic", "title": "Progress Bars", "description": "The <progress> element shows task completion. Use value for current progress and max for the total.

Note: This is not a self-closing tag! Write <progress>...</progress> with fallback text inside for older browsers.", - "task": "Create a progress bar showing 70% completion:
1. Add a <label> saying 'Download:'
2. Add a <progress> with value=\"70\" and max=\"100\"", + "task": "Create a progress bar showing 70% completion:
1. Add a <label> saying Download:
2. Add a <progress> with value=\"70\" and max=\"100\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } progress { width: 100%; height: 20px; border-radius: 10px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 10px; } progress::-webkit-progress-value { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; } progress::-moz-progress-bar { background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 10px; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "progress-indeterminate", "title": "Indeterminate Progress", "description": "When progress is unknown (like loading), omit the value attribute. This creates an animated indeterminate state.

Useful for network requests or processes with unknown duration.", - "task": "Create a loading indicator:
1. Add a <p> saying 'Loading...'
2. Add a <progress> without a value attribute", + "task": "Create a loading indicator:
1. Add a <p> saying Loading...
2. Add a <progress> without a value attribute", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } p { margin-bottom: 10px; color: #666; } progress { width: 100%; height: 8px; border-radius: 4px; } progress::-webkit-progress-bar { background: #e0e0e0; border-radius: 4px; }", "sandboxCSS": "", @@ -68,7 +68,7 @@ "id": "meter-gauge", "title": "Meter Gauges", "description": "The <meter> element displays a scalar value within a range. Use it for measurements like disk space, battery, or ratings.

Set low, high, and optimum to define good/bad ranges - the browser colors it accordingly!", - "task": "Create a battery level meter:
1. Add a <label> saying 'Battery:'
2. Add a <meter> with:
- value=\"0.8\"
- min=\"0\" and max=\"1\"
- low=\"0.2\" and high=\"0.8\"
- optimum=\"1\"", + "task": "Create a battery level meter:
1. Add a <label> saying Battery:
2. Add a <meter> with:
- value=\"0.8\"
- min=\"0\" and max=\"1\"
- low=\"0.2\" and high=\"0.8\"
- optimum=\"1\"", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }", "sandboxCSS": "", diff --git a/lessons/uk/25-html-datalist.json b/lessons/uk/25-html-datalist.json new file mode 100644 index 0000000..8d33f10 --- /dev/null +++ b/lessons/uk/25-html-datalist.json @@ -0,0 +1,78 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-datalist", + "title": "Datalist", + "description": "Provide suggestions for text inputs without JavaScript", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "datalist-basic", + "title": "Input with Suggestions", + "description": "The <datalist> element provides autocomplete suggestions for inputs. Connect it using the list attribute on the input matching the datalist's id.

Users can still type freely - suggestions are just helpers!", + "task": "Create a browser selector:
1. Add a <label> saying Browser:
2. Add an <input> with list=\"browsers\"
3. Add a <datalist id=\"browsers\"> with options for Chrome, Firefox, and Safari", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 6px; font-size: 16px; } input:focus { outline: 2px solid #1976d2; border-color: transparent; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n\n\n ", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "datalist", + "message": "Add a <datalist> element" + }, + { + "type": "attribute_value", + "value": { "selector": "input", "attr": "list", "value": "browsers" }, + "message": "Connect the input to datalist using list=\"browsers\"" + }, + { + "type": "element_count", + "value": { "selector": "option", "min": 3 }, + "message": "Add at least 3 <option> elements inside <datalist>" + }, + { + "type": "element_exists", + "value": "label", + "message": "Add a <label> for the input" + } + ] + }, + { + "id": "datalist-countries", + "title": "Country Selector", + "description": "Datalists work great for long lists like countries. Users can type to filter suggestions instantly.

The value attribute is what gets entered, and you can add display text after it.", + "task": "Create a country input:
1. Add a <label> saying Country:
2. Add an <input> with list=\"countries\"
3. Add a <datalist id=\"countries\"> with at least 4 country options", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 30px; background: linear-gradient(135deg, #e0f7fa 0%, #b2ebf2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } label { display: block; margin-bottom: 10px; font-weight: 600; color: #00695c; } input { width: 100%; padding: 12px 15px; border: 2px solid #26a69a; border-radius: 8px; font-size: 16px; background: white; } input:focus { outline: none; border-color: #00695c; box-shadow: 0 0 0 3px rgba(38,166,154,0.2); }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n\n\n ", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "datalist", + "message": "Add a <datalist> element" + }, + { + "type": "attribute_value", + "value": { "selector": "datalist", "attr": "id", "value": "countries" }, + "message": "Set id=\"countries\" on the datalist" + }, + { + "type": "attribute_value", + "value": { "selector": "input", "attr": "list", "value": "countries" }, + "message": "Connect the input using list=\"countries\"" + }, + { + "type": "element_count", + "value": { "selector": "option", "min": 4 }, + "message": "Add at least 4 country options" + } + ] + } + ] +} diff --git a/lessons/uk/27-html-dialog.json b/lessons/uk/27-html-dialog.json new file mode 100644 index 0000000..4b721c7 --- /dev/null +++ b/lessons/uk/27-html-dialog.json @@ -0,0 +1,83 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-dialog", + "title": "Dialogs", + "description": "Create modal dialogs without JavaScript libraries", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "dialog-basic", + "title": "Open Dialog", + "description": "The <dialog> element creates a native modal. Add the open attribute to show it.

Use <form method=\"dialog\"> inside to close it when the form submits - no JavaScript needed!", + "task": "Create a dialog with:
1. The open attribute to show it
2. An <h2> saying Welcome!
3. A <p> with a greeting message
4. A <form method=\"dialog\"> with a close button", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: #f5f5f5; } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 400px; } dialog::backdrop { background: rgba(0,0,0,0.5); } dialog h2 { margin: 0 0 15px 0; color: #1976d2; } dialog p { color: #666; margin: 0 0 20px 0; } dialog button { background: #1976d2; color: white; border: none; padding: 10px 25px; border-radius: 6px; cursor: pointer; font-size: 16px; } dialog button:hover { background: #1565c0; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n

Welcome!

\n

This is a native HTML dialog element.

\n
\n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "dialog", + "message": "Add a <dialog> element" + }, + { + "type": "attribute_value", + "value": { "selector": "dialog", "attr": "open", "value": true }, + "message": "Add the open attribute to show the dialog" + }, + { + "type": "element_exists", + "value": "dialog h2", + "message": "Add an <h2> heading inside the dialog" + }, + { + "type": "element_exists", + "value": "form[method='dialog']", + "message": "Add a <form method=\"dialog\"> for closing" + }, + { + "type": "element_exists", + "value": "dialog button", + "message": "Add a close button inside the form" + } + ] + }, + { + "id": "dialog-form", + "title": "Dialog + Form", + "description": "Dialogs can contain full forms. The method=\"dialog\" makes the form close the dialog on submit instead of sending data.

This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.", + "task": "Create a confirmation dialog:
1. Add open to show it
2. An <h2> saying Confirm Delete
3. A <p> asking Are you sure?
4. A <form method=\"dialog\"> with Cancel and Delete buttons", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: linear-gradient(135deg, #fce4ec 0%, #f8bbd9 100%); } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 350px; text-align: center; } dialog h2 { margin: 0 0 10px 0; color: #c62828; } dialog p { color: #666; margin: 0 0 20px 0; } dialog form { display: flex; gap: 10px; justify-content: center; } dialog button { padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px; border: none; } dialog button:first-child { background: #e0e0e0; color: #333; } dialog button:last-child { background: #c62828; color: white; } dialog button:hover { opacity: 0.9; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "\n

Confirm Delete

\n

Are you sure you want to delete this item?

\n
\n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "dialog[open]", + "message": "Add a <dialog> with the open attribute" + }, + { + "type": "element_exists", + "value": "dialog h2", + "message": "Add a heading to the dialog" + }, + { + "type": "element_exists", + "value": "form[method='dialog']", + "message": "Add a <form method=\"dialog\"> for the buttons" + }, + { + "type": "element_count", + "value": { "selector": "dialog button", "min": 2 }, + "message": "Add at least 2 buttons (Cancel and Confirm)" + } + ] + } + ] +} diff --git a/lessons/uk/28-html-forms-fieldset.json b/lessons/uk/28-html-forms-fieldset.json new file mode 100644 index 0000000..add7ef9 --- /dev/null +++ b/lessons/uk/28-html-forms-fieldset.json @@ -0,0 +1,127 @@ +{ + "$schema": "../../schemas/code-crispies-module-schema.json", + "id": "html-forms-fieldset", + "title": "Fieldsets", + "description": "Group form controls with fieldset and legend elements", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "fieldset-basic", + "title": "Grouping with Fieldset", + "description": "The <fieldset> element groups related form controls together. Add a <legend> as the first child to give the group a title.

This helps with accessibility and visual organization of complex forms.", + "task": "Create a form with a fieldset:
1. A <form> element
2. A <fieldset> inside
3. A <legend> saying Personal Info
4. Two labeled inputs for name and email", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f0f4f8; } form { max-width: 400px; } fieldset { border: 2px solid #3498db; border-radius: 10px; padding: 20px; background: white; } legend { color: #3498db; font-weight: 600; padding: 0 10px; font-size: 1.1rem; } label { display: block; margin: 15px 0 5px; color: #333; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; } input:focus { outline: 2px solid #3498db; border-color: transparent; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Personal Info\n \n \n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "form", + "message": "Add a <form> element" + }, + { + "type": "element_exists", + "value": "fieldset", + "message": "Add a <fieldset> inside the form" + }, + { + "type": "element_exists", + "value": "legend", + "message": "Add a <legend> to title your fieldset" + }, + { + "type": "element_count", + "value": { "selector": "label", "min": 2 }, + "message": "Add at least 2 labels" + }, + { + "type": "element_count", + "value": { "selector": "input", "min": 2 }, + "message": "Add at least 2 input fields" + } + ] + }, + { + "id": "fieldset-textarea", + "title": "Adding Textarea", + "description": "The <textarea> element creates a multi-line text input, perfect for longer content like messages or descriptions.

Use rows and cols attributes to set default size.", + "task": "Create a contact form:
1. A <fieldset> with <legend> Contact Us
2. A labeled <input> for email
3. A labeled <textarea> for the message
4. A submit <button>", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } form { max-width: 450px; margin: 0 auto; } fieldset { border: none; border-radius: 15px; padding: 30px; background: white; box-shadow: 0 10px 40px rgba(0,0,0,0.2); } legend { color: #667eea; font-weight: 700; padding: 0; font-size: 1.5rem; margin-bottom: 10px; } label { display: block; margin: 20px 0 8px; color: #333; font-weight: 500; } input, textarea { width: 100%; padding: 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: none; border-color: #667eea; } textarea { resize: vertical; min-height: 100px; } button { margin-top: 20px; width: 100%; padding: 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; } button:hover { opacity: 0.9; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Contact Us\n \n \n \n \n \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "fieldset", + "message": "Add a <fieldset> element" + }, + { + "type": "element_exists", + "value": "legend", + "message": "Add a <legend> element" + }, + { + "type": "element_exists", + "value": "textarea", + "message": "Add a <textarea> for the message" + }, + { + "type": "element_exists", + "value": "button", + "message": "Add a submit button" + }, + { + "type": "element_exists", + "value": "input", + "message": "Add an input field for email" + } + ] + }, + { + "id": "fieldset-multiple", + "title": "Multiple Fieldsets", + "description": "Complex forms can use multiple <fieldset> elements to organize different sections.

This improves usability for long forms like registration or checkout pages.", + "task": "Create a registration form with 2 fieldsets:
1. Account Info with username and password inputs
2. Preferences with a textarea for bio
3. A submit button outside the fieldsets", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } form { max-width: 500px; } fieldset { border: 1px solid #ddd; border-radius: 10px; padding: 20px; margin-bottom: 20px; background: white; } legend { color: #2c3e50; font-weight: 600; padding: 0 10px; } label { display: block; margin: 15px 0 5px; color: #555; } input, textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: 2px solid #3498db; border-color: transparent; } textarea { resize: vertical; min-height: 80px; } button { width: 100%; padding: 14px; background: #2c3e50; color: white; border: none; border-radius: 8px; font-size: 16px; cursor: pointer; } button:hover { background: #34495e; }", + "sandboxCSS": "", + "initialCode": "", + "solution": "
\n
\n Account Info\n \n \n \n \n
\n
\n Preferences\n \n \n
\n \n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_count", + "value": { "selector": "fieldset", "min": 2 }, + "message": "Create at least 2 fieldsets" + }, + { + "type": "element_count", + "value": { "selector": "legend", "min": 2 }, + "message": "Add a legend to each fieldset" + }, + { + "type": "element_exists", + "value": "textarea", + "message": "Add a textarea for the bio" + }, + { + "type": "element_exists", + "value": "button", + "message": "Add a submit button" + }, + { + "type": "element_count", + "value": { "selector": "input", "min": 2 }, + "message": "Add at least 2 input fields" + } + ] + } + ] +} diff --git a/lessons/uk/30-html-tables.json b/lessons/uk/30-html-tables.json index 1bdfaeb..f4d29cf 100644 --- a/lessons/uk/30-html-tables.json +++ b/lessons/uk/30-html-tables.json @@ -10,7 +10,7 @@ "id": "table-basic", "title": "Basic Table Structure", "description": "Tables use <table> with <tr> for rows. Inside rows, use <th> for headers and <td> for data cells.

The <caption> element provides an accessible title for the table.", - "task": "Create a simple table with:
1. A <caption> saying 'Fruit Prices'
2. A header row with 'Fruit' and 'Price' columns
3. At least 2 data rows", + "task": "Create a simple table with:
1. A <caption> saying Fruit Prices
2. A header row with Fruit and Price columns
3. At least 2 data rows", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f5f5f5; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px; font-weight: 600; font-size: 1.2rem; color: #333; background: #f8f9fa; } th, td { padding: 12px 20px; text-align: left; border-bottom: 1px solid #eee; } th { background: #3498db; color: white; font-weight: 500; } tr:hover { background: #f8f9fa; } tr:last-child td { border-bottom: none; }", "sandboxCSS": "", @@ -44,7 +44,7 @@ "id": "table-thead-tbody", "title": "Table Head & Body", "description": "Use <thead> to group header rows and <tbody> to group data rows. This helps browsers and assistive technology understand the table structure.

You can also use <tfoot> for footer rows like totals.", - "task": "Create a structured table:
1. A <caption> with 'Monthly Sales'
2. A <thead> with Month and Revenue headers
3. A <tbody> with at least 2 data rows", + "task": "Create a structured table:
1. A <caption> with Monthly Sales
2. A <thead> with Month and Revenue headers
3. A <tbody> with at least 2 data rows", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } table { border-collapse: collapse; width: 100%; max-width: 450px; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.2); } caption { padding: 20px; font-weight: 700; font-size: 1.3rem; color: white; background: transparent; text-shadow: 0 2px 4px rgba(0,0,0,0.2); caption-side: top; } thead { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } th { padding: 15px 20px; text-align: left; color: white; font-weight: 500; } tbody tr { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } td { padding: 15px 20px; color: #333; } tbody tr:last-child { border-bottom: none; }", "sandboxCSS": "", @@ -83,7 +83,7 @@ "id": "table-complete", "title": "Complete Table with Footer", "description": "Add <tfoot> to create a footer section for totals or summary data. The footer stays at the bottom even if tbody has many rows.

Combine all sections for a fully structured, accessible table.", - "task": "Create a complete table:
1. A <caption> with 'Order Summary'
2. A <thead> with Item and Price headers
3. A <tbody> with 2 items
4. A <tfoot> with a Total row", + "task": "Create a complete table:
1. A <caption> with Order Summary
2. A <thead> with Item and Price headers
3. A <tbody> with 2 items
4. A <tfoot> with a Total row", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } table { border-collapse: collapse; width: 100%; max-width: 400px; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } caption { padding: 15px 20px; font-weight: 600; font-size: 1.1rem; color: #333; text-align: left; background: #f8f9fa; border-bottom: 2px solid #eee; } th, td { padding: 12px 20px; text-align: left; } thead th { background: #2c3e50; color: white; } tbody td { border-bottom: 1px solid #eee; } tbody tr:hover { background: #f8f9fa; } tfoot { background: #ecf0f1; font-weight: 600; } tfoot td { border-top: 2px solid #2c3e50; color: #2c3e50; }", "sandboxCSS": "", diff --git a/lessons/uk/31-html-marquee.json b/lessons/uk/31-html-marquee.json index 7c2939a..ec3a486 100644 --- a/lessons/uk/31-html-marquee.json +++ b/lessons/uk/31-html-marquee.json @@ -10,7 +10,7 @@ "id": "marquee-basic", "title": "Scrolling Text", "description": "The <marquee> element creates scrolling text - a classic from the early web! While deprecated, it still works in most browsers.

Note: For production, use CSS animations instead. But for learning and fun, marquee is great!", - "task": "Create a simple marquee:
1. Add a <marquee> element
2. Put some text inside like 'Welcome to my website!'", + "task": "Create a simple marquee:
1. Add a <marquee> element
2. Put some text inside like Welcome to my website!", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%); min-height: 150px; display: flex; align-items: center; } marquee { font-size: 2rem; color: #00ff00; text-shadow: 0 0 10px #00ff00, 0 0 20px #00ff00; font-family: 'Courier New', monospace; }", "sandboxCSS": "",