fix(lessons): use code tags for quoted text, add syntax examples
Some checks failed
Deploy static content to Pages / deploy (push) Has been cancelled
Some checks failed
Deploy static content to Pages / deploy (push) Has been cancelled
- Fixed validation in welcome lesson (Hello World instead of Hello) - Replaced 'quoted text' with <code>quoted text</code> in all task descriptions - Added syntax examples to Transitions and Keyframes lessons - Updated all language versions (en, de, pl, es, ar, uk)
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
"id": "get-started",
|
||||
"title": "Get Started",
|
||||
"description": "<strong>Code Crispies</strong> is a free, open-source platform for learning web development through hands-on exercises. No account required!<br><br><strong>What you'll learn:</strong><br>• <strong>HTML</strong> - Semantic elements, forms, tables, SVG (<em>HTML Block & Inline</em>, <em>HTML Forms</em>, <em>HTML Tables</em>)<br>• <strong>CSS</strong> - Selectors, box model, flexbox, animations (<em>CSS Selectors</em>, <em>CSS Box Model</em>, <em>CSS Flexbox</em>)<br>• <strong>Responsive Design</strong> - Media queries and mobile-first layouts<br><br><strong>How it works:</strong><br>1. Read the task in the left panel<br>2. Write code in the editor<br>3. See live results in the preview<br>4. Get instant feedback with hints<br><br><strong>Keyboard shortcuts:</strong> <kbd>Ctrl+Z</kbd> to undo, <kbd>Ctrl+Shift+Z</kbd> to redo<br><br><strong>More resources:</strong><br>• <a href=\"https://nextlevelshit.github.io/html-over-js/\" target=\"_blank\">HTML over JS</a> - Native HTML vs JavaScript solutions<br>• <a href=\"https://nextlevelshit.github.io/web-engineering-mandala/\" target=\"_blank\">Web Engineering Mandala</a> - JavaScript technology roadmap",
|
||||
"task": "Hello World",
|
||||
"task": "Write <code>Hello World</code>",
|
||||
"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 <code>Hello World</code>"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{
|
||||
"id": "transitions-1",
|
||||
"title": "Transitions",
|
||||
"description": "Learn how to apply <kbd>transition</kbd> to properties for smooth changes on state changes.",
|
||||
"description": "Learn how to apply <kbd>transition</kbd> to properties for smooth changes on state changes.<br><br><pre>.element {\n transition: property duration;\n /* e.g. transition: background-color 0.3s; */\n}</pre>",
|
||||
"task": "Add <kbd>transition: background-color 0.3s</kbd> to <kbd>.btn</kbd> so the color fades smoothly on hover.",
|
||||
"previewHTML": "<button class=\"btn\">Hover Me</button>",
|
||||
"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 <kbd>@keyframes</kbd> and apply them via the <kbd>animation</kbd> shorthand.",
|
||||
"description": "Create named animations using <kbd>@keyframes</kbd> and apply them via the <kbd>animation</kbd> shorthand.<br><br><pre>@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}</pre>",
|
||||
"task": "Define a keyframe at <kbd>50%</kbd> with <kbd>transform: translateY(-20px)</kbd> and apply <kbd>animation: bounce 1s infinite</kbd> to <kbd>.ball</kbd>.",
|
||||
"previewHTML": "<div class=\"ball\"></div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"id": "semantic-containers",
|
||||
"title": "Semantic Tags",
|
||||
"description": "Modern HTML uses semantic containers that describe their content:<br><br><kbd><header></kbd> - Page or section header<br><kbd><nav></kbd> - Navigation links<br><kbd><main></kbd> - Main content area<br><kbd><section></kbd> - Thematic grouping<br><kbd><article></kbd> - Self-contained content<br><kbd><footer></kbd> - Page or section footer",
|
||||
"task": "Create a basic page structure:<br>1. Add a <kbd><header></kbd> with an <kbd><h1></kbd> containing the text 'My Website'<br>2. Add a <kbd><main></kbd> element with a paragraph saying 'Welcome to my site!'<br>3. Add a <kbd><footer></kbd> with a paragraph saying 'Copyright 2025'",
|
||||
"task": "Create a basic page structure:<br>1. Add a <kbd><header></kbd> with an <kbd><h1></kbd> containing the text <code>My Website</code><br>2. Add a <kbd><main></kbd> element with a paragraph saying <code>Welcome to my site!</code><br>3. Add a <kbd><footer></kbd> with a paragraph saying <code>Copyright 2025</code>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "form-structure",
|
||||
"title": "Form Structure",
|
||||
"description": "Every form needs a <kbd><form></kbd> wrapper. Inside, use <kbd><label></kbd> to describe inputs and <kbd><input></kbd> for user data entry.<br><br>The <kbd>for</kbd> attribute on labels should match the <kbd>id</kbd> on inputs for accessibility.",
|
||||
"task": "Create a form with:<br>1. A <kbd><label></kbd> with the text 'Name:' and <kbd>for=\"name\"</kbd> attribute<br>2. A text <kbd><input></kbd> with <kbd>id=\"name\"</kbd> and <kbd>name=\"name\"</kbd> attributes",
|
||||
"task": "Create a form with:<br>1. A <kbd><label></kbd> with the text <code>Name:</code> and <kbd>for=\"name\"</kbd> attribute<br>2. A text <kbd><input></kbd> with <kbd>id=\"name\"</kbd> and <kbd>name=\"name\"</kbd> 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:<br><br><kbd><button type=\"submit\"></kbd> - Preferred, flexible content<br><kbd><input type=\"submit\"></kbd> - Simple text-only button<br><br>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:<br><br><kbd><button type=\"submit\"></kbd> - Preferred, flexible content<br><kbd><input type=\"submit\"></kbd> - Simple text-only button<br><br>The button text should be action-oriented (e.g., <code>Sign In</code>, 'Register', 'Send').",
|
||||
"task": "Add a submit button to the form with the text <code>Sign In</code>.",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "details-summary-basic",
|
||||
"title": "First Widget",
|
||||
"description": "The <kbd><details></kbd> element creates a collapsible section. The <kbd><summary></kbd> provides the clickable label.<br><br>Click the summary to toggle the hidden content - no JavaScript needed!",
|
||||
"task": "Create a <kbd><details></kbd> element with:<br>1. A <kbd><summary></kbd> saying 'Click to reveal'<br>2. A <kbd><p></kbd> with the text 'This content was hidden!'",
|
||||
"task": "Create a <kbd><details></kbd> element with:<br>1. A <kbd><summary></kbd> saying <code>Click to reveal</code><br>2. A <kbd><p></kbd> with the text <code>This content was hidden!</code>",
|
||||
"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 <kbd><details></kbd> elements create an accordion-style FAQ. Each question can be expanded independently.<br><br><b>Pro tip:</b> Type <kbd>details*3>summary+p</kbd> and press Tab for Emmet expansion. <kbd>*3</kbd> creates 3 elements, <kbd>></kbd> nests inside, <kbd>+</kbd> adds siblings.",
|
||||
"task": "Create an FAQ section with:<br>1. An <kbd><h1></kbd> saying 'Frequently Asked Questions'<br>2. Three <kbd><details></kbd> elements, each with a question in <kbd><summary></kbd> and an answer in <kbd><p></kbd>",
|
||||
"task": "Create an FAQ section with:<br>1. An <kbd><h1></kbd> saying <code>Frequently Asked Questions</code><br>2. Three <kbd><details></kbd> elements, each with a question in <kbd><summary></kbd> and an answer in <kbd><p></kbd>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "progress-basic",
|
||||
"title": "Progress Bars",
|
||||
"description": "The <kbd><progress></kbd> element shows task completion. Use <kbd>value</kbd> for current progress and <kbd>max</kbd> for the total.<br><br><b>Note:</b> This is not a self-closing tag! Write <kbd><progress>...</progress></kbd> with fallback text inside for older browsers.",
|
||||
"task": "Create a progress bar showing 70% completion:<br>1. Add a <kbd><label></kbd> saying 'Download:'<br>2. Add a <kbd><progress></kbd> with <kbd>value=\"70\"</kbd> and <kbd>max=\"100\"</kbd>",
|
||||
"task": "Create a progress bar showing 70% completion:<br>1. Add a <kbd><label></kbd> saying <code>Download:</code><br>2. Add a <kbd><progress></kbd> with <kbd>value=\"70\"</kbd> and <kbd>max=\"100\"</kbd>",
|
||||
"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 <kbd>value</kbd> attribute. This creates an animated indeterminate state.<br><br>Useful for network requests or processes with unknown duration.",
|
||||
"task": "Create a loading indicator:<br>1. Add a <kbd><p></kbd> saying 'Loading...'<br>2. Add a <kbd><progress></kbd> without a value attribute",
|
||||
"task": "Create a loading indicator:<br>1. Add a <kbd><p></kbd> saying <code>Loading...</code><br>2. Add a <kbd><progress></kbd> 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 <kbd><meter></kbd> element displays a scalar value within a range. Use it for measurements like disk space, battery, or ratings.<br><br>Set <kbd>low</kbd>, <kbd>high</kbd>, and <kbd>optimum</kbd> to define good/bad ranges - the browser colors it accordingly!",
|
||||
"task": "Create a battery level meter:<br>1. Add a <kbd><label></kbd> saying 'Battery:'<br>2. Add a <kbd><meter></kbd> with:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> and <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> and <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"task": "Create a battery level meter:<br>1. Add a <kbd><label></kbd> saying <code>Battery:</code><br>2. Add a <kbd><meter></kbd> with:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> and <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> and <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"previewHTML": "",
|
||||
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }",
|
||||
"sandboxCSS": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "datalist-basic",
|
||||
"title": "Input with Suggestions",
|
||||
"description": "The <kbd><datalist></kbd> element provides autocomplete suggestions for inputs. Connect it using the <kbd>list</kbd> attribute on the input matching the datalist's <kbd>id</kbd>.<br><br>Users can still type freely - suggestions are just helpers!",
|
||||
"task": "Create a browser selector:<br>1. Add a <kbd><label></kbd> saying 'Browser:'<br>2. Add an <kbd><input></kbd> with <kbd>list=\"browsers\"</kbd><br>3. Add a <kbd><datalist id=\"browsers\"></kbd> with options for Chrome, Firefox, and Safari",
|
||||
"task": "Create a browser selector:<br>1. Add a <kbd><label></kbd> saying <code>Browser:</code><br>2. Add an <kbd><input></kbd> with <kbd>list=\"browsers\"</kbd><br>3. Add a <kbd><datalist id=\"browsers\"></kbd> 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.<br><br>The <kbd>value</kbd> attribute is what gets entered, and you can add display text after it.",
|
||||
"task": "Create a country input:<br>1. Add a <kbd><label></kbd> saying 'Country:'<br>2. Add an <kbd><input></kbd> with <kbd>list=\"countries\"</kbd><br>3. Add a <kbd><datalist id=\"countries\"></kbd> with at least 4 country options",
|
||||
"task": "Create a country input:<br>1. Add a <kbd><label></kbd> saying <code>Country:</code><br>2. Add an <kbd><input></kbd> with <kbd>list=\"countries\"</kbd><br>3. Add a <kbd><datalist id=\"countries\"></kbd> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "dialog-basic",
|
||||
"title": "Open Dialog",
|
||||
"description": "The <kbd><dialog></kbd> element creates a native modal. Add the <kbd>open</kbd> attribute to show it.<br><br>Use <kbd><form method=\"dialog\"></kbd> inside to close it when the form submits - no JavaScript needed!",
|
||||
"task": "Create a dialog with:<br>1. The <kbd>open</kbd> attribute to show it<br>2. An <kbd><h2></kbd> saying 'Welcome!'<br>3. A <kbd><p></kbd> with a greeting message<br>4. A <kbd><form method=\"dialog\"></kbd> with a close button",
|
||||
"task": "Create a dialog with:<br>1. The <kbd>open</kbd> attribute to show it<br>2. An <kbd><h2></kbd> saying <code>Welcome!</code><br>3. A <kbd><p></kbd> with a greeting message<br>4. A <kbd><form method=\"dialog\"></kbd> 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 <kbd>method=\"dialog\"</kbd> makes the form close the dialog on submit instead of sending data.<br><br>This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.",
|
||||
"task": "Create a confirmation dialog:<br>1. Add <kbd>open</kbd> to show it<br>2. An <kbd><h2></kbd> saying 'Confirm Delete'<br>3. A <kbd><p></kbd> asking 'Are you sure?'<br>4. A <kbd><form method=\"dialog\"></kbd> with Cancel and Delete buttons",
|
||||
"task": "Create a confirmation dialog:<br>1. Add <kbd>open</kbd> to show it<br>2. An <kbd><h2></kbd> saying <code>Confirm Delete</code><br>3. A <kbd><p></kbd> asking <code>Are you sure?</code><br>4. A <kbd><form method=\"dialog\"></kbd> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "fieldset-basic",
|
||||
"title": "Grouping with Fieldset",
|
||||
"description": "The <kbd><fieldset></kbd> element groups related form controls together. Add a <kbd><legend></kbd> as the first child to give the group a title.<br><br>This helps with accessibility and visual organization of complex forms.",
|
||||
"task": "Create a form with a fieldset:<br>1. A <kbd><form></kbd> element<br>2. A <kbd><fieldset></kbd> inside<br>3. A <kbd><legend></kbd> saying 'Personal Info'<br>4. Two labeled inputs for name and email",
|
||||
"task": "Create a form with a fieldset:<br>1. A <kbd><form></kbd> element<br>2. A <kbd><fieldset></kbd> inside<br>3. A <kbd><legend></kbd> saying <code>Personal Info</code><br>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 <kbd><textarea></kbd> element creates a multi-line text input, perfect for longer content like messages or descriptions.<br><br>Use <kbd>rows</kbd> and <kbd>cols</kbd> attributes to set default size.",
|
||||
"task": "Create a contact form:<br>1. A <kbd><fieldset></kbd> with <kbd><legend></kbd> 'Contact Us'<br>2. A labeled <kbd><input></kbd> for email<br>3. A labeled <kbd><textarea></kbd> for the message<br>4. A submit <kbd><button></kbd>",
|
||||
"task": "Create a contact form:<br>1. A <kbd><fieldset></kbd> with <kbd><legend></kbd> <code>Contact Us</code><br>2. A labeled <kbd><input></kbd> for email<br>3. A labeled <kbd><textarea></kbd> for the message<br>4. A submit <kbd><button></kbd>",
|
||||
"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 <kbd><fieldset></kbd> elements to organize different sections.<br><br>This improves usability for long forms like registration or checkout pages.",
|
||||
"task": "Create a registration form with 2 fieldsets:<br>1. 'Account Info' with username and password inputs<br>2. 'Preferences' with a textarea for bio<br>3. A submit button outside the fieldsets",
|
||||
"task": "Create a registration form with 2 fieldsets:<br>1. <code>Account Info</code> with username and password inputs<br>2. <code>Preferences</code> with a textarea for bio<br>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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "table-basic",
|
||||
"title": "Basic Table Structure",
|
||||
"description": "Tables use <kbd><table></kbd> with <kbd><tr></kbd> for rows. Inside rows, use <kbd><th></kbd> for headers and <kbd><td></kbd> for data cells.<br><br>The <kbd><caption></kbd> element provides an accessible title for the table.",
|
||||
"task": "Create a simple table with:<br>1. A <kbd><caption></kbd> saying 'Fruit Prices'<br>2. A header row with 'Fruit' and 'Price' columns<br>3. At least 2 data rows",
|
||||
"task": "Create a simple table with:<br>1. A <kbd><caption></kbd> saying <code>Fruit Prices</code><br>2. A header row with <code>Fruit</code> and <code>Price</code> columns<br>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 <kbd><thead></kbd> to group header rows and <kbd><tbody></kbd> to group data rows. This helps browsers and assistive technology understand the table structure.<br><br>You can also use <kbd><tfoot></kbd> for footer rows like totals.",
|
||||
"task": "Create a structured table:<br>1. A <kbd><caption></kbd> with 'Monthly Sales'<br>2. A <kbd><thead></kbd> with Month and Revenue headers<br>3. A <kbd><tbody></kbd> with at least 2 data rows",
|
||||
"task": "Create a structured table:<br>1. A <kbd><caption></kbd> with <code>Monthly Sales</code><br>2. A <kbd><thead></kbd> with Month and Revenue headers<br>3. A <kbd><tbody></kbd> 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 <kbd><tfoot></kbd> to create a footer section for totals or summary data. The footer stays at the bottom even if tbody has many rows.<br><br>Combine all sections for a fully structured, accessible table.",
|
||||
"task": "Create a complete table:<br>1. A <kbd><caption></kbd> with 'Order Summary'<br>2. A <kbd><thead></kbd> with Item and Price headers<br>3. A <kbd><tbody></kbd> with 2 items<br>4. A <kbd><tfoot></kbd> with a Total row",
|
||||
"task": "Create a complete table:<br>1. A <kbd><caption></kbd> with <code>Order Summary</code><br>2. A <kbd><thead></kbd> with Item and Price headers<br>3. A <kbd><tbody></kbd> with 2 items<br>4. A <kbd><tfoot></kbd> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "marquee-basic",
|
||||
"title": "Scrolling Text",
|
||||
"description": "The <kbd><marquee></kbd> element creates scrolling text - a classic from the early web! While deprecated, it still works in most browsers.<br><br>Note: For production, use CSS animations instead. But for learning and fun, marquee is great!",
|
||||
"task": "Create a simple marquee:<br>1. Add a <kbd><marquee></kbd> element<br>2. Put some text inside like 'Welcome to my website!'",
|
||||
"task": "Create a simple marquee:<br>1. Add a <kbd><marquee></kbd> element<br>2. Put some text inside like <code>Welcome to my website!</code>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "get-started",
|
||||
"title": "Get Started",
|
||||
"description": "<strong>Code Crispies</strong> is a free, open-source platform for learning web development through hands-on exercises. No account required!<br><br><strong>What you'll learn:</strong><br>• <strong>HTML</strong> - Semantic elements, forms, tables, SVG (<em>HTML Block & Inline</em>, <em>HTML Forms</em>, <em>HTML Tables</em>)<br>• <strong>CSS</strong> - Selectors, box model, flexbox, animations (<em>CSS Selectors</em>, <em>CSS Box Model</em>, <em>CSS Flexbox</em>)<br>• <strong>Responsive Design</strong> - Media queries and mobile-first layouts<br><br><strong>How it works:</strong><br>1. Read the task in the left panel<br>2. Write code in the editor<br>3. See live results in the preview<br>4. Get instant feedback with hints<br><br><strong>Keyboard shortcuts:</strong> <kbd>Ctrl+Z</kbd> to undo, <kbd>Ctrl+Shift+Z</kbd> to redo<br><br><strong>More resources:</strong><br>• <a href=\"https://nextlevelshit.github.io/html-over-js/\" target=\"_blank\">HTML over JS</a> - Native HTML vs JavaScript solutions<br>• <a href=\"https://nextlevelshit.github.io/web-engineering-mandala/\" target=\"_blank\">Web Engineering Mandala</a> - JavaScript technology roadmap",
|
||||
"task": "Hello World",
|
||||
"task": "Write <code>Hello World</code>",
|
||||
"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 <code>Hello World</code>"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -39,7 +39,7 @@
|
||||
"validations": [
|
||||
{
|
||||
"type": "contains",
|
||||
"value": "Hello",
|
||||
"value": "Hello World",
|
||||
"message": "Click Next to continue"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{
|
||||
"id": "transitions-1",
|
||||
"title": "Transitions",
|
||||
"description": "Learn how to apply <kbd>transition</kbd> to properties for smooth changes on state changes.",
|
||||
"description": "Learn how to apply <kbd>transition</kbd> to properties for smooth changes on state changes.<br><br><pre>.element {\n transition: property duration;\n /* e.g. transition: background-color 0.3s; */\n}</pre>",
|
||||
"task": "Add <kbd>transition: background-color 0.3s</kbd> to <kbd>.btn</kbd> so the color fades smoothly on hover.",
|
||||
"previewHTML": "<button class=\"btn\">Hover Me</button>",
|
||||
"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 <kbd>@keyframes</kbd> and apply them via the <kbd>animation</kbd> shorthand.",
|
||||
"description": "Create named animations using <kbd>@keyframes</kbd> and apply them via the <kbd>animation</kbd> shorthand.<br><br><pre>@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}</pre>",
|
||||
"task": "Define a keyframe at <kbd>50%</kbd> with <kbd>transform: translateY(-20px)</kbd> and apply <kbd>animation: bounce 1s infinite</kbd> to <kbd>.ball</kbd>.",
|
||||
"previewHTML": "<div class=\"ball\"></div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"id": "semantic-containers",
|
||||
"title": "Semantic Tags",
|
||||
"description": "Modern HTML uses semantic containers that describe their content:<br><br><kbd><header></kbd> - Page or section header<br><kbd><nav></kbd> - Navigation links<br><kbd><main></kbd> - Main content area<br><kbd><section></kbd> - Thematic grouping<br><kbd><article></kbd> - Self-contained content<br><kbd><footer></kbd> - Page or section footer",
|
||||
"task": "Create a basic page structure:<br>1. Add a <kbd><header></kbd> with an <kbd><h1></kbd> containing the text 'My Website'<br>2. Add a <kbd><main></kbd> element with a paragraph saying 'Welcome to my site!'<br>3. Add a <kbd><footer></kbd> with a paragraph saying 'Copyright 2025'",
|
||||
"task": "Create a basic page structure:<br>1. Add a <kbd><header></kbd> with an <kbd><h1></kbd> containing the text <code>My Website</code><br>2. Add a <kbd><main></kbd> element with a paragraph saying <code>Welcome to my site!</code><br>3. Add a <kbd><footer></kbd> with a paragraph saying <code>Copyright 2025</code>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "form-structure",
|
||||
"title": "Form Structure",
|
||||
"description": "Every form needs a <kbd><form></kbd> wrapper. Inside, use <kbd><label></kbd> to describe inputs and <kbd><input></kbd> for user data entry.<br><br>The <kbd>for</kbd> attribute on labels should match the <kbd>id</kbd> on inputs for accessibility.",
|
||||
"task": "Create a form with:<br>1. A <kbd><label></kbd> with the text 'Name:' and <kbd>for=\"name\"</kbd> attribute<br>2. A text <kbd><input></kbd> with <kbd>id=\"name\"</kbd> and <kbd>name=\"name\"</kbd> attributes",
|
||||
"task": "Create a form with:<br>1. A <kbd><label></kbd> with the text <code>Name:</code> and <kbd>for=\"name\"</kbd> attribute<br>2. A text <kbd><input></kbd> with <kbd>id=\"name\"</kbd> and <kbd>name=\"name\"</kbd> 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:<br><br><kbd><button type=\"submit\"></kbd> - Preferred, flexible content<br><kbd><input type=\"submit\"></kbd> - Simple text-only button<br><br>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:<br><br><kbd><button type=\"submit\"></kbd> - Preferred, flexible content<br><kbd><input type=\"submit\"></kbd> - Simple text-only button<br><br>The button text should be action-oriented (e.g., <code>Sign In</code>, 'Register', 'Send').",
|
||||
"task": "Add a submit button to the form with the text <code>Sign In</code>.",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "details-summary-basic",
|
||||
"title": "First Widget",
|
||||
"description": "The <kbd><details></kbd> element creates a collapsible section. The <kbd><summary></kbd> provides the clickable label.<br><br>Click the summary to toggle the hidden content - no JavaScript needed!",
|
||||
"task": "Create a <kbd><details></kbd> element with:<br>1. A <kbd><summary></kbd> saying 'Click to reveal'<br>2. A <kbd><p></kbd> with the text 'This content was hidden!'",
|
||||
"task": "Create a <kbd><details></kbd> element with:<br>1. A <kbd><summary></kbd> saying <code>Click to reveal</code><br>2. A <kbd><p></kbd> with the text <code>This content was hidden!</code>",
|
||||
"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 <kbd><details></kbd> elements create an accordion-style FAQ. Each question can be expanded independently.<br><br><b>Pro tip:</b> Type <kbd>details*3>summary+p</kbd> and press Tab for Emmet expansion. <kbd>*3</kbd> creates 3 elements, <kbd>></kbd> nests inside, <kbd>+</kbd> adds siblings.",
|
||||
"task": "Create an FAQ section with:<br>1. An <kbd><h1></kbd> saying 'Frequently Asked Questions'<br>2. Three <kbd><details></kbd> elements, each with a question in <kbd><summary></kbd> and an answer in <kbd><p></kbd>",
|
||||
"task": "Create an FAQ section with:<br>1. An <kbd><h1></kbd> saying <code>Frequently Asked Questions</code><br>2. Three <kbd><details></kbd> elements, each with a question in <kbd><summary></kbd> and an answer in <kbd><p></kbd>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "progress-basic",
|
||||
"title": "Progress Bars",
|
||||
"description": "The <kbd><progress></kbd> element shows task completion. Use <kbd>value</kbd> for current progress and <kbd>max</kbd> for the total.<br><br><b>Note:</b> This is not a self-closing tag! Write <kbd><progress>...</progress></kbd> with fallback text inside for older browsers.",
|
||||
"task": "Create a progress bar showing 70% completion:<br>1. Add a <kbd><label></kbd> saying 'Download:'<br>2. Add a <kbd><progress></kbd> with <kbd>value=\"70\"</kbd> and <kbd>max=\"100\"</kbd>",
|
||||
"task": "Create a progress bar showing 70% completion:<br>1. Add a <kbd><label></kbd> saying <code>Download:</code><br>2. Add a <kbd><progress></kbd> with <kbd>value=\"70\"</kbd> and <kbd>max=\"100\"</kbd>",
|
||||
"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 <kbd>value</kbd> attribute. This creates an animated indeterminate state.<br><br>Useful for network requests or processes with unknown duration.",
|
||||
"task": "Create a loading indicator:<br>1. Add a <kbd><p></kbd> saying 'Loading...'<br>2. Add a <kbd><progress></kbd> without a value attribute",
|
||||
"task": "Create a loading indicator:<br>1. Add a <kbd><p></kbd> saying <code>Loading...</code><br>2. Add a <kbd><progress></kbd> 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 <kbd><meter></kbd> element displays a scalar value within a range. Use it for measurements like disk space, battery, or ratings.<br><br>Set <kbd>low</kbd>, <kbd>high</kbd>, and <kbd>optimum</kbd> to define good/bad ranges - the browser colors it accordingly!",
|
||||
"task": "Create a battery level meter:<br>1. Add a <kbd><label></kbd> saying 'Battery:'<br>2. Add a <kbd><meter></kbd> with:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> and <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> and <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"task": "Create a battery level meter:<br>1. Add a <kbd><label></kbd> saying <code>Battery:</code><br>2. Add a <kbd><meter></kbd> with:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> and <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> and <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"previewHTML": "",
|
||||
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }",
|
||||
"sandboxCSS": "",
|
||||
|
||||
78
lessons/ar/25-html-datalist.json
Normal file
78
lessons/ar/25-html-datalist.json
Normal file
@@ -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 <kbd><datalist></kbd> element provides autocomplete suggestions for inputs. Connect it using the <kbd>list</kbd> attribute on the input matching the datalist's <kbd>id</kbd>.<br><br>Users can still type freely - suggestions are just helpers!",
|
||||
"task": "Create a browser selector:<br>1. Add a <kbd><label></kbd> saying <code>Browser:</code><br>2. Add an <kbd><input></kbd> with <kbd>list=\"browsers\"</kbd><br>3. Add a <kbd><datalist id=\"browsers\"></kbd> 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": "<label for=\"browser\">Browser:</label>\n<input type=\"text\" id=\"browser\" list=\"browsers\">\n<datalist id=\"browsers\">\n <option value=\"Chrome\">\n <option value=\"Firefox\">\n <option value=\"Safari\">\n</datalist>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "datalist",
|
||||
"message": "Add a <kbd><datalist></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "input", "attr": "list", "value": "browsers" },
|
||||
"message": "Connect the input to datalist using <kbd>list=</kbd>\"browsers\""
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "option", "min": 3 },
|
||||
"message": "Add at least 3 <kbd><option></kbd> elements inside <kbd><datalist></kbd>"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "label",
|
||||
"message": "Add a <kbd><label></kbd> 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.<br><br>The <kbd>value</kbd> attribute is what gets entered, and you can add display text after it.",
|
||||
"task": "Create a country input:<br>1. Add a <kbd><label></kbd> saying <code>Country:</code><br>2. Add an <kbd><input></kbd> with <kbd>list=\"countries\"</kbd><br>3. Add a <kbd><datalist id=\"countries\"></kbd> 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": "<label for=\"country\">Country:</label>\n<input type=\"text\" id=\"country\" list=\"countries\" placeholder=\"Start typing...\">\n<datalist id=\"countries\">\n <option value=\"Germany\">\n <option value=\"France\">\n <option value=\"Spain\">\n <option value=\"Italy\">\n</datalist>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "datalist",
|
||||
"message": "Add a <kbd><datalist></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "datalist", "attr": "id", "value": "countries" },
|
||||
"message": "Set <kbd>id=</kbd>\"countries\" on the datalist"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "input", "attr": "list", "value": "countries" },
|
||||
"message": "Connect the input using <kbd>list=</kbd>\"countries\""
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "option", "min": 4 },
|
||||
"message": "Add at least 4 country options"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
83
lessons/ar/27-html-dialog.json
Normal file
83
lessons/ar/27-html-dialog.json
Normal file
@@ -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 <kbd><dialog></kbd> element creates a native modal. Add the <kbd>open</kbd> attribute to show it.<br><br>Use <kbd><form method=\"dialog\"></kbd> inside to close it when the form submits - no JavaScript needed!",
|
||||
"task": "Create a dialog with:<br>1. The <kbd>open</kbd> attribute to show it<br>2. An <kbd><h2></kbd> saying <code>Welcome!</code><br>3. A <kbd><p></kbd> with a greeting message<br>4. A <kbd><form method=\"dialog\"></kbd> 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": "<dialog open>\n <h2>Welcome!</h2>\n <p>This is a native HTML dialog element.</p>\n <form method=\"dialog\">\n <button>Close</button>\n </form>\n</dialog>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog",
|
||||
"message": "Add a <kbd><dialog></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "dialog", "attr": "open", "value": true },
|
||||
"message": "Add the <kbd>open</kbd> attribute to show the dialog"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog h2",
|
||||
"message": "Add an <kbd><h2></kbd> heading inside the dialog"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "form[method='dialog']",
|
||||
"message": "Add a <kbd><form method=\"dialog\"></kbd> 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 <kbd>method=\"dialog\"</kbd> makes the form close the dialog on submit instead of sending data.<br><br>This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.",
|
||||
"task": "Create a confirmation dialog:<br>1. Add <kbd>open</kbd> to show it<br>2. An <kbd><h2></kbd> saying <code>Confirm Delete</code><br>3. A <kbd><p></kbd> asking <code>Are you sure?</code><br>4. A <kbd><form method=\"dialog\"></kbd> 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": "<dialog open>\n <h2>Confirm Delete</h2>\n <p>Are you sure you want to delete this item?</p>\n <form method=\"dialog\">\n <button value=\"cancel\">Cancel</button>\n <button value=\"delete\">Delete</button>\n </form>\n</dialog>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog[open]",
|
||||
"message": "Add a <kbd><dialog></kbd> 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 <kbd><form method=\"dialog\"></kbd> for the buttons"
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "dialog button", "min": 2 },
|
||||
"message": "Add at least 2 buttons (Cancel and Confirm)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
127
lessons/ar/28-html-forms-fieldset.json
Normal file
127
lessons/ar/28-html-forms-fieldset.json
Normal file
@@ -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 <kbd><fieldset></kbd> element groups related form controls together. Add a <kbd><legend></kbd> as the first child to give the group a title.<br><br>This helps with accessibility and visual organization of complex forms.",
|
||||
"task": "Create a form with a fieldset:<br>1. A <kbd><form></kbd> element<br>2. A <kbd><fieldset></kbd> inside<br>3. A <kbd><legend></kbd> saying <code>Personal Info</code><br>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": "<form>\n <fieldset>\n <legend>Personal Info</legend>\n <label for=\"name\">Name:</label>\n <input type=\"text\" id=\"name\" name=\"name\">\n <label for=\"email\">Email:</label>\n <input type=\"email\" id=\"email\" name=\"email\">\n </fieldset>\n</form>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "form",
|
||||
"message": "Add a <kbd><form></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "fieldset",
|
||||
"message": "Add a <kbd><fieldset></kbd> inside the form"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "legend",
|
||||
"message": "Add a <kbd><legend></kbd> 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 <kbd><textarea></kbd> element creates a multi-line text input, perfect for longer content like messages or descriptions.<br><br>Use <kbd>rows</kbd> and <kbd>cols</kbd> attributes to set default size.",
|
||||
"task": "Create a contact form:<br>1. A <kbd><fieldset></kbd> with <kbd><legend></kbd> <code>Contact Us</code><br>2. A labeled <kbd><input></kbd> for email<br>3. A labeled <kbd><textarea></kbd> for the message<br>4. A submit <kbd><button></kbd>",
|
||||
"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": "<form>\n <fieldset>\n <legend>Contact Us</legend>\n <label for=\"email\">Email:</label>\n <input type=\"email\" id=\"email\" name=\"email\">\n <label for=\"message\">Message:</label>\n <textarea id=\"message\" name=\"message\" rows=\"4\"></textarea>\n <button type=\"submit\">Send Message</button>\n </fieldset>\n</form>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "fieldset",
|
||||
"message": "Add a <kbd><fieldset></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "legend",
|
||||
"message": "Add a <kbd><legend></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "textarea",
|
||||
"message": "Add a <kbd><textarea></kbd> 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 <kbd><fieldset></kbd> elements to organize different sections.<br><br>This improves usability for long forms like registration or checkout pages.",
|
||||
"task": "Create a registration form with 2 fieldsets:<br>1. <code>Account Info</code> with username and password inputs<br>2. <code>Preferences</code> with a textarea for bio<br>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": "<form>\n <fieldset>\n <legend>Account Info</legend>\n <label for=\"username\">Username:</label>\n <input type=\"text\" id=\"username\" name=\"username\">\n <label for=\"password\">Password:</label>\n <input type=\"password\" id=\"password\" name=\"password\">\n </fieldset>\n <fieldset>\n <legend>Preferences</legend>\n <label for=\"bio\">Bio:</label>\n <textarea id=\"bio\" name=\"bio\"></textarea>\n </fieldset>\n <button type=\"submit\">Register</button>\n</form>",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "table-basic",
|
||||
"title": "Basic Table Structure",
|
||||
"description": "Tables use <kbd><table></kbd> with <kbd><tr></kbd> for rows. Inside rows, use <kbd><th></kbd> for headers and <kbd><td></kbd> for data cells.<br><br>The <kbd><caption></kbd> element provides an accessible title for the table.",
|
||||
"task": "Create a simple table with:<br>1. A <kbd><caption></kbd> saying 'Fruit Prices'<br>2. A header row with 'Fruit' and 'Price' columns<br>3. At least 2 data rows",
|
||||
"task": "Create a simple table with:<br>1. A <kbd><caption></kbd> saying <code>Fruit Prices</code><br>2. A header row with <code>Fruit</code> and <code>Price</code> columns<br>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 <kbd><thead></kbd> to group header rows and <kbd><tbody></kbd> to group data rows. This helps browsers and assistive technology understand the table structure.<br><br>You can also use <kbd><tfoot></kbd> for footer rows like totals.",
|
||||
"task": "Create a structured table:<br>1. A <kbd><caption></kbd> with 'Monthly Sales'<br>2. A <kbd><thead></kbd> with Month and Revenue headers<br>3. A <kbd><tbody></kbd> with at least 2 data rows",
|
||||
"task": "Create a structured table:<br>1. A <kbd><caption></kbd> with <code>Monthly Sales</code><br>2. A <kbd><thead></kbd> with Month and Revenue headers<br>3. A <kbd><tbody></kbd> 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 <kbd><tfoot></kbd> to create a footer section for totals or summary data. The footer stays at the bottom even if tbody has many rows.<br><br>Combine all sections for a fully structured, accessible table.",
|
||||
"task": "Create a complete table:<br>1. A <kbd><caption></kbd> with 'Order Summary'<br>2. A <kbd><thead></kbd> with Item and Price headers<br>3. A <kbd><tbody></kbd> with 2 items<br>4. A <kbd><tfoot></kbd> with a Total row",
|
||||
"task": "Create a complete table:<br>1. A <kbd><caption></kbd> with <code>Order Summary</code><br>2. A <kbd><thead></kbd> with Item and Price headers<br>3. A <kbd><tbody></kbd> with 2 items<br>4. A <kbd><tfoot></kbd> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "marquee-basic",
|
||||
"title": "Scrolling Text",
|
||||
"description": "The <kbd><marquee></kbd> element creates scrolling text - a classic from the early web! While deprecated, it still works in most browsers.<br><br>Note: For production, use CSS animations instead. But for learning and fun, marquee is great!",
|
||||
"task": "Create a simple marquee:<br>1. Add a <kbd><marquee></kbd> element<br>2. Put some text inside like 'Welcome to my website!'",
|
||||
"task": "Create a simple marquee:<br>1. Add a <kbd><marquee></kbd> element<br>2. Put some text inside like <code>Welcome to my website!</code>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "get-started",
|
||||
"title": "Los geht's",
|
||||
"description": "<strong>Code Crispies</strong> ist eine kostenlose Open-Source-Plattform zum Erlernen von Webentwicklung durch praktische Übungen. Kein Konto erforderlich!<br><br><strong>Was du lernst:</strong><br>• <strong>HTML</strong> - Semantische Elemente, Formulare, Tabellen, SVG (<em>HTML Block & Inline</em>, <em>HTML Formulare</em>, <em>HTML Tabellen</em>)<br>• <strong>CSS</strong> - Selektoren, Box-Model, Flexbox, Animationen (<em>CSS Selektoren</em>, <em>CSS Box-Model</em>, <em>CSS Flexbox</em>)<br>• <strong>Responsive Design</strong> - Media Queries und Mobile-First Layouts<br><br><strong>So funktioniert's:</strong><br>1. Lies die Aufgabe im linken Bereich<br>2. Schreibe Code im Editor<br>3. Sieh Live-Ergebnisse in der Vorschau<br>4. Bekomme sofortiges Feedback mit Hinweisen<br><br><strong>Tastenkürzel:</strong> <kbd>Strg+Z</kbd> rückgängig, <kbd>Strg+Umschalt+Z</kbd> wiederholen<br><br><strong>Weitere Ressourcen:</strong><br>• <a href=\"https://nextlevelshit.github.io/html-over-js/\" target=\"_blank\">HTML over JS</a> - Native HTML vs JavaScript-Lösungen<br>• <a href=\"https://nextlevelshit.github.io/web-engineering-mandala/\" target=\"_blank\">Web Engineering Mandala</a> - JavaScript Technologie-Roadmap",
|
||||
"task": "Hello World",
|
||||
"task": "Schreibe <code>Hello World</code>",
|
||||
"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 <code>Hello World</code>"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{
|
||||
"id": "transitions-1",
|
||||
"title": "Einfache Transitions",
|
||||
"description": "Lerne, wie du <code>transition</code> auf Eigenschaften anwendest für sanfte Änderungen bei Zustandswechseln.",
|
||||
"description": "Lerne, wie du <kbd>transition</kbd> auf Eigenschaften anwendest für sanfte Änderungen bei Zustandswechseln.<br><br><pre>.element {\n transition: property duration;\n /* z.B. transition: background-color 0.3s; */\n}</pre>",
|
||||
"task": "Füge <kbd>transition: background-color 0.3s</kbd> zu <kbd>.btn</kbd> hinzu, damit die Farbe beim Hover sanft überblendet.",
|
||||
"previewHTML": "<button class=\"btn\">Hover mich</button>",
|
||||
"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 <code>@keyframes</code> und wende sie mit der <code>animation</code> Kurzschreibweise an.",
|
||||
"description": "Erstelle benannte Animationen mit <kbd>@keyframes</kbd> und wende sie mit der <kbd>animation</kbd> Kurzschreibweise an.<br><br><pre>@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}</pre>",
|
||||
"task": "Definiere bei <kbd>50%</kbd> ein <kbd>transform: translateY(-20px)</kbd> und wende <kbd>animation: bounce 1s infinite</kbd> auf <kbd>.ball</kbd> an.",
|
||||
"previewHTML": "<div class=\"ball\"></div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: #ff0266; border-radius: 50%; margin: 2rem auto; }",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"id": "semantic-containers",
|
||||
"title": "Semantische Container-Elemente",
|
||||
"description": "Modernes HTML verwendet semantische Container, die ihren Inhalt beschreiben:<br><br><kbd><header></kbd> - Kopfbereich der Seite oder eines Abschnitts<br><kbd><nav></kbd> - Navigationslinks<br><kbd><main></kbd> - Hauptinhalt<br><kbd><section></kbd> - Thematische Gruppierung<br><kbd><article></kbd> - Eigenständiger Inhalt<br><kbd><footer></kbd> - Fußbereich der Seite oder eines Abschnitts",
|
||||
"task": "Erstelle eine einfache Seitenstruktur:<br>1. Füge ein <kbd><header></kbd> mit einem <kbd><h1></kbd> hinzu, das den Text 'Meine Webseite' enthält<br>2. Füge ein <kbd><main></kbd>-Element mit einem Absatz hinzu, der 'Willkommen auf meiner Seite!' sagt<br>3. Füge ein <kbd><footer></kbd> mit einem Absatz hinzu, der 'Copyright 2025' sagt",
|
||||
"task": "Erstelle eine einfache Seitenstruktur:<br>1. Füge ein <kbd><header></kbd> mit einem <kbd><h1></kbd> hinzu, das den Text <code>Meine Webseite</code> enthält<br>2. Füge ein <kbd><main></kbd>-Element mit einem Absatz hinzu, der <code>Willkommen auf meiner Seite!</code> sagt<br>3. Füge ein <kbd><footer></kbd> mit einem Absatz hinzu, der <code>Copyright 2025</code> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "form-structure",
|
||||
"title": "Formularstruktur",
|
||||
"description": "Jedes Formular benötigt einen <kbd><form></kbd>-Wrapper. Innerhalb verwendest du <kbd><label></kbd> zur Beschreibung der Eingaben und <kbd><input></kbd> für die Dateneingabe.<br><br>Das <kbd>for</kbd>-Attribut bei Labels sollte mit der <kbd>id</kbd> der Eingaben übereinstimmen für bessere Zugänglichkeit.",
|
||||
"task": "Erstelle ein Formular mit:<br>1. Einem <kbd><label></kbd> mit dem Text 'Name:' und dem <kbd>for=\"name\"</kbd>-Attribut<br>2. Einem Text-<kbd><input></kbd> mit <kbd>id=\"name\"</kbd> und <kbd>name=\"name\"</kbd>-Attributen",
|
||||
"task": "Erstelle ein Formular mit:<br>1. Einem <kbd><label></kbd> mit dem Text <code>Name:</code> und dem <kbd>for=\"name\"</kbd>-Attribut<br>2. Einem Text-<kbd><input></kbd> mit <kbd>id=\"name\"</kbd> und <kbd>name=\"name\"</kbd>-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:<br><br><kbd><button type=\"submit\"></kbd> - Bevorzugt, flexibler Inhalt<br><kbd><input type=\"submit\"></kbd> - Einfacher Text-Button<br><br>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:<br><br><kbd><button type=\"submit\"></kbd> - Bevorzugt, flexibler Inhalt<br><kbd><input type=\"submit\"></kbd> - Einfacher Text-Button<br><br>Der Button-Text sollte handlungsorientiert sein (z.B. <code>Anmelden</code>, 'Registrieren', 'Senden').",
|
||||
"task": "Füge dem Formular einen Absende-Button mit dem Text <code>Anmelden</code> 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 <code>Anmelden</code> anzeigen"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "details-summary-basic",
|
||||
"title": "Dein erstes Aufklapp-Element",
|
||||
"description": "Das <kbd><details></kbd>-Element erstellt einen aufklappbaren Bereich. Das <kbd><summary></kbd> bietet die anklickbare Beschriftung.<br><br>Klicke auf die Zusammenfassung, um den versteckten Inhalt anzuzeigen - kein JavaScript nötig!",
|
||||
"task": "Erstelle ein <kbd><details></kbd>-Element mit:<br>1. Einem <kbd><summary></kbd> mit dem Text 'Klicken zum Aufklappen'<br>2. Einem <kbd><p></kbd> mit dem Text 'Dieser Inhalt war versteckt!'",
|
||||
"task": "Erstelle ein <kbd><details></kbd>-Element mit:<br>1. Einem <kbd><summary></kbd> mit dem Text <code>Klicken zum Aufklappen</code><br>2. Einem <kbd><p></kbd> mit dem Text <code>Dieser Inhalt war versteckt!</code>",
|
||||
"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 <kbd><details></kbd>-Elemente erstellen ein Akkordeon-artiges FAQ. Jede Frage kann unabhängig aufgeklappt werden.<br><br><b>Pro-Tipp:</b> Tippe <kbd>details*3>summary+p</kbd> und drücke Tab für Emmet-Expansion. Das <kbd>*3</kbd> erstellt 3 Elemente, <kbd>></kbd> geht eine Ebene tiefer, <kbd>+</kbd> fügt Geschwister hinzu.",
|
||||
"task": "Erstelle einen FAQ-Bereich mit:<br>1. Einer <kbd><h1></kbd> mit dem Text 'Häufig gestellte Fragen'<br>2. Drei <kbd><details></kbd>-Elementen, jeweils mit einer Frage im <kbd><summary></kbd> und einer Antwort im <kbd><p></kbd>",
|
||||
"task": "Erstelle einen FAQ-Bereich mit:<br>1. Einer <kbd><h1></kbd> mit dem Text <code>Häufig gestellte Fragen</code><br>2. Drei <kbd><details></kbd>-Elementen, jeweils mit einer Frage im <kbd><summary></kbd> und einer Antwort im <kbd><p></kbd>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "progress-basic",
|
||||
"title": "Fortschrittsbalken",
|
||||
"description": "Das <kbd><progress></kbd>-Element zeigt den Aufgabenfortschritt. Verwende <kbd>value</kbd> für den aktuellen Stand und <kbd>max</kbd> für das Maximum.<br><br><b>Wichtig:</b> Dies ist kein selbstschließendes Tag! Schreibe <kbd><progress>...</progress></kbd> mit einem Fallback-Text dazwischen für ältere Browser.",
|
||||
"task": "Erstelle einen Fortschrittsbalken mit 70% Fortschritt:<br>1. Füge ein <kbd><label></kbd> mit 'Download:' hinzu<br>2. Füge ein <kbd><progress></kbd> mit <kbd>value=\"70\"</kbd> und <kbd>max=\"100\"</kbd> hinzu",
|
||||
"task": "Erstelle einen Fortschrittsbalken mit 70% Fortschritt:<br>1. Füge ein <kbd><label></kbd> mit <code>Download:</code> hinzu<br>2. Füge ein <kbd><progress></kbd> mit <kbd>value=\"70\"</kbd> und <kbd>max=\"100\"</kbd> 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 <kbd>value</kbd>-Attribut weg. Dies erstellt einen animierten unbestimmten Zustand.<br><br>Nützlich für Netzwerkanfragen oder Prozesse mit unbekannter Dauer.",
|
||||
"task": "Erstelle eine Ladeanzeige:<br>1. Füge ein <kbd><p></kbd> mit 'Lädt...' hinzu<br>2. Füge ein <kbd><progress></kbd> ohne value-Attribut hinzu",
|
||||
"task": "Erstelle eine Ladeanzeige:<br>1. Füge ein <kbd><p></kbd> mit <code>Lädt...</code> hinzu<br>2. Füge ein <kbd><progress></kbd> 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 <kbd><meter></kbd>-Element zeigt einen Skalarwert innerhalb eines Bereichs. Verwende es für Messungen wie Speicherplatz, Akku oder Bewertungen.<br><br>Setze <kbd>low</kbd>, <kbd>high</kbd> und <kbd>optimum</kbd>, um gute/schlechte Bereiche zu definieren - der Browser färbt es entsprechend ein!",
|
||||
"task": "Erstelle eine Akku-Anzeige:<br>1. Füge ein <kbd><label></kbd> mit 'Akku:' hinzu<br>2. Füge ein <kbd><meter></kbd> hinzu mit:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> und <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> und <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"task": "Erstelle eine Akku-Anzeige:<br>1. Füge ein <kbd><label></kbd> mit <code>Akku:</code> hinzu<br>2. Füge ein <kbd><meter></kbd> hinzu mit:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> und <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> und <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"previewHTML": "",
|
||||
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }",
|
||||
"sandboxCSS": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "datalist-basic",
|
||||
"title": "Eingabe mit Vorschlägen",
|
||||
"description": "Das <kbd><datalist></kbd>-Element bietet Autovervollständigungs-Vorschläge für Eingabefelder. Verbinde es mit dem <kbd>list</kbd>-Attribut am Input, das zur <kbd>id</kbd> der Datalist passt.<br><br>Benutzer können trotzdem frei tippen - Vorschläge sind nur Hilfen!",
|
||||
"task": "Erstelle eine Browser-Auswahl:<br>1. Füge ein <kbd><label></kbd> mit 'Browser:' hinzu<br>2. Füge ein <kbd><input></kbd> mit <kbd>list=\"browsers\"</kbd> hinzu<br>3. Füge eine <kbd><datalist id=\"browsers\"></kbd> mit Optionen für Chrome, Firefox und Safari hinzu",
|
||||
"task": "Erstelle eine Browser-Auswahl:<br>1. Füge ein <kbd><label></kbd> mit <code>Browser:</code> hinzu<br>2. Füge ein <kbd><input></kbd> mit <kbd>list=\"browsers\"</kbd> hinzu<br>3. Füge eine <kbd><datalist id=\"browsers\"></kbd> 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.<br><br>Das <kbd>value</kbd>-Attribut ist das, was eingegeben wird, und du kannst Anzeigetext dahinter hinzufügen.",
|
||||
"task": "Erstelle eine Länder-Eingabe:<br>1. Füge ein <kbd><label></kbd> mit 'Land:' hinzu<br>2. Füge ein <kbd><input></kbd> mit <kbd>list=\"countries\"</kbd> hinzu<br>3. Füge eine <kbd><datalist id=\"countries\"></kbd> mit mindestens 4 Länder-Optionen hinzu",
|
||||
"task": "Erstelle eine Länder-Eingabe:<br>1. Füge ein <kbd><label></kbd> mit <code>Land:</code> hinzu<br>2. Füge ein <kbd><input></kbd> mit <kbd>list=\"countries\"</kbd> hinzu<br>3. Füge eine <kbd><datalist id=\"countries\"></kbd> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "dialog-basic",
|
||||
"title": "Dialog öffnen",
|
||||
"description": "Das <kbd><dialog></kbd>-Element erstellt ein natives Modal. Füge das <kbd>open</kbd>-Attribut hinzu, um es anzuzeigen.<br><br>Verwende <kbd><form method=\"dialog\"></kbd> darin, um es beim Absenden des Formulars zu schließen - kein JavaScript nötig!",
|
||||
"task": "Erstelle einen Dialog mit:<br>1. Dem <kbd>open</kbd>-Attribut, um ihn anzuzeigen<br>2. Einem <kbd><h2></kbd> mit 'Willkommen!'<br>3. Einem <kbd><p></kbd> mit einer Begrüßungsnachricht<br>4. Einem <kbd><form method=\"dialog\"></kbd> mit einem Schließen-Button",
|
||||
"task": "Erstelle einen Dialog mit:<br>1. Dem <kbd>open</kbd>-Attribut, um ihn anzuzeigen<br>2. Einem <kbd><h2></kbd> mit <code>Willkommen!</code><br>3. Einem <kbd><p></kbd> mit einer Begrüßungsnachricht<br>4. Einem <kbd><form method=\"dialog\"></kbd> 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 <kbd>method=\"dialog\"</kbd> lässt das Formular den Dialog beim Absenden schließen, anstatt Daten zu senden.<br><br>Dieses Muster ist perfekt für Bestätigungsdialoge, schnelle Eingaben oder Einstellungspanels.",
|
||||
"task": "Erstelle einen Bestätigungsdialog:<br>1. Füge <kbd>open</kbd> hinzu, um ihn anzuzeigen<br>2. Ein <kbd><h2></kbd> mit 'Löschen bestätigen'<br>3. Ein <kbd><p></kbd> mit 'Bist du sicher?'<br>4. Ein <kbd><form method=\"dialog\"></kbd> mit Abbrechen- und Löschen-Buttons",
|
||||
"task": "Erstelle einen Bestätigungsdialog:<br>1. Füge <kbd>open</kbd> hinzu, um ihn anzuzeigen<br>2. Ein <kbd><h2></kbd> mit <code>Löschen bestätigen</code><br>3. Ein <kbd><p></kbd> mit <code>Bist du sicher?</code><br>4. Ein <kbd><form method=\"dialog\"></kbd> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "fieldset-basic",
|
||||
"title": "Gruppieren mit Fieldset",
|
||||
"description": "Das <kbd><fieldset></kbd>-Element gruppiert zusammengehörige Formularfelder. Füge ein <kbd><legend></kbd> als erstes Kind hinzu, um der Gruppe einen Titel zu geben.<br><br>Das verbessert die Zugänglichkeit und visuelle Organisation komplexer Formulare.",
|
||||
"task": "Erstelle ein Formular mit einem Fieldset:<br>1. Ein <kbd><form></kbd>-Element<br>2. Ein <kbd><fieldset></kbd> darin<br>3. Ein <kbd><legend></kbd> mit 'Persönliche Daten'<br>4. Zwei beschriftete Eingabefelder für Name und E-Mail",
|
||||
"task": "Erstelle ein Formular mit einem Fieldset:<br>1. Ein <kbd><form></kbd>-Element<br>2. Ein <kbd><fieldset></kbd> darin<br>3. Ein <kbd><legend></kbd> mit <code>Persönliche Daten</code><br>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 <kbd><textarea></kbd>-Element erstellt ein mehrzeiliges Textfeld, perfekt für längere Inhalte wie Nachrichten oder Beschreibungen.<br><br>Verwende <kbd>rows</kbd> und <kbd>cols</kbd> Attribute, um die Standardgröße festzulegen.",
|
||||
"task": "Erstelle ein Kontaktformular:<br>1. Ein <kbd><fieldset></kbd> mit <kbd><legend></kbd> 'Kontaktiere uns'<br>2. Ein beschriftetes <kbd><input></kbd> für E-Mail<br>3. Eine beschriftete <kbd><textarea></kbd> für die Nachricht<br>4. Einen Absende-<kbd><button></kbd>",
|
||||
"task": "Erstelle ein Kontaktformular:<br>1. Ein <kbd><fieldset></kbd> mit <kbd><legend></kbd> <code>Kontaktiere uns</code><br>2. Ein beschriftetes <kbd><input></kbd> für E-Mail<br>3. Eine beschriftete <kbd><textarea></kbd> für die Nachricht<br>4. Einen Absende-<kbd><button></kbd>",
|
||||
"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 <kbd><fieldset></kbd>-Elemente verwenden, um verschiedene Abschnitte zu organisieren.<br><br>Das verbessert die Benutzerfreundlichkeit bei langen Formularen wie Registrierung oder Checkout.",
|
||||
"task": "Erstelle ein Registrierungsformular mit 2 Fieldsets:<br>1. 'Kontodaten' mit Benutzername und Passwort<br>2. 'Einstellungen' mit einer Textarea für Bio<br>3. Einen Absende-Button außerhalb der Fieldsets",
|
||||
"task": "Erstelle ein Registrierungsformular mit 2 Fieldsets:<br>1. <code>Kontodaten</code> mit Benutzername und Passwort<br>2. <code>Einstellungen</code> mit einer Textarea für Bio<br>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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "table-basic",
|
||||
"title": "Grundlegende Tabellenstruktur",
|
||||
"description": "Tabellen verwenden <kbd><table></kbd> mit <kbd><tr></kbd> für Zeilen. In Zeilen nutze <kbd><th></kbd> für Überschriften und <kbd><td></kbd> für Datenzellen.<br><br>Das <kbd><caption></kbd>-Element bietet einen zugänglichen Titel für die Tabelle.",
|
||||
"task": "Erstelle eine einfache Tabelle mit:<br>1. Einer <kbd><caption></kbd> mit 'Obstpreise'<br>2. Einer Kopfzeile mit 'Obst' und 'Preis' Spalten<br>3. Mindestens 2 Datenzeilen",
|
||||
"task": "Erstelle eine einfache Tabelle mit:<br>1. Einer <kbd><caption></kbd> mit <code>Obstpreise</code><br>2. Einer Kopfzeile mit <code>Obst</code> und <code>Preis</code> Spalten<br>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 <kbd><thead></kbd> zum Gruppieren von Kopfzeilen und <kbd><tbody></kbd> zum Gruppieren von Datenzeilen. Das hilft Browsern und Hilfstechnologien, die Tabellenstruktur zu verstehen.<br><br>Du kannst auch <kbd><tfoot></kbd> für Fußzeilen wie Summen verwenden.",
|
||||
"task": "Erstelle eine strukturierte Tabelle:<br>1. Eine <kbd><caption></kbd> mit 'Monatliche Verkäufe'<br>2. Ein <kbd><thead></kbd> mit Monat und Umsatz Überschriften<br>3. Ein <kbd><tbody></kbd> mit mindestens 2 Datenzeilen",
|
||||
"task": "Erstelle eine strukturierte Tabelle:<br>1. Eine <kbd><caption></kbd> mit <code>Monatliche Verkäufe</code><br>2. Ein <kbd><thead></kbd> mit Monat und Umsatz Überschriften<br>3. Ein <kbd><tbody></kbd> 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 <kbd><tfoot></kbd> hinzu, um einen Fußbereich für Summen oder Zusammenfassungen zu erstellen. Der Fuß bleibt unten, auch wenn tbody viele Zeilen hat.<br><br>Kombiniere alle Abschnitte für eine vollständig strukturierte, zugängliche Tabelle.",
|
||||
"task": "Erstelle eine vollständige Tabelle:<br>1. Eine <kbd><caption></kbd> mit 'Bestellübersicht'<br>2. Ein <kbd><thead></kbd> mit Artikel und Preis Überschriften<br>3. Ein <kbd><tbody></kbd> mit 2 Artikeln<br>4. Ein <kbd><tfoot></kbd> mit einer Summenzeile",
|
||||
"task": "Erstelle eine vollständige Tabelle:<br>1. Eine <kbd><caption></kbd> mit <code>Bestellübersicht</code><br>2. Ein <kbd><thead></kbd> mit Artikel und Preis Überschriften<br>3. Ein <kbd><tbody></kbd> mit 2 Artikeln<br>4. Ein <kbd><tfoot></kbd> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "marquee-basic",
|
||||
"title": "Lauftext",
|
||||
"description": "Das <kbd><marquee></kbd>-Element erstellt Lauftext - ein Klassiker aus dem frühen Web! Obwohl veraltet, funktioniert es noch in den meisten Browsern.<br><br>Hinweis: Für produktive Seiten nutze CSS-Animationen. Aber zum Lernen und Spaß haben ist Marquee super!",
|
||||
"task": "Erstelle ein einfaches Marquee:<br>1. Füge ein <kbd><marquee></kbd>-Element hinzu<br>2. Schreibe Text hinein wie 'Willkommen auf meiner Website!'",
|
||||
"task": "Erstelle ein einfaches Marquee:<br>1. Füge ein <kbd><marquee></kbd>-Element hinzu<br>2. Schreibe Text hinein wie <code>Willkommen auf meiner Website!</code>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "get-started",
|
||||
"title": "Get Started",
|
||||
"description": "<strong>Code Crispies</strong> is a free, open-source platform for learning web development through hands-on exercises. No account required!<br><br><strong>What you'll learn:</strong><br>• <strong>HTML</strong> - Semantic elements, forms, tables, SVG (<em>HTML Block & Inline</em>, <em>HTML Forms</em>, <em>HTML Tables</em>)<br>• <strong>CSS</strong> - Selectors, box model, flexbox, animations (<em>CSS Selectors</em>, <em>CSS Box Model</em>, <em>CSS Flexbox</em>)<br>• <strong>Responsive Design</strong> - Media queries and mobile-first layouts<br><br><strong>How it works:</strong><br>1. Read the task in the left panel<br>2. Write code in the editor<br>3. See live results in the preview<br>4. Get instant feedback with hints<br><br><strong>Keyboard shortcuts:</strong> <kbd>Ctrl+Z</kbd> to undo, <kbd>Ctrl+Shift+Z</kbd> to redo<br><br><strong>More resources:</strong><br>• <a href=\"https://nextlevelshit.github.io/html-over-js/\" target=\"_blank\">HTML over JS</a> - Native HTML vs JavaScript solutions<br>• <a href=\"https://nextlevelshit.github.io/web-engineering-mandala/\" target=\"_blank\">Web Engineering Mandala</a> - JavaScript technology roadmap",
|
||||
"task": "Hello World",
|
||||
"task": "Write <code>Hello World</code>",
|
||||
"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 <code>Hello World</code>"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -39,7 +39,7 @@
|
||||
"validations": [
|
||||
{
|
||||
"type": "contains",
|
||||
"value": "Hello",
|
||||
"value": "Hello World",
|
||||
"message": "Click Next to continue"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{
|
||||
"id": "transitions-1",
|
||||
"title": "Transitions",
|
||||
"description": "Learn how to apply <kbd>transition</kbd> to properties for smooth changes on state changes.",
|
||||
"description": "Learn how to apply <kbd>transition</kbd> to properties for smooth changes on state changes.<br><br><pre>.element {\n transition: property duration;\n /* e.g. transition: background-color 0.3s; */\n}</pre>",
|
||||
"task": "Add <kbd>transition: background-color 0.3s</kbd> to <kbd>.btn</kbd> so the color fades smoothly on hover.",
|
||||
"previewHTML": "<button class=\"btn\">Hover Me</button>",
|
||||
"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 <kbd>@keyframes</kbd> and apply them via the <kbd>animation</kbd> shorthand.",
|
||||
"description": "Create named animations using <kbd>@keyframes</kbd> and apply them via the <kbd>animation</kbd> shorthand.<br><br><pre>@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}</pre>",
|
||||
"task": "Define a keyframe at <kbd>50%</kbd> with <kbd>transform: translateY(-20px)</kbd> and apply <kbd>animation: bounce 1s infinite</kbd> to <kbd>.ball</kbd>.",
|
||||
"previewHTML": "<div class=\"ball\"></div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"id": "semantic-containers",
|
||||
"title": "Semantic Tags",
|
||||
"description": "Modern HTML uses semantic containers that describe their content:<br><br><kbd><header></kbd> - Page or section header<br><kbd><nav></kbd> - Navigation links<br><kbd><main></kbd> - Main content area<br><kbd><section></kbd> - Thematic grouping<br><kbd><article></kbd> - Self-contained content<br><kbd><footer></kbd> - Page or section footer",
|
||||
"task": "Create a basic page structure:<br>1. Add a <kbd><header></kbd> with an <kbd><h1></kbd> containing the text 'My Website'<br>2. Add a <kbd><main></kbd> element with a paragraph saying 'Welcome to my site!'<br>3. Add a <kbd><footer></kbd> with a paragraph saying 'Copyright 2025'",
|
||||
"task": "Create a basic page structure:<br>1. Add a <kbd><header></kbd> with an <kbd><h1></kbd> containing the text <code>My Website</code><br>2. Add a <kbd><main></kbd> element with a paragraph saying <code>Welcome to my site!</code><br>3. Add a <kbd><footer></kbd> with a paragraph saying <code>Copyright 2025</code>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "form-structure",
|
||||
"title": "Form Structure",
|
||||
"description": "Every form needs a <kbd><form></kbd> wrapper. Inside, use <kbd><label></kbd> to describe inputs and <kbd><input></kbd> for user data entry.<br><br>The <kbd>for</kbd> attribute on labels should match the <kbd>id</kbd> on inputs for accessibility.",
|
||||
"task": "Create a form with:<br>1. A <kbd><label></kbd> with the text 'Name:' and <kbd>for=\"name\"</kbd> attribute<br>2. A text <kbd><input></kbd> with <kbd>id=\"name\"</kbd> and <kbd>name=\"name\"</kbd> attributes",
|
||||
"task": "Create a form with:<br>1. A <kbd><label></kbd> with the text <code>Name:</code> and <kbd>for=\"name\"</kbd> attribute<br>2. A text <kbd><input></kbd> with <kbd>id=\"name\"</kbd> and <kbd>name=\"name\"</kbd> 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:<br><br><kbd><button type=\"submit\"></kbd> - Preferred, flexible content<br><kbd><input type=\"submit\"></kbd> - Simple text-only button<br><br>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:<br><br><kbd><button type=\"submit\"></kbd> - Preferred, flexible content<br><kbd><input type=\"submit\"></kbd> - Simple text-only button<br><br>The button text should be action-oriented (e.g., <code>Sign In</code>, 'Register', 'Send').",
|
||||
"task": "Add a submit button to the form with the text <code>Sign In</code>.",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "details-summary-basic",
|
||||
"title": "First Widget",
|
||||
"description": "The <kbd><details></kbd> element creates a collapsible section. The <kbd><summary></kbd> provides the clickable label.<br><br>Click the summary to toggle the hidden content - no JavaScript needed!",
|
||||
"task": "Create a <kbd><details></kbd> element with:<br>1. A <kbd><summary></kbd> saying 'Click to reveal'<br>2. A <kbd><p></kbd> with the text 'This content was hidden!'",
|
||||
"task": "Create a <kbd><details></kbd> element with:<br>1. A <kbd><summary></kbd> saying <code>Click to reveal</code><br>2. A <kbd><p></kbd> with the text <code>This content was hidden!</code>",
|
||||
"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 <kbd><details></kbd> elements create an accordion-style FAQ. Each question can be expanded independently.<br><br><b>Pro tip:</b> Type <kbd>details*3>summary+p</kbd> and press Tab for Emmet expansion. <kbd>*3</kbd> creates 3 elements, <kbd>></kbd> nests inside, <kbd>+</kbd> adds siblings.",
|
||||
"task": "Create an FAQ section with:<br>1. An <kbd><h1></kbd> saying 'Frequently Asked Questions'<br>2. Three <kbd><details></kbd> elements, each with a question in <kbd><summary></kbd> and an answer in <kbd><p></kbd>",
|
||||
"task": "Create an FAQ section with:<br>1. An <kbd><h1></kbd> saying <code>Frequently Asked Questions</code><br>2. Three <kbd><details></kbd> elements, each with a question in <kbd><summary></kbd> and an answer in <kbd><p></kbd>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "progress-basic",
|
||||
"title": "Progress Bars",
|
||||
"description": "The <kbd><progress></kbd> element shows task completion. Use <kbd>value</kbd> for current progress and <kbd>max</kbd> for the total.<br><br><b>Note:</b> This is not a self-closing tag! Write <kbd><progress>...</progress></kbd> with fallback text inside for older browsers.",
|
||||
"task": "Create a progress bar showing 70% completion:<br>1. Add a <kbd><label></kbd> saying 'Download:'<br>2. Add a <kbd><progress></kbd> with <kbd>value=\"70\"</kbd> and <kbd>max=\"100\"</kbd>",
|
||||
"task": "Create a progress bar showing 70% completion:<br>1. Add a <kbd><label></kbd> saying <code>Download:</code><br>2. Add a <kbd><progress></kbd> with <kbd>value=\"70\"</kbd> and <kbd>max=\"100\"</kbd>",
|
||||
"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 <kbd>value</kbd> attribute. This creates an animated indeterminate state.<br><br>Useful for network requests or processes with unknown duration.",
|
||||
"task": "Create a loading indicator:<br>1. Add a <kbd><p></kbd> saying 'Loading...'<br>2. Add a <kbd><progress></kbd> without a value attribute",
|
||||
"task": "Create a loading indicator:<br>1. Add a <kbd><p></kbd> saying <code>Loading...</code><br>2. Add a <kbd><progress></kbd> 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 <kbd><meter></kbd> element displays a scalar value within a range. Use it for measurements like disk space, battery, or ratings.<br><br>Set <kbd>low</kbd>, <kbd>high</kbd>, and <kbd>optimum</kbd> to define good/bad ranges - the browser colors it accordingly!",
|
||||
"task": "Create a battery level meter:<br>1. Add a <kbd><label></kbd> saying 'Battery:'<br>2. Add a <kbd><meter></kbd> with:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> and <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> and <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"task": "Create a battery level meter:<br>1. Add a <kbd><label></kbd> saying <code>Battery:</code><br>2. Add a <kbd><meter></kbd> with:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> and <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> and <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"previewHTML": "",
|
||||
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }",
|
||||
"sandboxCSS": "",
|
||||
|
||||
78
lessons/es/25-html-datalist.json
Normal file
78
lessons/es/25-html-datalist.json
Normal file
@@ -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 <kbd><datalist></kbd> element provides autocomplete suggestions for inputs. Connect it using the <kbd>list</kbd> attribute on the input matching the datalist's <kbd>id</kbd>.<br><br>Users can still type freely - suggestions are just helpers!",
|
||||
"task": "Create a browser selector:<br>1. Add a <kbd><label></kbd> saying <code>Browser:</code><br>2. Add an <kbd><input></kbd> with <kbd>list=\"browsers\"</kbd><br>3. Add a <kbd><datalist id=\"browsers\"></kbd> 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": "<label for=\"browser\">Browser:</label>\n<input type=\"text\" id=\"browser\" list=\"browsers\">\n<datalist id=\"browsers\">\n <option value=\"Chrome\">\n <option value=\"Firefox\">\n <option value=\"Safari\">\n</datalist>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "datalist",
|
||||
"message": "Add a <kbd><datalist></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "input", "attr": "list", "value": "browsers" },
|
||||
"message": "Connect the input to datalist using <kbd>list=</kbd>\"browsers\""
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "option", "min": 3 },
|
||||
"message": "Add at least 3 <kbd><option></kbd> elements inside <kbd><datalist></kbd>"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "label",
|
||||
"message": "Add a <kbd><label></kbd> 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.<br><br>The <kbd>value</kbd> attribute is what gets entered, and you can add display text after it.",
|
||||
"task": "Create a country input:<br>1. Add a <kbd><label></kbd> saying <code>Country:</code><br>2. Add an <kbd><input></kbd> with <kbd>list=\"countries\"</kbd><br>3. Add a <kbd><datalist id=\"countries\"></kbd> 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": "<label for=\"country\">Country:</label>\n<input type=\"text\" id=\"country\" list=\"countries\" placeholder=\"Start typing...\">\n<datalist id=\"countries\">\n <option value=\"Germany\">\n <option value=\"France\">\n <option value=\"Spain\">\n <option value=\"Italy\">\n</datalist>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "datalist",
|
||||
"message": "Add a <kbd><datalist></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "datalist", "attr": "id", "value": "countries" },
|
||||
"message": "Set <kbd>id=</kbd>\"countries\" on the datalist"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "input", "attr": "list", "value": "countries" },
|
||||
"message": "Connect the input using <kbd>list=</kbd>\"countries\""
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "option", "min": 4 },
|
||||
"message": "Add at least 4 country options"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
83
lessons/es/27-html-dialog.json
Normal file
83
lessons/es/27-html-dialog.json
Normal file
@@ -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 <kbd><dialog></kbd> element creates a native modal. Add the <kbd>open</kbd> attribute to show it.<br><br>Use <kbd><form method=\"dialog\"></kbd> inside to close it when the form submits - no JavaScript needed!",
|
||||
"task": "Create a dialog with:<br>1. The <kbd>open</kbd> attribute to show it<br>2. An <kbd><h2></kbd> saying <code>Welcome!</code><br>3. A <kbd><p></kbd> with a greeting message<br>4. A <kbd><form method=\"dialog\"></kbd> 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": "<dialog open>\n <h2>Welcome!</h2>\n <p>This is a native HTML dialog element.</p>\n <form method=\"dialog\">\n <button>Close</button>\n </form>\n</dialog>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog",
|
||||
"message": "Add a <kbd><dialog></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "dialog", "attr": "open", "value": true },
|
||||
"message": "Add the <kbd>open</kbd> attribute to show the dialog"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog h2",
|
||||
"message": "Add an <kbd><h2></kbd> heading inside the dialog"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "form[method='dialog']",
|
||||
"message": "Add a <kbd><form method=\"dialog\"></kbd> 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 <kbd>method=\"dialog\"</kbd> makes the form close the dialog on submit instead of sending data.<br><br>This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.",
|
||||
"task": "Create a confirmation dialog:<br>1. Add <kbd>open</kbd> to show it<br>2. An <kbd><h2></kbd> saying <code>Confirm Delete</code><br>3. A <kbd><p></kbd> asking <code>Are you sure?</code><br>4. A <kbd><form method=\"dialog\"></kbd> 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": "<dialog open>\n <h2>Confirm Delete</h2>\n <p>Are you sure you want to delete this item?</p>\n <form method=\"dialog\">\n <button value=\"cancel\">Cancel</button>\n <button value=\"delete\">Delete</button>\n </form>\n</dialog>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog[open]",
|
||||
"message": "Add a <kbd><dialog></kbd> 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 <kbd><form method=\"dialog\"></kbd> for the buttons"
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "dialog button", "min": 2 },
|
||||
"message": "Add at least 2 buttons (Cancel and Confirm)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
127
lessons/es/28-html-forms-fieldset.json
Normal file
127
lessons/es/28-html-forms-fieldset.json
Normal file
@@ -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 <kbd><fieldset></kbd> element groups related form controls together. Add a <kbd><legend></kbd> as the first child to give the group a title.<br><br>This helps with accessibility and visual organization of complex forms.",
|
||||
"task": "Create a form with a fieldset:<br>1. A <kbd><form></kbd> element<br>2. A <kbd><fieldset></kbd> inside<br>3. A <kbd><legend></kbd> saying <code>Personal Info</code><br>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": "<form>\n <fieldset>\n <legend>Personal Info</legend>\n <label for=\"name\">Name:</label>\n <input type=\"text\" id=\"name\" name=\"name\">\n <label for=\"email\">Email:</label>\n <input type=\"email\" id=\"email\" name=\"email\">\n </fieldset>\n</form>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "form",
|
||||
"message": "Add a <kbd><form></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "fieldset",
|
||||
"message": "Add a <kbd><fieldset></kbd> inside the form"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "legend",
|
||||
"message": "Add a <kbd><legend></kbd> 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 <kbd><textarea></kbd> element creates a multi-line text input, perfect for longer content like messages or descriptions.<br><br>Use <kbd>rows</kbd> and <kbd>cols</kbd> attributes to set default size.",
|
||||
"task": "Create a contact form:<br>1. A <kbd><fieldset></kbd> with <kbd><legend></kbd> <code>Contact Us</code><br>2. A labeled <kbd><input></kbd> for email<br>3. A labeled <kbd><textarea></kbd> for the message<br>4. A submit <kbd><button></kbd>",
|
||||
"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": "<form>\n <fieldset>\n <legend>Contact Us</legend>\n <label for=\"email\">Email:</label>\n <input type=\"email\" id=\"email\" name=\"email\">\n <label for=\"message\">Message:</label>\n <textarea id=\"message\" name=\"message\" rows=\"4\"></textarea>\n <button type=\"submit\">Send Message</button>\n </fieldset>\n</form>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "fieldset",
|
||||
"message": "Add a <kbd><fieldset></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "legend",
|
||||
"message": "Add a <kbd><legend></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "textarea",
|
||||
"message": "Add a <kbd><textarea></kbd> 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 <kbd><fieldset></kbd> elements to organize different sections.<br><br>This improves usability for long forms like registration or checkout pages.",
|
||||
"task": "Create a registration form with 2 fieldsets:<br>1. <code>Account Info</code> with username and password inputs<br>2. <code>Preferences</code> with a textarea for bio<br>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": "<form>\n <fieldset>\n <legend>Account Info</legend>\n <label for=\"username\">Username:</label>\n <input type=\"text\" id=\"username\" name=\"username\">\n <label for=\"password\">Password:</label>\n <input type=\"password\" id=\"password\" name=\"password\">\n </fieldset>\n <fieldset>\n <legend>Preferences</legend>\n <label for=\"bio\">Bio:</label>\n <textarea id=\"bio\" name=\"bio\"></textarea>\n </fieldset>\n <button type=\"submit\">Register</button>\n</form>",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "table-basic",
|
||||
"title": "Basic Table Structure",
|
||||
"description": "Tables use <kbd><table></kbd> with <kbd><tr></kbd> for rows. Inside rows, use <kbd><th></kbd> for headers and <kbd><td></kbd> for data cells.<br><br>The <kbd><caption></kbd> element provides an accessible title for the table.",
|
||||
"task": "Create a simple table with:<br>1. A <kbd><caption></kbd> saying 'Fruit Prices'<br>2. A header row with 'Fruit' and 'Price' columns<br>3. At least 2 data rows",
|
||||
"task": "Create a simple table with:<br>1. A <kbd><caption></kbd> saying <code>Fruit Prices</code><br>2. A header row with <code>Fruit</code> and <code>Price</code> columns<br>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 <kbd><thead></kbd> to group header rows and <kbd><tbody></kbd> to group data rows. This helps browsers and assistive technology understand the table structure.<br><br>You can also use <kbd><tfoot></kbd> for footer rows like totals.",
|
||||
"task": "Create a structured table:<br>1. A <kbd><caption></kbd> with 'Monthly Sales'<br>2. A <kbd><thead></kbd> with Month and Revenue headers<br>3. A <kbd><tbody></kbd> with at least 2 data rows",
|
||||
"task": "Create a structured table:<br>1. A <kbd><caption></kbd> with <code>Monthly Sales</code><br>2. A <kbd><thead></kbd> with Month and Revenue headers<br>3. A <kbd><tbody></kbd> 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 <kbd><tfoot></kbd> to create a footer section for totals or summary data. The footer stays at the bottom even if tbody has many rows.<br><br>Combine all sections for a fully structured, accessible table.",
|
||||
"task": "Create a complete table:<br>1. A <kbd><caption></kbd> with 'Order Summary'<br>2. A <kbd><thead></kbd> with Item and Price headers<br>3. A <kbd><tbody></kbd> with 2 items<br>4. A <kbd><tfoot></kbd> with a Total row",
|
||||
"task": "Create a complete table:<br>1. A <kbd><caption></kbd> with <code>Order Summary</code><br>2. A <kbd><thead></kbd> with Item and Price headers<br>3. A <kbd><tbody></kbd> with 2 items<br>4. A <kbd><tfoot></kbd> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "marquee-basic",
|
||||
"title": "Scrolling Text",
|
||||
"description": "The <kbd><marquee></kbd> element creates scrolling text - a classic from the early web! While deprecated, it still works in most browsers.<br><br>Note: For production, use CSS animations instead. But for learning and fun, marquee is great!",
|
||||
"task": "Create a simple marquee:<br>1. Add a <kbd><marquee></kbd> element<br>2. Put some text inside like 'Welcome to my website!'",
|
||||
"task": "Create a simple marquee:<br>1. Add a <kbd><marquee></kbd> element<br>2. Put some text inside like <code>Welcome to my website!</code>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "get-started",
|
||||
"title": "Get Started",
|
||||
"description": "<strong>Code Crispies</strong> is a free, open-source platform for learning web development through hands-on exercises. No account required!<br><br><strong>What you'll learn:</strong><br>• <strong>HTML</strong> - Semantic elements, forms, tables, SVG (<em>HTML Block & Inline</em>, <em>HTML Forms</em>, <em>HTML Tables</em>)<br>• <strong>CSS</strong> - Selectors, box model, flexbox, animations (<em>CSS Selectors</em>, <em>CSS Box Model</em>, <em>CSS Flexbox</em>)<br>• <strong>Responsive Design</strong> - Media queries and mobile-first layouts<br><br><strong>How it works:</strong><br>1. Read the task in the left panel<br>2. Write code in the editor<br>3. See live results in the preview<br>4. Get instant feedback with hints<br><br><strong>Keyboard shortcuts:</strong> <kbd>Ctrl+Z</kbd> to undo, <kbd>Ctrl+Shift+Z</kbd> to redo<br><br><strong>More resources:</strong><br>• <a href=\"https://nextlevelshit.github.io/html-over-js/\" target=\"_blank\">HTML over JS</a> - Native HTML vs JavaScript solutions<br>• <a href=\"https://nextlevelshit.github.io/web-engineering-mandala/\" target=\"_blank\">Web Engineering Mandala</a> - JavaScript technology roadmap",
|
||||
"task": "Hello World",
|
||||
"task": "Write <code>Hello World</code>",
|
||||
"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 <code>Hello World</code>"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -39,7 +39,7 @@
|
||||
"validations": [
|
||||
{
|
||||
"type": "contains",
|
||||
"value": "Hello",
|
||||
"value": "Hello World",
|
||||
"message": "Click Next to continue"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{
|
||||
"id": "transitions-1",
|
||||
"title": "Transitions",
|
||||
"description": "Learn how to apply <kbd>transition</kbd> to properties for smooth changes on state changes.",
|
||||
"description": "Learn how to apply <kbd>transition</kbd> to properties for smooth changes on state changes.<br><br><pre>.element {\n transition: property duration;\n /* e.g. transition: background-color 0.3s; */\n}</pre>",
|
||||
"task": "Add <kbd>transition: background-color 0.3s</kbd> to <kbd>.btn</kbd> so the color fades smoothly on hover.",
|
||||
"previewHTML": "<button class=\"btn\">Hover Me</button>",
|
||||
"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 <kbd>@keyframes</kbd> and apply them via the <kbd>animation</kbd> shorthand.",
|
||||
"description": "Create named animations using <kbd>@keyframes</kbd> and apply them via the <kbd>animation</kbd> shorthand.<br><br><pre>@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}</pre>",
|
||||
"task": "Define a keyframe at <kbd>50%</kbd> with <kbd>transform: translateY(-20px)</kbd> and apply <kbd>animation: bounce 1s infinite</kbd> to <kbd>.ball</kbd>.",
|
||||
"previewHTML": "<div class=\"ball\"></div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"id": "semantic-containers",
|
||||
"title": "Semantic Tags",
|
||||
"description": "Modern HTML uses semantic containers that describe their content:<br><br><kbd><header></kbd> - Page or section header<br><kbd><nav></kbd> - Navigation links<br><kbd><main></kbd> - Main content area<br><kbd><section></kbd> - Thematic grouping<br><kbd><article></kbd> - Self-contained content<br><kbd><footer></kbd> - Page or section footer",
|
||||
"task": "Create a basic page structure:<br>1. Add a <kbd><header></kbd> with an <kbd><h1></kbd> containing the text 'My Website'<br>2. Add a <kbd><main></kbd> element with a paragraph saying 'Welcome to my site!'<br>3. Add a <kbd><footer></kbd> with a paragraph saying 'Copyright 2025'",
|
||||
"task": "Create a basic page structure:<br>1. Add a <kbd><header></kbd> with an <kbd><h1></kbd> containing the text <code>My Website</code><br>2. Add a <kbd><main></kbd> element with a paragraph saying <code>Welcome to my site!</code><br>3. Add a <kbd><footer></kbd> with a paragraph saying <code>Copyright 2025</code>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "form-structure",
|
||||
"title": "Form Structure",
|
||||
"description": "Every form needs a <kbd><form></kbd> wrapper. Inside, use <kbd><label></kbd> to describe inputs and <kbd><input></kbd> for user data entry.<br><br>The <kbd>for</kbd> attribute on labels should match the <kbd>id</kbd> on inputs for accessibility.",
|
||||
"task": "Create a form with:<br>1. A <kbd><label></kbd> with the text 'Name:' and <kbd>for=\"name\"</kbd> attribute<br>2. A text <kbd><input></kbd> with <kbd>id=\"name\"</kbd> and <kbd>name=\"name\"</kbd> attributes",
|
||||
"task": "Create a form with:<br>1. A <kbd><label></kbd> with the text <code>Name:</code> and <kbd>for=\"name\"</kbd> attribute<br>2. A text <kbd><input></kbd> with <kbd>id=\"name\"</kbd> and <kbd>name=\"name\"</kbd> 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:<br><br><kbd><button type=\"submit\"></kbd> - Preferred, flexible content<br><kbd><input type=\"submit\"></kbd> - Simple text-only button<br><br>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:<br><br><kbd><button type=\"submit\"></kbd> - Preferred, flexible content<br><kbd><input type=\"submit\"></kbd> - Simple text-only button<br><br>The button text should be action-oriented (e.g., <code>Sign In</code>, 'Register', 'Send').",
|
||||
"task": "Add a submit button to the form with the text <code>Sign In</code>.",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "details-summary-basic",
|
||||
"title": "First Widget",
|
||||
"description": "The <kbd><details></kbd> element creates a collapsible section. The <kbd><summary></kbd> provides the clickable label.<br><br>Click the summary to toggle the hidden content - no JavaScript needed!",
|
||||
"task": "Create a <kbd><details></kbd> element with:<br>1. A <kbd><summary></kbd> saying 'Click to reveal'<br>2. A <kbd><p></kbd> with the text 'This content was hidden!'",
|
||||
"task": "Create a <kbd><details></kbd> element with:<br>1. A <kbd><summary></kbd> saying <code>Click to reveal</code><br>2. A <kbd><p></kbd> with the text <code>This content was hidden!</code>",
|
||||
"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 <kbd><details></kbd> elements create an accordion-style FAQ. Each question can be expanded independently.<br><br><b>Pro tip:</b> Type <kbd>details*3>summary+p</kbd> and press Tab for Emmet expansion. <kbd>*3</kbd> creates 3 elements, <kbd>></kbd> nests inside, <kbd>+</kbd> adds siblings.",
|
||||
"task": "Create an FAQ section with:<br>1. An <kbd><h1></kbd> saying 'Frequently Asked Questions'<br>2. Three <kbd><details></kbd> elements, each with a question in <kbd><summary></kbd> and an answer in <kbd><p></kbd>",
|
||||
"task": "Create an FAQ section with:<br>1. An <kbd><h1></kbd> saying <code>Frequently Asked Questions</code><br>2. Three <kbd><details></kbd> elements, each with a question in <kbd><summary></kbd> and an answer in <kbd><p></kbd>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "progress-basic",
|
||||
"title": "Progress Bars",
|
||||
"description": "The <kbd><progress></kbd> element shows task completion. Use <kbd>value</kbd> for current progress and <kbd>max</kbd> for the total.<br><br><b>Note:</b> This is not a self-closing tag! Write <kbd><progress>...</progress></kbd> with fallback text inside for older browsers.",
|
||||
"task": "Create a progress bar showing 70% completion:<br>1. Add a <kbd><label></kbd> saying 'Download:'<br>2. Add a <kbd><progress></kbd> with <kbd>value=\"70\"</kbd> and <kbd>max=\"100\"</kbd>",
|
||||
"task": "Create a progress bar showing 70% completion:<br>1. Add a <kbd><label></kbd> saying <code>Download:</code><br>2. Add a <kbd><progress></kbd> with <kbd>value=\"70\"</kbd> and <kbd>max=\"100\"</kbd>",
|
||||
"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 <kbd>value</kbd> attribute. This creates an animated indeterminate state.<br><br>Useful for network requests or processes with unknown duration.",
|
||||
"task": "Create a loading indicator:<br>1. Add a <kbd><p></kbd> saying 'Loading...'<br>2. Add a <kbd><progress></kbd> without a value attribute",
|
||||
"task": "Create a loading indicator:<br>1. Add a <kbd><p></kbd> saying <code>Loading...</code><br>2. Add a <kbd><progress></kbd> 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 <kbd><meter></kbd> element displays a scalar value within a range. Use it for measurements like disk space, battery, or ratings.<br><br>Set <kbd>low</kbd>, <kbd>high</kbd>, and <kbd>optimum</kbd> to define good/bad ranges - the browser colors it accordingly!",
|
||||
"task": "Create a battery level meter:<br>1. Add a <kbd><label></kbd> saying 'Battery:'<br>2. Add a <kbd><meter></kbd> with:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> and <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> and <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"task": "Create a battery level meter:<br>1. Add a <kbd><label></kbd> saying <code>Battery:</code><br>2. Add a <kbd><meter></kbd> with:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> and <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> and <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"previewHTML": "",
|
||||
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }",
|
||||
"sandboxCSS": "",
|
||||
|
||||
78
lessons/pl/25-html-datalist.json
Normal file
78
lessons/pl/25-html-datalist.json
Normal file
@@ -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 <kbd><datalist></kbd> element provides autocomplete suggestions for inputs. Connect it using the <kbd>list</kbd> attribute on the input matching the datalist's <kbd>id</kbd>.<br><br>Users can still type freely - suggestions are just helpers!",
|
||||
"task": "Create a browser selector:<br>1. Add a <kbd><label></kbd> saying <code>Browser:</code><br>2. Add an <kbd><input></kbd> with <kbd>list=\"browsers\"</kbd><br>3. Add a <kbd><datalist id=\"browsers\"></kbd> 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": "<label for=\"browser\">Browser:</label>\n<input type=\"text\" id=\"browser\" list=\"browsers\">\n<datalist id=\"browsers\">\n <option value=\"Chrome\">\n <option value=\"Firefox\">\n <option value=\"Safari\">\n</datalist>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "datalist",
|
||||
"message": "Add a <kbd><datalist></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "input", "attr": "list", "value": "browsers" },
|
||||
"message": "Connect the input to datalist using <kbd>list=</kbd>\"browsers\""
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "option", "min": 3 },
|
||||
"message": "Add at least 3 <kbd><option></kbd> elements inside <kbd><datalist></kbd>"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "label",
|
||||
"message": "Add a <kbd><label></kbd> 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.<br><br>The <kbd>value</kbd> attribute is what gets entered, and you can add display text after it.",
|
||||
"task": "Create a country input:<br>1. Add a <kbd><label></kbd> saying <code>Country:</code><br>2. Add an <kbd><input></kbd> with <kbd>list=\"countries\"</kbd><br>3. Add a <kbd><datalist id=\"countries\"></kbd> 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": "<label for=\"country\">Country:</label>\n<input type=\"text\" id=\"country\" list=\"countries\" placeholder=\"Start typing...\">\n<datalist id=\"countries\">\n <option value=\"Germany\">\n <option value=\"France\">\n <option value=\"Spain\">\n <option value=\"Italy\">\n</datalist>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "datalist",
|
||||
"message": "Add a <kbd><datalist></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "datalist", "attr": "id", "value": "countries" },
|
||||
"message": "Set <kbd>id=</kbd>\"countries\" on the datalist"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "input", "attr": "list", "value": "countries" },
|
||||
"message": "Connect the input using <kbd>list=</kbd>\"countries\""
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "option", "min": 4 },
|
||||
"message": "Add at least 4 country options"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
83
lessons/pl/27-html-dialog.json
Normal file
83
lessons/pl/27-html-dialog.json
Normal file
@@ -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 <kbd><dialog></kbd> element creates a native modal. Add the <kbd>open</kbd> attribute to show it.<br><br>Use <kbd><form method=\"dialog\"></kbd> inside to close it when the form submits - no JavaScript needed!",
|
||||
"task": "Create a dialog with:<br>1. The <kbd>open</kbd> attribute to show it<br>2. An <kbd><h2></kbd> saying <code>Welcome!</code><br>3. A <kbd><p></kbd> with a greeting message<br>4. A <kbd><form method=\"dialog\"></kbd> 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": "<dialog open>\n <h2>Welcome!</h2>\n <p>This is a native HTML dialog element.</p>\n <form method=\"dialog\">\n <button>Close</button>\n </form>\n</dialog>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog",
|
||||
"message": "Add a <kbd><dialog></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "dialog", "attr": "open", "value": true },
|
||||
"message": "Add the <kbd>open</kbd> attribute to show the dialog"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog h2",
|
||||
"message": "Add an <kbd><h2></kbd> heading inside the dialog"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "form[method='dialog']",
|
||||
"message": "Add a <kbd><form method=\"dialog\"></kbd> 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 <kbd>method=\"dialog\"</kbd> makes the form close the dialog on submit instead of sending data.<br><br>This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.",
|
||||
"task": "Create a confirmation dialog:<br>1. Add <kbd>open</kbd> to show it<br>2. An <kbd><h2></kbd> saying <code>Confirm Delete</code><br>3. A <kbd><p></kbd> asking <code>Are you sure?</code><br>4. A <kbd><form method=\"dialog\"></kbd> 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": "<dialog open>\n <h2>Confirm Delete</h2>\n <p>Are you sure you want to delete this item?</p>\n <form method=\"dialog\">\n <button value=\"cancel\">Cancel</button>\n <button value=\"delete\">Delete</button>\n </form>\n</dialog>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog[open]",
|
||||
"message": "Add a <kbd><dialog></kbd> 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 <kbd><form method=\"dialog\"></kbd> for the buttons"
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "dialog button", "min": 2 },
|
||||
"message": "Add at least 2 buttons (Cancel and Confirm)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
127
lessons/pl/28-html-forms-fieldset.json
Normal file
127
lessons/pl/28-html-forms-fieldset.json
Normal file
@@ -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 <kbd><fieldset></kbd> element groups related form controls together. Add a <kbd><legend></kbd> as the first child to give the group a title.<br><br>This helps with accessibility and visual organization of complex forms.",
|
||||
"task": "Create a form with a fieldset:<br>1. A <kbd><form></kbd> element<br>2. A <kbd><fieldset></kbd> inside<br>3. A <kbd><legend></kbd> saying <code>Personal Info</code><br>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": "<form>\n <fieldset>\n <legend>Personal Info</legend>\n <label for=\"name\">Name:</label>\n <input type=\"text\" id=\"name\" name=\"name\">\n <label for=\"email\">Email:</label>\n <input type=\"email\" id=\"email\" name=\"email\">\n </fieldset>\n</form>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "form",
|
||||
"message": "Add a <kbd><form></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "fieldset",
|
||||
"message": "Add a <kbd><fieldset></kbd> inside the form"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "legend",
|
||||
"message": "Add a <kbd><legend></kbd> 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 <kbd><textarea></kbd> element creates a multi-line text input, perfect for longer content like messages or descriptions.<br><br>Use <kbd>rows</kbd> and <kbd>cols</kbd> attributes to set default size.",
|
||||
"task": "Create a contact form:<br>1. A <kbd><fieldset></kbd> with <kbd><legend></kbd> <code>Contact Us</code><br>2. A labeled <kbd><input></kbd> for email<br>3. A labeled <kbd><textarea></kbd> for the message<br>4. A submit <kbd><button></kbd>",
|
||||
"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": "<form>\n <fieldset>\n <legend>Contact Us</legend>\n <label for=\"email\">Email:</label>\n <input type=\"email\" id=\"email\" name=\"email\">\n <label for=\"message\">Message:</label>\n <textarea id=\"message\" name=\"message\" rows=\"4\"></textarea>\n <button type=\"submit\">Send Message</button>\n </fieldset>\n</form>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "fieldset",
|
||||
"message": "Add a <kbd><fieldset></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "legend",
|
||||
"message": "Add a <kbd><legend></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "textarea",
|
||||
"message": "Add a <kbd><textarea></kbd> 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 <kbd><fieldset></kbd> elements to organize different sections.<br><br>This improves usability for long forms like registration or checkout pages.",
|
||||
"task": "Create a registration form with 2 fieldsets:<br>1. <code>Account Info</code> with username and password inputs<br>2. <code>Preferences</code> with a textarea for bio<br>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": "<form>\n <fieldset>\n <legend>Account Info</legend>\n <label for=\"username\">Username:</label>\n <input type=\"text\" id=\"username\" name=\"username\">\n <label for=\"password\">Password:</label>\n <input type=\"password\" id=\"password\" name=\"password\">\n </fieldset>\n <fieldset>\n <legend>Preferences</legend>\n <label for=\"bio\">Bio:</label>\n <textarea id=\"bio\" name=\"bio\"></textarea>\n </fieldset>\n <button type=\"submit\">Register</button>\n</form>",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "table-basic",
|
||||
"title": "Basic Table Structure",
|
||||
"description": "Tables use <kbd><table></kbd> with <kbd><tr></kbd> for rows. Inside rows, use <kbd><th></kbd> for headers and <kbd><td></kbd> for data cells.<br><br>The <kbd><caption></kbd> element provides an accessible title for the table.",
|
||||
"task": "Create a simple table with:<br>1. A <kbd><caption></kbd> saying 'Fruit Prices'<br>2. A header row with 'Fruit' and 'Price' columns<br>3. At least 2 data rows",
|
||||
"task": "Create a simple table with:<br>1. A <kbd><caption></kbd> saying <code>Fruit Prices</code><br>2. A header row with <code>Fruit</code> and <code>Price</code> columns<br>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 <kbd><thead></kbd> to group header rows and <kbd><tbody></kbd> to group data rows. This helps browsers and assistive technology understand the table structure.<br><br>You can also use <kbd><tfoot></kbd> for footer rows like totals.",
|
||||
"task": "Create a structured table:<br>1. A <kbd><caption></kbd> with 'Monthly Sales'<br>2. A <kbd><thead></kbd> with Month and Revenue headers<br>3. A <kbd><tbody></kbd> with at least 2 data rows",
|
||||
"task": "Create a structured table:<br>1. A <kbd><caption></kbd> with <code>Monthly Sales</code><br>2. A <kbd><thead></kbd> with Month and Revenue headers<br>3. A <kbd><tbody></kbd> 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 <kbd><tfoot></kbd> to create a footer section for totals or summary data. The footer stays at the bottom even if tbody has many rows.<br><br>Combine all sections for a fully structured, accessible table.",
|
||||
"task": "Create a complete table:<br>1. A <kbd><caption></kbd> with 'Order Summary'<br>2. A <kbd><thead></kbd> with Item and Price headers<br>3. A <kbd><tbody></kbd> with 2 items<br>4. A <kbd><tfoot></kbd> with a Total row",
|
||||
"task": "Create a complete table:<br>1. A <kbd><caption></kbd> with <code>Order Summary</code><br>2. A <kbd><thead></kbd> with Item and Price headers<br>3. A <kbd><tbody></kbd> with 2 items<br>4. A <kbd><tfoot></kbd> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "marquee-basic",
|
||||
"title": "Scrolling Text",
|
||||
"description": "The <kbd><marquee></kbd> element creates scrolling text - a classic from the early web! While deprecated, it still works in most browsers.<br><br>Note: For production, use CSS animations instead. But for learning and fun, marquee is great!",
|
||||
"task": "Create a simple marquee:<br>1. Add a <kbd><marquee></kbd> element<br>2. Put some text inside like 'Welcome to my website!'",
|
||||
"task": "Create a simple marquee:<br>1. Add a <kbd><marquee></kbd> element<br>2. Put some text inside like <code>Welcome to my website!</code>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "get-started",
|
||||
"title": "Get Started",
|
||||
"description": "<strong>Code Crispies</strong> is a free, open-source platform for learning web development through hands-on exercises. No account required!<br><br><strong>What you'll learn:</strong><br>• <strong>HTML</strong> - Semantic elements, forms, tables, SVG (<em>HTML Block & Inline</em>, <em>HTML Forms</em>, <em>HTML Tables</em>)<br>• <strong>CSS</strong> - Selectors, box model, flexbox, animations (<em>CSS Selectors</em>, <em>CSS Box Model</em>, <em>CSS Flexbox</em>)<br>• <strong>Responsive Design</strong> - Media queries and mobile-first layouts<br><br><strong>How it works:</strong><br>1. Read the task in the left panel<br>2. Write code in the editor<br>3. See live results in the preview<br>4. Get instant feedback with hints<br><br><strong>Keyboard shortcuts:</strong> <kbd>Ctrl+Z</kbd> to undo, <kbd>Ctrl+Shift+Z</kbd> to redo<br><br><strong>More resources:</strong><br>• <a href=\"https://nextlevelshit.github.io/html-over-js/\" target=\"_blank\">HTML over JS</a> - Native HTML vs JavaScript solutions<br>• <a href=\"https://nextlevelshit.github.io/web-engineering-mandala/\" target=\"_blank\">Web Engineering Mandala</a> - JavaScript technology roadmap",
|
||||
"task": "Hello World",
|
||||
"task": "Write <code>Hello World</code>",
|
||||
"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 <code>Hello World</code>"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -39,7 +39,7 @@
|
||||
"validations": [
|
||||
{
|
||||
"type": "contains",
|
||||
"value": "Hello",
|
||||
"value": "Hello World",
|
||||
"message": "Click Next to continue"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{
|
||||
"id": "transitions-1",
|
||||
"title": "Transitions",
|
||||
"description": "Learn how to apply <kbd>transition</kbd> to properties for smooth changes on state changes.",
|
||||
"description": "Learn how to apply <kbd>transition</kbd> to properties for smooth changes on state changes.<br><br><pre>.element {\n transition: property duration;\n /* e.g. transition: background-color 0.3s; */\n}</pre>",
|
||||
"task": "Add <kbd>transition: background-color 0.3s</kbd> to <kbd>.btn</kbd> so the color fades smoothly on hover.",
|
||||
"previewHTML": "<button class=\"btn\">Hover Me</button>",
|
||||
"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 <kbd>@keyframes</kbd> and apply them via the <kbd>animation</kbd> shorthand.",
|
||||
"description": "Create named animations using <kbd>@keyframes</kbd> and apply them via the <kbd>animation</kbd> shorthand.<br><br><pre>@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}</pre>",
|
||||
"task": "Define a keyframe at <kbd>50%</kbd> with <kbd>transform: translateY(-20px)</kbd> and apply <kbd>animation: bounce 1s infinite</kbd> to <kbd>.ball</kbd>.",
|
||||
"previewHTML": "<div class=\"ball\"></div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"id": "semantic-containers",
|
||||
"title": "Semantic Tags",
|
||||
"description": "Modern HTML uses semantic containers that describe their content:<br><br><kbd><header></kbd> - Page or section header<br><kbd><nav></kbd> - Navigation links<br><kbd><main></kbd> - Main content area<br><kbd><section></kbd> - Thematic grouping<br><kbd><article></kbd> - Self-contained content<br><kbd><footer></kbd> - Page or section footer",
|
||||
"task": "Create a basic page structure:<br>1. Add a <kbd><header></kbd> with an <kbd><h1></kbd> containing the text 'My Website'<br>2. Add a <kbd><main></kbd> element with a paragraph saying 'Welcome to my site!'<br>3. Add a <kbd><footer></kbd> with a paragraph saying 'Copyright 2025'",
|
||||
"task": "Create a basic page structure:<br>1. Add a <kbd><header></kbd> with an <kbd><h1></kbd> containing the text <code>My Website</code><br>2. Add a <kbd><main></kbd> element with a paragraph saying <code>Welcome to my site!</code><br>3. Add a <kbd><footer></kbd> with a paragraph saying <code>Copyright 2025</code>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "form-structure",
|
||||
"title": "Form Structure",
|
||||
"description": "Every form needs a <kbd><form></kbd> wrapper. Inside, use <kbd><label></kbd> to describe inputs and <kbd><input></kbd> for user data entry.<br><br>The <kbd>for</kbd> attribute on labels should match the <kbd>id</kbd> on inputs for accessibility.",
|
||||
"task": "Create a form with:<br>1. A <kbd><label></kbd> with the text 'Name:' and <kbd>for=\"name\"</kbd> attribute<br>2. A text <kbd><input></kbd> with <kbd>id=\"name\"</kbd> and <kbd>name=\"name\"</kbd> attributes",
|
||||
"task": "Create a form with:<br>1. A <kbd><label></kbd> with the text <code>Name:</code> and <kbd>for=\"name\"</kbd> attribute<br>2. A text <kbd><input></kbd> with <kbd>id=\"name\"</kbd> and <kbd>name=\"name\"</kbd> 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:<br><br><kbd><button type=\"submit\"></kbd> - Preferred, flexible content<br><kbd><input type=\"submit\"></kbd> - Simple text-only button<br><br>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:<br><br><kbd><button type=\"submit\"></kbd> - Preferred, flexible content<br><kbd><input type=\"submit\"></kbd> - Simple text-only button<br><br>The button text should be action-oriented (e.g., <code>Sign In</code>, 'Register', 'Send').",
|
||||
"task": "Add a submit button to the form with the text <code>Sign In</code>.",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "details-summary-basic",
|
||||
"title": "First Widget",
|
||||
"description": "The <kbd><details></kbd> element creates a collapsible section. The <kbd><summary></kbd> provides the clickable label.<br><br>Click the summary to toggle the hidden content - no JavaScript needed!",
|
||||
"task": "Create a <kbd><details></kbd> element with:<br>1. A <kbd><summary></kbd> saying 'Click to reveal'<br>2. A <kbd><p></kbd> with the text 'This content was hidden!'",
|
||||
"task": "Create a <kbd><details></kbd> element with:<br>1. A <kbd><summary></kbd> saying <code>Click to reveal</code><br>2. A <kbd><p></kbd> with the text <code>This content was hidden!</code>",
|
||||
"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 <kbd><details></kbd> elements create an accordion-style FAQ. Each question can be expanded independently.<br><br><b>Pro tip:</b> Type <kbd>details*3>summary+p</kbd> and press Tab for Emmet expansion. <kbd>*3</kbd> creates 3 elements, <kbd>></kbd> nests inside, <kbd>+</kbd> adds siblings.",
|
||||
"task": "Create an FAQ section with:<br>1. An <kbd><h1></kbd> saying 'Frequently Asked Questions'<br>2. Three <kbd><details></kbd> elements, each with a question in <kbd><summary></kbd> and an answer in <kbd><p></kbd>",
|
||||
"task": "Create an FAQ section with:<br>1. An <kbd><h1></kbd> saying <code>Frequently Asked Questions</code><br>2. Three <kbd><details></kbd> elements, each with a question in <kbd><summary></kbd> and an answer in <kbd><p></kbd>",
|
||||
"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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "progress-basic",
|
||||
"title": "Progress Bars",
|
||||
"description": "The <kbd><progress></kbd> element shows task completion. Use <kbd>value</kbd> for current progress and <kbd>max</kbd> for the total.<br><br><b>Note:</b> This is not a self-closing tag! Write <kbd><progress>...</progress></kbd> with fallback text inside for older browsers.",
|
||||
"task": "Create a progress bar showing 70% completion:<br>1. Add a <kbd><label></kbd> saying 'Download:'<br>2. Add a <kbd><progress></kbd> with <kbd>value=\"70\"</kbd> and <kbd>max=\"100\"</kbd>",
|
||||
"task": "Create a progress bar showing 70% completion:<br>1. Add a <kbd><label></kbd> saying <code>Download:</code><br>2. Add a <kbd><progress></kbd> with <kbd>value=\"70\"</kbd> and <kbd>max=\"100\"</kbd>",
|
||||
"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 <kbd>value</kbd> attribute. This creates an animated indeterminate state.<br><br>Useful for network requests or processes with unknown duration.",
|
||||
"task": "Create a loading indicator:<br>1. Add a <kbd><p></kbd> saying 'Loading...'<br>2. Add a <kbd><progress></kbd> without a value attribute",
|
||||
"task": "Create a loading indicator:<br>1. Add a <kbd><p></kbd> saying <code>Loading...</code><br>2. Add a <kbd><progress></kbd> 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 <kbd><meter></kbd> element displays a scalar value within a range. Use it for measurements like disk space, battery, or ratings.<br><br>Set <kbd>low</kbd>, <kbd>high</kbd>, and <kbd>optimum</kbd> to define good/bad ranges - the browser colors it accordingly!",
|
||||
"task": "Create a battery level meter:<br>1. Add a <kbd><label></kbd> saying 'Battery:'<br>2. Add a <kbd><meter></kbd> with:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> and <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> and <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"task": "Create a battery level meter:<br>1. Add a <kbd><label></kbd> saying <code>Battery:</code><br>2. Add a <kbd><meter></kbd> with:<br> - <kbd>value=\"0.8\"</kbd><br> - <kbd>min=\"0\"</kbd> and <kbd>max=\"1\"</kbd><br> - <kbd>low=\"0.2\"</kbd> and <kbd>high=\"0.8\"</kbd><br> - <kbd>optimum=\"1\"</kbd>",
|
||||
"previewHTML": "",
|
||||
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; } meter { width: 100%; height: 25px; }",
|
||||
"sandboxCSS": "",
|
||||
|
||||
78
lessons/uk/25-html-datalist.json
Normal file
78
lessons/uk/25-html-datalist.json
Normal file
@@ -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 <kbd><datalist></kbd> element provides autocomplete suggestions for inputs. Connect it using the <kbd>list</kbd> attribute on the input matching the datalist's <kbd>id</kbd>.<br><br>Users can still type freely - suggestions are just helpers!",
|
||||
"task": "Create a browser selector:<br>1. Add a <kbd><label></kbd> saying <code>Browser:</code><br>2. Add an <kbd><input></kbd> with <kbd>list=\"browsers\"</kbd><br>3. Add a <kbd><datalist id=\"browsers\"></kbd> 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": "<label for=\"browser\">Browser:</label>\n<input type=\"text\" id=\"browser\" list=\"browsers\">\n<datalist id=\"browsers\">\n <option value=\"Chrome\">\n <option value=\"Firefox\">\n <option value=\"Safari\">\n</datalist>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "datalist",
|
||||
"message": "Add a <kbd><datalist></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "input", "attr": "list", "value": "browsers" },
|
||||
"message": "Connect the input to datalist using <kbd>list=</kbd>\"browsers\""
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "option", "min": 3 },
|
||||
"message": "Add at least 3 <kbd><option></kbd> elements inside <kbd><datalist></kbd>"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "label",
|
||||
"message": "Add a <kbd><label></kbd> 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.<br><br>The <kbd>value</kbd> attribute is what gets entered, and you can add display text after it.",
|
||||
"task": "Create a country input:<br>1. Add a <kbd><label></kbd> saying <code>Country:</code><br>2. Add an <kbd><input></kbd> with <kbd>list=\"countries\"</kbd><br>3. Add a <kbd><datalist id=\"countries\"></kbd> 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": "<label for=\"country\">Country:</label>\n<input type=\"text\" id=\"country\" list=\"countries\" placeholder=\"Start typing...\">\n<datalist id=\"countries\">\n <option value=\"Germany\">\n <option value=\"France\">\n <option value=\"Spain\">\n <option value=\"Italy\">\n</datalist>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "datalist",
|
||||
"message": "Add a <kbd><datalist></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "datalist", "attr": "id", "value": "countries" },
|
||||
"message": "Set <kbd>id=</kbd>\"countries\" on the datalist"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "input", "attr": "list", "value": "countries" },
|
||||
"message": "Connect the input using <kbd>list=</kbd>\"countries\""
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "option", "min": 4 },
|
||||
"message": "Add at least 4 country options"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
83
lessons/uk/27-html-dialog.json
Normal file
83
lessons/uk/27-html-dialog.json
Normal file
@@ -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 <kbd><dialog></kbd> element creates a native modal. Add the <kbd>open</kbd> attribute to show it.<br><br>Use <kbd><form method=\"dialog\"></kbd> inside to close it when the form submits - no JavaScript needed!",
|
||||
"task": "Create a dialog with:<br>1. The <kbd>open</kbd> attribute to show it<br>2. An <kbd><h2></kbd> saying <code>Welcome!</code><br>3. A <kbd><p></kbd> with a greeting message<br>4. A <kbd><form method=\"dialog\"></kbd> 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": "<dialog open>\n <h2>Welcome!</h2>\n <p>This is a native HTML dialog element.</p>\n <form method=\"dialog\">\n <button>Close</button>\n </form>\n</dialog>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog",
|
||||
"message": "Add a <kbd><dialog></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "dialog", "attr": "open", "value": true },
|
||||
"message": "Add the <kbd>open</kbd> attribute to show the dialog"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog h2",
|
||||
"message": "Add an <kbd><h2></kbd> heading inside the dialog"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "form[method='dialog']",
|
||||
"message": "Add a <kbd><form method=\"dialog\"></kbd> 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 <kbd>method=\"dialog\"</kbd> makes the form close the dialog on submit instead of sending data.<br><br>This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.",
|
||||
"task": "Create a confirmation dialog:<br>1. Add <kbd>open</kbd> to show it<br>2. An <kbd><h2></kbd> saying <code>Confirm Delete</code><br>3. A <kbd><p></kbd> asking <code>Are you sure?</code><br>4. A <kbd><form method=\"dialog\"></kbd> 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": "<dialog open>\n <h2>Confirm Delete</h2>\n <p>Are you sure you want to delete this item?</p>\n <form method=\"dialog\">\n <button value=\"cancel\">Cancel</button>\n <button value=\"delete\">Delete</button>\n </form>\n</dialog>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "dialog[open]",
|
||||
"message": "Add a <kbd><dialog></kbd> 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 <kbd><form method=\"dialog\"></kbd> for the buttons"
|
||||
},
|
||||
{
|
||||
"type": "element_count",
|
||||
"value": { "selector": "dialog button", "min": 2 },
|
||||
"message": "Add at least 2 buttons (Cancel and Confirm)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
127
lessons/uk/28-html-forms-fieldset.json
Normal file
127
lessons/uk/28-html-forms-fieldset.json
Normal file
@@ -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 <kbd><fieldset></kbd> element groups related form controls together. Add a <kbd><legend></kbd> as the first child to give the group a title.<br><br>This helps with accessibility and visual organization of complex forms.",
|
||||
"task": "Create a form with a fieldset:<br>1. A <kbd><form></kbd> element<br>2. A <kbd><fieldset></kbd> inside<br>3. A <kbd><legend></kbd> saying <code>Personal Info</code><br>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": "<form>\n <fieldset>\n <legend>Personal Info</legend>\n <label for=\"name\">Name:</label>\n <input type=\"text\" id=\"name\" name=\"name\">\n <label for=\"email\">Email:</label>\n <input type=\"email\" id=\"email\" name=\"email\">\n </fieldset>\n</form>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "form",
|
||||
"message": "Add a <kbd><form></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "fieldset",
|
||||
"message": "Add a <kbd><fieldset></kbd> inside the form"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "legend",
|
||||
"message": "Add a <kbd><legend></kbd> 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 <kbd><textarea></kbd> element creates a multi-line text input, perfect for longer content like messages or descriptions.<br><br>Use <kbd>rows</kbd> and <kbd>cols</kbd> attributes to set default size.",
|
||||
"task": "Create a contact form:<br>1. A <kbd><fieldset></kbd> with <kbd><legend></kbd> <code>Contact Us</code><br>2. A labeled <kbd><input></kbd> for email<br>3. A labeled <kbd><textarea></kbd> for the message<br>4. A submit <kbd><button></kbd>",
|
||||
"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": "<form>\n <fieldset>\n <legend>Contact Us</legend>\n <label for=\"email\">Email:</label>\n <input type=\"email\" id=\"email\" name=\"email\">\n <label for=\"message\">Message:</label>\n <textarea id=\"message\" name=\"message\" rows=\"4\"></textarea>\n <button type=\"submit\">Send Message</button>\n </fieldset>\n</form>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "fieldset",
|
||||
"message": "Add a <kbd><fieldset></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "legend",
|
||||
"message": "Add a <kbd><legend></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "textarea",
|
||||
"message": "Add a <kbd><textarea></kbd> 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 <kbd><fieldset></kbd> elements to organize different sections.<br><br>This improves usability for long forms like registration or checkout pages.",
|
||||
"task": "Create a registration form with 2 fieldsets:<br>1. <code>Account Info</code> with username and password inputs<br>2. <code>Preferences</code> with a textarea for bio<br>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": "<form>\n <fieldset>\n <legend>Account Info</legend>\n <label for=\"username\">Username:</label>\n <input type=\"text\" id=\"username\" name=\"username\">\n <label for=\"password\">Password:</label>\n <input type=\"password\" id=\"password\" name=\"password\">\n </fieldset>\n <fieldset>\n <legend>Preferences</legend>\n <label for=\"bio\">Bio:</label>\n <textarea id=\"bio\" name=\"bio\"></textarea>\n </fieldset>\n <button type=\"submit\">Register</button>\n</form>",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "table-basic",
|
||||
"title": "Basic Table Structure",
|
||||
"description": "Tables use <kbd><table></kbd> with <kbd><tr></kbd> for rows. Inside rows, use <kbd><th></kbd> for headers and <kbd><td></kbd> for data cells.<br><br>The <kbd><caption></kbd> element provides an accessible title for the table.",
|
||||
"task": "Create a simple table with:<br>1. A <kbd><caption></kbd> saying 'Fruit Prices'<br>2. A header row with 'Fruit' and 'Price' columns<br>3. At least 2 data rows",
|
||||
"task": "Create a simple table with:<br>1. A <kbd><caption></kbd> saying <code>Fruit Prices</code><br>2. A header row with <code>Fruit</code> and <code>Price</code> columns<br>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 <kbd><thead></kbd> to group header rows and <kbd><tbody></kbd> to group data rows. This helps browsers and assistive technology understand the table structure.<br><br>You can also use <kbd><tfoot></kbd> for footer rows like totals.",
|
||||
"task": "Create a structured table:<br>1. A <kbd><caption></kbd> with 'Monthly Sales'<br>2. A <kbd><thead></kbd> with Month and Revenue headers<br>3. A <kbd><tbody></kbd> with at least 2 data rows",
|
||||
"task": "Create a structured table:<br>1. A <kbd><caption></kbd> with <code>Monthly Sales</code><br>2. A <kbd><thead></kbd> with Month and Revenue headers<br>3. A <kbd><tbody></kbd> 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 <kbd><tfoot></kbd> to create a footer section for totals or summary data. The footer stays at the bottom even if tbody has many rows.<br><br>Combine all sections for a fully structured, accessible table.",
|
||||
"task": "Create a complete table:<br>1. A <kbd><caption></kbd> with 'Order Summary'<br>2. A <kbd><thead></kbd> with Item and Price headers<br>3. A <kbd><tbody></kbd> with 2 items<br>4. A <kbd><tfoot></kbd> with a Total row",
|
||||
"task": "Create a complete table:<br>1. A <kbd><caption></kbd> with <code>Order Summary</code><br>2. A <kbd><thead></kbd> with Item and Price headers<br>3. A <kbd><tbody></kbd> with 2 items<br>4. A <kbd><tfoot></kbd> 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": "",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"id": "marquee-basic",
|
||||
"title": "Scrolling Text",
|
||||
"description": "The <kbd><marquee></kbd> element creates scrolling text - a classic from the early web! While deprecated, it still works in most browsers.<br><br>Note: For production, use CSS animations instead. But for learning and fun, marquee is great!",
|
||||
"task": "Create a simple marquee:<br>1. Add a <kbd><marquee></kbd> element<br>2. Put some text inside like 'Welcome to my website!'",
|
||||
"task": "Create a simple marquee:<br>1. Add a <kbd><marquee></kbd> element<br>2. Put some text inside like <code>Welcome to my website!</code>",
|
||||
"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": "",
|
||||
|
||||
Reference in New Issue
Block a user