diff --git a/lessons/ar/00-basic-selectors.json b/lessons/ar/00-basic-selectors.json
new file mode 100644
index 0000000..6e55337
--- /dev/null
+++ b/lessons/ar/00-basic-selectors.json
@@ -0,0 +1,550 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "css-basic-selectors",
+ "title": "CSS Selectors",
+ "description": "CSS selectors are the foundation of styling web pages, allowing you to target specific HTML elements for styling. This module introduces fundamental selector types including element type selectors, class selectors, ID selectors, and the universal selector.",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "introduction-to-selectors",
+ "title": "What's a Selector?",
+ "description": "A CSS selector is the first part of a CSS rule that tells the browser which HTML elements should receive the styles defined in the declaration block. Selectors are essentially patterns that match against elements in your HTML document. Understanding selectors is fundamental because they determine which elements your CSS rules will affect. The element or elements targeted by a selector are referred to as the 'subject of the selector.' When writing a CSS rule, you first specify the selector, followed by curly braces that contain the style declarations. For example, to change the text color of elements, you can use the color property within your declaration block.
/* Element selector */\np {\n color: orangered;\n /* │ └─── Indicates the value of the expression\n │ \n └─────────── Indicates the property of the expression */\n}
",
+ "task": "Write a CSS rule using a type selector that targets all paragraph elements p in the document. Make the text blue by setting the color property to blue.",
+ "previewHTML": "
Introduction to CSS Selectors
\n
This paragraph should turn blue.
\n
This div element should remain unchanged.
\n
This second paragraph should also turn blue.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h1, p, div { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Write a type selector to target all paragraph elements */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "p { color: blue }",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^p\\s*{",
+ "message": "Start your rule with p { … } to select all paragraph elements",
+ "options": {
+ "caseSensitive": false
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color: property in your CSS rule"
+ },
+ {
+ "type": "contains",
+ "value": "blue",
+ "message": "Set the color value to blue"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "blue"
+ },
+ "message": "Use color: blue to set the text color"
+ },
+ {
+ "type": "regex",
+ "value": "p\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": false
+ }
+ }
+ ]
+ },
+ {
+ "id": "type-selectors",
+ "title": "Type Selectors",
+ "description": "Type selectors (also called tag name selectors or element selectors) target HTML elements based on their tag name. For example, p selects all paragraph elements, h1 selects all level-one headings, and div selects all division elements. Type selectors are the most fundamental way to select elements, applying styles consistently to all instances of a particular HTML element throughout your document. You can define a variety of CSS properties with type selectors, such as color for text color, background-color for the background, and font-weight for text emphasis. They provide a broad approach for styling your page and are often the starting point for more specific styling using other selector types.",
+ "task": "Write three separate CSS rules using type selectors to target specific HTML elements: make h2 headings purple, give span elements a yellow background, and make strong elements red.",
+ "previewHTML": "
Type Selectors Example
\n
Regular paragraph text with a highlighted span that should have a yellow background.
\n
Another paragraph with strong important text that should be red.
\n
Another Heading
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p, span, strong { padding: 3px; }",
+ "codePrefix": "/* Write three separate type selectors below */\n\n",
+ "initialCode": "/* 1. Make h2 headings purple */\n\n\n/* 2. Give span elements a yellow background */\n\n\n/* 3. Make strong elements red */\n",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "/* 1. Make h2 headings purple */\nh2 {\n color: purple;\n}\n\n/* 2. Give span elements a yellow background */\nspan {\n background-color: yellow;\n}\n\n/* 3. Make strong elements red */\nstrong {\n color: red;\n}",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^h2\\s*{",
+ "message": "Include an h2 { … } selector"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "purple"
+ },
+ "message": "Set the color property to purple for h2 elements"
+ },
+ {
+ "type": "regex",
+ "value": "h2\\s*{[^}]*}",
+ "message": "Make sure to close your h2 rule with a closing brace }"
+ },
+ {
+ "type": "regex",
+ "value": "^span\\s*{",
+ "message": "Include a span { … } selector"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "yellow"
+ },
+ "message": "Set a background-color: yellow for span elements"
+ },
+ {
+ "type": "regex",
+ "value": "span\\s*{[^}]*}",
+ "message": "Make sure to close your span rule with a closing brace }"
+ },
+ {
+ "type": "regex",
+ "value": "^strong\\s*{",
+ "message": "Include a strong { … } selector"
+ },
+ {
+ "type": "regex",
+ "value": "strong\\s*{\\s*color:\\s*red;[^}]*}",
+ "message": "Set the color: red for strong elements"
+ }
+ ]
+ },
+ {
+ "id": "class-selectors",
+ "title": "Class Selectors",
+ "description": "Class selectors target elements with a specific class attribute value. They begin with a dot (.) followed by the class name. Classes are powerful because they allow you to apply the same styles to multiple elements regardless of their type. An HTML element can have multiple classes (separated by spaces in the class attribute), and a class can be applied to any number of elements. When using class selectors, you can apply properties like background-color to set the background color of elements, and font-weight to control text thickness, making text bold or normal. This flexibility makes class selectors one of the most commonly used methods for applying styles in CSS, allowing for modular and reusable styling across your website.",
+ "task": "Create a CSS rule using a class selector that targets elements with the class highlight. Give these elements a yellow background and bold text.",
+ "previewHTML": "
Using Class Selectors
\n
This is a regular paragraph, but this span has the highlight class applied to it.
\n
This entire paragraph has the highlight class.
\n
\n
Regular list item
\n
This list item is highlighted
\n
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p, li { padding: 5px; margin-bottom: 10px; }",
+ "codePrefix": "/* Create a class selector for elements with the 'highlight' class */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.highlight\\s*{",
+ "message": "Start your rule with .highlight { … } to create a class selector",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color: property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "yellow"
+ },
+ "message": "Set the background color to yellow"
+ },
+ {
+ "type": "contains",
+ "value": "font-weight:",
+ "message": "Include the font-weight: property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "font-weight",
+ "expected": "bold"
+ },
+ "message": "Set the font-weight to bold"
+ },
+ {
+ "type": "regex",
+ "value": "\\.highlight\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "multiple-classes",
+ "title": "Multiple Classes",
+ "description": "HTML elements can have multiple classes applied simultaneously, allowing for composable and modular CSS designs. When an element has multiple classes, it will receive styles from all matching class selectors. This approach enables you to build a library of reusable CSS classes that can be combined in different ways. You can also target elements that have a specific combination of classes by chaining class selectors together without spaces (e.g., .class1.class2). When styling these elements, you might use properties like border-color to change the color of element borders, and background-color to set the background color of elements. This technique lets you create conditional styles that only apply when certain classes appear together.",
+ "task": "Complete the CSS rule that targets elements with both card and featured classes by chaining the selectors. Set the border-color to gold and the background-color to lemonchiffon to make featured cards stand out.",
+ "previewHTML": "
Multiple Class Combinations
\n
Regular Card
\n
Featured Card
\n
Just Featured (not a card)
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } .card { border: 2px solid gray; padding: 15px; margin-bottom: 10px; border-radius: 5px; }",
+ "sandboxCSS": "",
+ "codePrefix": "/* The .card class already has basic styling */\n/* Now target elements with BOTH classes: 'card' AND 'featured' */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": ".card.featured { border-color: gold; background-color: lemonchiffon }",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.card\\.featured\\s*{",
+ "message": "Chain the selectors as .card.featured (no space between them)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "border-color:",
+ "message": "Include the border-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "border-color",
+ "expected": "gold"
+ },
+ "message": "Set the border color to gold"
+ },
+ {
+ "type": "regex",
+ "value": "\\.card\\.featured\\s*{[^}]*;",
+ "message": "Make sure to end your CSS rule with a semicolon ;"
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "lemonchiffon"
+ },
+ "message": "Set the background color to lemonchiffon"
+ },
+ {
+ "type": "regex",
+ "value": "\\.card\\.featured\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "class-with-type",
+ "title": "Combining Types",
+ "description": "You can combine type selectors with class selectors to target specific HTML elements that have a certain class. This creates a more specific selector that only matches when both conditions are true: the element is of the specified type AND it has the specified class. For example, p.note would select paragraph elements with the class note, but would not select divs or spans with that same class. You can style these combined selections using properties like background-color to set a colored background for your elements. This approach allows you to apply different styles to the same class when it appears on different element types.",
+ "task": "Create a CSS rule that specifically targets <span> elements with the class highlight. Make those elements have an orange background, while other elements with the highlight class remain untouched.",
+ "previewHTML": "
Type and Class Combinations
\n
This paragraph has a highlighted span that should have an orange background.
\n
This paragraph has the highlight class but should NOT have an orange background.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } .highlight { font-weight: bold; }",
+ "sandboxCSS": "h2, p, span { padding: 5px; }",
+ "codePrefix": "/* The .highlight class already sets font-weight to bold */\n/* Now target ONLY span elements with the highlight class */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^span\\.highlight\\s*{",
+ "message": "Use span.highlight selector (no space between element and class)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "orange"
+ },
+ "message": "Set the background color to orange"
+ },
+ {
+ "type": "regex",
+ "value": "span\\.highlight\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "id-selectors",
+ "title": "ID Selectors",
+ "description": "ID selectors target elements with a specific id attribute. They begin with a hash/pound sign (#) followed by the ID name. Unlike classes, IDs must be unique within a document—each ID value should be used only once per page. ID selectors have higher specificity than class or element selectors, meaning they override those selectors when conflicts arise. When styling with ID selectors, you can use properties like color to define text color, and text-decoration to control the appearance of text, such as adding underlines to elements. Because of their uniqueness requirement, IDs are best used for one-of-a-kind elements like page headers, main navigation, or specific unique components that appear only once on a page.",
+ "task": "Create a CSS rule with an ID selector that targets the element with the ID main-title. Set its color to purple and add an underline with text-decoration: underline.",
+ "previewHTML": "
Main Page Title
\n
Regular paragraph content.
\n
Secondary Heading
\n
Introduction paragraph (different ID).
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h1, h2, p { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Create an ID selector to target the element with id=\"main-title\" */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^#main-title\\s*{",
+ "message": "Start your rule with #main-title to create an ID selector",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "purple"
+ },
+ "message": "Set the color to purple"
+ },
+ {
+ "type": "contains",
+ "value": "text-decoration:",
+ "message": "Include the text-decoration property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "text-decoration",
+ "expected": "underline"
+ },
+ "message": "Set the text-decoration to underline"
+ },
+ {
+ "type": "regex",
+ "value": "#main-title\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "id-with-type",
+ "title": "Type + ID",
+ "description": "Similar to how you can combine type and class selectors, you can also combine type selectors with ID selectors. For example, h1#title targets an h1 element with the ID 'title'. When using this combined approach, you can apply CSS properties like font-style to control the slant of the text, making it italic or normal. While this selector combination is more specific than using just the ID selector, it's often unnecessary since IDs should already be unique in a document. However, this technique can be useful for improving code readability or when you want to emphasize that a particular ID should only appear on a specific element type.",
+ "task": "Create a CSS rule that combines a type selector with an ID selector to target specifically a paragraph element with the ID special. Set its font style to italic.",
+ "previewHTML": "
Heading with ID \"special\" (should NOT be affected)
\n
Paragraph with ID \"special\" (should become italic)
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Create a combined type+ID selector for a paragraph with id=\"special\" */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^p#special\\s*{",
+ "message": "Use p#special to target paragraphs with ID special",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "font-style:",
+ "message": "Include the font-style property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "font-style",
+ "expected": "italic"
+ },
+ "message": "Set the font-style to italic"
+ },
+ {
+ "type": "regex",
+ "value": "p#special\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "selector-lists",
+ "title": "Selector Lists",
+ "description": "When multiple elements need the same styling, you can group them together using a selector list (also known as grouping selectors). Selector lists are created by separating individual selectors with commas. This approach reduces repetition in your CSS, making it more maintainable and efficient. For example, h1, h2, h3 { color: blue; } applies the same blue color to all three heading levels. When styling multiple selectors at once, you can apply properties like background-color to set the background, border-left to create a left border with a specific thickness, style, and color, and padding-left to create space between the content and the left border. Whitespace around commas is optional, and each selector in the list can be any valid selector type-elements, classes, IDs, or even more complex selectors.",
+ "task": "Create a selector list that applies the same styles to three different elements: paragraphs with class note, list items with class important, and the element with ID summary. Give them a lightyellow background, a gold left border, and some left padding.",
+ "previewHTML": "
This is a note paragraph.
\n
\n
Regular list item
\n
Important list item
\n
\n
Summary section
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "p, li, div { padding: 8px; margin-bottom: 8px; border: 1px dashed gray; }",
+ "codePrefix": "/* Create a selector list to apply the same styles to multiple different elements */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "p.note,\nli.important,\n#summary {\n background-color: lightyellow;\n border-left: 3px solid gold;\n padding-left: 10px\n}",
+ "validations": [
+ {
+ "type": "contains",
+ "value": "p.note",
+ "message": "Include p.note in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "li.important",
+ "message": "Include li.important in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "#summary",
+ "message": "Include #summary in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "regex",
+ "value": "(p\\.note|li\\.important|#summary)\\s*,\\s*(p\\.note|li\\.important|#summary)\\s*,\\s*(p\\.note|li\\.important|#summary)",
+ "message": "Create a comma-separated list with all three selectors",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "lightyellow"
+ },
+ "message": "Set the background color to lightyellow"
+ },
+ {
+ "type": "contains",
+ "value": "border-left:",
+ "message": "Include the border-left property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "border-left",
+ "expected": "3px solid gold"
+ },
+ "message": "Use border-left: 3px solid gold to create a left border"
+ },
+ {
+ "type": "contains",
+ "value": "padding-left:",
+ "message": "Include the padding-left property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "padding-left",
+ "expected": "10px"
+ },
+ "message": "Use padding-left: 10px to add left padding"
+ }
+ ]
+ },
+ {
+ "id": "universal-selector",
+ "title": "Universal (*)",
+ "description": "The universal selector is denoted by an asterisk (*) and matches any element of any type. It selects everything in the document or, when combined with other selectors, everything within a specific context. For example, * { margin: 0; } removes margins from all elements, while article * selects all elements inside article elements. When using the universal selector in combination with other selectors, you can apply properties like margin to control the spacing around elements. The universal selector is powerful but should be used carefully due to its broad impact. It's commonly used in CSS resets, to override default browser styling, or to target all children of a particular element.",
+ "task": "Use the universal selector to remove margins from all elements inside the container div. Create a rule using div.container * as the selector and set margin: 0.",
+ "previewHTML": "
\n
Inside Container
\n
This paragraph is inside the container.
\n
\n
List item inside container
\n
\n
\n
This paragraph is outside the container and should not be affected.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } div.container { border: 2px solid navy; padding: 15px; background-color: lavender; } h2, p, ul, li { margin: 15px 0; }",
+ "sandboxCSS": "",
+ "codePrefix": "/* Use the universal selector to target all elements inside the container */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^div\\.container\\s+\\*\\s*{",
+ "message": "Use div.container * selector (with a space between container and *)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "margin:",
+ "message": "Include the margin property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "margin",
+ "expected": "0"
+ },
+ "message": "Set margin to 0"
+ },
+ {
+ "type": "regex",
+ "value": "div\\.container\\s+\\*\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "specificity-basics",
+ "title": "Specificity",
+ "description": "CSS specificity determines which styles take precedence when multiple conflicting rules target the same element. Specificity follows a hierarchical system: inline styles have the highest specificity, followed by ID selectors, then class/attribute/pseudo-class selectors, and finally element/pseudo-element selectors. This can be conceptualized as a four-part score (inline, ID, class, element). When creating multiple rules that may target the same elements, you can use the color property to set text colors, and specificity will determine which color is actually applied. Understanding specificity is crucial for predictable styling and debugging CSS conflicts. When two selectors have equal specificity, the one that comes last in the stylesheet wins.",
+ "task": "Examine the existing CSS rules and add a new rule with higher specificity to override the text color of the paragraph. Create a rule using '.content p' as the selector and set color: green.",
+ "previewHTML": "
\n
What color will this paragraph be? Look at the CSS rules and their specificity.
\n
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "p { border: 1px dashed gray; padding: 10px; }",
+ "codePrefix": "/* These CSS rules target the same paragraph but have different specificity */\n\n/* Rule 1: Element selector (lowest specificity) */\np {\n color: red;\n}\n\n/* Rule 2: Descendant selector (higher specificity than just 'p') */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.content\\s+p\\s*{",
+ "message": "Use .content p as your selector (note the space between)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color property"
+ },
+ {
+ "type": "contains",
+ "value": "green",
+ "message": ""
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/ar/00-welcome.json b/lessons/ar/00-welcome.json
new file mode 100644
index 0000000..ce364c4
--- /dev/null
+++ b/lessons/ar/00-welcome.json
@@ -0,0 +1,62 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "welcome",
+ "title": "Code Crispies",
+ "description": "Welcome to Code Crispies - your interactive web development learning platform",
+ "mode": "html",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "get-started",
+ "title": "Get Started",
+ "description": "Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required!
What you'll learn: • HTML - Semantic elements, forms, tables, SVG (HTML Block & Inline, HTML Forms, HTML Tables) • CSS - Selectors, box model, flexbox, animations (CSS Selectors, CSS Box Model, CSS Flexbox) • Responsive Design - Media queries and mobile-first layouts
How it works: 1. Read the task in the left panel 2. Write code in the editor 3. See live results in the preview 4. Get instant feedback with hints
Keyboard shortcuts:Ctrl+Z to undo, Ctrl+Shift+Z to redo
More resources: • HTML over JS - Native HTML vs JavaScript solutions • Web Engineering Mandala - JavaScript technology roadmap",
+ "task": "Hello World",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }",
+ "sandboxCSS": "",
+ "initialCode": "",
+ "solution": "
Hello World
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "contains",
+ "value": "Hello",
+ "message": "Write 'Hello World'"
+ }
+ ]
+ },
+ {
+ "id": "overview",
+ "title": "Overview",
+ "description": "You're ready! Open the menu (☰) to explore all modules.
Recommended learning path: 1. HTML Block & Inline - Understand container vs inline elements 2. HTML Forms - Build interactive forms with validation 3. CSS Selectors - Target elements precisely 4. CSS Box Model - Master padding, margin, borders 5. CSS Flexbox - Create flexible layouts 6. CSS Animations - Add motion and transitions
Tips: • Use Show Expected to see the target result • Your progress is saved automatically • Try Emmet in HTML mode: ul>li*3 + Tab
",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": []
+ }
+ ]
+}
diff --git a/lessons/ar/01-box-model.json b/lessons/ar/01-box-model.json
new file mode 100644
index 0000000..a5b10a6
--- /dev/null
+++ b/lessons/ar/01-box-model.json
@@ -0,0 +1,180 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "box-model",
+ "title": "CSS Box Model",
+ "description": "Master the fundamental principles of space management in web design through the CSS box model. This module explores how content, padding, borders, and margins combine to create layout structures that are both visually appealing and structurally sound.",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "box-model-1",
+ "title": "Box Model Components",
+ "description": "The CSS box model consists of four concentric layers: content area (innermost), padding, border, and margin (outermost). Understanding how these components interact is essential for precise layout control.",
+ "task": "Add padding: 1rem to .box to create space between its content and border.",
+ "previewHTML": "
Block elements (containers) start on a new line and take full width. Examples: <div>, <p>, <h1>, <section>
Inline elements flow within text and only take needed width. Examples: <span>, <a>, <strong>, <em>",
+ "task": "Wrap the word important with <strong> tags to make it bold. Notice how the paragraph (block) takes full width while strong (inline) flows with text.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; } p { background: #e3f2fd; padding: 10px; } strong { background: #ffecb3; }",
+ "sandboxCSS": "",
+ "initialCode": "
This is a paragraph with an important word.
",
+ "solution": "
This is a paragraph with an important word.
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "p",
+ "message": "Add a <p> paragraph element"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "p", "child": "strong" },
+ "message": "Wrap the word important with <strong> tags"
+ }
+ ]
+ },
+ {
+ "id": "semantic-containers",
+ "title": "Semantic Tags",
+ "description": "Modern HTML uses semantic containers that describe their content:
<header> - Page or section header <nav> - Navigation links <main> - Main content area <section> - Thematic grouping <article> - Self-contained content <footer> - Page or section footer",
+ "task": "Create a basic page structure: 1. Add a <header> with an <h1> containing the text 'My Website' 2. Add a <main> element with a paragraph saying 'Welcome to my site!' 3. Add a <footer> with a paragraph saying 'Copyright 2025'",
+ "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": "",
+ "initialCode": "",
+ "solution": "\n
Use semantic elements when possible, div/span when no semantic element fits.",
+ "task": "Wrap the word 'highlighted' in a <span> to style it differently. Wrap the whole quote in a <div>.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: Georgia, serif; padding: 20px; } div { background: #f5f5f5; padding: 15px; border-left: 4px solid #1976d2; } span { background: #fff59d; padding: 2px 4px; }",
+ "sandboxCSS": "",
+ "initialCode": "The most highlighted moment was unforgettable.",
+ "solution": "
The most highlighted moment was unforgettable.
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "div",
+ "message": "Wrap everything in a <div> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "span",
+ "message": "Add a <span> around the word highlighted"
+ },
+ {
+ "type": "element_text",
+ "value": { "selector": "span", "text": "highlighted" },
+ "message": "The <span> should contain the word highlighted"
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/ar/21-html-forms-basic.json b/lessons/ar/21-html-forms-basic.json
new file mode 100644
index 0000000..d971cbf
--- /dev/null
+++ b/lessons/ar/21-html-forms-basic.json
@@ -0,0 +1,102 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "html-forms-basic",
+ "title": "HTML Forms",
+ "description": "Learn to create forms with various input types",
+ "mode": "html",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "form-structure",
+ "title": "Form Structure",
+ "description": "Every form needs a <form> wrapper. Inside, use <label> to describe inputs and <input> for user data entry.
The for attribute on labels should match the id on inputs for accessibility.",
+ "task": "Create a form with: 1. A <label> with the text 'Name:' and for=\"name\" attribute 2. A text <input> with id=\"name\" and name=\"name\" attributes",
+ "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": "",
+ "initialCode": "",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "form",
+ "message": "Wrap everything in a <form> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "label",
+ "message": "Add a <label> for your input"
+ },
+ {
+ "type": "element_exists",
+ "value": "input",
+ "message": "Add an <input> element"
+ },
+ {
+ "type": "attribute_value",
+ "value": { "selector": "label", "attr": "for", "value": null },
+ "message": "Add a for attribute to your label"
+ },
+ {
+ "type": "attribute_value",
+ "value": { "selector": "input", "attr": "id", "value": null },
+ "message": "Add an id attribute to your input"
+ }
+ ]
+ },
+ {
+ "id": "input-types",
+ "title": "Input Types",
+ "description": "Different input types provide appropriate keyboards and validation:
type=\"text\" - General text type=\"email\" - Email with @ validation type=\"password\" - Hidden characters type=\"number\" - Numeric keyboard type=\"tel\" - Phone keyboard",
+ "task": "Create a login form with two fields: 1. An email field: <label for=\"email\">Email:</label> and <input type=\"email\" id=\"email\"> 2. A password field: <label for=\"password\">Password:</label> and <input type=\"password\" id=\"password\">",
+ "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; }",
+ "sandboxCSS": "",
+ "initialCode": "",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "input[type='email']",
+ "message": "Add an input with type=\"email\""
+ },
+ {
+ "type": "element_exists",
+ "value": "input[type='password']",
+ "message": "Add an input with type=\"password\""
+ },
+ {
+ "type": "element_count",
+ "value": { "selector": "label", "min": 2 },
+ "message": "Add labels for both inputs"
+ }
+ ]
+ },
+ {
+ "id": "submit-button",
+ "title": "Submit Button",
+ "description": "Forms need a way to submit data. Use:
Click the summary to toggle the hidden content - no JavaScript needed!",
+ "task": "Create a <details> element with: 1. A <summary> saying 'Click to reveal' 2. A <p> with the text 'This content was hidden!'",
+ "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": "",
+ "initialCode": "",
+ "solution": "\n Click to reveal\n
This content was hidden!
\n",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "details",
+ "message": "Add a <details> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "summary",
+ "message": "Add a <summary> inside the details"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "details", "child": "summary" },
+ "message": "The <summary> must be inside <details>"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "details", "child": "p" },
+ "message": "Add a <p> inside <details> for the hidden content"
+ }
+ ]
+ },
+ {
+ "id": "details-open-attribute",
+ "title": "Pre-expanded Details",
+ "description": "By default, <details> is closed. Add the open attribute to show the content initially.
This is a boolean attribute - just add open without a value.",
+ "task": "Add the open attribute to the <details> element to show the content by default.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } details { border: 1px solid #ddd; border-radius: 8px; padding: 15px; background: #f9f9f9; } summary { font-weight: 600; cursor: pointer; } details p { margin-top: 15px; color: #666; }",
+ "sandboxCSS": "",
+ "initialCode": "\n FAQ: What is HTML5?\n
HTML5 is the latest version of the HTML standard with new semantic elements and APIs.
\n",
+ "solution": "\n FAQ: What is HTML5?\n
HTML5 is the latest version of the HTML standard with new semantic elements and APIs.
\n",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "attribute_value",
+ "value": { "selector": "details", "attr": "open", "value": true },
+ "message": "Add the open attribute to <details>"
+ }
+ ]
+ },
+ {
+ "id": "faq-accordion",
+ "title": "FAQ Accordion",
+ "description": "Multiple <details> elements create an accordion-style FAQ. Each question can be expanded independently.
",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { background: steelblue; color: white; padding: 1rem; margin: 8px; text-align: center; font-weight: bold; width: 3rem; height: 3rem; display: flex; align-items: center; justify-content: center; } .middle { background: mediumseagreen; }",
+ "sandboxCSS": ".wrap { border: 2px dashed #aaa; padding: 1rem; display: flex; height: 12rem; align-items: center; }",
+ "codePrefix": ".middle {\n ",
+ "initialCode": "",
+ "codeSuffix": "\n}",
+ "solution": "align-self: flex-start;",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "property_value",
+ "value": {
+ "property": "align-self",
+ "expected": "flex-start"
+ },
+ "message": "Set align-self: flex-start"
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/es/00-basic-selectors.json b/lessons/es/00-basic-selectors.json
new file mode 100644
index 0000000..6e55337
--- /dev/null
+++ b/lessons/es/00-basic-selectors.json
@@ -0,0 +1,550 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "css-basic-selectors",
+ "title": "CSS Selectors",
+ "description": "CSS selectors are the foundation of styling web pages, allowing you to target specific HTML elements for styling. This module introduces fundamental selector types including element type selectors, class selectors, ID selectors, and the universal selector.",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "introduction-to-selectors",
+ "title": "What's a Selector?",
+ "description": "A CSS selector is the first part of a CSS rule that tells the browser which HTML elements should receive the styles defined in the declaration block. Selectors are essentially patterns that match against elements in your HTML document. Understanding selectors is fundamental because they determine which elements your CSS rules will affect. The element or elements targeted by a selector are referred to as the 'subject of the selector.' When writing a CSS rule, you first specify the selector, followed by curly braces that contain the style declarations. For example, to change the text color of elements, you can use the color property within your declaration block.
/* Element selector */\np {\n color: orangered;\n /* │ └─── Indicates the value of the expression\n │ \n └─────────── Indicates the property of the expression */\n}
",
+ "task": "Write a CSS rule using a type selector that targets all paragraph elements p in the document. Make the text blue by setting the color property to blue.",
+ "previewHTML": "
Introduction to CSS Selectors
\n
This paragraph should turn blue.
\n
This div element should remain unchanged.
\n
This second paragraph should also turn blue.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h1, p, div { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Write a type selector to target all paragraph elements */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "p { color: blue }",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^p\\s*{",
+ "message": "Start your rule with p { … } to select all paragraph elements",
+ "options": {
+ "caseSensitive": false
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color: property in your CSS rule"
+ },
+ {
+ "type": "contains",
+ "value": "blue",
+ "message": "Set the color value to blue"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "blue"
+ },
+ "message": "Use color: blue to set the text color"
+ },
+ {
+ "type": "regex",
+ "value": "p\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": false
+ }
+ }
+ ]
+ },
+ {
+ "id": "type-selectors",
+ "title": "Type Selectors",
+ "description": "Type selectors (also called tag name selectors or element selectors) target HTML elements based on their tag name. For example, p selects all paragraph elements, h1 selects all level-one headings, and div selects all division elements. Type selectors are the most fundamental way to select elements, applying styles consistently to all instances of a particular HTML element throughout your document. You can define a variety of CSS properties with type selectors, such as color for text color, background-color for the background, and font-weight for text emphasis. They provide a broad approach for styling your page and are often the starting point for more specific styling using other selector types.",
+ "task": "Write three separate CSS rules using type selectors to target specific HTML elements: make h2 headings purple, give span elements a yellow background, and make strong elements red.",
+ "previewHTML": "
Type Selectors Example
\n
Regular paragraph text with a highlighted span that should have a yellow background.
\n
Another paragraph with strong important text that should be red.
\n
Another Heading
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p, span, strong { padding: 3px; }",
+ "codePrefix": "/* Write three separate type selectors below */\n\n",
+ "initialCode": "/* 1. Make h2 headings purple */\n\n\n/* 2. Give span elements a yellow background */\n\n\n/* 3. Make strong elements red */\n",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "/* 1. Make h2 headings purple */\nh2 {\n color: purple;\n}\n\n/* 2. Give span elements a yellow background */\nspan {\n background-color: yellow;\n}\n\n/* 3. Make strong elements red */\nstrong {\n color: red;\n}",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^h2\\s*{",
+ "message": "Include an h2 { … } selector"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "purple"
+ },
+ "message": "Set the color property to purple for h2 elements"
+ },
+ {
+ "type": "regex",
+ "value": "h2\\s*{[^}]*}",
+ "message": "Make sure to close your h2 rule with a closing brace }"
+ },
+ {
+ "type": "regex",
+ "value": "^span\\s*{",
+ "message": "Include a span { … } selector"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "yellow"
+ },
+ "message": "Set a background-color: yellow for span elements"
+ },
+ {
+ "type": "regex",
+ "value": "span\\s*{[^}]*}",
+ "message": "Make sure to close your span rule with a closing brace }"
+ },
+ {
+ "type": "regex",
+ "value": "^strong\\s*{",
+ "message": "Include a strong { … } selector"
+ },
+ {
+ "type": "regex",
+ "value": "strong\\s*{\\s*color:\\s*red;[^}]*}",
+ "message": "Set the color: red for strong elements"
+ }
+ ]
+ },
+ {
+ "id": "class-selectors",
+ "title": "Class Selectors",
+ "description": "Class selectors target elements with a specific class attribute value. They begin with a dot (.) followed by the class name. Classes are powerful because they allow you to apply the same styles to multiple elements regardless of their type. An HTML element can have multiple classes (separated by spaces in the class attribute), and a class can be applied to any number of elements. When using class selectors, you can apply properties like background-color to set the background color of elements, and font-weight to control text thickness, making text bold or normal. This flexibility makes class selectors one of the most commonly used methods for applying styles in CSS, allowing for modular and reusable styling across your website.",
+ "task": "Create a CSS rule using a class selector that targets elements with the class highlight. Give these elements a yellow background and bold text.",
+ "previewHTML": "
Using Class Selectors
\n
This is a regular paragraph, but this span has the highlight class applied to it.
\n
This entire paragraph has the highlight class.
\n
\n
Regular list item
\n
This list item is highlighted
\n
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p, li { padding: 5px; margin-bottom: 10px; }",
+ "codePrefix": "/* Create a class selector for elements with the 'highlight' class */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.highlight\\s*{",
+ "message": "Start your rule with .highlight { … } to create a class selector",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color: property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "yellow"
+ },
+ "message": "Set the background color to yellow"
+ },
+ {
+ "type": "contains",
+ "value": "font-weight:",
+ "message": "Include the font-weight: property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "font-weight",
+ "expected": "bold"
+ },
+ "message": "Set the font-weight to bold"
+ },
+ {
+ "type": "regex",
+ "value": "\\.highlight\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "multiple-classes",
+ "title": "Multiple Classes",
+ "description": "HTML elements can have multiple classes applied simultaneously, allowing for composable and modular CSS designs. When an element has multiple classes, it will receive styles from all matching class selectors. This approach enables you to build a library of reusable CSS classes that can be combined in different ways. You can also target elements that have a specific combination of classes by chaining class selectors together without spaces (e.g., .class1.class2). When styling these elements, you might use properties like border-color to change the color of element borders, and background-color to set the background color of elements. This technique lets you create conditional styles that only apply when certain classes appear together.",
+ "task": "Complete the CSS rule that targets elements with both card and featured classes by chaining the selectors. Set the border-color to gold and the background-color to lemonchiffon to make featured cards stand out.",
+ "previewHTML": "
Multiple Class Combinations
\n
Regular Card
\n
Featured Card
\n
Just Featured (not a card)
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } .card { border: 2px solid gray; padding: 15px; margin-bottom: 10px; border-radius: 5px; }",
+ "sandboxCSS": "",
+ "codePrefix": "/* The .card class already has basic styling */\n/* Now target elements with BOTH classes: 'card' AND 'featured' */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": ".card.featured { border-color: gold; background-color: lemonchiffon }",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.card\\.featured\\s*{",
+ "message": "Chain the selectors as .card.featured (no space between them)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "border-color:",
+ "message": "Include the border-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "border-color",
+ "expected": "gold"
+ },
+ "message": "Set the border color to gold"
+ },
+ {
+ "type": "regex",
+ "value": "\\.card\\.featured\\s*{[^}]*;",
+ "message": "Make sure to end your CSS rule with a semicolon ;"
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "lemonchiffon"
+ },
+ "message": "Set the background color to lemonchiffon"
+ },
+ {
+ "type": "regex",
+ "value": "\\.card\\.featured\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "class-with-type",
+ "title": "Combining Types",
+ "description": "You can combine type selectors with class selectors to target specific HTML elements that have a certain class. This creates a more specific selector that only matches when both conditions are true: the element is of the specified type AND it has the specified class. For example, p.note would select paragraph elements with the class note, but would not select divs or spans with that same class. You can style these combined selections using properties like background-color to set a colored background for your elements. This approach allows you to apply different styles to the same class when it appears on different element types.",
+ "task": "Create a CSS rule that specifically targets <span> elements with the class highlight. Make those elements have an orange background, while other elements with the highlight class remain untouched.",
+ "previewHTML": "
Type and Class Combinations
\n
This paragraph has a highlighted span that should have an orange background.
\n
This paragraph has the highlight class but should NOT have an orange background.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } .highlight { font-weight: bold; }",
+ "sandboxCSS": "h2, p, span { padding: 5px; }",
+ "codePrefix": "/* The .highlight class already sets font-weight to bold */\n/* Now target ONLY span elements with the highlight class */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^span\\.highlight\\s*{",
+ "message": "Use span.highlight selector (no space between element and class)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "orange"
+ },
+ "message": "Set the background color to orange"
+ },
+ {
+ "type": "regex",
+ "value": "span\\.highlight\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "id-selectors",
+ "title": "ID Selectors",
+ "description": "ID selectors target elements with a specific id attribute. They begin with a hash/pound sign (#) followed by the ID name. Unlike classes, IDs must be unique within a document—each ID value should be used only once per page. ID selectors have higher specificity than class or element selectors, meaning they override those selectors when conflicts arise. When styling with ID selectors, you can use properties like color to define text color, and text-decoration to control the appearance of text, such as adding underlines to elements. Because of their uniqueness requirement, IDs are best used for one-of-a-kind elements like page headers, main navigation, or specific unique components that appear only once on a page.",
+ "task": "Create a CSS rule with an ID selector that targets the element with the ID main-title. Set its color to purple and add an underline with text-decoration: underline.",
+ "previewHTML": "
Main Page Title
\n
Regular paragraph content.
\n
Secondary Heading
\n
Introduction paragraph (different ID).
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h1, h2, p { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Create an ID selector to target the element with id=\"main-title\" */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^#main-title\\s*{",
+ "message": "Start your rule with #main-title to create an ID selector",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "purple"
+ },
+ "message": "Set the color to purple"
+ },
+ {
+ "type": "contains",
+ "value": "text-decoration:",
+ "message": "Include the text-decoration property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "text-decoration",
+ "expected": "underline"
+ },
+ "message": "Set the text-decoration to underline"
+ },
+ {
+ "type": "regex",
+ "value": "#main-title\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "id-with-type",
+ "title": "Type + ID",
+ "description": "Similar to how you can combine type and class selectors, you can also combine type selectors with ID selectors. For example, h1#title targets an h1 element with the ID 'title'. When using this combined approach, you can apply CSS properties like font-style to control the slant of the text, making it italic or normal. While this selector combination is more specific than using just the ID selector, it's often unnecessary since IDs should already be unique in a document. However, this technique can be useful for improving code readability or when you want to emphasize that a particular ID should only appear on a specific element type.",
+ "task": "Create a CSS rule that combines a type selector with an ID selector to target specifically a paragraph element with the ID special. Set its font style to italic.",
+ "previewHTML": "
Heading with ID \"special\" (should NOT be affected)
\n
Paragraph with ID \"special\" (should become italic)
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Create a combined type+ID selector for a paragraph with id=\"special\" */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^p#special\\s*{",
+ "message": "Use p#special to target paragraphs with ID special",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "font-style:",
+ "message": "Include the font-style property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "font-style",
+ "expected": "italic"
+ },
+ "message": "Set the font-style to italic"
+ },
+ {
+ "type": "regex",
+ "value": "p#special\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "selector-lists",
+ "title": "Selector Lists",
+ "description": "When multiple elements need the same styling, you can group them together using a selector list (also known as grouping selectors). Selector lists are created by separating individual selectors with commas. This approach reduces repetition in your CSS, making it more maintainable and efficient. For example, h1, h2, h3 { color: blue; } applies the same blue color to all three heading levels. When styling multiple selectors at once, you can apply properties like background-color to set the background, border-left to create a left border with a specific thickness, style, and color, and padding-left to create space between the content and the left border. Whitespace around commas is optional, and each selector in the list can be any valid selector type-elements, classes, IDs, or even more complex selectors.",
+ "task": "Create a selector list that applies the same styles to three different elements: paragraphs with class note, list items with class important, and the element with ID summary. Give them a lightyellow background, a gold left border, and some left padding.",
+ "previewHTML": "
This is a note paragraph.
\n
\n
Regular list item
\n
Important list item
\n
\n
Summary section
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "p, li, div { padding: 8px; margin-bottom: 8px; border: 1px dashed gray; }",
+ "codePrefix": "/* Create a selector list to apply the same styles to multiple different elements */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "p.note,\nli.important,\n#summary {\n background-color: lightyellow;\n border-left: 3px solid gold;\n padding-left: 10px\n}",
+ "validations": [
+ {
+ "type": "contains",
+ "value": "p.note",
+ "message": "Include p.note in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "li.important",
+ "message": "Include li.important in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "#summary",
+ "message": "Include #summary in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "regex",
+ "value": "(p\\.note|li\\.important|#summary)\\s*,\\s*(p\\.note|li\\.important|#summary)\\s*,\\s*(p\\.note|li\\.important|#summary)",
+ "message": "Create a comma-separated list with all three selectors",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "lightyellow"
+ },
+ "message": "Set the background color to lightyellow"
+ },
+ {
+ "type": "contains",
+ "value": "border-left:",
+ "message": "Include the border-left property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "border-left",
+ "expected": "3px solid gold"
+ },
+ "message": "Use border-left: 3px solid gold to create a left border"
+ },
+ {
+ "type": "contains",
+ "value": "padding-left:",
+ "message": "Include the padding-left property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "padding-left",
+ "expected": "10px"
+ },
+ "message": "Use padding-left: 10px to add left padding"
+ }
+ ]
+ },
+ {
+ "id": "universal-selector",
+ "title": "Universal (*)",
+ "description": "The universal selector is denoted by an asterisk (*) and matches any element of any type. It selects everything in the document or, when combined with other selectors, everything within a specific context. For example, * { margin: 0; } removes margins from all elements, while article * selects all elements inside article elements. When using the universal selector in combination with other selectors, you can apply properties like margin to control the spacing around elements. The universal selector is powerful but should be used carefully due to its broad impact. It's commonly used in CSS resets, to override default browser styling, or to target all children of a particular element.",
+ "task": "Use the universal selector to remove margins from all elements inside the container div. Create a rule using div.container * as the selector and set margin: 0.",
+ "previewHTML": "
\n
Inside Container
\n
This paragraph is inside the container.
\n
\n
List item inside container
\n
\n
\n
This paragraph is outside the container and should not be affected.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } div.container { border: 2px solid navy; padding: 15px; background-color: lavender; } h2, p, ul, li { margin: 15px 0; }",
+ "sandboxCSS": "",
+ "codePrefix": "/* Use the universal selector to target all elements inside the container */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^div\\.container\\s+\\*\\s*{",
+ "message": "Use div.container * selector (with a space between container and *)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "margin:",
+ "message": "Include the margin property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "margin",
+ "expected": "0"
+ },
+ "message": "Set margin to 0"
+ },
+ {
+ "type": "regex",
+ "value": "div\\.container\\s+\\*\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "specificity-basics",
+ "title": "Specificity",
+ "description": "CSS specificity determines which styles take precedence when multiple conflicting rules target the same element. Specificity follows a hierarchical system: inline styles have the highest specificity, followed by ID selectors, then class/attribute/pseudo-class selectors, and finally element/pseudo-element selectors. This can be conceptualized as a four-part score (inline, ID, class, element). When creating multiple rules that may target the same elements, you can use the color property to set text colors, and specificity will determine which color is actually applied. Understanding specificity is crucial for predictable styling and debugging CSS conflicts. When two selectors have equal specificity, the one that comes last in the stylesheet wins.",
+ "task": "Examine the existing CSS rules and add a new rule with higher specificity to override the text color of the paragraph. Create a rule using '.content p' as the selector and set color: green.",
+ "previewHTML": "
\n
What color will this paragraph be? Look at the CSS rules and their specificity.
\n
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "p { border: 1px dashed gray; padding: 10px; }",
+ "codePrefix": "/* These CSS rules target the same paragraph but have different specificity */\n\n/* Rule 1: Element selector (lowest specificity) */\np {\n color: red;\n}\n\n/* Rule 2: Descendant selector (higher specificity than just 'p') */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.content\\s+p\\s*{",
+ "message": "Use .content p as your selector (note the space between)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color property"
+ },
+ {
+ "type": "contains",
+ "value": "green",
+ "message": ""
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/es/00-welcome.json b/lessons/es/00-welcome.json
new file mode 100644
index 0000000..ce364c4
--- /dev/null
+++ b/lessons/es/00-welcome.json
@@ -0,0 +1,62 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "welcome",
+ "title": "Code Crispies",
+ "description": "Welcome to Code Crispies - your interactive web development learning platform",
+ "mode": "html",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "get-started",
+ "title": "Get Started",
+ "description": "Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required!
What you'll learn: • HTML - Semantic elements, forms, tables, SVG (HTML Block & Inline, HTML Forms, HTML Tables) • CSS - Selectors, box model, flexbox, animations (CSS Selectors, CSS Box Model, CSS Flexbox) • Responsive Design - Media queries and mobile-first layouts
How it works: 1. Read the task in the left panel 2. Write code in the editor 3. See live results in the preview 4. Get instant feedback with hints
Keyboard shortcuts:Ctrl+Z to undo, Ctrl+Shift+Z to redo
More resources: • HTML over JS - Native HTML vs JavaScript solutions • Web Engineering Mandala - JavaScript technology roadmap",
+ "task": "Hello World",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }",
+ "sandboxCSS": "",
+ "initialCode": "",
+ "solution": "
Hello World
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "contains",
+ "value": "Hello",
+ "message": "Write 'Hello World'"
+ }
+ ]
+ },
+ {
+ "id": "overview",
+ "title": "Overview",
+ "description": "You're ready! Open the menu (☰) to explore all modules.
Recommended learning path: 1. HTML Block & Inline - Understand container vs inline elements 2. HTML Forms - Build interactive forms with validation 3. CSS Selectors - Target elements precisely 4. CSS Box Model - Master padding, margin, borders 5. CSS Flexbox - Create flexible layouts 6. CSS Animations - Add motion and transitions
Tips: • Use Show Expected to see the target result • Your progress is saved automatically • Try Emmet in HTML mode: ul>li*3 + Tab
",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": []
+ }
+ ]
+}
diff --git a/lessons/es/01-box-model.json b/lessons/es/01-box-model.json
new file mode 100644
index 0000000..a5b10a6
--- /dev/null
+++ b/lessons/es/01-box-model.json
@@ -0,0 +1,180 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "box-model",
+ "title": "CSS Box Model",
+ "description": "Master the fundamental principles of space management in web design through the CSS box model. This module explores how content, padding, borders, and margins combine to create layout structures that are both visually appealing and structurally sound.",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "box-model-1",
+ "title": "Box Model Components",
+ "description": "The CSS box model consists of four concentric layers: content area (innermost), padding, border, and margin (outermost). Understanding how these components interact is essential for precise layout control.",
+ "task": "Add padding: 1rem to .box to create space between its content and border.",
+ "previewHTML": "
Block elements (containers) start on a new line and take full width. Examples: <div>, <p>, <h1>, <section>
Inline elements flow within text and only take needed width. Examples: <span>, <a>, <strong>, <em>",
+ "task": "Wrap the word important with <strong> tags to make it bold. Notice how the paragraph (block) takes full width while strong (inline) flows with text.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; } p { background: #e3f2fd; padding: 10px; } strong { background: #ffecb3; }",
+ "sandboxCSS": "",
+ "initialCode": "
This is a paragraph with an important word.
",
+ "solution": "
This is a paragraph with an important word.
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "p",
+ "message": "Add a <p> paragraph element"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "p", "child": "strong" },
+ "message": "Wrap the word important with <strong> tags"
+ }
+ ]
+ },
+ {
+ "id": "semantic-containers",
+ "title": "Semantic Tags",
+ "description": "Modern HTML uses semantic containers that describe their content:
<header> - Page or section header <nav> - Navigation links <main> - Main content area <section> - Thematic grouping <article> - Self-contained content <footer> - Page or section footer",
+ "task": "Create a basic page structure: 1. Add a <header> with an <h1> containing the text 'My Website' 2. Add a <main> element with a paragraph saying 'Welcome to my site!' 3. Add a <footer> with a paragraph saying 'Copyright 2025'",
+ "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": "",
+ "initialCode": "",
+ "solution": "\n
Use semantic elements when possible, div/span when no semantic element fits.",
+ "task": "Wrap the word 'highlighted' in a <span> to style it differently. Wrap the whole quote in a <div>.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: Georgia, serif; padding: 20px; } div { background: #f5f5f5; padding: 15px; border-left: 4px solid #1976d2; } span { background: #fff59d; padding: 2px 4px; }",
+ "sandboxCSS": "",
+ "initialCode": "The most highlighted moment was unforgettable.",
+ "solution": "
The most highlighted moment was unforgettable.
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "div",
+ "message": "Wrap everything in a <div> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "span",
+ "message": "Add a <span> around the word highlighted"
+ },
+ {
+ "type": "element_text",
+ "value": { "selector": "span", "text": "highlighted" },
+ "message": "The <span> should contain the word highlighted"
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/es/21-html-forms-basic.json b/lessons/es/21-html-forms-basic.json
new file mode 100644
index 0000000..d971cbf
--- /dev/null
+++ b/lessons/es/21-html-forms-basic.json
@@ -0,0 +1,102 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "html-forms-basic",
+ "title": "HTML Forms",
+ "description": "Learn to create forms with various input types",
+ "mode": "html",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "form-structure",
+ "title": "Form Structure",
+ "description": "Every form needs a <form> wrapper. Inside, use <label> to describe inputs and <input> for user data entry.
The for attribute on labels should match the id on inputs for accessibility.",
+ "task": "Create a form with: 1. A <label> with the text 'Name:' and for=\"name\" attribute 2. A text <input> with id=\"name\" and name=\"name\" attributes",
+ "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": "",
+ "initialCode": "",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "form",
+ "message": "Wrap everything in a <form> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "label",
+ "message": "Add a <label> for your input"
+ },
+ {
+ "type": "element_exists",
+ "value": "input",
+ "message": "Add an <input> element"
+ },
+ {
+ "type": "attribute_value",
+ "value": { "selector": "label", "attr": "for", "value": null },
+ "message": "Add a for attribute to your label"
+ },
+ {
+ "type": "attribute_value",
+ "value": { "selector": "input", "attr": "id", "value": null },
+ "message": "Add an id attribute to your input"
+ }
+ ]
+ },
+ {
+ "id": "input-types",
+ "title": "Input Types",
+ "description": "Different input types provide appropriate keyboards and validation:
type=\"text\" - General text type=\"email\" - Email with @ validation type=\"password\" - Hidden characters type=\"number\" - Numeric keyboard type=\"tel\" - Phone keyboard",
+ "task": "Create a login form with two fields: 1. An email field: <label for=\"email\">Email:</label> and <input type=\"email\" id=\"email\"> 2. A password field: <label for=\"password\">Password:</label> and <input type=\"password\" id=\"password\">",
+ "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; }",
+ "sandboxCSS": "",
+ "initialCode": "",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "input[type='email']",
+ "message": "Add an input with type=\"email\""
+ },
+ {
+ "type": "element_exists",
+ "value": "input[type='password']",
+ "message": "Add an input with type=\"password\""
+ },
+ {
+ "type": "element_count",
+ "value": { "selector": "label", "min": 2 },
+ "message": "Add labels for both inputs"
+ }
+ ]
+ },
+ {
+ "id": "submit-button",
+ "title": "Submit Button",
+ "description": "Forms need a way to submit data. Use:
Click the summary to toggle the hidden content - no JavaScript needed!",
+ "task": "Create a <details> element with: 1. A <summary> saying 'Click to reveal' 2. A <p> with the text 'This content was hidden!'",
+ "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": "",
+ "initialCode": "",
+ "solution": "\n Click to reveal\n
This content was hidden!
\n",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "details",
+ "message": "Add a <details> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "summary",
+ "message": "Add a <summary> inside the details"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "details", "child": "summary" },
+ "message": "The <summary> must be inside <details>"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "details", "child": "p" },
+ "message": "Add a <p> inside <details> for the hidden content"
+ }
+ ]
+ },
+ {
+ "id": "details-open-attribute",
+ "title": "Pre-expanded Details",
+ "description": "By default, <details> is closed. Add the open attribute to show the content initially.
This is a boolean attribute - just add open without a value.",
+ "task": "Add the open attribute to the <details> element to show the content by default.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } details { border: 1px solid #ddd; border-radius: 8px; padding: 15px; background: #f9f9f9; } summary { font-weight: 600; cursor: pointer; } details p { margin-top: 15px; color: #666; }",
+ "sandboxCSS": "",
+ "initialCode": "\n FAQ: What is HTML5?\n
HTML5 is the latest version of the HTML standard with new semantic elements and APIs.
\n",
+ "solution": "\n FAQ: What is HTML5?\n
HTML5 is the latest version of the HTML standard with new semantic elements and APIs.
\n",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "attribute_value",
+ "value": { "selector": "details", "attr": "open", "value": true },
+ "message": "Add the open attribute to <details>"
+ }
+ ]
+ },
+ {
+ "id": "faq-accordion",
+ "title": "FAQ Accordion",
+ "description": "Multiple <details> elements create an accordion-style FAQ. Each question can be expanded independently.
",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { background: steelblue; color: white; padding: 1rem; margin: 8px; text-align: center; font-weight: bold; width: 3rem; height: 3rem; display: flex; align-items: center; justify-content: center; } .middle { background: mediumseagreen; }",
+ "sandboxCSS": ".wrap { border: 2px dashed #aaa; padding: 1rem; display: flex; height: 12rem; align-items: center; }",
+ "codePrefix": ".middle {\n ",
+ "initialCode": "",
+ "codeSuffix": "\n}",
+ "solution": "align-self: flex-start;",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "property_value",
+ "value": {
+ "property": "align-self",
+ "expected": "flex-start"
+ },
+ "message": "Set align-self: flex-start"
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/pl/00-basic-selectors.json b/lessons/pl/00-basic-selectors.json
new file mode 100644
index 0000000..6e55337
--- /dev/null
+++ b/lessons/pl/00-basic-selectors.json
@@ -0,0 +1,550 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "css-basic-selectors",
+ "title": "CSS Selectors",
+ "description": "CSS selectors are the foundation of styling web pages, allowing you to target specific HTML elements for styling. This module introduces fundamental selector types including element type selectors, class selectors, ID selectors, and the universal selector.",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "introduction-to-selectors",
+ "title": "What's a Selector?",
+ "description": "A CSS selector is the first part of a CSS rule that tells the browser which HTML elements should receive the styles defined in the declaration block. Selectors are essentially patterns that match against elements in your HTML document. Understanding selectors is fundamental because they determine which elements your CSS rules will affect. The element or elements targeted by a selector are referred to as the 'subject of the selector.' When writing a CSS rule, you first specify the selector, followed by curly braces that contain the style declarations. For example, to change the text color of elements, you can use the color property within your declaration block.
/* Element selector */\np {\n color: orangered;\n /* │ └─── Indicates the value of the expression\n │ \n └─────────── Indicates the property of the expression */\n}
",
+ "task": "Write a CSS rule using a type selector that targets all paragraph elements p in the document. Make the text blue by setting the color property to blue.",
+ "previewHTML": "
Introduction to CSS Selectors
\n
This paragraph should turn blue.
\n
This div element should remain unchanged.
\n
This second paragraph should also turn blue.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h1, p, div { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Write a type selector to target all paragraph elements */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "p { color: blue }",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^p\\s*{",
+ "message": "Start your rule with p { … } to select all paragraph elements",
+ "options": {
+ "caseSensitive": false
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color: property in your CSS rule"
+ },
+ {
+ "type": "contains",
+ "value": "blue",
+ "message": "Set the color value to blue"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "blue"
+ },
+ "message": "Use color: blue to set the text color"
+ },
+ {
+ "type": "regex",
+ "value": "p\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": false
+ }
+ }
+ ]
+ },
+ {
+ "id": "type-selectors",
+ "title": "Type Selectors",
+ "description": "Type selectors (also called tag name selectors or element selectors) target HTML elements based on their tag name. For example, p selects all paragraph elements, h1 selects all level-one headings, and div selects all division elements. Type selectors are the most fundamental way to select elements, applying styles consistently to all instances of a particular HTML element throughout your document. You can define a variety of CSS properties with type selectors, such as color for text color, background-color for the background, and font-weight for text emphasis. They provide a broad approach for styling your page and are often the starting point for more specific styling using other selector types.",
+ "task": "Write three separate CSS rules using type selectors to target specific HTML elements: make h2 headings purple, give span elements a yellow background, and make strong elements red.",
+ "previewHTML": "
Type Selectors Example
\n
Regular paragraph text with a highlighted span that should have a yellow background.
\n
Another paragraph with strong important text that should be red.
\n
Another Heading
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p, span, strong { padding: 3px; }",
+ "codePrefix": "/* Write three separate type selectors below */\n\n",
+ "initialCode": "/* 1. Make h2 headings purple */\n\n\n/* 2. Give span elements a yellow background */\n\n\n/* 3. Make strong elements red */\n",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "/* 1. Make h2 headings purple */\nh2 {\n color: purple;\n}\n\n/* 2. Give span elements a yellow background */\nspan {\n background-color: yellow;\n}\n\n/* 3. Make strong elements red */\nstrong {\n color: red;\n}",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^h2\\s*{",
+ "message": "Include an h2 { … } selector"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "purple"
+ },
+ "message": "Set the color property to purple for h2 elements"
+ },
+ {
+ "type": "regex",
+ "value": "h2\\s*{[^}]*}",
+ "message": "Make sure to close your h2 rule with a closing brace }"
+ },
+ {
+ "type": "regex",
+ "value": "^span\\s*{",
+ "message": "Include a span { … } selector"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "yellow"
+ },
+ "message": "Set a background-color: yellow for span elements"
+ },
+ {
+ "type": "regex",
+ "value": "span\\s*{[^}]*}",
+ "message": "Make sure to close your span rule with a closing brace }"
+ },
+ {
+ "type": "regex",
+ "value": "^strong\\s*{",
+ "message": "Include a strong { … } selector"
+ },
+ {
+ "type": "regex",
+ "value": "strong\\s*{\\s*color:\\s*red;[^}]*}",
+ "message": "Set the color: red for strong elements"
+ }
+ ]
+ },
+ {
+ "id": "class-selectors",
+ "title": "Class Selectors",
+ "description": "Class selectors target elements with a specific class attribute value. They begin with a dot (.) followed by the class name. Classes are powerful because they allow you to apply the same styles to multiple elements regardless of their type. An HTML element can have multiple classes (separated by spaces in the class attribute), and a class can be applied to any number of elements. When using class selectors, you can apply properties like background-color to set the background color of elements, and font-weight to control text thickness, making text bold or normal. This flexibility makes class selectors one of the most commonly used methods for applying styles in CSS, allowing for modular and reusable styling across your website.",
+ "task": "Create a CSS rule using a class selector that targets elements with the class highlight. Give these elements a yellow background and bold text.",
+ "previewHTML": "
Using Class Selectors
\n
This is a regular paragraph, but this span has the highlight class applied to it.
\n
This entire paragraph has the highlight class.
\n
\n
Regular list item
\n
This list item is highlighted
\n
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p, li { padding: 5px; margin-bottom: 10px; }",
+ "codePrefix": "/* Create a class selector for elements with the 'highlight' class */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.highlight\\s*{",
+ "message": "Start your rule with .highlight { … } to create a class selector",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color: property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "yellow"
+ },
+ "message": "Set the background color to yellow"
+ },
+ {
+ "type": "contains",
+ "value": "font-weight:",
+ "message": "Include the font-weight: property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "font-weight",
+ "expected": "bold"
+ },
+ "message": "Set the font-weight to bold"
+ },
+ {
+ "type": "regex",
+ "value": "\\.highlight\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "multiple-classes",
+ "title": "Multiple Classes",
+ "description": "HTML elements can have multiple classes applied simultaneously, allowing for composable and modular CSS designs. When an element has multiple classes, it will receive styles from all matching class selectors. This approach enables you to build a library of reusable CSS classes that can be combined in different ways. You can also target elements that have a specific combination of classes by chaining class selectors together without spaces (e.g., .class1.class2). When styling these elements, you might use properties like border-color to change the color of element borders, and background-color to set the background color of elements. This technique lets you create conditional styles that only apply when certain classes appear together.",
+ "task": "Complete the CSS rule that targets elements with both card and featured classes by chaining the selectors. Set the border-color to gold and the background-color to lemonchiffon to make featured cards stand out.",
+ "previewHTML": "
Multiple Class Combinations
\n
Regular Card
\n
Featured Card
\n
Just Featured (not a card)
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } .card { border: 2px solid gray; padding: 15px; margin-bottom: 10px; border-radius: 5px; }",
+ "sandboxCSS": "",
+ "codePrefix": "/* The .card class already has basic styling */\n/* Now target elements with BOTH classes: 'card' AND 'featured' */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": ".card.featured { border-color: gold; background-color: lemonchiffon }",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.card\\.featured\\s*{",
+ "message": "Chain the selectors as .card.featured (no space between them)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "border-color:",
+ "message": "Include the border-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "border-color",
+ "expected": "gold"
+ },
+ "message": "Set the border color to gold"
+ },
+ {
+ "type": "regex",
+ "value": "\\.card\\.featured\\s*{[^}]*;",
+ "message": "Make sure to end your CSS rule with a semicolon ;"
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "lemonchiffon"
+ },
+ "message": "Set the background color to lemonchiffon"
+ },
+ {
+ "type": "regex",
+ "value": "\\.card\\.featured\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "class-with-type",
+ "title": "Combining Types",
+ "description": "You can combine type selectors with class selectors to target specific HTML elements that have a certain class. This creates a more specific selector that only matches when both conditions are true: the element is of the specified type AND it has the specified class. For example, p.note would select paragraph elements with the class note, but would not select divs or spans with that same class. You can style these combined selections using properties like background-color to set a colored background for your elements. This approach allows you to apply different styles to the same class when it appears on different element types.",
+ "task": "Create a CSS rule that specifically targets <span> elements with the class highlight. Make those elements have an orange background, while other elements with the highlight class remain untouched.",
+ "previewHTML": "
Type and Class Combinations
\n
This paragraph has a highlighted span that should have an orange background.
\n
This paragraph has the highlight class but should NOT have an orange background.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } .highlight { font-weight: bold; }",
+ "sandboxCSS": "h2, p, span { padding: 5px; }",
+ "codePrefix": "/* The .highlight class already sets font-weight to bold */\n/* Now target ONLY span elements with the highlight class */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^span\\.highlight\\s*{",
+ "message": "Use span.highlight selector (no space between element and class)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "orange"
+ },
+ "message": "Set the background color to orange"
+ },
+ {
+ "type": "regex",
+ "value": "span\\.highlight\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "id-selectors",
+ "title": "ID Selectors",
+ "description": "ID selectors target elements with a specific id attribute. They begin with a hash/pound sign (#) followed by the ID name. Unlike classes, IDs must be unique within a document—each ID value should be used only once per page. ID selectors have higher specificity than class or element selectors, meaning they override those selectors when conflicts arise. When styling with ID selectors, you can use properties like color to define text color, and text-decoration to control the appearance of text, such as adding underlines to elements. Because of their uniqueness requirement, IDs are best used for one-of-a-kind elements like page headers, main navigation, or specific unique components that appear only once on a page.",
+ "task": "Create a CSS rule with an ID selector that targets the element with the ID main-title. Set its color to purple and add an underline with text-decoration: underline.",
+ "previewHTML": "
Main Page Title
\n
Regular paragraph content.
\n
Secondary Heading
\n
Introduction paragraph (different ID).
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h1, h2, p { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Create an ID selector to target the element with id=\"main-title\" */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^#main-title\\s*{",
+ "message": "Start your rule with #main-title to create an ID selector",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "purple"
+ },
+ "message": "Set the color to purple"
+ },
+ {
+ "type": "contains",
+ "value": "text-decoration:",
+ "message": "Include the text-decoration property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "text-decoration",
+ "expected": "underline"
+ },
+ "message": "Set the text-decoration to underline"
+ },
+ {
+ "type": "regex",
+ "value": "#main-title\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "id-with-type",
+ "title": "Type + ID",
+ "description": "Similar to how you can combine type and class selectors, you can also combine type selectors with ID selectors. For example, h1#title targets an h1 element with the ID 'title'. When using this combined approach, you can apply CSS properties like font-style to control the slant of the text, making it italic or normal. While this selector combination is more specific than using just the ID selector, it's often unnecessary since IDs should already be unique in a document. However, this technique can be useful for improving code readability or when you want to emphasize that a particular ID should only appear on a specific element type.",
+ "task": "Create a CSS rule that combines a type selector with an ID selector to target specifically a paragraph element with the ID special. Set its font style to italic.",
+ "previewHTML": "
Heading with ID \"special\" (should NOT be affected)
\n
Paragraph with ID \"special\" (should become italic)
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Create a combined type+ID selector for a paragraph with id=\"special\" */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^p#special\\s*{",
+ "message": "Use p#special to target paragraphs with ID special",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "font-style:",
+ "message": "Include the font-style property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "font-style",
+ "expected": "italic"
+ },
+ "message": "Set the font-style to italic"
+ },
+ {
+ "type": "regex",
+ "value": "p#special\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "selector-lists",
+ "title": "Selector Lists",
+ "description": "When multiple elements need the same styling, you can group them together using a selector list (also known as grouping selectors). Selector lists are created by separating individual selectors with commas. This approach reduces repetition in your CSS, making it more maintainable and efficient. For example, h1, h2, h3 { color: blue; } applies the same blue color to all three heading levels. When styling multiple selectors at once, you can apply properties like background-color to set the background, border-left to create a left border with a specific thickness, style, and color, and padding-left to create space between the content and the left border. Whitespace around commas is optional, and each selector in the list can be any valid selector type-elements, classes, IDs, or even more complex selectors.",
+ "task": "Create a selector list that applies the same styles to three different elements: paragraphs with class note, list items with class important, and the element with ID summary. Give them a lightyellow background, a gold left border, and some left padding.",
+ "previewHTML": "
This is a note paragraph.
\n
\n
Regular list item
\n
Important list item
\n
\n
Summary section
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "p, li, div { padding: 8px; margin-bottom: 8px; border: 1px dashed gray; }",
+ "codePrefix": "/* Create a selector list to apply the same styles to multiple different elements */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "p.note,\nli.important,\n#summary {\n background-color: lightyellow;\n border-left: 3px solid gold;\n padding-left: 10px\n}",
+ "validations": [
+ {
+ "type": "contains",
+ "value": "p.note",
+ "message": "Include p.note in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "li.important",
+ "message": "Include li.important in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "#summary",
+ "message": "Include #summary in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "regex",
+ "value": "(p\\.note|li\\.important|#summary)\\s*,\\s*(p\\.note|li\\.important|#summary)\\s*,\\s*(p\\.note|li\\.important|#summary)",
+ "message": "Create a comma-separated list with all three selectors",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "lightyellow"
+ },
+ "message": "Set the background color to lightyellow"
+ },
+ {
+ "type": "contains",
+ "value": "border-left:",
+ "message": "Include the border-left property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "border-left",
+ "expected": "3px solid gold"
+ },
+ "message": "Use border-left: 3px solid gold to create a left border"
+ },
+ {
+ "type": "contains",
+ "value": "padding-left:",
+ "message": "Include the padding-left property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "padding-left",
+ "expected": "10px"
+ },
+ "message": "Use padding-left: 10px to add left padding"
+ }
+ ]
+ },
+ {
+ "id": "universal-selector",
+ "title": "Universal (*)",
+ "description": "The universal selector is denoted by an asterisk (*) and matches any element of any type. It selects everything in the document or, when combined with other selectors, everything within a specific context. For example, * { margin: 0; } removes margins from all elements, while article * selects all elements inside article elements. When using the universal selector in combination with other selectors, you can apply properties like margin to control the spacing around elements. The universal selector is powerful but should be used carefully due to its broad impact. It's commonly used in CSS resets, to override default browser styling, or to target all children of a particular element.",
+ "task": "Use the universal selector to remove margins from all elements inside the container div. Create a rule using div.container * as the selector and set margin: 0.",
+ "previewHTML": "
\n
Inside Container
\n
This paragraph is inside the container.
\n
\n
List item inside container
\n
\n
\n
This paragraph is outside the container and should not be affected.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } div.container { border: 2px solid navy; padding: 15px; background-color: lavender; } h2, p, ul, li { margin: 15px 0; }",
+ "sandboxCSS": "",
+ "codePrefix": "/* Use the universal selector to target all elements inside the container */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^div\\.container\\s+\\*\\s*{",
+ "message": "Use div.container * selector (with a space between container and *)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "margin:",
+ "message": "Include the margin property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "margin",
+ "expected": "0"
+ },
+ "message": "Set margin to 0"
+ },
+ {
+ "type": "regex",
+ "value": "div\\.container\\s+\\*\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "specificity-basics",
+ "title": "Specificity",
+ "description": "CSS specificity determines which styles take precedence when multiple conflicting rules target the same element. Specificity follows a hierarchical system: inline styles have the highest specificity, followed by ID selectors, then class/attribute/pseudo-class selectors, and finally element/pseudo-element selectors. This can be conceptualized as a four-part score (inline, ID, class, element). When creating multiple rules that may target the same elements, you can use the color property to set text colors, and specificity will determine which color is actually applied. Understanding specificity is crucial for predictable styling and debugging CSS conflicts. When two selectors have equal specificity, the one that comes last in the stylesheet wins.",
+ "task": "Examine the existing CSS rules and add a new rule with higher specificity to override the text color of the paragraph. Create a rule using '.content p' as the selector and set color: green.",
+ "previewHTML": "
\n
What color will this paragraph be? Look at the CSS rules and their specificity.
\n
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "p { border: 1px dashed gray; padding: 10px; }",
+ "codePrefix": "/* These CSS rules target the same paragraph but have different specificity */\n\n/* Rule 1: Element selector (lowest specificity) */\np {\n color: red;\n}\n\n/* Rule 2: Descendant selector (higher specificity than just 'p') */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.content\\s+p\\s*{",
+ "message": "Use .content p as your selector (note the space between)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color property"
+ },
+ {
+ "type": "contains",
+ "value": "green",
+ "message": ""
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/pl/00-welcome.json b/lessons/pl/00-welcome.json
new file mode 100644
index 0000000..ce364c4
--- /dev/null
+++ b/lessons/pl/00-welcome.json
@@ -0,0 +1,62 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "welcome",
+ "title": "Code Crispies",
+ "description": "Welcome to Code Crispies - your interactive web development learning platform",
+ "mode": "html",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "get-started",
+ "title": "Get Started",
+ "description": "Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required!
What you'll learn: • HTML - Semantic elements, forms, tables, SVG (HTML Block & Inline, HTML Forms, HTML Tables) • CSS - Selectors, box model, flexbox, animations (CSS Selectors, CSS Box Model, CSS Flexbox) • Responsive Design - Media queries and mobile-first layouts
How it works: 1. Read the task in the left panel 2. Write code in the editor 3. See live results in the preview 4. Get instant feedback with hints
Keyboard shortcuts:Ctrl+Z to undo, Ctrl+Shift+Z to redo
More resources: • HTML over JS - Native HTML vs JavaScript solutions • Web Engineering Mandala - JavaScript technology roadmap",
+ "task": "Hello World",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }",
+ "sandboxCSS": "",
+ "initialCode": "",
+ "solution": "
Hello World
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "contains",
+ "value": "Hello",
+ "message": "Write 'Hello World'"
+ }
+ ]
+ },
+ {
+ "id": "overview",
+ "title": "Overview",
+ "description": "You're ready! Open the menu (☰) to explore all modules.
Recommended learning path: 1. HTML Block & Inline - Understand container vs inline elements 2. HTML Forms - Build interactive forms with validation 3. CSS Selectors - Target elements precisely 4. CSS Box Model - Master padding, margin, borders 5. CSS Flexbox - Create flexible layouts 6. CSS Animations - Add motion and transitions
Tips: • Use Show Expected to see the target result • Your progress is saved automatically • Try Emmet in HTML mode: ul>li*3 + Tab
",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": []
+ }
+ ]
+}
diff --git a/lessons/pl/01-box-model.json b/lessons/pl/01-box-model.json
new file mode 100644
index 0000000..a5b10a6
--- /dev/null
+++ b/lessons/pl/01-box-model.json
@@ -0,0 +1,180 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "box-model",
+ "title": "CSS Box Model",
+ "description": "Master the fundamental principles of space management in web design through the CSS box model. This module explores how content, padding, borders, and margins combine to create layout structures that are both visually appealing and structurally sound.",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "box-model-1",
+ "title": "Box Model Components",
+ "description": "The CSS box model consists of four concentric layers: content area (innermost), padding, border, and margin (outermost). Understanding how these components interact is essential for precise layout control.",
+ "task": "Add padding: 1rem to .box to create space between its content and border.",
+ "previewHTML": "
Block elements (containers) start on a new line and take full width. Examples: <div>, <p>, <h1>, <section>
Inline elements flow within text and only take needed width. Examples: <span>, <a>, <strong>, <em>",
+ "task": "Wrap the word important with <strong> tags to make it bold. Notice how the paragraph (block) takes full width while strong (inline) flows with text.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; } p { background: #e3f2fd; padding: 10px; } strong { background: #ffecb3; }",
+ "sandboxCSS": "",
+ "initialCode": "
This is a paragraph with an important word.
",
+ "solution": "
This is a paragraph with an important word.
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "p",
+ "message": "Add a <p> paragraph element"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "p", "child": "strong" },
+ "message": "Wrap the word important with <strong> tags"
+ }
+ ]
+ },
+ {
+ "id": "semantic-containers",
+ "title": "Semantic Tags",
+ "description": "Modern HTML uses semantic containers that describe their content:
<header> - Page or section header <nav> - Navigation links <main> - Main content area <section> - Thematic grouping <article> - Self-contained content <footer> - Page or section footer",
+ "task": "Create a basic page structure: 1. Add a <header> with an <h1> containing the text 'My Website' 2. Add a <main> element with a paragraph saying 'Welcome to my site!' 3. Add a <footer> with a paragraph saying 'Copyright 2025'",
+ "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": "",
+ "initialCode": "",
+ "solution": "\n
Use semantic elements when possible, div/span when no semantic element fits.",
+ "task": "Wrap the word 'highlighted' in a <span> to style it differently. Wrap the whole quote in a <div>.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: Georgia, serif; padding: 20px; } div { background: #f5f5f5; padding: 15px; border-left: 4px solid #1976d2; } span { background: #fff59d; padding: 2px 4px; }",
+ "sandboxCSS": "",
+ "initialCode": "The most highlighted moment was unforgettable.",
+ "solution": "
The most highlighted moment was unforgettable.
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "div",
+ "message": "Wrap everything in a <div> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "span",
+ "message": "Add a <span> around the word highlighted"
+ },
+ {
+ "type": "element_text",
+ "value": { "selector": "span", "text": "highlighted" },
+ "message": "The <span> should contain the word highlighted"
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/pl/21-html-forms-basic.json b/lessons/pl/21-html-forms-basic.json
new file mode 100644
index 0000000..d971cbf
--- /dev/null
+++ b/lessons/pl/21-html-forms-basic.json
@@ -0,0 +1,102 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "html-forms-basic",
+ "title": "HTML Forms",
+ "description": "Learn to create forms with various input types",
+ "mode": "html",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "form-structure",
+ "title": "Form Structure",
+ "description": "Every form needs a <form> wrapper. Inside, use <label> to describe inputs and <input> for user data entry.
The for attribute on labels should match the id on inputs for accessibility.",
+ "task": "Create a form with: 1. A <label> with the text 'Name:' and for=\"name\" attribute 2. A text <input> with id=\"name\" and name=\"name\" attributes",
+ "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": "",
+ "initialCode": "",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "form",
+ "message": "Wrap everything in a <form> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "label",
+ "message": "Add a <label> for your input"
+ },
+ {
+ "type": "element_exists",
+ "value": "input",
+ "message": "Add an <input> element"
+ },
+ {
+ "type": "attribute_value",
+ "value": { "selector": "label", "attr": "for", "value": null },
+ "message": "Add a for attribute to your label"
+ },
+ {
+ "type": "attribute_value",
+ "value": { "selector": "input", "attr": "id", "value": null },
+ "message": "Add an id attribute to your input"
+ }
+ ]
+ },
+ {
+ "id": "input-types",
+ "title": "Input Types",
+ "description": "Different input types provide appropriate keyboards and validation:
type=\"text\" - General text type=\"email\" - Email with @ validation type=\"password\" - Hidden characters type=\"number\" - Numeric keyboard type=\"tel\" - Phone keyboard",
+ "task": "Create a login form with two fields: 1. An email field: <label for=\"email\">Email:</label> and <input type=\"email\" id=\"email\"> 2. A password field: <label for=\"password\">Password:</label> and <input type=\"password\" id=\"password\">",
+ "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; }",
+ "sandboxCSS": "",
+ "initialCode": "",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "input[type='email']",
+ "message": "Add an input with type=\"email\""
+ },
+ {
+ "type": "element_exists",
+ "value": "input[type='password']",
+ "message": "Add an input with type=\"password\""
+ },
+ {
+ "type": "element_count",
+ "value": { "selector": "label", "min": 2 },
+ "message": "Add labels for both inputs"
+ }
+ ]
+ },
+ {
+ "id": "submit-button",
+ "title": "Submit Button",
+ "description": "Forms need a way to submit data. Use:
Click the summary to toggle the hidden content - no JavaScript needed!",
+ "task": "Create a <details> element with: 1. A <summary> saying 'Click to reveal' 2. A <p> with the text 'This content was hidden!'",
+ "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": "",
+ "initialCode": "",
+ "solution": "\n Click to reveal\n
This content was hidden!
\n",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "details",
+ "message": "Add a <details> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "summary",
+ "message": "Add a <summary> inside the details"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "details", "child": "summary" },
+ "message": "The <summary> must be inside <details>"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "details", "child": "p" },
+ "message": "Add a <p> inside <details> for the hidden content"
+ }
+ ]
+ },
+ {
+ "id": "details-open-attribute",
+ "title": "Pre-expanded Details",
+ "description": "By default, <details> is closed. Add the open attribute to show the content initially.
This is a boolean attribute - just add open without a value.",
+ "task": "Add the open attribute to the <details> element to show the content by default.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } details { border: 1px solid #ddd; border-radius: 8px; padding: 15px; background: #f9f9f9; } summary { font-weight: 600; cursor: pointer; } details p { margin-top: 15px; color: #666; }",
+ "sandboxCSS": "",
+ "initialCode": "\n FAQ: What is HTML5?\n
HTML5 is the latest version of the HTML standard with new semantic elements and APIs.
\n",
+ "solution": "\n FAQ: What is HTML5?\n
HTML5 is the latest version of the HTML standard with new semantic elements and APIs.
\n",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "attribute_value",
+ "value": { "selector": "details", "attr": "open", "value": true },
+ "message": "Add the open attribute to <details>"
+ }
+ ]
+ },
+ {
+ "id": "faq-accordion",
+ "title": "FAQ Accordion",
+ "description": "Multiple <details> elements create an accordion-style FAQ. Each question can be expanded independently.
",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { background: steelblue; color: white; padding: 1rem; margin: 8px; text-align: center; font-weight: bold; width: 3rem; height: 3rem; display: flex; align-items: center; justify-content: center; } .middle { background: mediumseagreen; }",
+ "sandboxCSS": ".wrap { border: 2px dashed #aaa; padding: 1rem; display: flex; height: 12rem; align-items: center; }",
+ "codePrefix": ".middle {\n ",
+ "initialCode": "",
+ "codeSuffix": "\n}",
+ "solution": "align-self: flex-start;",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "property_value",
+ "value": {
+ "property": "align-self",
+ "expected": "flex-start"
+ },
+ "message": "Set align-self: flex-start"
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/uk/00-basic-selectors.json b/lessons/uk/00-basic-selectors.json
new file mode 100644
index 0000000..6e55337
--- /dev/null
+++ b/lessons/uk/00-basic-selectors.json
@@ -0,0 +1,550 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "css-basic-selectors",
+ "title": "CSS Selectors",
+ "description": "CSS selectors are the foundation of styling web pages, allowing you to target specific HTML elements for styling. This module introduces fundamental selector types including element type selectors, class selectors, ID selectors, and the universal selector.",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "introduction-to-selectors",
+ "title": "What's a Selector?",
+ "description": "A CSS selector is the first part of a CSS rule that tells the browser which HTML elements should receive the styles defined in the declaration block. Selectors are essentially patterns that match against elements in your HTML document. Understanding selectors is fundamental because they determine which elements your CSS rules will affect. The element or elements targeted by a selector are referred to as the 'subject of the selector.' When writing a CSS rule, you first specify the selector, followed by curly braces that contain the style declarations. For example, to change the text color of elements, you can use the color property within your declaration block.
/* Element selector */\np {\n color: orangered;\n /* │ └─── Indicates the value of the expression\n │ \n └─────────── Indicates the property of the expression */\n}
",
+ "task": "Write a CSS rule using a type selector that targets all paragraph elements p in the document. Make the text blue by setting the color property to blue.",
+ "previewHTML": "
Introduction to CSS Selectors
\n
This paragraph should turn blue.
\n
This div element should remain unchanged.
\n
This second paragraph should also turn blue.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h1, p, div { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Write a type selector to target all paragraph elements */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "p { color: blue }",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^p\\s*{",
+ "message": "Start your rule with p { … } to select all paragraph elements",
+ "options": {
+ "caseSensitive": false
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color: property in your CSS rule"
+ },
+ {
+ "type": "contains",
+ "value": "blue",
+ "message": "Set the color value to blue"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "blue"
+ },
+ "message": "Use color: blue to set the text color"
+ },
+ {
+ "type": "regex",
+ "value": "p\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": false
+ }
+ }
+ ]
+ },
+ {
+ "id": "type-selectors",
+ "title": "Type Selectors",
+ "description": "Type selectors (also called tag name selectors or element selectors) target HTML elements based on their tag name. For example, p selects all paragraph elements, h1 selects all level-one headings, and div selects all division elements. Type selectors are the most fundamental way to select elements, applying styles consistently to all instances of a particular HTML element throughout your document. You can define a variety of CSS properties with type selectors, such as color for text color, background-color for the background, and font-weight for text emphasis. They provide a broad approach for styling your page and are often the starting point for more specific styling using other selector types.",
+ "task": "Write three separate CSS rules using type selectors to target specific HTML elements: make h2 headings purple, give span elements a yellow background, and make strong elements red.",
+ "previewHTML": "
Type Selectors Example
\n
Regular paragraph text with a highlighted span that should have a yellow background.
\n
Another paragraph with strong important text that should be red.
\n
Another Heading
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p, span, strong { padding: 3px; }",
+ "codePrefix": "/* Write three separate type selectors below */\n\n",
+ "initialCode": "/* 1. Make h2 headings purple */\n\n\n/* 2. Give span elements a yellow background */\n\n\n/* 3. Make strong elements red */\n",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "/* 1. Make h2 headings purple */\nh2 {\n color: purple;\n}\n\n/* 2. Give span elements a yellow background */\nspan {\n background-color: yellow;\n}\n\n/* 3. Make strong elements red */\nstrong {\n color: red;\n}",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^h2\\s*{",
+ "message": "Include an h2 { … } selector"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "purple"
+ },
+ "message": "Set the color property to purple for h2 elements"
+ },
+ {
+ "type": "regex",
+ "value": "h2\\s*{[^}]*}",
+ "message": "Make sure to close your h2 rule with a closing brace }"
+ },
+ {
+ "type": "regex",
+ "value": "^span\\s*{",
+ "message": "Include a span { … } selector"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "yellow"
+ },
+ "message": "Set a background-color: yellow for span elements"
+ },
+ {
+ "type": "regex",
+ "value": "span\\s*{[^}]*}",
+ "message": "Make sure to close your span rule with a closing brace }"
+ },
+ {
+ "type": "regex",
+ "value": "^strong\\s*{",
+ "message": "Include a strong { … } selector"
+ },
+ {
+ "type": "regex",
+ "value": "strong\\s*{\\s*color:\\s*red;[^}]*}",
+ "message": "Set the color: red for strong elements"
+ }
+ ]
+ },
+ {
+ "id": "class-selectors",
+ "title": "Class Selectors",
+ "description": "Class selectors target elements with a specific class attribute value. They begin with a dot (.) followed by the class name. Classes are powerful because they allow you to apply the same styles to multiple elements regardless of their type. An HTML element can have multiple classes (separated by spaces in the class attribute), and a class can be applied to any number of elements. When using class selectors, you can apply properties like background-color to set the background color of elements, and font-weight to control text thickness, making text bold or normal. This flexibility makes class selectors one of the most commonly used methods for applying styles in CSS, allowing for modular and reusable styling across your website.",
+ "task": "Create a CSS rule using a class selector that targets elements with the class highlight. Give these elements a yellow background and bold text.",
+ "previewHTML": "
Using Class Selectors
\n
This is a regular paragraph, but this span has the highlight class applied to it.
\n
This entire paragraph has the highlight class.
\n
\n
Regular list item
\n
This list item is highlighted
\n
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p, li { padding: 5px; margin-bottom: 10px; }",
+ "codePrefix": "/* Create a class selector for elements with the 'highlight' class */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.highlight\\s*{",
+ "message": "Start your rule with .highlight { … } to create a class selector",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color: property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "yellow"
+ },
+ "message": "Set the background color to yellow"
+ },
+ {
+ "type": "contains",
+ "value": "font-weight:",
+ "message": "Include the font-weight: property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "font-weight",
+ "expected": "bold"
+ },
+ "message": "Set the font-weight to bold"
+ },
+ {
+ "type": "regex",
+ "value": "\\.highlight\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "multiple-classes",
+ "title": "Multiple Classes",
+ "description": "HTML elements can have multiple classes applied simultaneously, allowing for composable and modular CSS designs. When an element has multiple classes, it will receive styles from all matching class selectors. This approach enables you to build a library of reusable CSS classes that can be combined in different ways. You can also target elements that have a specific combination of classes by chaining class selectors together without spaces (e.g., .class1.class2). When styling these elements, you might use properties like border-color to change the color of element borders, and background-color to set the background color of elements. This technique lets you create conditional styles that only apply when certain classes appear together.",
+ "task": "Complete the CSS rule that targets elements with both card and featured classes by chaining the selectors. Set the border-color to gold and the background-color to lemonchiffon to make featured cards stand out.",
+ "previewHTML": "
Multiple Class Combinations
\n
Regular Card
\n
Featured Card
\n
Just Featured (not a card)
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } .card { border: 2px solid gray; padding: 15px; margin-bottom: 10px; border-radius: 5px; }",
+ "sandboxCSS": "",
+ "codePrefix": "/* The .card class already has basic styling */\n/* Now target elements with BOTH classes: 'card' AND 'featured' */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": ".card.featured { border-color: gold; background-color: lemonchiffon }",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.card\\.featured\\s*{",
+ "message": "Chain the selectors as .card.featured (no space between them)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "border-color:",
+ "message": "Include the border-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "border-color",
+ "expected": "gold"
+ },
+ "message": "Set the border color to gold"
+ },
+ {
+ "type": "regex",
+ "value": "\\.card\\.featured\\s*{[^}]*;",
+ "message": "Make sure to end your CSS rule with a semicolon ;"
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "lemonchiffon"
+ },
+ "message": "Set the background color to lemonchiffon"
+ },
+ {
+ "type": "regex",
+ "value": "\\.card\\.featured\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "class-with-type",
+ "title": "Combining Types",
+ "description": "You can combine type selectors with class selectors to target specific HTML elements that have a certain class. This creates a more specific selector that only matches when both conditions are true: the element is of the specified type AND it has the specified class. For example, p.note would select paragraph elements with the class note, but would not select divs or spans with that same class. You can style these combined selections using properties like background-color to set a colored background for your elements. This approach allows you to apply different styles to the same class when it appears on different element types.",
+ "task": "Create a CSS rule that specifically targets <span> elements with the class highlight. Make those elements have an orange background, while other elements with the highlight class remain untouched.",
+ "previewHTML": "
Type and Class Combinations
\n
This paragraph has a highlighted span that should have an orange background.
\n
This paragraph has the highlight class but should NOT have an orange background.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } .highlight { font-weight: bold; }",
+ "sandboxCSS": "h2, p, span { padding: 5px; }",
+ "codePrefix": "/* The .highlight class already sets font-weight to bold */\n/* Now target ONLY span elements with the highlight class */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^span\\.highlight\\s*{",
+ "message": "Use span.highlight selector (no space between element and class)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "orange"
+ },
+ "message": "Set the background color to orange"
+ },
+ {
+ "type": "regex",
+ "value": "span\\.highlight\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "id-selectors",
+ "title": "ID Selectors",
+ "description": "ID selectors target elements with a specific id attribute. They begin with a hash/pound sign (#) followed by the ID name. Unlike classes, IDs must be unique within a document—each ID value should be used only once per page. ID selectors have higher specificity than class or element selectors, meaning they override those selectors when conflicts arise. When styling with ID selectors, you can use properties like color to define text color, and text-decoration to control the appearance of text, such as adding underlines to elements. Because of their uniqueness requirement, IDs are best used for one-of-a-kind elements like page headers, main navigation, or specific unique components that appear only once on a page.",
+ "task": "Create a CSS rule with an ID selector that targets the element with the ID main-title. Set its color to purple and add an underline with text-decoration: underline.",
+ "previewHTML": "
Main Page Title
\n
Regular paragraph content.
\n
Secondary Heading
\n
Introduction paragraph (different ID).
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h1, h2, p { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Create an ID selector to target the element with id=\"main-title\" */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^#main-title\\s*{",
+ "message": "Start your rule with #main-title to create an ID selector",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "color",
+ "expected": "purple"
+ },
+ "message": "Set the color to purple"
+ },
+ {
+ "type": "contains",
+ "value": "text-decoration:",
+ "message": "Include the text-decoration property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "text-decoration",
+ "expected": "underline"
+ },
+ "message": "Set the text-decoration to underline"
+ },
+ {
+ "type": "regex",
+ "value": "#main-title\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "id-with-type",
+ "title": "Type + ID",
+ "description": "Similar to how you can combine type and class selectors, you can also combine type selectors with ID selectors. For example, h1#title targets an h1 element with the ID 'title'. When using this combined approach, you can apply CSS properties like font-style to control the slant of the text, making it italic or normal. While this selector combination is more specific than using just the ID selector, it's often unnecessary since IDs should already be unique in a document. However, this technique can be useful for improving code readability or when you want to emphasize that a particular ID should only appear on a specific element type.",
+ "task": "Create a CSS rule that combines a type selector with an ID selector to target specifically a paragraph element with the ID special. Set its font style to italic.",
+ "previewHTML": "
Heading with ID \"special\" (should NOT be affected)
\n
Paragraph with ID \"special\" (should become italic)
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "h2, p { padding: 8px; margin-bottom: 10px; border: 1px dashed #ccc; }",
+ "codePrefix": "/* Create a combined type+ID selector for a paragraph with id=\"special\" */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^p#special\\s*{",
+ "message": "Use p#special to target paragraphs with ID special",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "font-style:",
+ "message": "Include the font-style property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "font-style",
+ "expected": "italic"
+ },
+ "message": "Set the font-style to italic"
+ },
+ {
+ "type": "regex",
+ "value": "p#special\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "selector-lists",
+ "title": "Selector Lists",
+ "description": "When multiple elements need the same styling, you can group them together using a selector list (also known as grouping selectors). Selector lists are created by separating individual selectors with commas. This approach reduces repetition in your CSS, making it more maintainable and efficient. For example, h1, h2, h3 { color: blue; } applies the same blue color to all three heading levels. When styling multiple selectors at once, you can apply properties like background-color to set the background, border-left to create a left border with a specific thickness, style, and color, and padding-left to create space between the content and the left border. Whitespace around commas is optional, and each selector in the list can be any valid selector type-elements, classes, IDs, or even more complex selectors.",
+ "task": "Create a selector list that applies the same styles to three different elements: paragraphs with class note, list items with class important, and the element with ID summary. Give them a lightyellow background, a gold left border, and some left padding.",
+ "previewHTML": "
This is a note paragraph.
\n
\n
Regular list item
\n
Important list item
\n
\n
Summary section
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "p, li, div { padding: 8px; margin-bottom: 8px; border: 1px dashed gray; }",
+ "codePrefix": "/* Create a selector list to apply the same styles to multiple different elements */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "solution": "p.note,\nli.important,\n#summary {\n background-color: lightyellow;\n border-left: 3px solid gold;\n padding-left: 10px\n}",
+ "validations": [
+ {
+ "type": "contains",
+ "value": "p.note",
+ "message": "Include p.note in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "li.important",
+ "message": "Include li.important in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "#summary",
+ "message": "Include #summary in your selector list",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "regex",
+ "value": "(p\\.note|li\\.important|#summary)\\s*,\\s*(p\\.note|li\\.important|#summary)\\s*,\\s*(p\\.note|li\\.important|#summary)",
+ "message": "Create a comma-separated list with all three selectors",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "background-color:",
+ "message": "Include the background-color property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "background-color",
+ "expected": "lightyellow"
+ },
+ "message": "Set the background color to lightyellow"
+ },
+ {
+ "type": "contains",
+ "value": "border-left:",
+ "message": "Include the border-left property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "border-left",
+ "expected": "3px solid gold"
+ },
+ "message": "Use border-left: 3px solid gold to create a left border"
+ },
+ {
+ "type": "contains",
+ "value": "padding-left:",
+ "message": "Include the padding-left property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "padding-left",
+ "expected": "10px"
+ },
+ "message": "Use padding-left: 10px to add left padding"
+ }
+ ]
+ },
+ {
+ "id": "universal-selector",
+ "title": "Universal (*)",
+ "description": "The universal selector is denoted by an asterisk (*) and matches any element of any type. It selects everything in the document or, when combined with other selectors, everything within a specific context. For example, * { margin: 0; } removes margins from all elements, while article * selects all elements inside article elements. When using the universal selector in combination with other selectors, you can apply properties like margin to control the spacing around elements. The universal selector is powerful but should be used carefully due to its broad impact. It's commonly used in CSS resets, to override default browser styling, or to target all children of a particular element.",
+ "task": "Use the universal selector to remove margins from all elements inside the container div. Create a rule using div.container * as the selector and set margin: 0.",
+ "previewHTML": "
\n
Inside Container
\n
This paragraph is inside the container.
\n
\n
List item inside container
\n
\n
\n
This paragraph is outside the container and should not be affected.
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; } div.container { border: 2px solid navy; padding: 15px; background-color: lavender; } h2, p, ul, li { margin: 15px 0; }",
+ "sandboxCSS": "",
+ "codePrefix": "/* Use the universal selector to target all elements inside the container */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^div\\.container\\s+\\*\\s*{",
+ "message": "Use div.container * selector (with a space between container and *)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "margin:",
+ "message": "Include the margin property"
+ },
+ {
+ "type": "property_value",
+ "value": {
+ "property": "margin",
+ "expected": "0"
+ },
+ "message": "Set margin to 0"
+ },
+ {
+ "type": "regex",
+ "value": "div\\.container\\s+\\*\\s*{[^}]*}",
+ "message": "Make sure to close your CSS rule with a closing brace }",
+ "options": {
+ "caseSensitive": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "specificity-basics",
+ "title": "Specificity",
+ "description": "CSS specificity determines which styles take precedence when multiple conflicting rules target the same element. Specificity follows a hierarchical system: inline styles have the highest specificity, followed by ID selectors, then class/attribute/pseudo-class selectors, and finally element/pseudo-element selectors. This can be conceptualized as a four-part score (inline, ID, class, element). When creating multiple rules that may target the same elements, you can use the color property to set text colors, and specificity will determine which color is actually applied. Understanding specificity is crucial for predictable styling and debugging CSS conflicts. When two selectors have equal specificity, the one that comes last in the stylesheet wins.",
+ "task": "Examine the existing CSS rules and add a new rule with higher specificity to override the text color of the paragraph. Create a rule using '.content p' as the selector and set color: green.",
+ "previewHTML": "
\n
What color will this paragraph be? Look at the CSS rules and their specificity.
\n
",
+ "previewBaseCSS": "body { font-family: sans-serif; line-height: 1.5; padding: 20px; }",
+ "sandboxCSS": "p { border: 1px dashed gray; padding: 10px; }",
+ "codePrefix": "/* These CSS rules target the same paragraph but have different specificity */\n\n/* Rule 1: Element selector (lowest specificity) */\np {\n color: red;\n}\n\n/* Rule 2: Descendant selector (higher specificity than just 'p') */\n",
+ "initialCode": "",
+ "codeSuffix": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "regex",
+ "value": "^\\.content\\s+p\\s*{",
+ "message": "Use .content p as your selector (note the space between)",
+ "options": {
+ "caseSensitive": true
+ }
+ },
+ {
+ "type": "contains",
+ "value": "color:",
+ "message": "Include the color property"
+ },
+ {
+ "type": "contains",
+ "value": "green",
+ "message": ""
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/uk/00-welcome.json b/lessons/uk/00-welcome.json
new file mode 100644
index 0000000..ce364c4
--- /dev/null
+++ b/lessons/uk/00-welcome.json
@@ -0,0 +1,62 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "welcome",
+ "title": "Code Crispies",
+ "description": "Welcome to Code Crispies - your interactive web development learning platform",
+ "mode": "html",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "get-started",
+ "title": "Get Started",
+ "description": "Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required!
What you'll learn: • HTML - Semantic elements, forms, tables, SVG (HTML Block & Inline, HTML Forms, HTML Tables) • CSS - Selectors, box model, flexbox, animations (CSS Selectors, CSS Box Model, CSS Flexbox) • Responsive Design - Media queries and mobile-first layouts
How it works: 1. Read the task in the left panel 2. Write code in the editor 3. See live results in the preview 4. Get instant feedback with hints
Keyboard shortcuts:Ctrl+Z to undo, Ctrl+Shift+Z to redo
More resources: • HTML over JS - Native HTML vs JavaScript solutions • Web Engineering Mandala - JavaScript technology roadmap",
+ "task": "Hello World",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; text-align: center; } h1 { color: #6366f1; }",
+ "sandboxCSS": "",
+ "initialCode": "",
+ "solution": "
Hello World
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "contains",
+ "value": "Hello",
+ "message": "Write 'Hello World'"
+ }
+ ]
+ },
+ {
+ "id": "overview",
+ "title": "Overview",
+ "description": "You're ready! Open the menu (☰) to explore all modules.
Recommended learning path: 1. HTML Block & Inline - Understand container vs inline elements 2. HTML Forms - Build interactive forms with validation 3. CSS Selectors - Target elements precisely 4. CSS Box Model - Master padding, margin, borders 5. CSS Flexbox - Create flexible layouts 6. CSS Animations - Add motion and transitions
Tips: • Use Show Expected to see the target result • Your progress is saved automatically • Try Emmet in HTML mode: ul>li*3 + Tab
",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": []
+ }
+ ]
+}
diff --git a/lessons/uk/01-box-model.json b/lessons/uk/01-box-model.json
new file mode 100644
index 0000000..a5b10a6
--- /dev/null
+++ b/lessons/uk/01-box-model.json
@@ -0,0 +1,180 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "box-model",
+ "title": "CSS Box Model",
+ "description": "Master the fundamental principles of space management in web design through the CSS box model. This module explores how content, padding, borders, and margins combine to create layout structures that are both visually appealing and structurally sound.",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "box-model-1",
+ "title": "Box Model Components",
+ "description": "The CSS box model consists of four concentric layers: content area (innermost), padding, border, and margin (outermost). Understanding how these components interact is essential for precise layout control.",
+ "task": "Add padding: 1rem to .box to create space between its content and border.",
+ "previewHTML": "
Block elements (containers) start on a new line and take full width. Examples: <div>, <p>, <h1>, <section>
Inline elements flow within text and only take needed width. Examples: <span>, <a>, <strong>, <em>",
+ "task": "Wrap the word important with <strong> tags to make it bold. Notice how the paragraph (block) takes full width while strong (inline) flows with text.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; } p { background: #e3f2fd; padding: 10px; } strong { background: #ffecb3; }",
+ "sandboxCSS": "",
+ "initialCode": "
This is a paragraph with an important word.
",
+ "solution": "
This is a paragraph with an important word.
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "p",
+ "message": "Add a <p> paragraph element"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "p", "child": "strong" },
+ "message": "Wrap the word important with <strong> tags"
+ }
+ ]
+ },
+ {
+ "id": "semantic-containers",
+ "title": "Semantic Tags",
+ "description": "Modern HTML uses semantic containers that describe their content:
<header> - Page or section header <nav> - Navigation links <main> - Main content area <section> - Thematic grouping <article> - Self-contained content <footer> - Page or section footer",
+ "task": "Create a basic page structure: 1. Add a <header> with an <h1> containing the text 'My Website' 2. Add a <main> element with a paragraph saying 'Welcome to my site!' 3. Add a <footer> with a paragraph saying 'Copyright 2025'",
+ "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": "",
+ "initialCode": "",
+ "solution": "\n
Use semantic elements when possible, div/span when no semantic element fits.",
+ "task": "Wrap the word 'highlighted' in a <span> to style it differently. Wrap the whole quote in a <div>.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: Georgia, serif; padding: 20px; } div { background: #f5f5f5; padding: 15px; border-left: 4px solid #1976d2; } span { background: #fff59d; padding: 2px 4px; }",
+ "sandboxCSS": "",
+ "initialCode": "The most highlighted moment was unforgettable.",
+ "solution": "
The most highlighted moment was unforgettable.
",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "div",
+ "message": "Wrap everything in a <div> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "span",
+ "message": "Add a <span> around the word highlighted"
+ },
+ {
+ "type": "element_text",
+ "value": { "selector": "span", "text": "highlighted" },
+ "message": "The <span> should contain the word highlighted"
+ }
+ ]
+ }
+ ]
+}
diff --git a/lessons/uk/21-html-forms-basic.json b/lessons/uk/21-html-forms-basic.json
new file mode 100644
index 0000000..d971cbf
--- /dev/null
+++ b/lessons/uk/21-html-forms-basic.json
@@ -0,0 +1,102 @@
+{
+ "$schema": "../../schemas/code-crispies-module-schema.json",
+ "id": "html-forms-basic",
+ "title": "HTML Forms",
+ "description": "Learn to create forms with various input types",
+ "mode": "html",
+ "difficulty": "beginner",
+ "lessons": [
+ {
+ "id": "form-structure",
+ "title": "Form Structure",
+ "description": "Every form needs a <form> wrapper. Inside, use <label> to describe inputs and <input> for user data entry.
The for attribute on labels should match the id on inputs for accessibility.",
+ "task": "Create a form with: 1. A <label> with the text 'Name:' and for=\"name\" attribute 2. A text <input> with id=\"name\" and name=\"name\" attributes",
+ "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": "",
+ "initialCode": "",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "form",
+ "message": "Wrap everything in a <form> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "label",
+ "message": "Add a <label> for your input"
+ },
+ {
+ "type": "element_exists",
+ "value": "input",
+ "message": "Add an <input> element"
+ },
+ {
+ "type": "attribute_value",
+ "value": { "selector": "label", "attr": "for", "value": null },
+ "message": "Add a for attribute to your label"
+ },
+ {
+ "type": "attribute_value",
+ "value": { "selector": "input", "attr": "id", "value": null },
+ "message": "Add an id attribute to your input"
+ }
+ ]
+ },
+ {
+ "id": "input-types",
+ "title": "Input Types",
+ "description": "Different input types provide appropriate keyboards and validation:
type=\"text\" - General text type=\"email\" - Email with @ validation type=\"password\" - Hidden characters type=\"number\" - Numeric keyboard type=\"tel\" - Phone keyboard",
+ "task": "Create a login form with two fields: 1. An email field: <label for=\"email\">Email:</label> and <input type=\"email\" id=\"email\"> 2. A password field: <label for=\"password\">Password:</label> and <input type=\"password\" id=\"password\">",
+ "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; }",
+ "sandboxCSS": "",
+ "initialCode": "",
+ "solution": "",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "input[type='email']",
+ "message": "Add an input with type=\"email\""
+ },
+ {
+ "type": "element_exists",
+ "value": "input[type='password']",
+ "message": "Add an input with type=\"password\""
+ },
+ {
+ "type": "element_count",
+ "value": { "selector": "label", "min": 2 },
+ "message": "Add labels for both inputs"
+ }
+ ]
+ },
+ {
+ "id": "submit-button",
+ "title": "Submit Button",
+ "description": "Forms need a way to submit data. Use:
Click the summary to toggle the hidden content - no JavaScript needed!",
+ "task": "Create a <details> element with: 1. A <summary> saying 'Click to reveal' 2. A <p> with the text 'This content was hidden!'",
+ "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": "",
+ "initialCode": "",
+ "solution": "\n Click to reveal\n
This content was hidden!
\n",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "element_exists",
+ "value": "details",
+ "message": "Add a <details> element"
+ },
+ {
+ "type": "element_exists",
+ "value": "summary",
+ "message": "Add a <summary> inside the details"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "details", "child": "summary" },
+ "message": "The <summary> must be inside <details>"
+ },
+ {
+ "type": "parent_child",
+ "value": { "parent": "details", "child": "p" },
+ "message": "Add a <p> inside <details> for the hidden content"
+ }
+ ]
+ },
+ {
+ "id": "details-open-attribute",
+ "title": "Pre-expanded Details",
+ "description": "By default, <details> is closed. Add the open attribute to show the content initially.
This is a boolean attribute - just add open without a value.",
+ "task": "Add the open attribute to the <details> element to show the content by default.",
+ "previewHTML": "",
+ "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } details { border: 1px solid #ddd; border-radius: 8px; padding: 15px; background: #f9f9f9; } summary { font-weight: 600; cursor: pointer; } details p { margin-top: 15px; color: #666; }",
+ "sandboxCSS": "",
+ "initialCode": "\n FAQ: What is HTML5?\n
HTML5 is the latest version of the HTML standard with new semantic elements and APIs.
\n",
+ "solution": "\n FAQ: What is HTML5?\n
HTML5 is the latest version of the HTML standard with new semantic elements and APIs.
\n",
+ "previewContainer": "preview-area",
+ "validations": [
+ {
+ "type": "attribute_value",
+ "value": { "selector": "details", "attr": "open", "value": true },
+ "message": "Add the open attribute to <details>"
+ }
+ ]
+ },
+ {
+ "id": "faq-accordion",
+ "title": "FAQ Accordion",
+ "description": "Multiple <details> elements create an accordion-style FAQ. Each question can be expanded independently.