{"id":"l-css-1-3","courseId":"css-mastery","lang":"en","kind":"lesson","summary":"Organise a stylesheet into cascade layers so that whole groups of styles (reset, components, utilities) win by layer order instead of by specificity.","html":"<p>Waypoint’s stylesheet has grown four kinds of CSS: a reset, base element styles, components like the session card, and one-job utilities like <code dir=\"ltr\">.bg-accent</code> that the content team sprinkles into the HTML. Utilities are supposed to win; that’s their whole point. But a component selector such as <code dir=\"ltr\">.schedule .session-card .badge</code> scores (0,3,0), and a utility scores (0,1,0), so the utility loses and someone writes <code dir=\"ltr\">!important</code> again.</p>\n<p>Specificity was never designed to express “this <em>group</em> of styles outranks that group”. <strong>Cascade layers</strong> were.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Declare the order, then fill the layers</h2>\n<p>A layer is a named bucket of rules. You announce the order once, at the top of your CSS:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">styles.css</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@layer</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> reset</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">base</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">components</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">utilities</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span></code></pre></div>\n<p>Then you put rules into layers, in any order, from any file:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">CSS</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@layer</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> components</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  .schedule</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> .session-card</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> .badge</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">    background</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#ede9fe</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">    color</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#5b21b6</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@layer</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> utilities</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  .bg-accent</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">background</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#fde68a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>Now <code dir=\"ltr\">.bg-accent</code> wins over the component’s background, despite its lower specificity. When the cascade reaches step 3 (layers) it sees the two declarations come from different layers and picks the later-declared one. It never gets to specificity. Inside a single layer, specificity and source order work as you already know.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 640 300\" role=\"img\" aria-labelledby=\"l-css-1-3-t1\"><defs><marker id=\"arrow-l-css-1-3-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-css-1-3-t1\">Layers stacked from lowest to highest priority: reset, base, components, utilities, and unlayered styles on top. An arrow on the right points upward labelled higher priority.</title>\n  <rect class=\"d-box\" x=\"60\" y=\"236\" width=\"400\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"80\" y=\"264\">@layer reset</text>\n  <rect class=\"d-box\" x=\"60\" y=\"184\" width=\"400\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"80\" y=\"212\">@layer base</text>\n  <rect class=\"d-box-primary\" x=\"60\" y=\"132\" width=\"400\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"80\" y=\"160\">@layer components</text>\n  <rect class=\"d-box-accent\" x=\"60\" y=\"80\" width=\"400\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"80\" y=\"108\">@layer utilities</text>\n  <rect class=\"d-box-warn\" x=\"60\" y=\"20\" width=\"400\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label-strong\" x=\"80\" y=\"48\">Unlayered styles</text>\n  <path class=\"d-arrow\" d=\"M500 276 L500 30\" marker-end=\"url(#arrow-l-css-1-3-1)\"/>\n  <text class=\"d-label\" x=\"516\" y=\"150\">higher</text>\n  <text class=\"d-label\" x=\"516\" y=\"172\">priority</text>\n</svg><figcaption>Later layers win for normal declarations; unlayered styles sit on top</figcaption></figure>\n<p>The first <code dir=\"ltr\">@layer</code> statement fixes the order. If you write <code dir=\"ltr\">@layer utilities { … }</code> before ever declaring <code dir=\"ltr\">components</code>, utilities becomes the <em>first</em> (lowest) layer. That’s why the order statement belongs at the very top of your entry stylesheet.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Unlayered styles win</h2>\n<p>Any normal rule that isn’t inside a layer goes into an implicit layer that beats all named layers. It feels backwards at first, but it’s what makes layers adoptable: you can wrap new code in layers today, and the existing unlayered CSS keeps its current behaviour.</p>\n<p>The flip side is a classic trap.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Leaving legacy CSS outside the layers</span></p>\n<p>You layer your new components, and an old <code dir=\"ltr\">h2 { color: black; }</code> from the prototype still beats them, because unlayered beats layered. Wrap legacy or third-party CSS in its own early layer, for example <code dir=\"ltr\">@layer legacy, reset, base, components, utilities;</code>, so it loses to everything you write on purpose.</p>\n</aside>\n<p>Third-party files can go straight into a layer when you import them:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">CSS</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@layer</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> vendor</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">reset</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">base</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">components</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">utilities</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@import</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> url</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"calendar-widget.css\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">layer</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">vendor</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre></div>\n<p>Note that <code dir=\"ltr\">@import</code> rules must come before other rules except <code dir=\"ltr\">@charset</code> and <code dir=\"ltr\">@layer</code> statements, which is exactly why the order statement can sit above it.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Important declarations flip the order</h2>\n<p>For <code dir=\"ltr\">!important</code> declarations, layer priority reverses: an important rule in <code dir=\"ltr\">reset</code> beats an important rule in <code dir=\"ltr\">utilities</code>, and important layered rules beat important unlayered ones. This mirrors how origins work (a user’s important rule beats yours) and gives low layers a way to protect something essential. A reset can lock <code dir=\"ltr\">[hidden] { display: none !important; }</code> and no component can accidentally show hidden content.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">Nested layers and reverting</h2>\n<p>Layers can nest: <code dir=\"ltr\">@layer components.cards { … }</code> creates a <code dir=\"ltr\">cards</code> layer inside <code dir=\"ltr\">components</code>, and its priority is resolved inside its parent. Teams use this to give each component folder its own sub-layer without affecting global order.</p>\n<p>One more keyword pairs with layers: <code dir=\"ltr\">revert-layer</code>. Setting <code dir=\"ltr\">color: revert-layer</code> rolls the property back to whatever the previous layers would have produced, as if this layer’s declaration didn’t exist. It’s handy in a utility such as <code dir=\"ltr\">.reset-color { color: revert-layer; }</code>.</p>\n<h2 id=\"sec-5\" tabindex=\"-1\">Adopting layers in an existing codebase</h2>\n<p>You don’t need a rewrite. On the Waypoint codebase, the migration took one afternoon and three steps:</p>\n<ol>\n<li>Add the order statement at the very top of the entry stylesheet, including a <code dir=\"ltr\">legacy</code> layer first.</li>\n<li>Wrap every existing file in <code dir=\"ltr\">@layer legacy { … }</code>, or import it with <code dir=\"ltr\">layer(legacy)</code>. Nothing changes visually, because the old rules still compete with each other exactly as before.</li>\n<li>As you touch a component, move its rules out of <code dir=\"ltr\">legacy</code> and into <code dir=\"ltr\">components</code>. Each move can only make your new code <em>stronger</em> relative to the old code, never weaker.</li>\n</ol>\n<p>The pay-off arrives quickly: new components stop needing heavy selectors to beat old ones, and you can delete <code dir=\"ltr\">!important</code> declarations one by one as their reason disappears.</p>\n<aside class=\"callout callout-note\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/></svg><span><span class=\"callout-kind\">Note</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Support</span></p>\n<p>Cascade layers are Baseline widely available: every current browser has supported them since early 2022. You can ship them without a fallback.</p>\n</aside>\n<h2 id=\"sec-6\" tabindex=\"-1\">Your turn</h2>\n<p>In the exercise, the Waypoint badge has a heavy component selector and a utility that should override it. Put the rules into layers so the utility wins without editing any selector or adding <code dir=\"ltr\">!important</code>. Layers solve “which group wins”; the next lesson tackles “which value”, using custom properties as the dials your components expose.</p>\n","toc":[{"id":"sec-1","text":"Declare the order, then fill the layers","level":2},{"id":"sec-2","text":"Unlayered styles win","level":2},{"id":"sec-3","text":"Important declarations flip the order","level":2},{"id":"sec-4","text":"Nested layers and reverting","level":2},{"id":"sec-5","text":"Adopting layers in an existing codebase","level":2},{"id":"sec-6","text":"Your turn","level":2}],"takeaways":["Declare your layer order once at the top with <code dir=\"ltr\">@layer reset, base, components, utilities;</code> and later layers win over earlier ones regardless of specificity.","Specificity still matters, but only between declarations inside the same layer.","Normal styles that are not in any layer beat every layered style, so wrap legacy or third-party CSS in a layer instead of leaving it loose.","<code dir=\"ltr\">!important</code> reverses layer order: an important declaration in an early layer beats an important one in a later layer."],"further":[{"title":"@layer (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@layer"},{"title":"Cascade layers (MDN guide)","url":"https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Cascade_layers"},{"title":"A complete guide to CSS cascade layers (CSS-Tricks)","url":"https://css-tricks.com/css-cascade-layers/"}],"quiz":[{"q":"<p>Which background does <code dir=\"ltr\">.badge.sold-out</code> get?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">CSS</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@layer</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> components</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">utilities</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@layer</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> utilities</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.sold-out</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">background</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">gold</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; } }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@layer</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> components</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.schedule</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> .card</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> .badge</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">background</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">lavender</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; } }</span></span></code></pre></div>\n","options":[{"text":"lavender, because its selector has higher specificity.","why":"Specificity is only compared within a layer. The two rules are in different layers, so layer order decides first."},{"text":"lavender, because the components block appears later in the file.","why":"Layer order comes from the first <code dir=\"ltr\">@layer</code> statement, not from where each block appears."},{"text":"gold, because <code dir=\"ltr\">utilities</code> is declared after <code dir=\"ltr\">components</code>.","why":"Correct. Later-declared layers win for normal declarations, before specificity is consulted."}],"answer":2},{"q":"<p>You add <code dir=\"ltr\">@layer reset, components;</code> and wrap your code, but an old rule <code dir=\"ltr\">h2 { color: black; }</code> sitting outside any layer still overrides your component headings. Why?</p>","options":[{"text":"Unlayered normal styles have higher priority than every layer.","why":"Correct. Unlayered styles act like an implicit final layer. Wrap the legacy CSS in its own early layer to demote it."},{"text":"Type selectors always beat layered rules.","why":"Selector type has nothing to do with it; any unlayered selector, even <code dir=\"ltr\">*</code>, beats layered rules for normal declarations."},{"text":"The browser ignores <code dir=\"ltr\">@layer</code> when a file mixes layered and unlayered rules.","why":"Mixing is fully supported. The browser places all unlayered rules in an implicit layer above the named ones."},{"text":"The <code dir=\"ltr\">h2</code> rule loads first, so it is locked in.","why":"Source order is the last tie-breaker, and here layering already separates the two rules."}],"answer":0},{"q":"<p>How do you put a third-party stylesheet into the lowest-priority layer?</p>","options":[{"text":"<code dir=\"ltr\">@layer vendor { @import url(picker.css); }</code>","why":"<code dir=\"ltr\">@import</code> can’t appear inside a layer block; imports must come at the top of a stylesheet."},{"text":"<code dir=\"ltr\">@import url(picker.css) layer(vendor);</code> after declaring <code dir=\"ltr\">@layer vendor, base, components;</code>","why":"Correct. The <code dir=\"ltr\">layer()</code> function on <code dir=\"ltr\">@import</code> puts the whole file into the named layer, and the order statement makes that layer first."},{"text":"<code dir=\"ltr\">&lt;link rel=&quot;stylesheet&quot; href=&quot;picker.css&quot; layer=&quot;vendor&quot;&gt;</code>","why":"There is no <code dir=\"ltr\">layer</code> attribute on <code dir=\"ltr\">&lt;link&gt;</code>; layering a file is done with <code dir=\"ltr\">@import … layer()</code>."}],"answer":1},{"q":"<p>In a <code dir=\"ltr\">components</code> layer, a declaration says <code dir=\"ltr\">color: red !important</code>. In a later <code dir=\"ltr\">utilities</code> layer, another says <code dir=\"ltr\">color: blue !important</code>. Which wins?</p>","options":[{"text":"blue, because utilities is the later layer.","why":"That’s the rule for normal declarations. For important ones the layer order flips."},{"text":"red, because important declarations reverse layer order.","why":"Correct. Reversal lets a low-level layer (such as a reset) protect a value with <code dir=\"ltr\">!important</code> that later layers can’t override."},{"text":"Whichever selector has the higher specificity.","why":"Layer order is still resolved before specificity; specificity only compares declarations from the same layer."}],"answer":1}],"exercise":{"kind":"web","mode":"dom","starter":{"html":"<section class=\"schedule\">\n  <article class=\"session-card\">\n    <h2>Container Queries in Production</h2>\n    <span class=\"badge\">Layout</span>\n    <span class=\"badge bg-accent\">Sold out</span>\n  </article>\n</section>","css":"/* reset */\n* { box-sizing: border-box; }\nh2 { margin: 0 0 0.5rem; }\n\n/* components */\n.schedule .session-card .badge {\n  display: inline-block;\n  padding: 0.2em 0.7em;\n  border-radius: 999px;\n  background: #ede9fe;\n  color: #5b21b6;\n}\n\n/* utilities: these should always win */\n.bg-accent { background: #fde68a; }\n"},"solution":{"html":"<section class=\"schedule\">\n  <article class=\"session-card\">\n    <h2>Container Queries in Production</h2>\n    <span class=\"badge\">Layout</span>\n    <span class=\"badge bg-accent\">Sold out</span>\n  </article>\n</section>","css":"@layer reset, components, utilities;\n\n@layer reset {\n  * { box-sizing: border-box; }\n  h2 { margin: 0 0 0.5rem; }\n}\n\n@layer components {\n  .schedule .session-card .badge {\n    display: inline-block;\n    padding: 0.2em 0.7em;\n    border-radius: 999px;\n    background: #ede9fe;\n    color: #5b21b6;\n  }\n}\n\n@layer utilities {\n  .bg-accent { background: #fde68a; }\n}\n"},"checks":[{"id":"utility-wins","test":"return style('.badge.bg-accent', 'background-color') === 'rgb(253, 230, 138)';"},{"id":"component-kept","test":"const b = $$('.badge').find((el) => !el.classList.contains('bg-accent')); return style(b, 'background-color') === 'rgb(237, 233, 254)' && style(b, 'color') === 'rgb(91, 33, 182)';"},{"id":"uses-layers","test":"const all = []; const walk = (rules) => { for (const r of rules) { all.push(r); if (r.cssRules) walk(r.cssRules); } }; for (const s of document.styleSheets) walk(s.cssRules); return all.some((r) => r instanceof CSSLayerBlockRule);"},{"id":"selector-unchanged","test":"const all = []; const walk = (rules) => { for (const r of rules) { all.push(r); if (r.cssRules) walk(r.cssRules); } }; for (const s of document.styleSheets) walk(s.cssRules); return all.some((r) => r.selectorText === '.schedule .session-card .badge');"},{"id":"no-important","test":"return ![...document.querySelectorAll('style')].some((s) => s.textContent.includes('!important'));"}],"title":"Let the utility win with layers","prompt":"<p>The <strong>Sold out</strong> badge should be yellow (<code dir=\"ltr\">#fde68a</code>) because it has the <code dir=\"ltr\">.bg-accent</code> utility, but the component selector <code dir=\"ltr\">.schedule .session-card .badge</code> out-scores it. Put the rules into cascade layers (<code dir=\"ltr\">reset</code>, <code dir=\"ltr\">components</code>, <code dir=\"ltr\">utilities</code>, in that order) so utilities win. Keep every selector exactly as it is and don’t use <code dir=\"ltr\">!important</code>. The plain <strong>Layout</strong> badge must keep its lavender component style.</p>\n","hints":["Start the stylesheet with one statement that declares all three layer names in priority order.","Wrap each group of rules in <code dir=\"ltr\">@layer name { … }</code>. Order inside the file doesn’t matter once the statement fixes it.","<code dir=\"ltr\">@layer reset, components, utilities;</code> then <code dir=\"ltr\">@layer components { … }</code> and <code dir=\"ltr\">@layer utilities { .bg-accent { … } }</code>."],"explanation":"<p>The <code dir=\"ltr\">@layer reset, components, utilities;</code> statement fixes the order, so any normal declaration in <code dir=\"ltr\">utilities</code> beats any declaration in <code dir=\"ltr\">components</code>, before specificity is compared. The badge selector keeps its (0,3,0) weight, which still matters inside the components layer, but it no longer competes with the utility. Leaving <code dir=\"ltr\">.bg-accent</code> unlayered would also make it win, because unlayered styles beat layered ones, but naming the layer keeps the intent visible to the next person.</p>\n","checkLabels":{"utility-wins":"The Sold out badge is yellow","component-kept":"The Layout badge keeps its lavender component style","uses-layers":"The CSS uses @layer blocks","selector-unchanged":"The component selector is unchanged","no-important":"No !important anywhere in the CSS"}},"words":719,"readMinutes":3}