From bd3f9ab96ad7b6fb017acb07c3ee05863185689b Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Tue, 13 Jan 2026 21:11:41 +0100 Subject: [PATCH] refactor: rewrite CSS lessons with realistic real-world examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Box Model: profile cards, alerts, buttons instead of generic boxes - Flexbox: navigation bars, headers, toolbars, card layouts - Grid: photo gallery with SVG images, product cards, dashboard layout - Colors: notification alerts, buttons, badges with visible changes - Units/Variables: article width, brand variables, sidebar calc, hero vh - Responsive: feature cards grid instead of numbered divs - Added missing solutions to enable "Show Expected" feature - Fixed barely visible border color change in colors lesson 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- lessons/00-basic-selectors.json | 6 + lessons/01-advanced-selectors.json | 2 +- lessons/01-box-model.json | 145 +++++++------ lessons/03-colors.json | 58 ++--- lessons/04-typography.json | 20 +- lessons/05-units-variables.json | 103 ++++----- lessons/08-responsive.json | 14 +- lessons/flexbox.json | 144 +++++------- lessons/grid.json | 337 ++++++++++------------------- 9 files changed, 348 insertions(+), 481 deletions(-) diff --git a/lessons/00-basic-selectors.json b/lessons/00-basic-selectors.json index 915798c..4e01bfd 100644 --- a/lessons/00-basic-selectors.json +++ b/lessons/00-basic-selectors.json @@ -129,6 +129,7 @@ "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", + "solution": ".highlight {\n background-color: yellow;\n font-weight: bold;\n}", "validations": [ { "type": "regex", @@ -249,6 +250,7 @@ "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", + "solution": "span.highlight {\n background-color: orange;\n}", "validations": [ { "type": "regex", @@ -293,6 +295,7 @@ "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", + "solution": "#main-title {\n color: purple;\n text-decoration: underline;\n}", "validations": [ { "type": "regex", @@ -350,6 +353,7 @@ "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", + "solution": "p#special {\n font-style: italic;\n}", "validations": [ { "type": "regex", @@ -481,6 +485,7 @@ "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", + "solution": "div.container * {\n margin: 0;\n}", "validations": [ { "type": "regex", @@ -525,6 +530,7 @@ "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", + "solution": ".content p {\n color: green;\n}", "validations": [ { "type": "regex", diff --git a/lessons/01-advanced-selectors.json b/lessons/01-advanced-selectors.json index 71c0880..1e9ee3a 100644 --- a/lessons/01-advanced-selectors.json +++ b/lessons/01-advanced-selectors.json @@ -17,7 +17,7 @@ "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", - "solution": "input[type=\"text\"] {\n background-color: lightblue;\n border: 2px solid blue\n}", + "solution": "input[type=\"text\"] {\n background-color: lightblue;\n border: 2px solid blue;\n}", "validations": [ { "type": "regex", diff --git a/lessons/01-box-model.json b/lessons/01-box-model.json index 57c4a70..ed32a0d 100644 --- a/lessons/01-box-model.json +++ b/lessons/01-box-model.json @@ -7,13 +7,13 @@ "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": "Set padding to 1rem to create space between the content and border.", - "previewHTML": "
Box Model Components
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { background-color: lavender; border: 2px dashed slategray; }", + "title": "Padding", + "description": "Every element in CSS is a box with four layers: content, padding, border, and margin. Padding creates breathing room between your content and the box's edge.

Without padding, text presses against borders awkwardly. Padding makes content readable and visually balanced.

.card {\n  padding: 1rem;\n}
", + "task": "This profile card looks cramped. Add padding: 1rem to .card so the text has room to breathe.", + "previewHTML": "

Sarah Chen

Frontend Developer

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }", "sandboxCSS": "", - "codePrefix": ".box {\n ", + "codePrefix": ".card {\n ", "initialCode": "", "codeSuffix": "\n}", "solution": "padding: 1rem;", @@ -28,56 +28,56 @@ }, { "id": "box-model-2", - "title": "Adding Borders", - "description": "Borders outline an element, creating visual separation from surrounding content. The border shorthand accepts three values: width, style, and color.", - "task": "Set border to 2px solid darkslategray.", - "previewHTML": "
This box needs a border
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { background-color: mintcream; padding: 1rem; }", + "title": "Borders", + "description": "Borders create visual boundaries around elements. The border shorthand takes three values: width, style, and color.

Common styles: solid, dashed, dotted, none", + "task": "Add a subtle left accent to the card with border-left: 4px solid steelblue.", + "previewHTML": "

Sarah Chen

Frontend Developer

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }", "sandboxCSS": "", - "codePrefix": ".box {\n ", + "codePrefix": ".card {\n ", "initialCode": "", "codeSuffix": "\n}", - "solution": "border: 2px solid darkslategray;", + "solution": "border-left: 4px solid steelblue;", "previewContainer": "preview-area", "validations": [ { "type": "regex", - "value": "border:\\s*2px\\s+solid\\s+darkslategray", - "message": "Set border: 2px solid darkslategray", + "value": "border-left:\\s*4px\\s+solid\\s+steelblue", + "message": "Set border-left: 4px solid steelblue", "options": { "caseSensitive": false } } ] }, { "id": "box-model-3", - "title": "Adding Margins", - "description": "Margins create space between elements, controlling how they relate to one another within a layout. Unlike padding (which affects internal spacing), margins exist outside the element's border.", - "task": "Set margin to 1rem to create space between this element and its neighbors.", - "previewHTML": "
This box needs margins
Adjacent element
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .container { background-color: whitesmoke; padding: 8px; } .outer { background-color: plum; padding: 1rem; border: 2px solid orchid; } .neighbor { background-color: lightblue; padding: 1rem; border: 2px solid steelblue; }", + "title": "Margins", + "description": "Margins create space outside the element, separating it from neighbors. While padding pushes content inward, margins push other elements away.", + "task": "Add space between these two profile cards with margin-bottom: 1rem on .card.", + "previewHTML": "

Sarah Chen

Frontend Developer

Alex Rivera

UX Designer

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }", "sandboxCSS": "", - "codePrefix": ".outer {\n ", + "codePrefix": ".card {\n ", "initialCode": "", "codeSuffix": "\n}", - "solution": "margin: 1rem;", + "solution": "margin-bottom: 1rem;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", - "value": { "property": "margin", "expected": "1rem" }, - "message": "Set margin: 1rem" + "value": { "property": "margin-bottom", "expected": "1rem" }, + "message": "Set margin-bottom: 1rem" } ] }, { "id": "box-model-4", - "title": "Box Sizing: Border-Box", - "description": "The box-sizing property determines how element dimensions are calculated. The default content-box excludes padding and border from width/height, while border-box includes them, making layout calculations more intuitive.", - "task": "Set box-sizing to border-box so padding and border are included in the width.", - "previewHTML": "
Content-box (default)
Border-box
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .sizing-demo { display: flex; gap: 1rem; } .box { width: 200px; padding: 1rem; border: 4px solid teal; background: lightcyan; } .default { box-sizing: content-box; }", + "title": "Box Sizing", + "description": "By default, width only sets the content width. Padding and borders add to the total. This causes layout headaches.

box-sizing: border-box includes padding and border in the width, making sizing predictable. Most developers apply this to all elements.", + "task": "Both cards have width: 200px. The left uses default sizing (content-box), making it wider than expected. Fix the right card with box-sizing: border-box.", + "previewHTML": "
Content-box
Border-box
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .demo { display: flex; gap: 1rem; } .card { width: 200px; padding: 1rem; border: 4px solid steelblue; background: white; border-radius: 8px; }", "sandboxCSS": "", - "codePrefix": ".sized {\n ", + "codePrefix": ".fix {\n ", "initialCode": "", "codeSuffix": "\n}", "solution": "box-sizing: border-box;", @@ -92,87 +92,98 @@ }, { "id": "box-model-5", - "title": "Margin Collapse", - "description": "When two vertical margins meet, they collapse to the larger value instead of adding up. Understanding this behavior is crucial for consistent vertical spacing.", - "task": "Set margin-bottom to 2rem. Notice the space between paragraphs equals 2rem (not 3rem) due to margin collapse.", - "previewHTML": "

This paragraph has a bottom margin.

This paragraph has a top margin of 1rem.

", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .collapse-demo { border: 1px solid silver; padding: 8px; background: ghostwhite; } .second { margin-top: 1rem; background: linen; }", + "title": "Padding Shorthand", + "description": "Padding accepts 1-4 values:
• 1 value: all sides
• 2 values: vertical | horizontal
• 4 values: top | right | bottom | left", + "task": "This button needs more horizontal space than vertical. Set padding: 8px 1rem (8px top/bottom, 1rem left/right).", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .btn { background: steelblue; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; }", "sandboxCSS": "", - "codePrefix": ".first {\n ", + "codePrefix": ".btn {\n ", "initialCode": "", "codeSuffix": "\n}", - "solution": "margin-bottom: 2rem;", + "solution": "padding: 8px 1rem;", "previewContainer": "preview-area", "validations": [ { - "type": "property_value", - "value": { "property": "margin-bottom", "expected": "2rem" }, - "message": "Set margin-bottom: 2rem" + "type": "regex", + "value": "padding:\\s*8px\\s+1rem", + "message": "Set padding: 8px 1rem", + "options": { "caseSensitive": false } } ] }, { "id": "box-model-6", - "title": "Margin Shorthand Notation", - "description": "The margin shorthand can set all four sides at once. Two values set vertical (top/bottom) and horizontal (left/right) margins respectively.", - "task": "Set margin to 1rem 2rem for 1rem top/bottom and 2rem left/right.", - "previewHTML": "
This box needs margins: 1rem top/bottom, 2rem left/right
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .container { background-color: whitesmoke; padding: 8px; } .spaced { background-color: honeydew; border: 2px solid mediumseagreen; padding: 1rem; }", + "title": "Margin Shorthand", + "description": "Margin uses the same shorthand pattern as padding. A common pattern is centering block elements horizontally with margin: 0 auto.", + "task": "Center this card horizontally. Set margin: 0 auto to auto-calculate equal left/right margins.", + "previewHTML": "

Sarah Chen

Frontend Developer

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { width: 250px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; border-left: 4px solid steelblue; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }", "sandboxCSS": "", - "codePrefix": ".spaced {\n ", + "codePrefix": ".card {\n ", "initialCode": "", "codeSuffix": "\n}", - "solution": "margin: 1rem 2rem;", + "solution": "margin: 0 auto;", "previewContainer": "preview-area", "validations": [ { "type": "regex", - "value": "margin:\\s*1rem\\s+2rem", - "message": "Set margin: 1rem 2rem", + "value": "margin:\\s*0\\s+auto", + "message": "Set margin: 0 auto", "options": { "caseSensitive": false } } ] }, { "id": "box-model-7", - "title": "Padding Shorthand Notation", - "description": "Like margin, padding shorthand allows setting all sides at once. A single value applies to all four sides equally.", - "task": "Set padding to 2rem to add equal padding on all sides.", - "previewHTML": "
This box needs equal padding on all sides
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .padded { background-color: papayawhip; border: 2px solid orange; }", + "title": "Border Radius", + "description": "While not part of the classic box model, border-radius rounds the corners of an element's border box. Use 50% on a square element to create a circle.", + "task": "Make the avatar image circular with border-radius: 50%.", + "previewHTML": "
\"Avatar\"

Sarah Chen

Frontend Developer

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; text-align: center; } .avatar { width: 80px; height: 80px; background: #ddd; margin-bottom: 8px; } .card h3 { margin: 0 0 4px; } .card p { margin: 0; color: #666; }", "sandboxCSS": "", - "codePrefix": ".padded {\n ", + "codePrefix": ".avatar {\n ", "initialCode": "", "codeSuffix": "\n}", - "solution": "padding: 2rem;", + "solution": "border-radius: 50%;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", - "value": { "property": "padding", "expected": "2rem" }, - "message": "Set padding: 2rem" + "value": { "property": "border-radius", "expected": "50%" }, + "message": "Set border-radius: 50%" } ] }, { "id": "box-model-8", - "title": "Border on Specific Sides", - "description": "For granular control, you can target specific sides with border-top, border-right, border-bottom, or border-left.", - "task": "Set border-bottom to 4px solid dodgerblue.", - "previewHTML": "
This element needs only a bottom border
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .line { padding: 1rem; background-color: aliceblue; }", + "title": "Complete Card", + "description": "Let's combine everything. This notification card needs styling to look polished.", + "task": "Style the notification: add padding: 1rem, border-left: 4px solid coral, and border-radius: 4px.", + "previewHTML": "
New message

You have 3 unread notifications

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { background: seashell; } .alert strong { color: coral; } .alert p { margin: 4px 0 0; color: #666; }", "sandboxCSS": "", - "codePrefix": ".line {\n ", + "codePrefix": ".alert {\n ", "initialCode": "", "codeSuffix": "\n}", - "solution": "border-bottom: 4px solid dodgerblue;", + "solution": "padding: 1rem;\n border-left: 4px solid coral;\n border-radius: 4px;", "previewContainer": "preview-area", "validations": [ + { + "type": "property_value", + "value": { "property": "padding", "expected": "1rem" }, + "message": "Set padding: 1rem" + }, { "type": "regex", - "value": "border-bottom:\\s*4px\\s+solid\\s+dodgerblue", - "message": "Set border-bottom: 4px solid dodgerblue", + "value": "border-left:\\s*4px\\s+solid\\s+coral", + "message": "Set border-left: 4px solid coral", "options": { "caseSensitive": false } + }, + { + "type": "property_value", + "value": { "property": "border-radius", "expected": "4px" }, + "message": "Set border-radius: 4px" } ] } diff --git a/lessons/03-colors.json b/lessons/03-colors.json index 8296f9a..1aee288 100644 --- a/lessons/03-colors.json +++ b/lessons/03-colors.json @@ -8,54 +8,57 @@ { "id": "colors-1", "title": "Background Color", - "description": "Color is one of the most powerful tools in web design. It creates visual hierarchy, conveys meaning, and establishes brand identity. CSS provides multiple ways to specify colors.

CSS named colors: CSS includes 147 named colors like steelblue, coral, gold, and tomato. These are easy to remember and read.

The background-color property: Sets the fill color behind an element's content and padding areas. The color extends to the edge of the element's border.

.box {\n  background-color: lightblue;\n}
", - "task": "Set background-color to lightcyan on .box.", - "previewHTML": "
Background Demo
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { padding: 1rem; border: 2px solid steelblue; }", + "description": "Color is one of the most powerful tools in web design. It creates visual hierarchy, conveys meaning, and establishes brand identity. CSS provides multiple ways to specify colors.

CSS named colors: CSS includes 147 named colors like steelblue, coral, gold, and tomato. These are easy to remember and read.

The background-color property: Sets the fill color behind an element's content and padding areas.

.card {\n  background-color: lightblue;\n}
", + "task": "This notification card needs a subtle background. Set background-color: seashell on .alert.", + "previewHTML": "
New message

You have 3 unread notifications

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { padding: 1rem; border-left: 4px solid coral; border-radius: 4px; } .alert strong { display: block; margin-bottom: 4px; } .alert p { margin: 0; color: #666; font-size: 0.9rem; }", "sandboxCSS": "", - "codePrefix": ".box {\n ", + "codePrefix": ".alert {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "background-color: seashell;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", - "value": { "property": "background-color", "expected": "lightcyan" }, - "message": "Set background-color: lightcyan" + "value": { "property": "background-color", "expected": "seashell" }, + "message": "Set background-color: seashell" } ] }, { "id": "colors-2", "title": "Text Color", - "description": "The color property sets the color of text content. Good contrast between text and background is essential for readability.", - "task": "Set color to darkslategray on .box.", - "previewHTML": "
Color & Contrast
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { padding: 1rem; background-color: lightcyan; border: 2px solid steelblue; }", + "description": "The color property sets the color of text content. Good contrast between text and background is essential for readability and accessibility.", + "task": "Make the alert title stand out by setting color: coral on .title.", + "previewHTML": "
Warning

Your session will expire in 5 minutes

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .alert { padding: 1rem; background-color: seashell; border-left: 4px solid coral; border-radius: 4px; } .alert .title { display: block; margin-bottom: 4px; } .alert p { margin: 0; color: #666; font-size: 0.9rem; }", "sandboxCSS": "", - "codePrefix": ".box {\n ", + "codePrefix": ".title {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "color: coral;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", - "value": { "property": "color", "expected": "darkslategray" }, - "message": "Set color: darkslategray" + "value": { "property": "color", "expected": "coral" }, + "message": "Set color: coral" } ] }, { "id": "colors-3", "title": "Border Color", - "description": "Borders can have their own color using border-color, or you can specify color in the border shorthand.", - "task": "Set border-color to coral on .box.", - "previewHTML": "
Border Color
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { padding: 1rem; background-color: seashell; border: 4px solid gray; }", + "description": "Borders can have their own color using border-color. This is useful when you want to change just the color without redefining the entire border.", + "task": "This card needs an accent border. Set border-color: coral on .card.", + "previewHTML": "

Premium Plan

Unlimited access to all features

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .card { padding: 1rem; background: white; border: 4px solid #ddd; border-radius: 8px; } .card h3 { margin: 0 0 8px; } .card p { margin: 0; color: #666; }", "sandboxCSS": "", - "codePrefix": ".box {\n ", + "codePrefix": ".card {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "border-color: coral;", "previewContainer": "preview-area", "validations": [ { @@ -67,21 +70,22 @@ }, { "id": "colors-4", - "title": "Color Formats", - "description": "Besides named colors, CSS supports hex codes (#ff6347), RGB (rgb(255, 99, 71)), and HSL (hsl(9, 100%, 64%)) formats.", - "task": "Set background-color to #f0e68c (khaki in hex) on .box.", - "previewHTML": "
Hex Color
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { padding: 1rem; border: 2px solid olive; }", + "title": "Hex Colors", + "description": "Beyond named colors, CSS supports hex codes (#ff6347), RGB (rgb(255, 99, 71)), and HSL (hsl(9, 100%, 64%)). Hex codes are the most common format in professional projects.", + "task": "Set the badge background to gold using its hex code: background-color: #ffd700.", + "previewHTML": "NEW", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .badge { display: inline-block; padding: 4px 12px; border-radius: 999px; font-size: 0.75rem; font-weight: bold; text-transform: uppercase; color: #333; }", "sandboxCSS": "", - "codePrefix": ".box {\n ", + "codePrefix": ".badge {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "background-color: #ffd700;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", - "value": { "property": "background-color", "expected": "#f0e68c" }, - "message": "Set background-color: #f0e68c" + "value": { "property": "background-color", "expected": "#ffd700" }, + "message": "Set background-color: #ffd700" } ] } diff --git a/lessons/04-typography.json b/lessons/04-typography.json index 5221b82..cb6c7f9 100644 --- a/lessons/04-typography.json +++ b/lessons/04-typography.json @@ -13,10 +13,11 @@ "previewHTML": "

This text shows the chosen font family.

", "previewBaseCSS": "body { padding: 1rem; }", "sandboxCSS": "", - "codePrefix": "/* Set font family */\n.text {", + "codePrefix": ".text {\n ", "initialCode": "", - "codeSuffix": "}", + "codeSuffix": "\n}", "previewContainer": "preview-area", + "solution": "font-family: Georgia, serif;", "validations": [ { "type": "contains", @@ -40,10 +41,11 @@ "previewHTML": "

Readable Heading

", "previewBaseCSS": "body { padding: 1rem; }", "sandboxCSS": "", - "codePrefix": "/* Set size and line height */\n.heading {", + "codePrefix": ".heading {\n ", "initialCode": "", - "codeSuffix": "}", + "codeSuffix": "\n}", "previewContainer": "preview-area", + "solution": "font-size: 1.5rem;\n line-height: 1.5;", "validations": [ { "type": "contains", "value": "font-size", "message": "Use font-size property", "options": { "caseSensitive": false } }, { @@ -72,10 +74,11 @@ "previewHTML": "

This text should stand out.

", "previewBaseCSS": "body { padding: 1rem; }", "sandboxCSS": "", - "codePrefix": "/* Emphasize text */\n.emphasis {", + "codePrefix": ".emphasis {\n ", "initialCode": "", - "codeSuffix": "}", + "codeSuffix": "\n}", "previewContainer": "preview-area", + "solution": "font-style: italic;\n font-weight: bold;", "validations": [ { "type": "contains", "value": "font-style", "message": "Use font-style property", "options": { "caseSensitive": false } }, { @@ -104,10 +107,11 @@ "previewHTML": "

Fancy text effect!

", "previewBaseCSS": "body { padding: 1rem; } .fancy { font-size: 1.25rem; }", "sandboxCSS": "", - "codePrefix": "/* Decorate text */\n.fancy {", + "codePrefix": ".fancy {\n ", "initialCode": "", - "codeSuffix": "}", + "codeSuffix": "\n}", "previewContainer": "preview-area", + "solution": "text-decoration: underline;\n text-shadow: 1px 1px 2px gray;", "validations": [ { "type": "contains", diff --git a/lessons/05-units-variables.json b/lessons/05-units-variables.json index 2ffb2da..5a76705 100644 --- a/lessons/05-units-variables.json +++ b/lessons/05-units-variables.json @@ -7,109 +7,94 @@ "lessons": [ { "id": "units-1", - "title": "Absolute vs. Relative Units", - "description": "Learn the difference between px, rem, em, %, and vw/vh for flexible, responsive layouts.

width: 80%;      /* relative to parent */\nmax-width: 40rem; /* relative to root font */\npadding: 16px;   /* fixed pixels */
", - "task": "Set the width of .box to 80% and max-width to 40rem.", - "previewHTML": "
Resize me!
", - "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .box { background: #f5f5f5; padding: 1rem; }", + "title": "Relative Units", + "description": "CSS offers two types of units: absolute (like px) and relative (like % and rem). Relative units adapt to their context, making layouts flexible and accessible.

Common relative units:
• % – Relative to parent element
• rem – Relative to root font size (typically 16px)
• em – Relative to element's font size

A common pattern for readable content: set width: 100% so it fills available space, then max-width: 40rem to cap line length for readability.", + "task": "This article text runs too wide on large screens. Add max-width: 40rem to .article for optimal reading width.", + "previewHTML": "

The Art of Typography

Good typography is invisible. When text is set well, readers absorb information without noticing the design decisions that make it comfortable to read. Line length is crucial—too wide and eyes get lost, too narrow and reading becomes choppy.

The ideal line length is 45-75 characters per line. At typical font sizes, this works out to roughly 40rem maximum width.

", + "previewBaseCSS": "body { font-family: Georgia, serif; padding: 1rem; background: #f9f9f9; } .article { background: white; padding: 2rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } .article h2 { margin: 0 0 1rem; color: #333; } .article p { margin: 0 0 1rem; line-height: 1.6; color: #444; } .article p:last-child { margin-bottom: 0; }", "sandboxCSS": "", - "codePrefix": "/* Set flexible sizing */\n.box {", + "codePrefix": ".article {\n ", "initialCode": "", - "codeSuffix": "}", - "solution": " width: 80%;\n max-width: 40rem;", + "codeSuffix": "\n}", + "solution": "max-width: 40rem;", "previewContainer": "preview-area", "validations": [ - { "type": "contains", "value": "width", "message": "Use width property", "options": { "caseSensitive": false } }, - { "type": "property_value", "value": { "property": "width", "expected": "80%" }, "message": "Set width to 80%" }, - { "type": "contains", "value": "max-width", "message": "Use max-width property", "options": { "caseSensitive": false } }, { "type": "property_value", "value": { "property": "max-width", "expected": "40rem" }, - "message": "Set max-width to 40rem" + "message": "Set max-width: 40rem" } ] }, { "id": "units-2", - "title": "CSS Custom Properties", - "description": "Define and reuse variables (--custom properties) to centralize your theme values.

:root {\n  --main-color: mediumpurple;\n}\n.themed {\n  border-color: var(--main-color);\n}
", - "task": "Create a --main-color variable in :root with mediumpurple and apply it as the border color on .themed.", - "previewHTML": "
Variable Box
", - "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .themed { padding: 1rem; border: 0.125rem solid #ddd; }", + "title": "CSS Variables", + "description": "CSS custom properties (variables) let you define reusable values. Define them with --name and use them with var(--name). Variables defined on :root are available everywhere.", + "task": "Define --brand: steelblue in :root, then use it as the background color for .btn.", + "previewHTML": "
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .actions { display: flex; gap: 1rem; } .btn { color: white; border: none; padding: 12px 24px; border-radius: 6px; font-size: 1rem; cursor: pointer; background: #ccc; }", "sandboxCSS": "", - "codePrefix": "/* Define and use a CSS variable */\n:root {", + "codePrefix": ":root {\n ", "initialCode": "", - "codeSuffix": "}\n.themed { }", - "solution": " --main-color: mediumpurple;\n}\n.themed {\n border-color: var(--main-color);", + "codeSuffix": "\n}\n\n.btn {\n background: var(--brand);\n}", + "solution": "--brand: steelblue;", "previewContainer": "preview-area", "validations": [ { "type": "contains", - "value": "--main-color", - "message": "Define --main-color in :root", + "value": "--brand", + "message": "Define --brand variable", "options": { "caseSensitive": false } }, { "type": "contains", - "value": "var(--main-color)", - "message": "Use var(--main-color)", + "value": "steelblue", + "message": "Set the value to steelblue", "options": { "caseSensitive": false } - }, - { - "type": "property_value", - "value": { "property": "border", "expected": "var(--main-color)" }, - "message": "Apply variable to border color", - "options": { "exact": false } } ] }, { "id": "units-3", - "title": "Unit Calculations (calc)", - "description": "Use the calc() function to combine different units in one expression.

width: calc(100% - 2rem);\nmin-height: calc(10vh + 1rem);
", - "task": "Set the width of .sized to calc(100% - 2rem) and min-height to calc(10vh + 1rem).", - "previewHTML": "
Calc Demo
", - "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .sized { background: #e8f5e9; padding: 1rem; }", + "title": "calc() Function", + "description": "The calc() function lets you mix different units in calculations. This is essential for layouts that combine fixed and flexible sizing, like a sidebar layout.", + "task": "The main content should fill the remaining space after the 200px sidebar. Set width: calc(100% - 200px) on .main.", + "previewHTML": "

Main Content

This area should fill the remaining width after accounting for the fixed-width sidebar.

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .layout { display: flex; gap: 1rem; } .sidebar { width: 200px; background: #1a1a2e; color: white; padding: 1rem; border-radius: 8px; flex-shrink: 0; } .main { background: white; padding: 1rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .main h2 { margin: 0 0 8px; } .main p { margin: 0; color: #666; }", "sandboxCSS": "", - "codePrefix": "/* Use calc for dynamic sizing */\n.sized {", + "codePrefix": ".main {\n ", "initialCode": "", - "codeSuffix": "}", - "solution": " width: calc(100% - 2rem);\n min-height: calc(10vh + 1rem);", + "codeSuffix": "\n}", + "solution": "width: calc(100% - 200px);", "previewContainer": "preview-area", "validations": [ - { "type": "contains", "value": "calc", "message": "Use calc() function", "options": { "caseSensitive": false } }, { "type": "regex", - "value": "width:\\s*calc\\(100% - 2rem\\)", - "message": "Width should be calc(100% - 2rem)", - "options": { "caseSensitive": false } - }, - { - "type": "regex", - "value": "min-height:\\s*calc\\(10vh \\+ 1rem\\)", - "message": "Min-height should be calc(10vh + 1rem)", + "value": "width:\\s*calc\\(\\s*100%\\s*-\\s*200px\\s*\\)", + "message": "Set width: calc(100% - 200px)", "options": { "caseSensitive": false } } ] }, { "id": "units-4", - "title": "Viewport & Responsive Units", - "description": "Control layouts relative to viewport size with vw, vh, and vmin/vmax units.

width: 50vw;   /* 50% of viewport width */\nheight: 20vh;  /* 20% of viewport height */
", - "task": "Give .view a width of 50vw and height of 20vh.", - "previewHTML": "
Viewport Box
", - "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .view { background: #ffe0b2; }", + "title": "Viewport Units", + "description": "Viewport units size elements relative to the browser window:
• vw – 1% of viewport width
• vh – 1% of viewport height

These are perfect for full-screen sections like hero banners.", + "task": "Make this hero section fill the viewport height by setting min-height: 100vh on .hero.", + "previewHTML": "

Welcome

Scroll down to explore

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; } .hero { background: linear-gradient(135deg, #1a1a2e 0%, steelblue 100%); color: white; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 2rem; } .hero h1 { margin: 0 0 1rem; font-size: 2.5rem; } .hero p { margin: 0; opacity: 0.8; }", "sandboxCSS": "", - "codePrefix": "/* Use viewport units */\n.view {", + "codePrefix": ".hero {\n ", "initialCode": "", - "codeSuffix": "}", - "solution": " width: 50vw;\n height: 20vh;", + "codeSuffix": "\n}", + "solution": "min-height: 100vh;", "previewContainer": "preview-area", "validations": [ - { "type": "contains", "value": "vw", "message": "Use vw unit", "options": { "caseSensitive": false } }, - { "type": "contains", "value": "vh", "message": "Use vh unit", "options": { "caseSensitive": false } }, - { "type": "property_value", "value": { "property": "width", "expected": "50vw" }, "message": "Set width to 50vw" }, - { "type": "property_value", "value": { "property": "height", "expected": "20vh" }, "message": "Set height to 20vh" } + { + "type": "property_value", + "value": { "property": "min-height", "expected": "100vh" }, + "message": "Set min-height: 100vh" + } ] } ] diff --git a/lessons/08-responsive.json b/lessons/08-responsive.json index cd1be02..edc9bf8 100644 --- a/lessons/08-responsive.json +++ b/lessons/08-responsive.json @@ -59,15 +59,15 @@ { "id": "responsive-3", "title": "Responsive Grid", - "description": "Combine CSS Grid with auto-fit or auto-fill for responsive column layouts.", - "task": "Add display: grid, grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)), and gap: 1rem to .cards.", - "previewHTML": "
1
2
3
4
", - "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .cards > div { background: #d1c4e9; padding: 1rem; }", + "description": "Combine CSS Grid with auto-fit or auto-fill for responsive column layouts that automatically adjust the number of columns based on available space.", + "task": "Add display: grid, grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)), and gap: 1rem to .features.", + "previewHTML": "

Fast

Lightning quick load times

Secure

Enterprise-grade security

Reliable

99.9% uptime guaranteed

Support

24/7 customer service

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .feature { background: white; padding: 1rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .feature h3 { margin: 0 0 8px; color: steelblue; } .feature p { margin: 0; color: #666; font-size: 0.9rem; }", "sandboxCSS": "", - "codePrefix": "/* Create a responsive grid */\n.cards {", + "codePrefix": ".features {\n ", "initialCode": "", - "codeSuffix": "}", - "solution": " display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 1rem;", + "codeSuffix": "\n}", + "solution": "display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 1rem;", "previewContainer": "preview-area", "validations": [ { diff --git a/lessons/flexbox.json b/lessons/flexbox.json index 9e10eea..6646dd2 100644 --- a/lessons/flexbox.json +++ b/lessons/flexbox.json @@ -8,12 +8,12 @@ { "id": "flexbox-1", "title": "Container", - "description": "Before flexbox, creating even simple layouts required floats, positioning hacks, or table-based layouts. Flexbox (Flexible Box Layout) revolutionized CSS by providing a one-dimensional layout system designed specifically for distributing space and aligning content.

How it works: When you set display: flex on an element, it becomes a flex container. Its direct children automatically become flex items that flow along a main axis (horizontal by default). This single property transforms stacked block elements into a horizontal row.

The two axes:
• Main axis – The primary direction items flow (row = left→right)
• Cross axis – Perpendicular to main (row = top→bottom)

.container {\n  display: flex;\n  /* Items now flow horizontally */\n}
", - "task": "Add display: flex to .wrap to arrange the boxes horizontally.", - "previewHTML": "
1
2
3
", - "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; }", - "sandboxCSS": ".wrap { border: 2px dashed #aaa; padding: 1rem; }", - "codePrefix": ".wrap {\n ", + "description": "Before flexbox, creating even simple layouts required floats, positioning hacks, or table-based layouts. Flexbox (Flexible Box Layout) revolutionized CSS by providing a one-dimensional layout system designed specifically for distributing space and aligning content.

How it works: When you set display: flex on an element, it becomes a flex container. Its direct children automatically become flex items that flow along a main axis (horizontal by default). This single property transforms stacked block elements into a horizontal row.

The two axes:
• Main axis – The primary direction items flow (row = left→right)
• Cross axis – Perpendicular to main (row = top→bottom)

.nav {\n  display: flex;\n}
", + "task": "This navigation menu stacks vertically. Add display: flex to .nav to arrange the links horizontally.", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; } .nav { background: #1a1a2e; padding: 1rem; } .nav a { color: white; text-decoration: none; padding: 8px 1rem; border-radius: 4px; } .nav a:hover { background: rgba(255,255,255,0.1); }", + "sandboxCSS": "", + "codePrefix": ".nav {\n ", "initialCode": "", "codeSuffix": "\n}", "solution": "display: flex;", @@ -21,61 +21,41 @@ "validations": [ { "type": "property_value", - "value": { - "property": "display", - "expected": "flex" - }, + "value": { "property": "display", "expected": "flex" }, "message": "Set display: flex" } ] }, { "id": "flexbox-2", - "title": "Direction & Wrap", - "description": "Control the direction and wrapping of flex items within a container.", - "task": "Add flex-direction: column and flex-wrap: wrap to .wrap.", - "previewHTML": "
1
2
3
4
5
", - "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; }", - "sandboxCSS": ".wrap { border: 2px dashed #aaa; padding: 1rem; height: 16rem; display: flex; }", - "codePrefix": ".wrap {\n ", + "title": "Gap", + "description": "The gap property adds consistent spacing between flex items without needing margins. It only creates space between items, not around the edges.", + "task": "Add gap: 1rem to space out the navigation links evenly.", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; } .nav { background: #1a1a2e; padding: 1rem; display: flex; } .nav a { color: white; text-decoration: none; padding: 8px 1rem; border-radius: 4px; background: rgba(255,255,255,0.1); }", + "sandboxCSS": "", + "codePrefix": ".nav {\n ", "initialCode": "", "codeSuffix": "\n}", - "solution": "flex-direction: column;\n flex-wrap: wrap;", + "solution": "gap: 1rem;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", - "value": { - "property": "flex-direction", - "expected": "column" - }, - "message": "Set flex-direction: column", - "options": { - "exact": true - } - }, - { - "type": "property_value", - "value": { - "property": "flex-wrap", - "expected": "wrap" - }, - "message": "Set flex-wrap: wrap", - "options": { - "exact": true - } + "value": { "property": "gap", "expected": "1rem" }, + "message": "Set gap: 1rem" } ] }, { "id": "flexbox-3", "title": "Justify Content", - "description": "Learn how to align flex items along the main axis of the flex container.", - "task": "Add justify-content: space-between to .wrap to distribute the boxes evenly.", - "previewHTML": "
1
2
3
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { background: steelblue; color: white; padding: 1rem; text-align: center; font-weight: bold; width: 3rem; height: 3rem; display: flex; align-items: center; justify-content: center; }", - "sandboxCSS": ".wrap { border: 2px dashed #aaa; padding: 1rem; display: flex; }", - "codePrefix": ".wrap {\n ", + "description": "justify-content distributes items along the main axis. Common values:
• flex-start – pack items at the start
• flex-end – pack at the end
• center – center items
• space-between – equal space between items
• space-around – equal space around items", + "task": "Push the \"Login\" button to the right by setting justify-content: space-between on the nav.", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; } .nav { background: #1a1a2e; padding: 1rem; display: flex; } .links { display: flex; gap: 8px; } .nav a { color: white; text-decoration: none; padding: 8px 1rem; border-radius: 4px; } .nav a:hover { background: rgba(255,255,255,0.1); } .login { background: steelblue; }", + "sandboxCSS": "", + "codePrefix": ".nav {\n ", "initialCode": "", "codeSuffix": "\n}", "solution": "justify-content: space-between;", @@ -83,26 +63,20 @@ "validations": [ { "type": "property_value", - "value": { - "property": "justify-content", - "expected": "space-between" - }, - "message": "Set justify-content: space-between", - "options": { - "exact": true - } + "value": { "property": "justify-content", "expected": "space-between" }, + "message": "Set justify-content: space-between" } ] }, { "id": "flexbox-4", "title": "Align Items", - "description": "Control how flex items are aligned along the cross axis of the flex container.", - "task": "Add align-items: center to .wrap to vertically center the boxes.", - "previewHTML": "
1
2
3
", - "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; display: flex; justify-content: center; } .tall { height: 6rem; } .short { height: 3rem; }", - "sandboxCSS": ".wrap { border: 2px dashed #aaa; padding: 1rem; display: flex; height: 10rem; }", - "codePrefix": ".wrap {\n ", + "description": "align-items controls alignment on the cross axis (vertical when flex-direction is row). Values include:
• stretch – stretch to fill (default)
• flex-start – align to top
• flex-end – align to bottom
• center – center vertically", + "task": "The logo and nav links have different heights. Center them vertically with align-items: center.", + "previewHTML": "
ACME
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; } .header { background: white; padding: 1rem 2rem; display: flex; justify-content: space-between; border-bottom: 1px solid #eee; } .logo { font-size: 1.5rem; font-weight: bold; color: steelblue; } nav { display: flex; gap: 1rem; } nav a { color: #333; text-decoration: none; font-size: 0.9rem; }", + "sandboxCSS": "", + "codePrefix": ".header {\n ", "initialCode": "", "codeSuffix": "\n}", "solution": "align-items: center;", @@ -110,62 +84,50 @@ "validations": [ { "type": "property_value", - "value": { - "property": "align-items", - "expected": "center" - }, - "message": "Set align-items: center", - "options": { - "exact": true - } + "value": { "property": "align-items", "expected": "center" }, + "message": "Set align-items: center" } ] }, { "id": "flexbox-5", - "title": "Flex Grow", - "description": "The flex property controls how much an item grows relative to others.", - "task": "Add flex: 2 to .box2 to make it grow twice as wide.", - "previewHTML": "
1
2
3
", - "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { color: white; padding: 1rem; margin: 8px; text-align: center; font-weight: bold; display: flex; align-items: center; justify-content: center; } .box1 { background: coral; flex: 1; } .box2 { background: mediumseagreen; } .box3 { background: gold; flex: 1; }", - "sandboxCSS": ".wrap { border: 2px dashed #aaa; padding: 1rem; display: flex; }", - "codePrefix": ".box2 {\n ", + "title": "Flex Wrap", + "description": "By default, flex items squeeze onto one line. flex-wrap: wrap allows items to flow onto multiple lines when they run out of space.", + "task": "These cards overflow the container. Add flex-wrap: wrap to allow them to wrap to new rows.", + "previewHTML": "
Card 1
Card 2
Card 3
Card 4
Card 5
Card 6
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .cards { display: flex; gap: 1rem; } .card { background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); min-width: 120px; text-align: center; }", + "sandboxCSS": "", + "codePrefix": ".cards {\n ", "initialCode": "", "codeSuffix": "\n}", - "solution": "flex: 2;", + "solution": "flex-wrap: wrap;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", - "value": { - "property": "flex", - "expected": "2" - }, - "message": "Set flex: 2" + "value": { "property": "flex-wrap", "expected": "wrap" }, + "message": "Set flex-wrap: wrap" } ] }, { "id": "flexbox-6", - "title": "Align Self", - "description": "Use align-self to override alignment for a single flex item.", - "task": "Add align-self: flex-start to .middle to move it to the top.", - "previewHTML": "
1
2
3
", - "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 ", + "title": "Flex Grow", + "description": "The flex property on items controls how they grow and shrink. flex: 1 makes an item grow to fill available space. Multiple items with flex: 1 share space equally.", + "task": "Make the search input expand to fill available space by setting flex: 1 on .search.", + "previewHTML": "
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .toolbar { display: flex; gap: 8px; padding: 1rem; background: #f5f5f5; border-radius: 8px; } .search { padding: 8px 1rem; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem; } .btn { padding: 8px 1rem; background: steelblue; color: white; border: none; border-radius: 4px; cursor: pointer; }", + "sandboxCSS": "", + "codePrefix": ".search {\n ", "initialCode": "", "codeSuffix": "\n}", - "solution": "align-self: flex-start;", + "solution": "flex: 1;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", - "value": { - "property": "align-self", - "expected": "flex-start" - }, - "message": "Set align-self: flex-start" + "value": { "property": "flex", "expected": "1" }, + "message": "Set flex: 1" } ] } diff --git a/lessons/grid.json b/lessons/grid.json index eb40907..fcc141c 100644 --- a/lessons/grid.json +++ b/lessons/grid.json @@ -8,253 +8,148 @@ { "id": "grid-1", "title": "Grid Container", - "description": "CSS Grid is a two-dimensional layout system, meaning it can handle both columns AND rows simultaneously. While Flexbox excels at one-dimensional layouts (a single row or column), Grid shines when you need precise control over both dimensions.

How it works: Set display: grid on a container. Then define your column structure with grid-template-columns. The fr unit represents a fraction of available space.

Key properties:
• grid-template-columns – Defines column sizes
• repeat(3, 1fr) – Creates 3 equal columns
• gap – Adds spacing between grid cells

.grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 1rem;\n}
", - "task": "Add display: grid, grid-template-columns: repeat(3, 1fr), and gap: 1rem to .grid.", - "previewHTML": "
1
2
3
4
5
6
", - "previewBaseCSS": "body { font-family: system-ui, -apple-system, sans-serif; padding: 1.25rem; } .item { background-color: #9b59b6; color: white; padding: 1.25rem; text-align: center; font-weight: bold; }", - "sandboxCSS": ".grid { border: 0.125rem dashed #ccc; padding: 1rem; }", - "codePrefix": "/* Create a grid with 3 equal columns and gap */\n", + "description": "CSS Grid is a two-dimensional layout system, meaning it can handle both columns AND rows simultaneously. While Flexbox excels at one-dimensional layouts (a single row or column), Grid shines when you need precise control over both dimensions—like photo galleries, dashboards, or page layouts.

How it works: Set display: grid on a container, then define columns with grid-template-columns. The fr unit represents a fraction of available space—1fr 1fr 1fr creates three equal columns.

Key properties:
• grid-template-columns – Defines column sizes
• repeat(3, 1fr) – Shorthand for 3 equal columns
• gap – Adds spacing between grid cells

.gallery {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 1rem;\n}
", + "task": "This photo gallery displays images in a single column. Add display: grid to .gallery to enable grid layout.", + "previewHTML": "
\"Mountains\"
Mountain View
\"Beach\"
Sunny Beach
\"City\"
City Skyline
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .photo { margin: 0; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .photo img { width: 100%; display: block; } .photo figcaption { padding: 8px; text-align: center; font-size: 0.9rem; color: #666; }", + "sandboxCSS": "", + "codePrefix": ".gallery {\n ", "initialCode": "", - "codeSuffix": "", - "solution": ".grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 1rem;\n}", + "codeSuffix": "\n}", + "solution": "display: grid;", "previewContainer": "preview-area", "validations": [ - { - "type": "contains", - "value": ".grid", - "message": "Use the .grid class selector", - "options": { - "caseSensitive": false - } - }, { "type": "property_value", - "value": { - "property": "display", - "expected": "grid" - }, - "message": "Set display: grid", - "options": { - "exact": true - } - }, - { - "type": "regex", - "value": "grid-template-columns:\\s*repeat\\(\\s*3\\s*,\\s*1fr\\s*\\)", - "message": "Set grid-template-columns: repeat(3, 1fr)", - "options": { - "caseSensitive": false - } - }, - { - "type": "property_value", - "value": { - "property": "gap", - "expected": "1rem" - }, - "message": "Set gap: 1rem", - "options": { - "exact": true - } + "value": { "property": "display", "expected": "grid" }, + "message": "Set display: grid" } ] }, { "id": "grid-2", - "title": "Grid Template Areas", - "description": "Use named grid areas to create visual layouts that are easy to understand.", - "task": "Add grid-template-areas to create a layout with header spanning full width, sidebar and content in middle, and footer spanning full width.", - "previewHTML": "
Header
Main Content
", - "previewBaseCSS": "body { font-family: system-ui, -apple-system, sans-serif; padding: 1.25rem; } .page > div { padding: 1.25rem; color: white; text-align: center; font-weight: bold; } .header { background-color: #e74c3c; } .sidebar { background-color: #3498db; } .content { background-color: #2ecc71; } .footer { background-color: #f39c12; }", - "sandboxCSS": ".page { border: 0.125rem dashed #ccc; padding: 1rem; height: 25rem; }", - "codePrefix": "/* Create a layout using grid-template-areas */\n.page {\n display: grid;\n grid-template-columns: 12rem 1fr;\n grid-template-rows: auto 1fr auto;\n gap: 1rem;\n \n /* Add your grid-template-areas code below */\n", + "title": "Grid Columns", + "description": "The grid-template-columns property defines how many columns your grid has and how wide each one should be. The fr unit divides available space into fractions—1fr 1fr 1fr creates three equal columns.", + "task": "Add grid-template-columns: repeat(3, 1fr) to display the photos in three equal columns.", + "previewHTML": "
\"Mountains\"
Mountain View
\"Beach\"
Sunny Beach
\"City\"
City Skyline
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .gallery { display: grid; } .photo { margin: 0; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .photo img { width: 100%; display: block; } .photo figcaption { padding: 8px; text-align: center; font-size: 0.9rem; color: #666; }", + "sandboxCSS": "", + "codePrefix": ".gallery {\n ", "initialCode": "", - "codeSuffix": "\n}\n\n/* Define which element goes in which grid area */\n.header {\n grid-area: header;\n}\n\n.sidebar {\n grid-area: sidebar;\n}\n\n.content {\n grid-area: content;\n}\n\n.footer {\n grid-area: footer;\n}", - "solution": "grid-template-areas:\n \"header header\"\n \"sidebar content\"\n \"footer footer\";", + "codeSuffix": "\n}", + "solution": "grid-template-columns: repeat(3, 1fr);", + "previewContainer": "preview-area", + "validations": [ + { + "type": "regex", + "value": "grid-template-columns:\\s*repeat\\(\\s*3\\s*,\\s*1fr\\s*\\)", + "message": "Set grid-template-columns: repeat(3, 1fr)", + "options": { "caseSensitive": false } + } + ] + }, + { + "id": "grid-3", + "title": "Grid Gap", + "description": "Just like in Flexbox, the gap property adds consistent spacing between grid cells. It creates gutters between columns and rows without adding space around the edges.", + "task": "Add gap: 1rem to create breathing room between the photos.", + "previewHTML": "
\"Mountains\"
Mountain View
\"Beach\"
Sunny Beach
\"City\"
City Skyline
\"Tree\"
Lone Tree
\"Sunset\"
Golden Sunset
\"Clouds\"
Cloudy Sky
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .gallery { display: grid; grid-template-columns: repeat(3, 1fr); } .photo { margin: 0; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .photo img { width: 100%; display: block; } .photo figcaption { padding: 8px; text-align: center; font-size: 0.9rem; color: #666; }", + "sandboxCSS": "", + "codePrefix": ".gallery {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "gap: 1rem;", + "previewContainer": "preview-area", + "validations": [ + { + "type": "property_value", + "value": { "property": "gap", "expected": "1rem" }, + "message": "Set gap: 1rem" + } + ] + }, + { + "id": "grid-4", + "title": "Spanning Columns", + "description": "Grid items can span multiple columns using grid-column: span N. This is perfect for featured content that needs more visual prominence, like a hero image in a gallery.", + "task": "Make the featured photo span two columns by adding grid-column: span 2 to .featured.", + "previewHTML": "
FEATURED%3C/svg%3E\" alt=\"Featured\">
Featured Landscape
\"Beach\"
Beach
\"City\"
City
\"Tree\"
Tree
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .gallery { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; } .photo { margin: 0; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .photo img { width: 100%; display: block; } .photo figcaption { padding: 8px; text-align: center; font-size: 0.9rem; color: #666; }", + "sandboxCSS": "", + "codePrefix": ".featured {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "grid-column: span 2;", + "previewContainer": "preview-area", + "validations": [ + { + "type": "regex", + "value": "grid-column:\\s*span\\s+2", + "message": "Set grid-column: span 2", + "options": { "caseSensitive": false } + } + ] + }, + { + "id": "grid-5", + "title": "Auto-Fit Columns", + "description": "For truly responsive grids, use auto-fit with minmax(). This automatically creates as many columns as will fit, with each being at least a minimum width but growing to fill space.", + "task": "Add grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)) to make the product grid responsive.", + "previewHTML": "

Wireless Headphones

$79

Smart Watch

$199

Portable Speaker

$49

USB-C Hub

$35

Webcam HD

$89

Keyboard

$129

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .products { display: grid; gap: 1rem; } .product { background: white; border-radius: 8px; padding: 1rem; box-shadow: 0 2px 4px rgba(0,0,0,0.1); text-align: center; } .product .img { height: 80px; border-radius: 4px; margin-bottom: 8px; } .product h3 { margin: 0 0 4px; font-size: 0.95rem; } .product p { margin: 0; color: steelblue; font-weight: bold; }", + "sandboxCSS": "", + "codePrefix": ".products {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));", + "previewContainer": "preview-area", + "validations": [ + { + "type": "regex", + "value": "grid-template-columns:\\s*repeat\\(\\s*auto-fit\\s*,\\s*minmax\\(\\s*150px\\s*,\\s*1fr\\s*\\)\\s*\\)", + "message": "Set grid-template-columns: repeat(auto-fit, minmax(150px, 1fr))", + "options": { "caseSensitive": false } + } + ] + }, + { + "id": "grid-6", + "title": "Grid Template Areas", + "description": "For complex page layouts, grid-template-areas lets you name regions and place items visually. It's like drawing your layout with ASCII art—incredibly readable and maintainable.", + "task": "Complete the dashboard layout by adding the grid-template-areas. The header should span full width, then nav and main side by side, then footer spanning full width.", + "previewHTML": "
Dashboard Header
Main Content Area
Footer
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #f5f5f5; } .dashboard { display: grid; grid-template-columns: 180px 1fr; grid-template-rows: auto 1fr auto; gap: 1rem; min-height: 300px; } .header { grid-area: header; background: #1a1a2e; color: white; padding: 1rem; border-radius: 8px; } .nav { grid-area: nav; background: steelblue; color: white; padding: 1rem; border-radius: 8px; } .main { grid-area: main; background: white; padding: 1rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .footer { grid-area: footer; background: #333; color: white; padding: 1rem; border-radius: 8px; text-align: center; }", + "sandboxCSS": "", + "codePrefix": ".dashboard {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "grid-template-areas:\n \"header header\"\n \"nav main\"\n \"footer footer\";", "previewContainer": "preview-area", "validations": [ { "type": "contains", "value": "grid-template-areas", "message": "Use the grid-template-areas property", - "options": { - "caseSensitive": false - } + "options": { "caseSensitive": false } }, { "type": "regex", - "value": "grid-template-areas:\\s*['\"]header\\s+header['\"]\\s*['\"]sidebar\\s+content['\"]\\s*['\"]footer\\s+footer['\"]", - "message": "Create areas: \"header header\" \"sidebar content\" \"footer footer\"", - "options": { - "caseSensitive": false - } - } - ] - }, - { - "id": "grid-3", - "title": "Spanning Grid Cells", - "description": "Make grid items span multiple grid cells horizontally or vertically.", - "task": "Add grid-column: span 2 and grid-row: span 2 to .featured to span 2x2 cells.", - "previewHTML": "
1
2
4
5
6
7
8
9
", - "previewBaseCSS": "body { font-family: system-ui, -apple-system, sans-serif; padding: 1.25rem; } .item { background-color: #9b59b6; color: white; padding: 1.25rem; text-align: center; font-weight: bold; } .featured { background-color: #e74c3c; }", - "sandboxCSS": ".grid { border: 0.125rem dashed #ccc; padding: 1rem; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }", - "codePrefix": "/* Make the featured item span 2x2 cells */\n", - "initialCode": "", - "codeSuffix": "", - "solution": ".featured {\n grid-column: span 2;\n grid-row: span 2;\n}", - "previewContainer": "preview-area", - "validations": [ - { - "type": "contains", - "value": ".featured", - "message": "Use the .featured class selector", - "options": { - "caseSensitive": false - } + "value": "['\"]header\\s+header['\"]", + "message": "Header should span both columns: \"header header\"", + "options": { "caseSensitive": false } }, { "type": "regex", - "value": "grid-column:\\s*span\\s+2", - "message": "Set grid-column: span 2", - "options": { - "caseSensitive": false - } + "value": "['\"]nav\\s+main['\"]", + "message": "Nav and main should be side by side: \"nav main\"", + "options": { "caseSensitive": false } }, { "type": "regex", - "value": "grid-row:\\s*span\\s+2", - "message": "Set grid-row: span 2", - "options": { - "caseSensitive": false - } - } - ] - }, - { - "id": "grid-4", - "title": "Automatic Grid Placement", - "description": "Learn how to use auto-placement and auto-fit/auto-fill for responsive grid layouts.", - "task": "Add display: grid and grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)) to .cards.", - "previewHTML": "
Card 1
Card 2
Card 3
Card 4
Card 5
Card 6
", - "previewBaseCSS": "body { font-family: system-ui, -apple-system, sans-serif; padding: 1.25rem; } .card { background-color: #3498db; color: white; padding: 1.25rem; text-align: center; font-weight: bold; height: 6rem; display: flex; align-items: center; justify-content: center; }", - "sandboxCSS": ".cards { border: 0.125rem dashed #ccc; padding: 1rem; }", - "codePrefix": "/* Create a responsive grid with auto-fit columns */\n", - "initialCode": "", - "codeSuffix": "", - "solution": ".cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));\n}", - "previewContainer": "preview-area", - "validations": [ - { - "type": "contains", - "value": ".cards", - "message": "Use the .cards class selector", - "options": { - "caseSensitive": false - } - }, - { - "type": "property_value", - "value": { - "property": "display", - "expected": "grid" - }, - "message": "Set display: grid", - "options": { - "exact": true - } - }, - { - "type": "regex", - "value": "grid-template-columns:\\s*repeat\\(\\s*auto-fit\\s*,\\s*minmax\\(\\s*10rem\\s*,\\s*1fr\\s*\\)\\s*\\)", - "message": "Set grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr))", - "options": { - "caseSensitive": false - } - } - ] - }, - { - "id": "grid-5", - "title": "Grid Alignment", - "description": "Control the alignment of grid items within their cells on both axes.", - "task": "Add justify-items: center and align-items: center to center items within their cells.", - "previewHTML": "
1
2
3
4
5
6
", - "previewBaseCSS": "body { font-family: system-ui, -apple-system, sans-serif; padding: 1.25rem; } .item { background-color: #9b59b6; color: white; padding: 1.25rem; text-align: center; font-weight: bold; width: 3rem; height: 3rem; } .tall { height: 6rem; } .wide { width: 6rem; }", - "sandboxCSS": ".cells { border: 0.125rem dashed #ccc; padding: 1rem; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; height: 25rem; }", - "codePrefix": "/* Center grid items both horizontally and vertically */\n.cells {\n /* Add alignment properties below */\n", - "initialCode": "", - "codeSuffix": "\n}", - "solution": "justify-items: center;\n align-items: center;", - "previewContainer": "preview-area", - "validations": [ - { - "type": "property_value", - "value": { - "property": "justify-items", - "expected": "center" - }, - "message": "Set justify-items: center", - "options": { - "exact": true - } - }, - { - "type": "property_value", - "value": { - "property": "align-items", - "expected": "center" - }, - "message": "Set align-items: center", - "options": { - "exact": true - } - } - ] - }, - { - "id": "grid-6", - "title": "Overlapping Grid Items", - "description": "Learn how to create overlapping layouts by using grid positioning and z-index.", - "task": "Add grid-column: 1, grid-row: 1, and z-index: 1 to .overlay to position it above the base.", - "previewHTML": "
Base Content
Overlay
", - "previewBaseCSS": "body { font-family: system-ui, -apple-system, sans-serif; padding: 1.25rem; } .stack { position: relative; height: 15rem; } .base { background-color: #3498db; color: white; padding: 1.25rem; display: flex; align-items: center; justify-content: center; font-weight: bold; } .overlay { background-color: rgba(231, 76, 60, 0.7); color: white; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.5rem; }", - "sandboxCSS": ".stack { border: 0.125rem dashed #ccc; padding: 1rem; display: grid; grid-template-columns: 1fr; grid-template-rows: 1fr; }", - "codePrefix": "/* Position the overlay to cover the entire grid */\n.base {\n grid-column: 1;\n grid-row: 1;\n}\n\n.overlay {\n /* Add your code below to position the overlay */\n", - "initialCode": "", - "codeSuffix": "\n}", - "solution": "grid-column: 1;\n grid-row: 1;\n z-index: 1;", - "previewContainer": "preview-area", - "validations": [ - { - "type": "property_value", - "value": { - "property": "grid-column", - "expected": "1" - }, - "message": "Set grid-column: 1", - "options": { - "exact": true - } - }, - { - "type": "property_value", - "value": { - "property": "grid-row", - "expected": "1" - }, - "message": "Set grid-row: 1", - "options": { - "exact": true - } - }, - { - "type": "regex", - "value": "z-index:\\s*[1-9]\\d*", - "message": "Set z-index to a positive number", - "options": { - "caseSensitive": false - } + "value": "['\"]footer\\s+footer['\"]", + "message": "Footer should span both columns: \"footer footer\"", + "options": { "caseSensitive": false } } ] }