fix(lessons): improve validation completeness and best practices
- 00-basic-selectors: add missing validation message, fix semicolons - 05-units-variables: replace hex color with named color, use round numbers - 08-responsive: rename "Flex Grids" to "Responsive Grid" for clarity - 24-html-progress-meter: add missing high/max/optimum validations - 32-html-svg: add comprehensive attribute validations for SVG elements
This commit is contained in:
@@ -186,7 +186,7 @@
|
||||
"initialCode": "",
|
||||
"codeSuffix": "",
|
||||
"previewContainer": "preview-area",
|
||||
"solution": ".card.featured { border-color: gold; background-color: lemonchiffon }",
|
||||
"solution": ".card.featured { border-color: gold; background-color: lemonchiffon; }",
|
||||
"validations": [
|
||||
{
|
||||
"type": "regex",
|
||||
@@ -394,7 +394,7 @@
|
||||
"initialCode": "",
|
||||
"codeSuffix": "",
|
||||
"previewContainer": "preview-area",
|
||||
"solution": "p.note,\nli.important,\n#summary {\n background-color: lightyellow;\n border-left: 3px solid gold;\n padding-left: 10px\n}",
|
||||
"solution": "p.note,\nli.important,\n#summary {\n background-color: lightyellow;\n border-left: 3px solid gold;\n padding-left: 10px;\n}",
|
||||
"validations": [
|
||||
{
|
||||
"type": "contains",
|
||||
@@ -542,7 +542,7 @@
|
||||
{
|
||||
"type": "contains",
|
||||
"value": "green",
|
||||
"message": ""
|
||||
"message": "Set the color to <kbd>green</kbd>"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
{
|
||||
"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 <kbd>width</kbd> of <kbd>.box</kbd> to <kbd>80%</kbd> and <kbd>max-width</kbd> to <kbd>37.5rem</kbd>.",
|
||||
"description": "Learn the difference between px, rem, em, %, and vw/vh for flexible, responsive layouts.<br><br><pre>width: 80%; /* relative to parent */\nmax-width: 40rem; /* relative to root font */\npadding: 16px; /* fixed pixels */</pre>",
|
||||
"task": "Set the <kbd>width</kbd> of <kbd>.box</kbd> to <kbd>80%</kbd> and <kbd>max-width</kbd> to <kbd>40rem</kbd>.",
|
||||
"previewHTML": "<div class=\"box\">Resize me!</div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .box { background: #f5f5f5; padding: 1rem; }",
|
||||
"sandboxCSS": "",
|
||||
"codePrefix": "/* Set flexible sizing */\n.box {",
|
||||
"initialCode": "",
|
||||
"codeSuffix": "}",
|
||||
"solution": " width: 80%;\n max-width: 37.5rem;",
|
||||
"solution": " width: 80%;\n max-width: 40rem;",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{ "type": "contains", "value": "width", "message": "Use <kbd>width</kbd> property", "options": { "caseSensitive": false } },
|
||||
@@ -24,23 +24,23 @@
|
||||
{ "type": "contains", "value": "max-width", "message": "Use <kbd>max-width</kbd> property", "options": { "caseSensitive": false } },
|
||||
{
|
||||
"type": "property_value",
|
||||
"value": { "property": "max-width", "expected": "37.5rem" },
|
||||
"message": "Set max-width to <kbd>37.5rem</kbd>"
|
||||
"value": { "property": "max-width", "expected": "40rem" },
|
||||
"message": "Set max-width to <kbd>40rem</kbd>"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "units-2",
|
||||
"title": "CSS Custom Properties",
|
||||
"description": "Define and reuse variables (--custom properties) to centralize your theme values.<br><br><pre>:root {\n --main-color: #6200ee;\n}\n.element {\n color: var(--main-color);\n}</pre>",
|
||||
"task": "Create a <kbd>--main-color</kbd> variable in <kbd>:root</kbd> with <kbd>#6200ee</kbd> and apply it as the border color on <kbd>.themed</kbd>.",
|
||||
"description": "Define and reuse variables (--custom properties) to centralize your theme values.<br><br><pre>:root {\n --main-color: mediumpurple;\n}\n.themed {\n border-color: var(--main-color);\n}</pre>",
|
||||
"task": "Create a <kbd>--main-color</kbd> variable in <kbd>:root</kbd> with <kbd>mediumpurple</kbd> and apply it as the border color on <kbd>.themed</kbd>.",
|
||||
"previewHTML": "<div class=\"themed\">Variable Box</div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .themed { padding: 1rem; border: 0.125rem solid #ddd; }",
|
||||
"sandboxCSS": "",
|
||||
"codePrefix": "/* Define and use a CSS variable */\n:root {",
|
||||
"initialCode": "",
|
||||
"codeSuffix": "}\n.themed { }",
|
||||
"solution": " --main-color: #6200ee;\n}\n.themed {\n border-color: var(--main-color);",
|
||||
"solution": " --main-color: mediumpurple;\n}\n.themed {\n border-color: var(--main-color);",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
@@ -66,7 +66,7 @@
|
||||
{
|
||||
"id": "units-3",
|
||||
"title": "Unit Calculations (calc)",
|
||||
"description": "Use the <kbd>calc()</kbd> function to combine different units in one expression.<br><br><pre>.element {\n width: calc(100% - 2rem);\n height: calc(50vh + 1rem);\n}</pre>",
|
||||
"description": "Use the <kbd>calc()</kbd> function to combine different units in one expression.<br><br><pre>width: calc(100% - 2rem);\nmin-height: calc(10vh + 1rem);</pre>",
|
||||
"task": "Set the <kbd>width</kbd> of <kbd>.sized</kbd> to <kbd>calc(100% - 2rem)</kbd> and <kbd>min-height</kbd> to <kbd>calc(10vh + 1rem)</kbd>.",
|
||||
"previewHTML": "<div class=\"sized\">Calc Demo</div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .sized { background: #e8f5e9; padding: 1rem; }",
|
||||
@@ -95,7 +95,7 @@
|
||||
{
|
||||
"id": "units-4",
|
||||
"title": "Viewport & Responsive Units",
|
||||
"description": "Control layouts relative to viewport size with vw, vh, and vmin/vmax units.",
|
||||
"description": "Control layouts relative to viewport size with vw, vh, and vmin/vmax units.<br><br><pre>width: 50vw; /* 50% of viewport width */\nheight: 20vh; /* 20% of viewport height */</pre>",
|
||||
"task": "Give <kbd>.view</kbd> a <kbd>width</kbd> of <kbd>50vw</kbd> and <kbd>height</kbd> of <kbd>20vh</kbd>.",
|
||||
"previewHTML": "<div class=\"view\">Viewport Box</div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .view { background: #ffe0b2; }",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{
|
||||
"id": "responsive-1",
|
||||
"title": "Media Queries",
|
||||
"description": "Understand the syntax and use cases for CSS media queries to apply styles conditionally based on viewport characteristics.<br><br><pre>@media (max-width: 600px) {\n .element {\n background: lightcoral;\n }\n}</pre>",
|
||||
"description": "Understand the syntax and use cases for CSS media queries to apply styles conditionally based on viewport characteristics.<br><br><pre>@media (max-width: 600px) {\n .panel {\n background: lightcoral;\n }\n}</pre>",
|
||||
"task": "Write a media query with <kbd>@media (max-width: 600px)</kbd> that changes <kbd>.panel</kbd> background to <kbd>lightcoral</kbd>.",
|
||||
"previewHTML": "<div class=\"panel\">Resize the window</div>",
|
||||
"previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; } .panel { padding: 1rem; background: lightblue; }",
|
||||
@@ -58,7 +58,7 @@
|
||||
},
|
||||
{
|
||||
"id": "responsive-3",
|
||||
"title": "Flex Grids",
|
||||
"title": "Responsive Grid",
|
||||
"description": "Combine CSS Grid with <kbd>auto-fit</kbd> or <kbd>auto-fill</kbd> for responsive column layouts.",
|
||||
"task": "Add <kbd>display: grid</kbd>, <kbd>grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))</kbd>, and <kbd>gap: 1rem</kbd> to <kbd>.cards</kbd>.",
|
||||
"previewHTML": "<div class=\"cards\"><div>1</div><div>2</div><div>3</div><div>4</div></div>",
|
||||
|
||||
@@ -86,11 +86,31 @@
|
||||
"value": { "selector": "meter", "attr": "value", "value": "0.8" },
|
||||
"message": "Set <kbd>value=</kbd>\"0.8\" on the meter"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "meter", "attr": "min", "value": "0" },
|
||||
"message": "Set <kbd>min=</kbd>\"0\" on the meter"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "meter", "attr": "max", "value": "1" },
|
||||
"message": "Set <kbd>max=</kbd>\"1\" on the meter"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "meter", "attr": "low", "value": "0.2" },
|
||||
"message": "Set <kbd>low=</kbd>\"0.2\" to define the low threshold"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "meter", "attr": "high", "value": "0.8" },
|
||||
"message": "Set <kbd>high=</kbd>\"0.8\" to define the high threshold"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "meter", "attr": "optimum", "value": "1" },
|
||||
"message": "Set <kbd>optimum=</kbd>\"1\" to indicate the optimal value"
|
||||
},
|
||||
{
|
||||
"type": "element_exists",
|
||||
"value": "label",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f5f5f5; display: flex; justify-content: center; } svg { background: white; border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); }",
|
||||
"sandboxCSS": "",
|
||||
"initialCode": "",
|
||||
"solution": "<svg width=\"200\" height=\"200\">\n <circle cx=\"100\" cy=\"100\" r=\"50\" fill=\"#3498db\" />\n</svg>",
|
||||
"solution": "<svg width=\"200\" height=\"200\">\n <circle cx=\"100\" cy=\"100\" r=\"50\" fill=\"steelblue\" />\n</svg>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
@@ -28,6 +28,16 @@
|
||||
"value": "circle",
|
||||
"message": "Add a <kbd><circle></kbd> element inside the SVG"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "svg", "attr": "width", "value": "200" },
|
||||
"message": "Set <kbd>width=</kbd>\"200\" on the SVG"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "svg", "attr": "height", "value": "200" },
|
||||
"message": "Set <kbd>height=</kbd>\"200\" on the SVG"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "circle", "attr": "cx", "value": "100" },
|
||||
@@ -37,6 +47,11 @@
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "circle", "attr": "cy", "value": "100" },
|
||||
"message": "Set <kbd>cy=</kbd>\"100\" for the circle's vertical center"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "circle", "attr": "r", "value": "50" },
|
||||
"message": "Set <kbd>r=</kbd>\"50\" for the circle's radius"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -49,7 +64,7 @@
|
||||
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 200px; display: flex; justify-content: center; align-items: center; } svg { background: white; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); }",
|
||||
"sandboxCSS": "",
|
||||
"initialCode": "",
|
||||
"solution": "<svg width=\"200\" height=\"150\">\n <rect x=\"20\" y=\"20\" width=\"80\" height=\"60\" fill=\"#e74c3c\" />\n <line x1=\"120\" y1=\"30\" x2=\"180\" y2=\"90\" stroke=\"#2c3e50\" stroke-width=\"3\" />\n</svg>",
|
||||
"solution": "<svg width=\"200\" height=\"150\">\n <rect x=\"20\" y=\"20\" width=\"80\" height=\"60\" fill=\"tomato\" />\n <line x1=\"120\" y1=\"30\" x2=\"180\" y2=\"90\" stroke=\"slategray\" stroke-width=\"3\" />\n</svg>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
@@ -66,6 +81,61 @@
|
||||
"type": "element_exists",
|
||||
"value": "line",
|
||||
"message": "Add a <kbd><line></kbd> element"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "svg", "attr": "width", "value": "200" },
|
||||
"message": "Set <kbd>width=</kbd>\"200\" on the SVG"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "svg", "attr": "height", "value": "150" },
|
||||
"message": "Set <kbd>height=</kbd>\"150\" on the SVG"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "rect", "attr": "x", "value": "20" },
|
||||
"message": "Set <kbd>x=</kbd>\"20\" on the rect"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "rect", "attr": "y", "value": "20" },
|
||||
"message": "Set <kbd>y=</kbd>\"20\" on the rect"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "rect", "attr": "width", "value": "80" },
|
||||
"message": "Set <kbd>width=</kbd>\"80\" on the rect"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "rect", "attr": "height", "value": "60" },
|
||||
"message": "Set <kbd>height=</kbd>\"60\" on the rect"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "line", "attr": "x1", "value": "120" },
|
||||
"message": "Set <kbd>x1=</kbd>\"120\" on the line"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "line", "attr": "y1", "value": "30" },
|
||||
"message": "Set <kbd>y1=</kbd>\"30\" on the line"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "line", "attr": "x2", "value": "180" },
|
||||
"message": "Set <kbd>x2=</kbd>\"180\" on the line"
|
||||
},
|
||||
{
|
||||
"type": "attribute_value",
|
||||
"value": { "selector": "line", "attr": "y2", "value": "90" },
|
||||
"message": "Set <kbd>y2=</kbd>\"90\" on the line"
|
||||
},
|
||||
{
|
||||
"type": "contains",
|
||||
"value": "stroke",
|
||||
"message": "Add a <kbd>stroke</kbd> color to the line"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -78,7 +148,7 @@
|
||||
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); min-height: 250px; display: flex; justify-content: center; align-items: center; } svg { background: white; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.15); }",
|
||||
"sandboxCSS": "",
|
||||
"initialCode": "",
|
||||
"solution": "<svg width=\"200\" height=\"200\">\n <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"#f1c40f\" stroke=\"#e67e22\" stroke-width=\"4\" />\n <circle cx=\"70\" cy=\"80\" r=\"10\" fill=\"#2c3e50\" />\n <circle cx=\"130\" cy=\"80\" r=\"10\" fill=\"#2c3e50\" />\n <line x1=\"60\" y1=\"130\" x2=\"140\" y2=\"130\" stroke=\"#2c3e50\" stroke-width=\"4\" stroke-linecap=\"round\" />\n</svg>",
|
||||
"solution": "<svg width=\"200\" height=\"200\">\n <circle cx=\"100\" cy=\"100\" r=\"80\" fill=\"gold\" stroke=\"orange\" stroke-width=\"4\" />\n <circle cx=\"70\" cy=\"80\" r=\"10\" fill=\"darkslategray\" />\n <circle cx=\"130\" cy=\"80\" r=\"10\" fill=\"darkslategray\" />\n <line x1=\"60\" y1=\"130\" x2=\"140\" y2=\"130\" stroke=\"darkslategray\" stroke-width=\"4\" stroke-linecap=\"round\" />\n</svg>",
|
||||
"previewContainer": "preview-area",
|
||||
"validations": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user