{ "$schema": "../schemas/code-crispies-module-schema.json", "id": "css-basic-selectors", "title": "CSS Basics", "description": "Learn the fundamental building blocks of CSS: properties, values, and selectors. This module teaches you the syntax rules that every CSS declaration follows.", "difficulty": "beginner", "lessons": [ { "id": "css-properties", "title": "CSS Properties", "description": "CSS styles elements using declarations - pairs of properties and values. Every declaration follows the same pattern:

property: value;

The property is what you want to change (like color or background). The value is what you're setting it to. A colon separates them, and a semicolon ends the line.

Values come in different types:
Keywords: red, bold, center
Numbers with units: 16px, 2rem, 100%
Colors: steelblue, #ff0000", "task": "Complete the declaration by adding color: coral; to change the text color.", "previewHTML": "

This text should turn coral.

", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } .text { font-size: 1.25rem; }", "sandboxCSS": "", "codePrefix": ".text {\n ", "initialCode": "", "codeSuffix": "\n}", "previewContainer": "preview-area", "solution": "color: coral;", "validations": [ { "type": "property_value", "value": { "property": "color", "expected": "coral" }, "message": "Add color: coral;" } ] }, { "id": "multiple-properties", "title": "Multiple Properties", "description": "A rule can have multiple declarations. Each one goes on its own line, and each needs a semicolon at the end:

.box {
background: gold;
color: navy;
padding: 1rem;
}

The order usually doesn't matter - CSS applies them all. When properties conflict, the last one wins.", "task": "Add two declarations: background: lavender; and padding: 1rem;", "previewHTML": "
A styled card with background and padding.
", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } .card { border-radius: 8px; }", "sandboxCSS": "", "codePrefix": ".card {\n ", "initialCode": "", "codeSuffix": "\n}", "previewContainer": "preview-area", "solution": "background: lavender;\n padding: 1rem;", "validations": [ { "type": "property_value", "value": { "property": "background", "expected": "lavender" }, "message": "Add background: lavender;" }, { "type": "property_value", "value": { "property": "padding", "expected": "1rem" }, "message": "Add padding: 1rem;" } ] }, { "id": "type-selectors", "title": "Type Selectors", "description": "A selector tells the browser which elements to style. The simplest selector is a type selector — just the HTML tag name.

p {
color: steelblue;
}

This rule targets every <p> element on the page. Type selectors are great for setting base styles.", "task": "Style all paragraphs. Write a rule with p as the selector and set color: steelblue.", "previewHTML": "
\n

Fresh Roasted Coffee

\n

Our beans are sourced from small farms in Colombia and Ethiopia.

\n

Each batch is roasted weekly to ensure peak freshness.

\n
", "previewBaseCSS": "body { font-family: system-ui; padding: 1rem; } h2 { margin: 0 0 1rem; }", "sandboxCSS": "", "codePrefix": "", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "solution": "p {\n color: steelblue;\n}", "validations": [ { "type": "regex", "value": "p\\s*\\{", "message": "Start with p { to select paragraphs" }, { "type": "property_value", "value": { "property": "color", "expected": "steelblue" }, "message": "Set color: steelblue" } ] }, { "id": "styling-links", "title": "Styling Links", "description": "Type selectors work for any HTML element. The a selector targets all links on a page.

Links have a default blue color and underline. You can change both with CSS — use color for the text and text-decoration: none to remove the underline.", "task": "Style the navigation links. Write a rule with a as the selector and set color: coral.", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 1rem; } nav { display: flex; gap: 1.5rem; }", "sandboxCSS": "", "codePrefix": "", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "solution": "a {\n color: coral;\n}", "validations": [ { "type": "regex", "value": "a\\s*\\{", "message": "Start with a { to select links" }, { "type": "property_value", "value": { "property": "color", "expected": "coral" }, "message": "Set color: coral" } ] }, { "id": "class-selectors", "title": "Class Selectors", "description": "Type selectors style all elements of that type. But what if you want to style just some of them?

Class selectors target elements with a specific class attribute. They start with a dot:

.badge {
background: coral;
}

This styles only elements with class=\"badge\".", "task": "Style the notification badge. Write a rule with .badge as the selector and set background: tomato.", "previewHTML": "
\n

Dashboard

\n 3\n
\n

You have new notifications waiting.

", "previewBaseCSS": "body { font-family: system-ui; padding: 1rem; } header { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem; } h1 { margin: 0; font-size: 1.5rem; } .badge { color: white; padding: 0.25rem 0.5rem; border-radius: 999px; font-size: 0.875rem; } p { color: #555; margin: 0; }", "sandboxCSS": "", "codePrefix": "", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "solution": ".badge {\n background: tomato;\n}", "validations": [ { "type": "regex", "value": "\\.badge\\s*\\{", "message": "Start with .badge { (don't forget the dot!)" }, { "type": "property_value", "value": { "property": "background", "expected": "tomato" }, "message": "Set background: tomato" } ] }, { "id": "button-variants", "title": "Button Variants", "description": "Elements can have multiple classes. When you chain class selectors without spaces, you target elements that have all those classes:

.btn.primary {
background: steelblue;
}

This targets elements with both class=\"btn primary\", not just .btn or just .primary.", "task": "Style the primary button. Write a rule with .btn.primary as the selector and set background: steelblue.", "previewHTML": "
\n \n \n
", "previewBaseCSS": "body { font-family: system-ui; padding: 1rem; } .actions { display: flex; gap: 0.75rem; } .btn { padding: 0.5rem 1rem; border: none; border-radius: 6px; font-size: 1rem; cursor: pointer; background: #e0e0e0; color: #333; }", "sandboxCSS": "", "codePrefix": "", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "solution": ".btn.primary {\n background: steelblue;\n}", "validations": [ { "type": "regex", "value": "\\.btn\\.primary\\s*\\{", "message": "Use .btn.primary { (no space between classes)" }, { "type": "property_value", "value": { "property": "background", "expected": "steelblue" }, "message": "Set background: steelblue" } ] }, { "id": "specific-elements", "title": "Targeting Specific Elements", "description": "Sometimes you want a class to look different on different elements. Combine a type selector with a class selector (no space) to be more specific:

a.btn {
text-decoration: none;
}

This styles only <a> elements with the btn class, not <button> elements with that class.", "task": "Remove the underline from link buttons. Write a rule with a.btn as the selector and set text-decoration: none.", "previewHTML": "
\n \n Link Button\n
", "previewBaseCSS": "body { font-family: system-ui; padding: 1rem; } .actions { display: flex; gap: 0.75rem; align-items: center; } .btn { padding: 0.5rem 1rem; border: none; border-radius: 6px; font-size: 1rem; cursor: pointer; background: steelblue; color: white; }", "sandboxCSS": "", "codePrefix": "", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "solution": "a.btn {\n text-decoration: none;\n}", "validations": [ { "type": "regex", "value": "a\\.btn\\s*\\{", "message": "Use a.btn { (type + class, no space)" }, { "type": "property_value", "value": { "property": "text-decoration", "expected": "none" }, "message": "Set text-decoration: none" } ] }, { "id": "grouping-selectors", "title": "Grouping Selectors", "description": "When multiple elements need the same styles, list them separated by commas. This keeps your CSS clean and maintainable.

h1, h2, h3 {
color: steelblue;
}

This applies the same color to all three heading levels in one rule.", "task": "Style all headings consistently. Add color: steelblue to the grouped h1, h2, h3 selector.", "previewHTML": "

Main Title

Introduction paragraph with some text.

Section Heading

More content here.

Subsection

Final paragraph.

", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } article { max-width: 500px; } p { color: #555; line-height: 1.6; }", "sandboxCSS": "", "codePrefix": "h1, h2, h3 {\n ", "initialCode": "", "codeSuffix": "\n}", "previewContainer": "preview-area", "solution": "color: steelblue;", "validations": [ { "type": "property_value", "value": { "property": "color", "expected": "steelblue" }, "message": "Set color: steelblue" } ] }, { "id": "descendant-selectors", "title": "Descendant Selectors", "description": "Target elements inside other elements using a space between selectors. This is one of the most useful patterns in CSS.

.nav a {
color: white;
}

This styles only links inside .nav, leaving other links unchanged.", "task": "Style navigation links differently. Write a rule with .nav a as the selector and set color: white.", "previewHTML": "

Read more in our documentation.

", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; margin: 0; } .nav { background: steelblue; padding: 1rem; display: flex; gap: 1rem; border-radius: 8px; margin-bottom: 1rem; } .nav a { text-decoration: none; } p a { color: steelblue; }", "sandboxCSS": "", "codePrefix": "", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "solution": ".nav a {\n color: white;\n}", "validations": [ { "type": "regex", "value": "\\.nav\\s+a\\s*\\{", "message": "Use .nav a { (space between .nav and a)" }, { "type": "property_value", "value": { "property": "color", "expected": "white" }, "message": "Set color: white" } ] }, { "id": "nested-styling", "title": "Nested Styling", "description": "Descendant selectors let you create contextual styles. The same element can look different depending on where it appears.

For example, paragraphs in a .card might be smaller than paragraphs in an article.", "task": "Make paragraphs inside the card smaller. Write a rule with .card p as the selector and set font-size: 0.9rem.", "previewHTML": "

Article Title

This is a regular article paragraph with normal-sized text for comfortable reading.

Quick Tip

Card paragraphs should be slightly smaller to fit the compact design.

Back to regular article text here.

", "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 1rem; } article { max-width: 500px; } h2 { color: steelblue; margin-top: 0; } p { line-height: 1.6; color: #444; } .card { background: #f0f4f8; padding: 1rem; border-radius: 8px; border-left: 4px solid steelblue; } .card strong { color: steelblue; display: block; margin-bottom: 0.5rem; }", "sandboxCSS": "", "codePrefix": "", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "solution": ".card p {\n font-size: 0.9rem;\n}", "validations": [ { "type": "regex", "value": "\\.card\\s+p\\s*\\{", "message": "Use .card p { (space between .card and p)" }, { "type": "property_value", "value": { "property": "font-size", "expected": "0.9rem" }, "message": "Set font-size: 0.9rem" } ] } ] }