{ "$schema": "../schemas/code-crispies-module-schema.json", "id": "selectors", "title": "Specificity", "description": "Master the art of targeting HTML elements using various CSS selectors, from basics to specificity rules.", "difficulty": "beginner", "lessons": [ { "id": "selectors-1", "title": "Element Selectors", "description": "Learn to target HTML elements by their tag name and apply styling.", "task": "Use the element selector to make all <p> tags have a dark gray color.", "previewHTML": "

This paragraph should be dark gray.

This one too!

", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Style all paragraphs */\n", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "concept": { "explanation": "Element selectors work by matching the tag name against every node in the DOM tree. The browser traverses the entire document, checking each element's type. When it finds a match, it applies the styles. This is why p selects ALL paragraph elements—the browser doesn't stop after the first match. Element selectors have the lowest specificity (0,0,0,1), making them easy to override with classes or IDs.", "diagram": "Browser DOM Traversal\n\n\n \n

← Check: is this a

? NO\n

← Check: is this a

? YES ✓ Apply styles\n

← Check: is this a

? NO\n

← Check: is this a

? YES ✓ Apply styles\n \n\n\nSpecificity: 0,0,0,1 (lowest)" }, "validations": [ { "type": "contains", "value": "p {", "message": "Use the element selector p", "options": { "caseSensitive": false } }, { "type": "contains", "value": "color", "message": "Include the color property", "options": { "caseSensitive": false } }, { "type": "property_value", "value": { "property": "color", "expected": "darkgray" }, "message": "Set color to darkgray", "options": { "exact": false } } ] }, { "id": "selectors-2", "title": "Class Selectors", "description": "Use class selectors to style elements sharing the same class name.", "task": "Apply a blueviolet text color to any element with the class 'title'.", "previewHTML": "

Hello World

Another Heading

", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Style elements with class 'title' */\n", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "concept": { "explanation": "Class selectors match elements based on their class attribute, not their tag type. This is powerful because multiple elements of different types can share the same class. The browser checks each element's class attribute for a match—it doesn't care if it's an h2, div, or span. Class selectors have medium specificity (0,0,1,0), which is 10x higher than element selectors, allowing them to override type-based styles.", "diagram": "Class Attribute Matching\n\n.title { color: blueviolet; }\n ↓\nBrowser searches for class=\"title\"\n ↓\n

✓ MATCH (class=\"title\")\n

✗ no class attribute\n
✓ MATCH (different type, same class!)\n\nSpecificity: 0,0,1,0\n(10x stronger than element selectors)" }, "validations": [ { "type": "contains", "value": ".title", "message": "Use the .title class selector", "options": { "caseSensitive": false } }, { "type": "property_value", "value": { "property": "color", "expected": "blueviolet" }, "message": "Set color to blueviolet", "options": { "exact": false } } ] }, { "id": "selectors-3", "title": "ID Selectors", "description": "Target a unique element by its ID to apply specific styling.", "task": "Make the element with id=\"description\" have orangered text.", "previewHTML": "
This is the description text.
This is another div.
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Style the element with ID 'description' */\n", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "concept": { "explanation": "ID selectors match a single unique element by its id attribute. IDs must be unique per page, so #description can only match one element maximum. ID selectors have very high specificity (0,1,0,0)—100x stronger than class selectors! This makes IDs powerful but dangerous: their high specificity makes them hard to override later, which is why many developers prefer classes for styling.", "diagram": "ID High Specificity\n\n#description { color: orangered; }\n ↓\nSearches for id=\"description\" (unique!)\n ↓\n
✓ MATCH (only this one)\n
✗ no id\n
✗ different id\n\nSpecificity Comparison:\n ID: 0,1,0,0 (100 points)\n Class: 0,0,1,0 (10 points)\n Element: 0,0,0,1 (1 point)\n\nID wins almost all conflicts!" }, "validations": [ { "type": "contains", "value": "#description", "message": "Use the #description ID selector", "options": { "caseSensitive": false } }, { "type": "property_value", "value": { "property": "color", "expected": "orangered" }, "message": "Set color to orangered", "options": { "exact": false } } ] }, { "id": "selectors-4", "title": "Combined Selectors & Specificity", "description": "Discover how combining selectors controls which rules take precedence.", "task": "Use a class and element selector together (e.g., div.note) to give a yellow background to the note box.", "previewHTML": "
Important note!
Regular div
", "previewBaseCSS": "body { font-family: sans-serif; padding: 1rem; }", "sandboxCSS": "", "codePrefix": "/* Style div with class 'note' */\n", "initialCode": "", "codeSuffix": "", "previewContainer": "preview-area", "concept": { "explanation": "Combining selectors creates AND logic—both conditions must be true. The selector div.note (no space between!) requires the element to be a div AND have class=\"note\". Combining also adds specificity: div.note = 0,0,1,1 (element + class), which beats .note alone = 0,0,1,0. This specificity cascade is how CSS resolves conflicts when multiple rules target the same element—higher specificity always wins.", "diagram": "Combined Selector AND Logic\n\ndiv.note { ... }\n ↑ ↑ no space = BOTH required\n │ └─ class=\"note\"\n └───── element type
\n\n
✓ MATCH (div AND class)\n

✗ wrong element type\n

✗ missing class\n\nSpecificity Addition:\n div.note = 0,0,1,1 (11 points)\n .note = 0,0,1,0 (10 points)\n div = 0,0,0,1 (1 point)\n\nCombining selectors = higher specificity!" }, "validations": [ { "type": "contains", "value": "div.note", "message": "Use the div.note combined selector", "options": { "caseSensitive": false } }, { "type": "property_value", "value": { "property": "background-color", "expected": "yellow" }, "message": "Set background-color to yellow", "options": { "exact": false } } ] } ] }