From 1073b2a486a98d57e951f7196d8c4f024bd5f0ec Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Tue, 30 Dec 2025 16:22:24 +0100 Subject: [PATCH] fix: improve lesson content with kbd tags, solutions, and animation contrast - Add tags to CSS property values and selectors in task descriptions - Add solution code to all lessons for better guidance - Improve animation contrast in transitions lesson (black/white instead of similar purples, dramatic color changes for visibility) - Simplify validation messages with kbd formatting --- lessons/01-box-model.json | 46 +++--- lessons/05-units-variables.json | 42 ++--- lessons/06-transitions-animations.json | 85 +++++------ lessons/08-responsive.json | 58 +++---- lessons/flexbox.json | 74 +++------ lessons/grid.json | 203 +++++-------------------- 6 files changed, 185 insertions(+), 323 deletions(-) diff --git a/lessons/01-box-model.json b/lessons/01-box-model.json index 1856165..231ada1 100644 --- a/lessons/01-box-model.json +++ b/lessons/01-box-model.json @@ -1,7 +1,7 @@ { "$schema": "../schemas/code-crispies-module-schema.json", "id": "box-model", - "title": "WIP: Padding, Borders, and Margins", + "title": "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": [ @@ -9,19 +9,20 @@ "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.", + "task": "Add padding: 1rem to .box to create space between its 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; }", "sandboxCSS": "", "codePrefix": ".box {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "padding: 1rem;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", "value": { "property": "padding", "expected": "1rem" }, - "message": "Set padding to '1rem'" + "message": "Set padding: 1rem" } ] }, @@ -29,19 +30,20 @@ "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": "Add border: 2px solid darkslategray to .box.", + "task": "Add border: 2px solid darkslategray to .box.", "previewHTML": "
This box needs a border
", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .box { background-color: mintcream; padding: 1rem; }", "sandboxCSS": "", "codePrefix": ".box {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "border: 2px solid darkslategray;", "previewContainer": "preview-area", "validations": [ { "type": "regex", "value": "border:\\s*2px\\s+solid\\s+darkslategray", - "message": "Set border to '2px solid darkslategray'", + "message": "Set border: 2px solid darkslategray", "options": { "caseSensitive": false } } ] @@ -50,39 +52,41 @@ "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": "Add margin: 1rem to .margin-box to create space between it and the adjacent element.", + "task": "Add margin: 1rem to .margin-box to create space between it and the adjacent element.", "previewHTML": "
This box needs margins
Adjacent element
", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .container { background-color: whitesmoke; padding: 8px; } .margin-box { background-color: plum; padding: 1rem; border: 2px solid orchid; } .neighbor { background-color: lightblue; padding: 1rem; border: 2px solid steelblue; }", "sandboxCSS": "", "codePrefix": ".margin-box {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "margin: 1rem;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", "value": { "property": "margin", "expected": "1rem" }, - "message": "Set margin to '1rem'" + "message": "Set margin: 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": "Add box-sizing: border-box to .border-box so padding and border are included in its width.", + "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": "Add box-sizing: border-box to .border-box so padding and border are included in its 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; } .content-box { box-sizing: content-box; }", "sandboxCSS": "", "codePrefix": ".border-box {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "box-sizing: border-box;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", "value": { "property": "box-sizing", "expected": "border-box" }, - "message": "Set box-sizing to 'border-box'" + "message": "Set box-sizing: border-box" } ] }, @@ -90,19 +94,20 @@ "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": "Add margin-bottom: 2rem to .first. Notice the space between paragraphs equals 2rem (not 3rem) due to margin collapse.", + "task": "Add margin-bottom: 2rem to .first. 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; }", "sandboxCSS": "", "codePrefix": ".first {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "margin-bottom: 2rem;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", "value": { "property": "margin-bottom", "expected": "2rem" }, - "message": "Set margin-bottom to '2rem'" + "message": "Set margin-bottom: 2rem" } ] }, @@ -110,19 +115,20 @@ "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": "Add margin: 1rem 2rem to .shorthand-box for 1rem top/bottom and 2rem left/right.", + "task": "Add margin: 1rem 2rem to .shorthand-box 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; } .shorthand-box { background-color: honeydew; border: 2px solid mediumseagreen; padding: 1rem; }", "sandboxCSS": "", "codePrefix": ".shorthand-box {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "margin: 1rem 2rem;", "previewContainer": "preview-area", "validations": [ { "type": "regex", "value": "margin:\\s*1rem\\s+2rem", - "message": "Set margin to '1rem 2rem'", + "message": "Set margin: 1rem 2rem", "options": { "caseSensitive": false } } ] @@ -131,39 +137,41 @@ "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": "Add padding: 1.5rem to .padding-box to add equal padding on all sides.", + "task": "Add padding: 1.5rem to .padding-box 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; } .padding-box { background-color: papayawhip; border: 2px solid orange; }", "sandboxCSS": "", "codePrefix": ".padding-box {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "padding: 1.5rem;", "previewContainer": "preview-area", "validations": [ { "type": "property_value", "value": { "property": "padding", "expected": "1.5rem" }, - "message": "Set padding to '1.5rem'" + "message": "Set padding: 1.5rem" } ] }, { "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": "Add border-bottom: 4px solid dodgerblue to .border-demo.", + "description": "For granular control, you can target specific sides with border-top, border-right, border-bottom, or border-left.", + "task": "Add border-bottom: 4px solid dodgerblue to .border-demo.", "previewHTML": "
This element needs only a bottom border
", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .border-demo { padding: 1rem; background-color: aliceblue; }", "sandboxCSS": "", "codePrefix": ".border-demo {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "border-bottom: 4px solid dodgerblue;", "previewContainer": "preview-area", "validations": [ { "type": "regex", "value": "border-bottom:\\s*4px\\s+solid\\s+dodgerblue", - "message": "Set border-bottom to '4px solid dodgerblue'", + "message": "Set border-bottom: 4px solid dodgerblue", "options": { "caseSensitive": false } } ] diff --git a/lessons/05-units-variables.json b/lessons/05-units-variables.json index 82afc57..682c4b2 100644 --- a/lessons/05-units-variables.json +++ b/lessons/05-units-variables.json @@ -1,7 +1,7 @@ { "$schema": "../schemas/code-crispies-module-schema.json", "id": "units-variables", - "title": "Units, var() and calc()", + "title": "Units & Vars", "description": "Understand the variety of CSS measurement units and how to define and use custom properties for maintainable styles.", "difficulty": "beginner", "lessons": [ @@ -9,36 +9,38 @@ "id": "units-1", "title": "Absolute vs. Relative Units", "description": "Learn the difference between px, rem, em, %, and vw/vh for flexible, responsive layouts.", - "task": "Set the width of '.unit-box' to 80% and max-width to 37.5rem.", + "task": "Set the width of .unit-box to 80% and max-width to 37.5rem.", "previewHTML": "
Resize me!
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .unit-box { background: #f5f5f5; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Set flexible sizing */\n.unit-box {", "initialCode": "", "codeSuffix": "}", + "solution": " width: 80%;\n max-width: 37.5rem;", "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": "37.5rem" }, "message": "Set max-width to '37.5rem'" } + { "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": "37.5rem" }, "message": "Set max-width to 37.5rem" } ] }, { "id": "units-2", "title": "CSS Custom Properties", "description": "Define and reuse variables (--custom properties) to centralize your theme values.", - "task": "Create a --main-color variable in :root with #6200ee and apply it as the border color on '.var-box'.", + "task": "Create a --main-color variable in :root with #6200ee and apply it as the border color on .var-box.", "previewHTML": "
Variable Box
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .var-box { padding: 1rem; border: 0.125rem solid #ddd; }", "sandboxCSS": "", "codePrefix": "/* Define and use a CSS variable */\n:root {", "initialCode": "", "codeSuffix": "}\n.var-box { }", + "solution": " --main-color: #6200ee;\n}\n.var-box {\n border-color: var(--main-color);", "previewContainer": "preview-area", "validations": [ - { "type": "contains", "value": "--main-color", "message": "Define '--main-color' in :root", "options": { "caseSensitive": false } }, - { "type": "contains", "value": "var(--main-color)", "message": "Use var(--main-color)", "options": { "caseSensitive": false } }, + { "type": "contains", "value": "--main-color", "message": "Define --main-color in :root", "options": { "caseSensitive": false } }, + { "type": "contains", "value": "var(--main-color)", "message": "Use var(--main-color)", "options": { "caseSensitive": false } }, { "type": "property_value", "value": { "property": "border", "expected": "var(--main-color)" }, @@ -50,27 +52,28 @@ { "id": "units-3", "title": "Unit Calculations (calc)", - "description": "Use the calc() function to combine different units in one expression.", - "task": "Set the width of '.calc-box' to calc(100% - 2rem) and min-height to calc(10vh + 1rem).", + "description": "Use the calc() function to combine different units in one expression.", + "task": "Set the width of .calc-box to calc(100% - 2rem) and min-height to calc(10vh + 1rem).", "previewHTML": "
Calc Demo
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .calc-box { background: #e8f5e9; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Use calc for dynamic sizing */\n.calc-box {", "initialCode": "", "codeSuffix": "}", + "solution": " width: calc(100% - 2rem);\n min-height: calc(10vh + 1rem);", "previewContainer": "preview-area", "validations": [ - { "type": "contains", "value": "calc", "message": "Use 'calc()' function", "options": { "caseSensitive": false } }, + { "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)", + "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)", + "message": "Min-height should be calc(10vh + 1rem)", "options": { "caseSensitive": false } } ] @@ -79,19 +82,20 @@ "id": "units-4", "title": "Viewport & Responsive Units", "description": "Control layouts relative to viewport size with vw, vh, and vmin/vmax units.", - "task": "Give '.viewport-box' a width of 50vw and height of 20vh.", + "task": "Give .viewport-box a width of 50vw and height of 20vh.", "previewHTML": "
Viewport Box
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .viewport-box { background: #ffe0b2; }", "sandboxCSS": "", "codePrefix": "/* Use viewport units */\n.viewport-box {", "initialCode": "", "codeSuffix": "}", + "solution": " width: 50vw;\n height: 20vh;", "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": "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" } ] } ] diff --git a/lessons/06-transitions-animations.json b/lessons/06-transitions-animations.json index bdb7a17..1d5b942 100644 --- a/lessons/06-transitions-animations.json +++ b/lessons/06-transitions-animations.json @@ -1,129 +1,128 @@ { "$schema": "../schemas/code-crispies-module-schema.json", "id": "transitions-animations", - "title": "Transitions & Animations", + "title": "Animations", "description": "Bring interactivity to your UI by smoothly transitioning properties and creating keyframe-driven animations.", "difficulty": "beginner", "lessons": [ { "id": "transitions-1", - "title": "Simple Transitions", - "description": "Learn how to apply transition to properties for smooth changes on state changes.", - "task": "Add a hover transition on a button so its background-color fades over 0.3s.", + "title": "Transitions", + "description": "Learn how to apply transition to properties for smooth changes on state changes.", + "task": "Add transition: background-color 0.3s to .btn so the color fades smoothly on hover.", "previewHTML": "", - "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .btn { background: #6200ee; color: white; padding: 0.5rem 1rem; border: none; } .btn:hover { background: #3700b3; }", + "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .btn { background: black; color: white; padding: 0.5rem 1rem; border: none; cursor: pointer; } .btn:hover { background: white; color: black; }", "sandboxCSS": "", "codePrefix": "/* Add transition */\n.btn {", "initialCode": "", "codeSuffix": "}", + "solution": " transition: background-color 0.3s;", "previewContainer": "preview-area", "validations": [ - { "type": "contains", "value": "transition", "message": "Use the 'transition' property", "options": { "caseSensitive": false } }, + { "type": "contains", "value": "transition", "message": "Use the transition property", "options": { "caseSensitive": false } }, { "type": "regex", "value": "transition:\\s*background-color\\s*0\\.3s", - "message": "Transition background-color over 0.3s", + "message": "Set transition: background-color 0.3s", "options": { "caseSensitive": false } } ] }, { "id": "transitions-2", - "title": "Transition Timing Functions", - "description": "Explore easing functions like ease, linear, ease-in, ease-out to control animation pacing.", - "task": "Modify the button to use 'ease-in-out' timing for its transition.", + "title": "Timing Funcs", + "description": "Explore easing functions like ease, linear, ease-in, ease-out to control animation pacing.", + "task": "Set transition-timing-function to ease-in-out on .btn.", "previewHTML": "", - "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .btn { background: #6200ee; color: white; padding: 0.5rem 1rem; border: none; transition: background-color 0.3s; } .btn:hover { background: #03dac6; }", + "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .btn { background: navy; color: gold; padding: 0.5rem 1rem; border: none; cursor: pointer; transition: all 0.5s; } .btn:hover { background: gold; color: navy; transform: scale(1.1); }", "sandboxCSS": "", "codePrefix": "/* Set timing function */\n.btn {", "initialCode": "", "codeSuffix": "}", + "solution": " transition-timing-function: ease-in-out;", "previewContainer": "preview-area", "validations": [ { "type": "contains", "value": "transition-timing-function", - "message": "Use 'transition-timing-function'", + "message": "Use transition-timing-function", "options": { "caseSensitive": false } }, { "type": "property_value", "value": { "property": "transition-timing-function", "expected": "ease-in-out" }, - "message": "Set timing to 'ease-in-out'" + "message": "Set timing to ease-in-out" } ] }, { "id": "transitions-3", - "title": "Keyframe Animations Basics", - "description": "Create named animations using @keyframes and apply them via the animation shorthand.", - "task": "Define a keyframe named 'bounce' that moves an element up 20px at 50% and apply it to '.ball' over 1s infinite.", + "title": "Keyframes", + "description": "Create named animations using @keyframes and apply them via the animation shorthand.", + "task": "Define a keyframe at 50% with transform: translateY(-20px) and apply animation: bounce 1s infinite to .ball.", "previewHTML": "
", - "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: #ff0266; border-radius: 50%; margin: 2rem auto; }", + "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .ball { width: 50px; height: 50px; background: crimson; border-radius: 50%; margin: 2rem auto; }", "sandboxCSS": "", "codePrefix": "/* Define keyframes and apply animation */\n@keyframes bounce {", "initialCode": "", "codeSuffix": "}\n.ball { }", + "solution": " 50% { transform: translateY(-20px); }\n}\n.ball {\n animation: bounce 1s infinite;", "previewContainer": "preview-area", "validations": [ - { "type": "contains", "value": "@keyframes bounce", "message": "Define '@keyframes bounce'", "options": { "caseSensitive": false } }, + { "type": "contains", "value": "@keyframes bounce", "message": "Define @keyframes bounce", "options": { "caseSensitive": false } }, { "type": "regex", "value": "50%.*transform: translateY\\(-20px\\)", - "message": "At 50%, move up 20px", + "message": "At 50%, use transform: translateY(-20px)", "options": { "caseSensitive": false } }, - { "type": "contains", "value": "animation", "message": "Use 'animation' property on .ball", "options": { "caseSensitive": false } }, + { "type": "contains", "value": "animation", "message": "Use animation property on .ball", "options": { "caseSensitive": false } }, { "type": "regex", "value": "animation:.*bounce.*1s.*infinite", - "message": "Apply 'bounce 1s infinite'", + "message": "Apply animation: bounce 1s infinite", "options": { "caseSensitive": false } } ] }, { "id": "transitions-4", - "title": "Animation Properties Deep Dive", - "description": "Fine-tune animations with delay, iteration-count, direction, and fill-mode.", - "task": "Animate '.box' using a 'fade' keyframe over 2s, delay 1s, run twice, and remain visible after.", - "previewHTML": "
Fade Demo
", - "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .box { width: 100px; height: 100px; background: #4caf50; margin: 2rem auto; }", + "title": "Anim Properties", + "description": "Fine-tune animations with animation-delay, animation-iteration-count, animation-direction, and animation-fill-mode.", + "task": "Apply the pulse animation to .box with animation-name: pulse, animation-duration: 2s, animation-delay: 1s, animation-iteration-count: 2, and animation-fill-mode: forwards.", + "previewHTML": "
Pulse
", + "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .box { width: 100px; height: 100px; background: black; color: white; display: flex; align-items: center; justify-content: center; margin: 2rem auto; } @keyframes pulse { 0% { background: black; color: white; transform: scale(1); } 50% { background: white; color: black; transform: scale(1.2); } 100% { background: limegreen; color: black; transform: scale(1); } }", "sandboxCSS": "", - "codePrefix": "/* Define fade and set properties */\n@keyframes fade { from { opacity: 0; } to { opacity: 1; } }\n.box {", + "codePrefix": "/* Apply animation properties */\n.box {", "initialCode": "", "codeSuffix": "}", + "solution": " animation-name: pulse;\n animation-duration: 2s;\n animation-delay: 1s;\n animation-iteration-count: 2;\n animation-fill-mode: forwards;", "previewContainer": "preview-area", "validations": [ { - "type": "contains", - "value": "animation-delay", - "message": "Use 'animation-delay' property", - "options": { "caseSensitive": false } + "type": "property_value", + "value": { "property": "animation-name", "expected": "pulse" }, + "message": "Set animation-name: pulse" }, { - "type": "contains", - "value": "animation-iteration-count", - "message": "Use 'animation-iteration-count' property", - "options": { "caseSensitive": false } + "type": "property_value", + "value": { "property": "animation-duration", "expected": "2s" }, + "message": "Set animation-duration: 2s" }, { - "type": "contains", - "value": "animation-fill-mode", - "message": "Use 'animation-fill-mode' property", - "options": { "caseSensitive": false } + "type": "property_value", + "value": { "property": "animation-delay", "expected": "1s" }, + "message": "Set animation-delay: 1s" }, - { "type": "property_value", "value": { "property": "animation-duration", "expected": "2s" }, "message": "Duration should be 2s" }, - { "type": "property_value", "value": { "property": "animation-delay", "expected": "1s" }, "message": "Delay should be 1s" }, { "type": "property_value", "value": { "property": "animation-iteration-count", "expected": "2" }, - "message": "Iteration count should be 2" + "message": "Set animation-iteration-count: 2" }, { "type": "property_value", "value": { "property": "animation-fill-mode", "expected": "forwards" }, - "message": "Fill mode should be forwards" + "message": "Set animation-fill-mode: forwards" } ] } diff --git a/lessons/08-responsive.json b/lessons/08-responsive.json index 089a03f..718fbb1 100644 --- a/lessons/08-responsive.json +++ b/lessons/08-responsive.json @@ -1,114 +1,118 @@ { "$schema": "../schemas/code-crispies-module-schema.json", "id": "responsive-design", - "title": "Responsive Design & Media Queries", + "title": "Responsive", "description": "Make your layouts adapt to different screen sizes using media queries and fluid design techniques.", "difficulty": "intermediate", "lessons": [ { "id": "responsive-1", - "title": "Introduction to Media Queries", + "title": "Media Queries", "description": "Understand the syntax and use cases for CSS media queries to apply styles conditionally based on viewport characteristics.", - "task": "Write a media query that applies when the viewport is at most 600px wide and changes the background of '.responsive-box' to lightcoral.", + "task": "Write a media query with @media (max-width: 600px) that changes .responsive-box background to lightcoral.", "previewHTML": "
Resize the window
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .responsive-box { padding: 1rem; background: lightblue; }", "sandboxCSS": "", "codePrefix": "/* Add your media query below */\n", "initialCode": "", "codeSuffix": "", + "solution": "@media (max-width: 600px) {\n .responsive-box {\n background: lightcoral;\n }\n}", "previewContainer": "preview-area", "validations": [ { "type": "regex", "value": "@media\\s*\\(max-width:\\s*600px\\)", - "message": "Use a media query for max-width: 600px", + "message": "Use @media (max-width: 600px)", "options": { "caseSensitive": false } }, { "type": "contains", "value": ".responsive-box", - "message": "Target '.responsive-box' inside the media query", + "message": "Target .responsive-box inside the media query", "options": { "caseSensitive": false } }, - { "type": "contains", "value": "background", "message": "Change the 'background' property", "options": { "caseSensitive": false } }, { "type": "property_value", "value": { "property": "background", "expected": "lightcoral" }, - "message": "Set background to 'lightcoral'", + "message": "Set background: lightcoral", "options": { "exact": false } } ] }, { "id": "responsive-2", - "title": "Fluid Typography", - "description": "Use relative units like vw to make font sizes scale with the viewport width.", - "task": "Set the font-size of '.fluid-text' to 5vw so it scales as the viewport changes.", + "title": "Fluid Type", + "description": "Use relative units like vw to make font sizes scale with the viewport width.", + "task": "Set font-size: 5vw on .fluid-text so it scales as the viewport changes.", "previewHTML": "

Fluid Typography

", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Apply fluid font sizing */\n.fluid-text {", "initialCode": "", "codeSuffix": "}", + "solution": " font-size: 5vw;", "previewContainer": "preview-area", "validations": [ - { "type": "contains", "value": "font-size", "message": "Use 'font-size' property", "options": { "caseSensitive": false } }, - { "type": "contains", "value": "vw", "message": "Use 'vw' unit for fluid sizing", "options": { "caseSensitive": false } }, - { "type": "property_value", "value": { "property": "font-size", "expected": "5vw" }, "message": "Set font-size to '5vw'" } + { "type": "property_value", "value": { "property": "font-size", "expected": "5vw" }, "message": "Set font-size: 5vw" } ] }, { "id": "responsive-3", - "title": "Flexible Grids", - "description": "Combine CSS Grid with auto-fit or auto-fill for responsive column layouts.", - "task": "Define '.grid-responsive' with grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); and a gap of 1rem.", + "title": "Flex Grids", + "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 .grid-responsive.", "previewHTML": "
1
2
3
4
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .grid-responsive > div { background: #d1c4e9; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Create a responsive grid */\n.grid-responsive {", "initialCode": "", "codeSuffix": "}", + "solution": " display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 1rem;", "previewContainer": "preview-area", "validations": [ { - "type": "contains", - "value": "grid-template-columns", - "message": "Define 'grid-template-columns'", - "options": { "caseSensitive": false } + "type": "property_value", + "value": { "property": "display", "expected": "grid" }, + "message": "Set display: grid" }, { "type": "regex", "value": "repeat\\(auto-fit,\\s*minmax\\(200px,\\s*1fr\\)\\)", - "message": "Use repeat(auto-fit, minmax(200px, 1fr))", + "message": "Use repeat(auto-fit, minmax(200px, 1fr))", "options": { "caseSensitive": false } }, - { "type": "contains", "value": "gap", "message": "Use 'gap' property", "options": { "caseSensitive": false } } + { + "type": "property_value", + "value": { "property": "gap", "expected": "1rem" }, + "message": "Set gap: 1rem" + } ] }, { "id": "responsive-4", - "title": "Mobile-First Media Queries", + "title": "Mobile-First", "description": "Adopt a mobile-first approach by writing base styles for small screens and enhancing for larger viewports.", - "task": "Write a media query for min-width 768px that sets '.sidebar' width to 250px.", + "task": "Write a media query with @media (min-width: 768px) that sets .sidebar width to 250px.", "previewHTML": "", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .sidebar { background: #c8e6c9; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Add mobile-first enhancement */\n", "initialCode": "", "codeSuffix": "", + "solution": "@media (min-width: 768px) {\n .sidebar {\n width: 250px;\n }\n}", "previewContainer": "preview-area", "validations": [ { "type": "regex", "value": "@media\\s*\\(min-width:\\s*768px\\)", - "message": "Use a media query for min-width: 768px", + "message": "Use @media (min-width: 768px)", "options": { "caseSensitive": false } }, - { "type": "contains", "value": ".sidebar", "message": "Target '.sidebar' inside media query", "options": { "caseSensitive": false } }, + { "type": "contains", "value": ".sidebar", "message": "Target .sidebar inside media query", "options": { "caseSensitive": false } }, { "type": "property_value", "value": { "property": "width", "expected": "250px" }, - "message": "Set width to '250px'", + "message": "Set width: 250px", "options": { "exact": false } } ] diff --git a/lessons/flexbox.json b/lessons/flexbox.json index 807dec0..f98606a 100644 --- a/lessons/flexbox.json +++ b/lessons/flexbox.json @@ -1,21 +1,22 @@ { "$schema": "../schemas/code-crispies-module-schema.json", "id": "flexbox", - "title": "CSS Flexbox", + "title": "Flexbox", "description": "Master the flexible box layout model for modern responsive designs", "difficulty": "intermediate", "lessons": [ { "id": "flexbox-1", - "title": "Flexbox Container Basics", + "title": "Container", "description": "Learn how to create a flex container and understand the main and cross axes.", - "task": "Add display: flex to .flex-container to create a flexbox layout.", + "task": "Add display: flex to .flex-container to create a flexbox layout.", "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": ".flex-container { border: 2px dashed #aaa; padding: 1rem; }", "codePrefix": ".flex-container {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "display: flex;", "previewContainer": "preview-area", "validations": [ { @@ -24,46 +25,31 @@ "property": "display", "expected": "flex" }, - "message": "Set display to 'flex'" + "message": "Set display: flex" } ] }, { "id": "flexbox-2", - "title": "Flex Direction and Wrap", + "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 .flex-container.", + "task": "Add flex-direction: column and flex-wrap: wrap to .flex-container.", "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": ".flex-container { border: 2px dashed #aaa; padding: 1rem; height: 16rem; display: flex; }", "codePrefix": ".flex-container {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "flex-direction: column;\n flex-wrap: wrap;", "previewContainer": "preview-area", "validations": [ - { - "type": "contains", - "value": "flex-direction", - "message": "Use the 'flex-direction' property", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "flex-wrap", - "message": "Use the 'flex-wrap' property", - "options": { - "caseSensitive": false - } - }, { "type": "property_value", "value": { "property": "flex-direction", "expected": "column" }, - "message": "Set the flex-direction to 'column'", + "message": "Set flex-direction: column", "options": { "exact": true } @@ -74,7 +60,7 @@ "property": "flex-wrap", "expected": "wrap" }, - "message": "Set flex-wrap to 'wrap'", + "message": "Set flex-wrap: wrap", "options": { "exact": true } @@ -85,30 +71,23 @@ "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 .flex-container to distribute the boxes evenly.", + "task": "Add justify-content: space-between to .flex-container 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": ".flex-container { border: 2px dashed #aaa; padding: 1rem; display: flex; }", "codePrefix": ".flex-container {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "justify-content: space-between;", "previewContainer": "preview-area", "validations": [ - { - "type": "contains", - "value": "justify-content", - "message": "Use the 'justify-content' property", - "options": { - "caseSensitive": false - } - }, { "type": "property_value", "value": { "property": "justify-content", "expected": "space-between" }, - "message": "Set justify-content to 'space-between'", + "message": "Set justify-content: space-between", "options": { "exact": true } @@ -119,30 +98,23 @@ "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 .flex-container to vertically center the boxes.", + "task": "Add align-items: center to .flex-container 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": ".flex-container { border: 2px dashed #aaa; padding: 1rem; display: flex; height: 10rem; }", "codePrefix": ".flex-container {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "align-items: center;", "previewContainer": "preview-area", "validations": [ - { - "type": "contains", - "value": "align-items", - "message": "Use the 'align-items' property", - "options": { - "caseSensitive": false - } - }, { "type": "property_value", "value": { "property": "align-items", "expected": "center" }, - "message": "Set align-items to 'center'", + "message": "Set align-items: center", "options": { "exact": true } @@ -152,14 +124,15 @@ { "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.", + "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": ".flex-container { border: 2px dashed #aaa; padding: 1rem; display: flex; }", "codePrefix": ".box2 {\n ", "initialCode": "", "codeSuffix": "\n}", + "solution": "flex: 2;", "previewContainer": "preview-area", "validations": [ { @@ -168,21 +141,22 @@ "property": "flex", "expected": "2" }, - "message": "Set flex to '2'" + "message": "Set flex: 2" } ] }, { "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.", + "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": ".flex-container { 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": [ { @@ -191,7 +165,7 @@ "property": "align-self", "expected": "flex-start" }, - "message": "Set align-self to 'flex-start'" + "message": "Set align-self: flex-start" } ] } diff --git a/lessons/grid.json b/lessons/grid.json index c06a5ea..f00a3c1 100644 --- a/lessons/grid.json +++ b/lessons/grid.json @@ -1,7 +1,7 @@ { "$schema": "../schemas/code-crispies-module-schema.json", "id": "grid", - "title": "CSS Grid", + "title": "Grid", "description": "Master the grid layout system for complex two-dimensional layouts", "difficulty": "intermediate", "lessons": [ @@ -9,43 +9,20 @@ "id": "grid-1", "title": "Grid Container Basics", "description": "Learn how to create a grid container and define basic grid structures.", - "task": "Create a grid with 3 columns of equal width and a 1rem gap between grid items.", + "task": "Create a .grid-container with display: grid, grid-template-columns: repeat(3, 1fr), and gap: 1rem.", "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-container { border: 0.125rem dashed #ccc; padding: 1rem; }", "codePrefix": "/* Create a grid with 3 equal columns and gap */\n", "initialCode": "", "codeSuffix": "", + "solution": ".grid-container {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 1rem;\n}", "previewContainer": "preview-area", "validations": [ { "type": "contains", "value": ".grid-container", - "message": "Use the '.grid-container' class selector", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "display", - "message": "Use the 'display' property", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "grid-template-columns", - "message": "Use the 'grid-template-columns' property", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "gap", - "message": "Use the 'gap' property", + "message": "Use the .grid-container class selector", "options": { "caseSensitive": false } @@ -56,7 +33,7 @@ "property": "display", "expected": "grid" }, - "message": "Set display to 'grid'", + "message": "Set display: grid", "options": { "exact": true } @@ -64,7 +41,7 @@ { "type": "regex", "value": "grid-template-columns:\\s*repeat\\(\\s*3\\s*,\\s*1fr\\s*\\)", - "message": "Set grid-template-columns to 'repeat(3, 1fr)'", + "message": "Set grid-template-columns: repeat(3, 1fr)", "options": { "caseSensitive": false } @@ -75,7 +52,7 @@ "property": "gap", "expected": "1rem" }, - "message": "Set gap to '1rem'", + "message": "Set gap: 1rem", "options": { "exact": true } @@ -86,51 +63,20 @@ "id": "grid-2", "title": "Grid Template Areas", "description": "Use named grid areas to create visual layouts that are easy to understand.", - "task": "Create a layout with a header, sidebar, main content area, and footer using grid-template-areas.", + "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; } .grid-layout > 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": ".grid-layout { border: 0.125rem dashed #ccc; padding: 1rem; height: 25rem; }", "codePrefix": "/* Create a layout using grid-template-areas */\n.grid-layout {\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", "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\";", "previewContainer": "preview-area", "validations": [ { "type": "contains", "value": "grid-template-areas", - "message": "Use the 'grid-template-areas' property", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "header", - "message": "Define a 'header' area", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "sidebar", - "message": "Define a 'sidebar' area", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "content", - "message": "Define a 'content' area", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "footer", - "message": "Define a 'footer' area", + "message": "Use the grid-template-areas property", "options": { "caseSensitive": false } @@ -138,7 +84,7 @@ { "type": "regex", "value": "grid-template-areas:\\s*['\"]header\\s+header['\"]\\s*['\"]sidebar\\s+content['\"]\\s*['\"]footer\\s+footer['\"]", - "message": "Create a layout with header spanning full width, sidebar and content in middle row, and footer spanning full width", + "message": "Create areas: \"header header\" \"sidebar content\" \"footer footer\"", "options": { "caseSensitive": false } @@ -149,35 +95,20 @@ "id": "grid-3", "title": "Spanning Grid Cells", "description": "Make grid items span multiple grid cells horizontally or vertically.", - "task": "Make the featured item span 2 columns and 2 rows using grid-column and grid-row.", + "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-container { 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 - } - }, - { - "type": "contains", - "value": "grid-column", - "message": "Use the 'grid-column' property", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "grid-row", - "message": "Use the 'grid-row' property", + "message": "Use the .featured class selector", "options": { "caseSensitive": false } @@ -185,7 +116,7 @@ { "type": "regex", "value": "grid-column:\\s*span\\s+2", - "message": "Set grid-column to 'span 2'", + "message": "Set grid-column: span 2", "options": { "caseSensitive": false } @@ -193,7 +124,7 @@ { "type": "regex", "value": "grid-row:\\s*span\\s+2", - "message": "Set grid-row to 'span 2'", + "message": "Set grid-row: span 2", "options": { "caseSensitive": false } @@ -203,60 +134,40 @@ { "id": "grid-4", "title": "Automatic Grid Placement", - "description": "Learn how to use auto-placement and auto-fit/auto-fill for responsive grid layouts.", - "task": "Create a responsive grid with auto-fit that has at least 10rem wide columns that fill the available space.", + "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 .responsive-grid.", "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": ".responsive-grid { border: 0.125rem dashed #ccc; padding: 1rem; }", "codePrefix": "/* Create a responsive grid with auto-fit columns */\n", "initialCode": "", "codeSuffix": "", + "solution": ".responsive-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));\n}", "previewContainer": "preview-area", "validations": [ { "type": "contains", "value": ".responsive-grid", - "message": "Use the '.responsive-grid' class selector", + "message": "Use the .responsive-grid class selector", "options": { "caseSensitive": false } }, { - "type": "contains", - "value": "display", - "message": "Use the 'display' property", + "type": "property_value", + "value": { + "property": "display", + "expected": "grid" + }, + "message": "Set display: grid", "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "grid-template-columns", - "message": "Use the 'grid-template-columns' property", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "minmax", - "message": "Use the 'minmax' function", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "auto-fit", - "message": "Use 'auto-fit'", - "options": { - "caseSensitive": false + "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 to 'repeat(auto-fit, minmax(10rem, 1fr))'", + "message": "Set grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr))", "options": { "caseSensitive": false } @@ -267,38 +178,23 @@ "id": "grid-5", "title": "Grid Alignment", "description": "Control the alignment of grid items within their cells on both axes.", - "task": "Center the grid items both horizontally and vertically within their grid cells.", + "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": ".align-grid { 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.align-grid {\n /* Add alignment properties below */\n", "initialCode": "", "codeSuffix": "\n}", + "solution": "justify-items: center;\n align-items: center;", "previewContainer": "preview-area", "validations": [ - { - "type": "contains", - "value": "justify-items", - "message": "Use the 'justify-items' property", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "align-items", - "message": "Use the 'align-items' property", - "options": { - "caseSensitive": false - } - }, { "type": "property_value", "value": { "property": "justify-items", "expected": "center" }, - "message": "Set justify-items to 'center'", + "message": "Set justify-items: center", "options": { "exact": true } @@ -309,7 +205,7 @@ "property": "align-items", "expected": "center" }, - "message": "Set align-items to 'center'", + "message": "Set align-items: center", "options": { "exact": true } @@ -319,47 +215,24 @@ { "id": "grid-6", "title": "Overlapping Grid Items", - "description": "Learn how to create overlapping layouts by using grid positioning and z-index.", - "task": "Position the overlay element to cover the entire grid area and use z-index to place it above other 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; } .overlap-grid { 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": ".overlap-grid { 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": "contains", - "value": "grid-column", - "message": "Use the 'grid-column' property", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "grid-row", - "message": "Use the 'grid-row' property", - "options": { - "caseSensitive": false - } - }, - { - "type": "contains", - "value": "z-index", - "message": "Use the 'z-index' property", - "options": { - "caseSensitive": false - } - }, { "type": "property_value", "value": { "property": "grid-column", "expected": "1" }, - "message": "Set grid-column to '1'", + "message": "Set grid-column: 1", "options": { "exact": true } @@ -370,7 +243,7 @@ "property": "grid-row", "expected": "1" }, - "message": "Set grid-row to '1'", + "message": "Set grid-row: 1", "options": { "exact": true } @@ -378,7 +251,7 @@ { "type": "regex", "value": "z-index:\\s*[1-9]\\d*", - "message": "Set z-index to a positive number", + "message": "Set z-index to a positive number", "options": { "caseSensitive": false }