diff --git a/lessons/04-typography.json b/lessons/04-typography.json index 490d3d9..9dda4b8 100644 --- a/lessons/04-typography.json +++ b/lessons/04-typography.json @@ -98,6 +98,53 @@ "message": "Set letter-spacing to 1px" } ] + }, + { + "id": "text-decoration", + "title": "Text Decoration", + "description": "The text-decoration property adds lines to text. Common values:

underline — line below text
line-through — strikethrough
none — removes decoration (useful for links)

You can also style decorations with text-decoration-color and text-decoration-style.", + "task": "Show the old price with a strikethrough. Add text-decoration: line-through.", + "previewHTML": "
$49.99$29.99
", + "previewBaseCSS": "body { font-family: system-ui; padding: 1rem; } .price-box { display: flex; gap: 1rem; align-items: center; } .old-price { color: #999; font-size: 1rem; } .new-price { color: coral; font-size: 1.5rem; font-weight: bold; }", + "sandboxCSS": "", + "codePrefix": ".old-price {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "previewContainer": "preview-area", + "solution": "text-decoration: line-through;", + "validations": [ + { + "type": "property_value", + "value": { "property": "text-decoration", "expected": "line-through" }, + "message": "Set text-decoration to line-through" + } + ] + }, + { + "id": "text-shadow", + "title": "Text Shadow", + "description": "The text-shadow property adds shadow effects to text. The syntax is:

text-shadow: x-offset y-offset blur color;

Example: text-shadow: 2px 2px 4px gray creates a soft shadow offset down and right.", + "task": "Add depth to the heading with text-shadow: 2px 2px 4px gray.", + "previewHTML": "

Welcome

", + "previewBaseCSS": "body { font-family: system-ui; padding: 2rem; background: linear-gradient(135deg, #667eea, #764ba2); } .hero-title { margin: 0; font-size: 3rem; color: white; text-align: center; }", + "sandboxCSS": "", + "codePrefix": ".hero-title {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "previewContainer": "preview-area", + "solution": "text-shadow: 2px 2px 4px gray;", + "validations": [ + { + "type": "contains", + "value": "text-shadow", + "message": "Use text-shadow property" + }, + { + "type": "contains", + "value": "2px 2px", + "message": "Set offset to 2px 2px" + } + ] } ] } diff --git a/lessons/09-gradients.json b/lessons/09-gradients.json new file mode 100644 index 0000000..808c01a --- /dev/null +++ b/lessons/09-gradients.json @@ -0,0 +1,92 @@ +{ + "$schema": "../schemas/code-crispies-module-schema.json", + "id": "css-gradients", + "title": "CSS Gradients", + "description": "Create smooth color transitions with CSS gradients.", + "difficulty": "intermediate", + "lessons": [ + { + "id": "gradients-1", + "title": "Linear Gradient", + "description": "Gradients create smooth transitions between colors. The linear-gradient() function creates a gradient along a straight line.

Basic syntax:
background: linear-gradient(color1, color2);

By default, gradients flow from top to bottom.", + "task": "Add a gradient background from coral to gold.", + "previewHTML": "

Summer Sale

Up to 50% off

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .card { padding: 2rem; border-radius: 12px; color: white; text-shadow: 0 1px 2px rgba(0,0,0,0.2); } .card h3 { margin: 0 0 8px; font-size: 1.5rem; } .card p { margin: 0; opacity: 0.9; }", + "sandboxCSS": "", + "codePrefix": ".card {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "background: linear-gradient(coral, gold);", + "previewContainer": "preview-area", + "validations": [ + { + "type": "contains", + "value": "linear-gradient", + "message": "Use linear-gradient()" + }, + { + "type": "contains", + "value": "coral", + "message": "Include coral as the first color" + }, + { + "type": "contains", + "value": "gold", + "message": "Include gold as the second color" + } + ] + }, + { + "id": "gradients-2", + "title": "Gradient Direction", + "description": "Control the gradient direction by adding an angle or keyword before the colors.

Keywords: to right, to left, to bottom right
Angles: 45deg, 90deg, 180deg

background: linear-gradient(to right, blue, purple);
", + "task": "Make the gradient flow from left to right using to right.", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 2rem; } .btn { padding: 1rem 2rem; border: none; border-radius: 8px; font-size: 1rem; font-weight: 600; color: white; cursor: pointer; }", + "sandboxCSS": "", + "codePrefix": ".btn {\n background: linear-gradient(", + "initialCode": "", + "codeSuffix": ", steelblue, mediumseagreen);\n}", + "solution": "to right", + "previewContainer": "preview-area", + "validations": [ + { + "type": "contains", + "value": "to right", + "message": "Add to right to set the direction" + } + ] + }, + { + "id": "gradients-3", + "title": "Radial Gradient", + "description": "The radial-gradient() function creates a gradient that radiates from a center point outward in a circular or elliptical pattern.

background: radial-gradient(circle, white, steelblue);

Add circle for a perfect circular gradient.", + "task": "Create a radial gradient from white to steelblue.", + "previewHTML": "
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 2rem; display: flex; justify-content: center; } .orb { width: 150px; height: 150px; border-radius: 50%; box-shadow: 0 8px 32px rgba(70, 130, 180, 0.4); }", + "sandboxCSS": "", + "codePrefix": ".orb {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "background: radial-gradient(circle, white, steelblue);", + "previewContainer": "preview-area", + "validations": [ + { + "type": "contains", + "value": "radial-gradient", + "message": "Use radial-gradient()" + }, + { + "type": "contains", + "value": "white", + "message": "Start with white" + }, + { + "type": "contains", + "value": "steelblue", + "message": "End with steelblue" + } + ] + } + ] +} diff --git a/lessons/11-filters.json b/lessons/11-filters.json new file mode 100644 index 0000000..11fdd7b --- /dev/null +++ b/lessons/11-filters.json @@ -0,0 +1,108 @@ +{ + "$schema": "../schemas/code-crispies-module-schema.json", + "id": "css-filters", + "title": "CSS Filters", + "description": "Apply visual effects like blur, brightness, and shadows with CSS filters.", + "difficulty": "intermediate", + "lessons": [ + { + "id": "filters-1", + "title": "Blur Filter", + "description": "The filter property applies visual effects to elements. The blur() function creates a Gaussian blur effect.

filter: blur(4px);

Higher values create more blur. This is great for backgrounds or creating depth.", + "task": "Blur the background image using filter: blur(4px).", + "previewHTML": "

Welcome

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; margin: 0; height: 200px; position: relative; overflow: hidden; } .bg { position: absolute; inset: 0; background: linear-gradient(45deg, coral, gold, steelblue); } .content { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; height: 100%; } .content h2 { color: white; text-shadow: 0 2px 8px rgba(0,0,0,0.3); margin: 0; }", + "sandboxCSS": "", + "codePrefix": ".bg {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "filter: blur(4px);", + "previewContainer": "preview-area", + "validations": [ + { + "type": "property_value", + "value": { "property": "filter", "expected": "blur(4px)" }, + "message": "Set filter: blur(4px)" + } + ] + }, + { + "id": "filters-2", + "title": "Grayscale Filter", + "description": "The grayscale() function removes color from an element. Use values from 0% (full color) to 100% (fully grayscale).

filter: grayscale(100%);

Great for hover effects or disabled states.", + "task": "Make the image grayscale with filter: grayscale(100%).", + "previewHTML": "
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .photo { width: 200px; height: 150px; background: linear-gradient(135deg, coral 0%, gold 50%, steelblue 100%); border-radius: 8px; }", + "sandboxCSS": "", + "codePrefix": ".photo {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "filter: grayscale(100%);", + "previewContainer": "preview-area", + "validations": [ + { + "type": "contains", + "value": "grayscale", + "message": "Use grayscale() filter" + }, + { + "type": "contains", + "value": "100%", + "message": "Set to 100% for full grayscale" + } + ] + }, + { + "id": "filters-3", + "title": "Brightness Filter", + "description": "The brightness() function adjusts how bright an element appears. Values below 100% darken, above 100% brighten.

filter: brightness(150%);
", + "task": "Brighten the card with filter: brightness(120%).", + "previewHTML": "
Featured
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; background: #1a1a2e; } .card { padding: 2rem; background: linear-gradient(135deg, #4a4a6a, #2a2a4a); border-radius: 12px; text-align: center; } .card span { color: gold; font-weight: 600; text-transform: uppercase; letter-spacing: 2px; }", + "sandboxCSS": "", + "codePrefix": ".card {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "filter: brightness(120%);", + "previewContainer": "preview-area", + "validations": [ + { + "type": "contains", + "value": "brightness", + "message": "Use brightness() filter" + }, + { + "type": "contains", + "value": "120%", + "message": "Set to 120%" + } + ] + }, + { + "id": "filters-4", + "title": "Drop Shadow", + "description": "The drop-shadow() filter creates a shadow that follows the shape of the element, including transparency. Unlike box-shadow, it works on images with transparent backgrounds.

filter: drop-shadow(2px 4px 6px black);
", + "task": "Add a drop shadow with filter: drop-shadow(4px 4px 8px gray).", + "previewHTML": "
", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 2rem; display: flex; justify-content: center; } .icon { font-size: 4rem; color: gold; }", + "sandboxCSS": "", + "codePrefix": ".icon {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "filter: drop-shadow(4px 4px 8px gray);", + "previewContainer": "preview-area", + "validations": [ + { + "type": "contains", + "value": "drop-shadow", + "message": "Use drop-shadow() filter" + }, + { + "type": "contains", + "value": "4px 4px 8px", + "message": "Set shadow offset and blur" + } + ] + } + ] +} diff --git a/lessons/12-positioning.json b/lessons/12-positioning.json new file mode 100644 index 0000000..dcfb0af --- /dev/null +++ b/lessons/12-positioning.json @@ -0,0 +1,98 @@ +{ + "$schema": "../schemas/code-crispies-module-schema.json", + "id": "css-positioning", + "title": "CSS Positioning", + "description": "Control element placement with CSS positioning properties.", + "difficulty": "intermediate", + "lessons": [ + { + "id": "position-1", + "title": "Relative Position", + "description": "The position property controls how elements are placed. relative keeps the element in normal flow but allows you to offset it with top, right, bottom, left.

.box {\n  position: relative;\n  top: 10px;\n}
", + "task": "Make the badge position relative so we can offset it.", + "previewHTML": "
NEW

Product

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .card { padding: 1rem; background: white; border: 2px solid #eee; border-radius: 8px; } .card h3 { margin: 0; } .badge { display: inline-block; padding: 2px 8px; background: coral; color: white; font-size: 0.7rem; font-weight: bold; border-radius: 4px; }", + "sandboxCSS": "", + "codePrefix": ".badge {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "position: relative;", + "previewContainer": "preview-area", + "validations": [ + { + "type": "property_value", + "value": { "property": "position", "expected": "relative" }, + "message": "Set position: relative" + } + ] + }, + { + "id": "position-2", + "title": "Offset Properties", + "description": "With position: relative, use offset properties to nudge the element from its original position:

top - pushes down from top
left - pushes right from left

Negative values move in the opposite direction.", + "task": "Move the badge up with top: -8px.", + "previewHTML": "
NEW

Product

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .card { padding: 1rem; background: white; border: 2px solid #eee; border-radius: 8px; } .card h3 { margin: 0; } .badge { display: inline-block; padding: 2px 8px; background: coral; color: white; font-size: 0.7rem; font-weight: bold; border-radius: 4px; position: relative; }", + "sandboxCSS": "", + "codePrefix": ".badge {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "top: -8px;", + "previewContainer": "preview-area", + "validations": [ + { + "type": "property_value", + "value": { "property": "top", "expected": "-8px" }, + "message": "Set top: -8px" + } + ] + }, + { + "id": "position-3", + "title": "Absolute Position", + "description": "position: absolute removes the element from normal flow and positions it relative to its nearest positioned ancestor (or the viewport if none exists).

Always set a parent to position: relative to contain absolute children.", + "task": "Position the close button absolutely.", + "previewHTML": "

Modal

Content here

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .modal { position: relative; padding: 2rem; background: white; border-radius: 12px; box-shadow: 0 4px 24px rgba(0,0,0,0.15); max-width: 250px; } .modal h3 { margin: 0 0 8px; } .modal p { margin: 0; color: #666; } .close { width: 32px; height: 32px; border: none; background: #f5f5f5; border-radius: 50%; font-size: 1.2rem; cursor: pointer; }", + "sandboxCSS": "", + "codePrefix": ".close {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "position: absolute;", + "previewContainer": "preview-area", + "validations": [ + { + "type": "property_value", + "value": { "property": "position", "expected": "absolute" }, + "message": "Set position: absolute" + } + ] + }, + { + "id": "position-4", + "title": "Placing Absolute Elements", + "description": "Combine position: absolute with offset properties to place elements precisely.

.close {\n  position: absolute;\n  top: 8px;\n  right: 8px;\n}
", + "task": "Move the close button to the top right corner with top: 8px and right: 8px.", + "previewHTML": "

Modal

Content here

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .modal { position: relative; padding: 2rem; background: white; border-radius: 12px; box-shadow: 0 4px 24px rgba(0,0,0,0.15); max-width: 250px; } .modal h3 { margin: 0 0 8px; } .modal p { margin: 0; color: #666; } .close { position: absolute; width: 32px; height: 32px; border: none; background: #f5f5f5; border-radius: 50%; font-size: 1.2rem; cursor: pointer; }", + "sandboxCSS": "", + "codePrefix": ".close {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "top: 8px;\n right: 8px;", + "previewContainer": "preview-area", + "validations": [ + { + "type": "property_value", + "value": { "property": "top", "expected": "8px" }, + "message": "Set top: 8px" + }, + { + "type": "property_value", + "value": { "property": "right", "expected": "8px" }, + "message": "Set right: 8px" + } + ] + } + ] +} diff --git a/lessons/13-pseudo-elements.json b/lessons/13-pseudo-elements.json new file mode 100644 index 0000000..02da42d --- /dev/null +++ b/lessons/13-pseudo-elements.json @@ -0,0 +1,113 @@ +{ + "$schema": "../schemas/code-crispies-module-schema.json", + "id": "css-pseudo-elements", + "title": "CSS Pseudo-elements", + "description": "Create decorative elements and style specific parts of content with pseudo-elements.", + "difficulty": "intermediate", + "lessons": [ + { + "id": "pseudo-1", + "title": "The ::before Element", + "description": "Pseudo-elements let you style specific parts of an element. ::before creates a virtual element as the first child.

It requires the content property to display anything (even if empty).

.item::before {\n  content: \"→ \";\n}
", + "task": "Add a bullet before each list item using ::before with content: \"• \".", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .list { list-style: none; padding: 0; margin: 0; } .list li { padding: 8px 0; }", + "sandboxCSS": "", + "codePrefix": ".list li::before {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "content: \"• \";", + "previewContainer": "preview-area", + "validations": [ + { + "type": "contains", + "value": "content", + "message": "Use the content property" + }, + { + "type": "contains", + "value": "•", + "message": "Add a bullet character " + } + ] + }, + { + "id": "pseudo-2", + "title": "Styling ::before", + "description": "Pseudo-elements can be styled like any element. Add color, size, margins, and more.

.item::before {\n  content: \"★\";\n  color: gold;\n  margin-right: 8px;\n}
", + "task": "Style the bullet with color: coral.", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .list { list-style: none; padding: 0; margin: 0; } .list li { padding: 8px 0; } .list li::before { content: \"• \"; }", + "sandboxCSS": "", + "codePrefix": ".list li::before {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "color: coral;", + "previewContainer": "preview-area", + "validations": [ + { + "type": "property_value", + "value": { "property": "color", "expected": "coral" }, + "message": "Set color: coral" + } + ] + }, + { + "id": "pseudo-3", + "title": "The ::after Element", + "description": "::after works like ::before but inserts content as the last child. Common uses include badges, icons, or decorative elements.

.new::after {\n  content: \" ✓\";\n  color: green;\n}
", + "task": "Add a checkmark after completed items with content: \" ✓\".", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .list { list-style: none; padding: 0; margin: 0; } .list li { padding: 8px 0; }", + "sandboxCSS": "", + "codePrefix": ".done::after {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "content: \" ✓\";", + "previewContainer": "preview-area", + "validations": [ + { + "type": "contains", + "value": "content", + "message": "Use the content property" + }, + { + "type": "contains", + "value": "✓", + "message": "Add a checkmark " + } + ] + }, + { + "id": "pseudo-4", + "title": "Decorative Lines", + "description": "Pseudo-elements with content: \"\" can create decorative shapes when combined with width, height, and background.

.title::after {\n  content: \"\";\n  display: block;\n  width: 50px;\n  height: 3px;\n  background: coral;\n}
", + "task": "Create an underline decoration with width: 40px, height: 3px, and background: steelblue.", + "previewHTML": "

About Us

We build great things.

", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } .title { margin: 0 0 1rem; } .title::after { content: \"\"; display: block; margin-top: 8px; } p { margin: 0; color: #666; }", + "sandboxCSS": "", + "codePrefix": ".title::after {\n ", + "initialCode": "", + "codeSuffix": "\n}", + "solution": "width: 40px;\n height: 3px;\n background: steelblue;", + "previewContainer": "preview-area", + "validations": [ + { + "type": "property_value", + "value": { "property": "width", "expected": "40px" }, + "message": "Set width: 40px" + }, + { + "type": "property_value", + "value": { "property": "height", "expected": "3px" }, + "message": "Set height: 3px" + }, + { + "type": "property_value", + "value": { "property": "background", "expected": "steelblue" }, + "message": "Set background: steelblue" + } + ] + } + ] +} diff --git a/lessons/30-html-tables.json b/lessons/30-html-tables.json index 9fd3c63..21f2e2a 100644 --- a/lessons/30-html-tables.json +++ b/lessons/30-html-tables.json @@ -39,6 +39,54 @@ "message": "Add 3 rows (1 header + 2 data rows)" } ] + }, + { + "id": "table-sections", + "title": "Table Sections", + "description": "Semantic table sections improve accessibility and allow for separate styling:

<thead> — header section
<tbody> — main content
<tfoot> — footer (totals, summaries)", + "task": "Wrap the header row in <thead> and data rows in <tbody>.", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } table { border-collapse: collapse; width: 100%; max-width: 350px; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } th, td { padding: 12px 16px; text-align: left; } thead { background: steelblue; color: white; } tbody tr:nth-child(even) { background: #f8f9fa; }", + "sandboxCSS": "", + "initialCode": "\n \n \n \n \n \n \n \n \n \n \n \n \n
NameScore
Alice95
Bob87
", + "solution": "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
NameScore
Alice95
Bob87
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "thead", + "message": "Add a <thead> section for the header" + }, + { + "type": "element_exists", + "value": "tbody", + "message": "Add a <tbody> section for the data" + } + ] + }, + { + "id": "table-colspan", + "title": "Spanning Columns", + "description": "The colspan attribute lets a cell span multiple columns. This is useful for headers that group multiple columns or footer totals.

<td colspan=\"2\">...</td>
", + "task": "Add a footer row that spans both columns using colspan=\"2\".", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } table { border-collapse: collapse; width: 100%; max-width: 350px; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } th, td { padding: 12px 16px; text-align: left; border-bottom: 1px solid #eee; } thead { background: steelblue; color: white; } tfoot { background: #f0f0f0; font-weight: 600; }", + "sandboxCSS": "", + "initialCode": "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
ItemPrice
Coffee$4
Cake$6
", + "solution": "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
ItemPrice
Coffee$4
Cake$6
Total: $10
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "tfoot", + "message": "Add a <tfoot> section" + }, + { + "type": "contains", + "value": "colspan", + "message": "Use colspan to span columns" + } + ] } ] } diff --git a/lessons/33-html-semantic.json b/lessons/33-html-semantic.json new file mode 100644 index 0000000..df148f8 --- /dev/null +++ b/lessons/33-html-semantic.json @@ -0,0 +1,88 @@ +{ + "$schema": "../schemas/code-crispies-module-schema.json", + "id": "html-semantic", + "title": "Semantic HTML", + "mode": "html", + "description": "Use meaningful HTML elements to structure content properly.", + "difficulty": "beginner", + "lessons": [ + { + "id": "semantic-1", + "title": "The
Element", + "description": "The <article> element represents self-contained content that could be distributed independently, like a blog post, news article, or comment.

<article>\n  <h2>Article Title</h2>\n  <p>Article content...</p>\n</article>
", + "task": "Wrap the blog post content in an <article> element.", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } article { padding: 1rem; background: #f9f9f9; border-left: 4px solid steelblue; border-radius: 4px; } h2 { margin: 0 0 8px; color: steelblue; } p { margin: 0; color: #555; line-height: 1.5; }", + "sandboxCSS": "", + "codePrefix": "", + "initialCode": "

My First Post

\n

This is a blog post about learning HTML.

", + "codeSuffix": "", + "solution": "
\n

My First Post

\n

This is a blog post about learning HTML.

\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "contains", + "value": "
", + "message": "Add an opening <article> tag" + }, + { + "type": "contains", + "value": "
", + "message": "Add a closing </article> tag" + } + ] + }, + { + "id": "semantic-2", + "title": "The
Element", + "description": "The <section> element represents a thematic grouping of content, typically with a heading. Use it to divide a page into logical sections.

<section>\n  <h2>Features</h2>\n  <p>Our product features...</p>\n</section>
", + "task": "Wrap the features content in a <section> element.", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } section { padding: 1rem; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); border-radius: 8px; } h2 { margin: 0 0 12px; } ul { margin: 0; padding-left: 1.5rem; } li { margin: 4px 0; color: #444; }", + "sandboxCSS": "", + "codePrefix": "", + "initialCode": "

Features

\n", + "codeSuffix": "", + "solution": "
\n

Features

\n
    \n
  • Fast performance
  • \n
  • Easy to use
  • \n
\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "contains", + "value": "
", + "message": "Add an opening <section> tag" + }, + { + "type": "contains", + "value": "
", + "message": "Add a closing </section> tag" + } + ] + }, + { + "id": "semantic-3", + "title": "The