{"id":"l-css-1-1","courseId":"css-mastery","lang":"en","kind":"intro","summary":"Explain the order in which the browser resolves competing declarations, so you can predict which rule wins before you open DevTools.","html":"<p>Every CSS bug that makes you say “but I <em>wrote</em> that rule” has the same root cause: more than one declaration wanted the same property on the same element, and the browser picked a different one than you did. The browser isn’t being random. It runs a fixed sorting algorithm called the <strong>cascade</strong>, and once you can run it in your head, CSS stops feeling like whack-a-mole.</p>\n<p>This course teaches CSS from the browser’s side. You’ll learn what each modern feature does, and you’ll also learn <em>why the browser behaves the way it does</em>, because that’s what lets you predict the result before you reload.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">The page you’ll build</h2>\n<p>All course long you’ll refine one real page: the schedule for <strong>Waypoint Conf 2026</strong>, a two-day front-end conference. It has a sticky header, a filter bar with track checkboxes, session cards (time, title, speaker, room, track badge), a speaker grid with photos, and a dark mode. It starts as plain, slightly broken CSS. By the end it uses layers, container queries, <code dir=\"ltr\">oklch()</code> colors, view transitions and an architecture a team of five could share.</p>\n<p>Here is the first conflict you’ll meet on it:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">HTML</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:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"session\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"keynote\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"session-title\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Keynote: The Platform Is the Framework&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\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:#0550AE;--shiki-dark:#79C0FF\">#keynote</span><span style=\"color:#116329;--shiki-dark:#7EE787\"> h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">color</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#6d28d9</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.session</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> .session-title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">color</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#1f2937</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span></code></pre></div>\n<p>The second rule comes later, so many people expect the title to be dark grey. It’s purple. To see why, you need the sorting order.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">The cascade, step by step</h2>\n<p>When several declarations target the same property on the same element, the browser sorts them by these criteria, in order, and stops at the first one that separates them:</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 300\" role=\"img\" aria-labelledby=\"l-css-1-1-t1\"><defs><marker id=\"arrow-l-css-1-1-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-1-t1\">Five stacked steps: origin and importance, inline style, cascade layers, specificity, order of appearance. An arrow runs down through them; the first step that separates two declarations decides the winner.</title>\n  <rect class=\"d-box-primary\" x=\"40\" y=\"16\" width=\"420\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label-strong\" x=\"60\" y=\"44\">1. Origin and importance</text>\n  <rect class=\"d-box\" x=\"40\" y=\"72\" width=\"420\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"60\" y=\"100\">2. Inline style attribute</text>\n  <rect class=\"d-box\" x=\"40\" y=\"128\" width=\"420\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"60\" y=\"156\">3. Cascade layers (@layer)</text>\n  <rect class=\"d-box\" x=\"40\" y=\"184\" width=\"420\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"60\" y=\"212\">4. Specificity</text>\n  <rect class=\"d-box\" x=\"40\" y=\"240\" width=\"420\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"60\" y=\"268\">5. Order of appearance</text>\n  <path class=\"d-arrow\" d=\"M500 30 L500 270\" marker-end=\"url(#arrow-l-css-1-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"520\" y=\"140\">tie? go down</text>\n  <text class=\"d-label-muted\" x=\"520\" y=\"162\">a step</text>\n</svg><figcaption>The cascade compares declarations criterion by criterion and stops at the first difference</figcaption></figure>\n<ol>\n<li><strong>Origin and importance.</strong> Declarations come from the browser’s default stylesheet (user-agent), the user (accessibility settings, extensions) or you (author). Normal author rules beat browser defaults. <code dir=\"ltr\">!important</code> flips the order, so a user’s important rule beats yours. That’s deliberate: someone who needs large text must be able to override your design.</li>\n<li><strong>Inline styles.</strong> A <code dir=\"ltr\">style=&quot;…&quot;</code> attribute beats any selector in a stylesheet. (The spec also has a step just before this one for Shadow DOM encapsulation; it only matters once you build web components.)</li>\n<li><strong>Cascade layers.</strong> Rules in later-declared <code dir=\"ltr\">@layer</code>s beat rules in earlier ones, regardless of specificity. You’ll use this in <a href=\"/lesson/css-mastery/l-css-1-3\">Cascade Layers with @layer</a>.</li>\n<li><strong>Specificity.</strong> How precisely the selector targets the element. IDs beat classes, classes beat element types.</li>\n<li><strong>Order of appearance.</strong> If everything above ties, the declaration that appears last wins.</li>\n</ol>\n<p>Back to the keynote: both rules are normal author styles, neither is inline, neither is in a layer. Step 4 decides it. <code dir=\"ltr\">#keynote h2</code> contains an ID, <code dir=\"ltr\">.session .session-title</code> contains only classes, and one ID outranks any number of classes. Purple wins, and order never gets a vote.</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>Reaching for source order first</span></p>\n<p>“Move the rule lower” is the most common CSS fix, and it only works when specificity already ties. If the rule you moved has lower specificity, it still loses. Check specificity before you move anything.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">When nobody wins</h2>\n<p>Sometimes no declaration targets a property at all. The browser still needs a value, so it defaults: <strong>inherited properties</strong> such as <code dir=\"ltr\">color</code>, <code dir=\"ltr\">font-family</code> and <code dir=\"ltr\">line-height</code> copy the parent’s value, and <strong>non-inherited properties</strong> such as <code dir=\"ltr\">border</code>, <code dir=\"ltr\">padding</code> and <code dir=\"ltr\">background-color</code> get their initial value. That’s why setting <code dir=\"ltr\">font-family</code> on <code dir=\"ltr\">body</code> styles the whole page, while setting <code dir=\"ltr\">padding</code> on <code dir=\"ltr\">body</code> pads only the body.</p>\n<aside class=\"callout callout-tip\" 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=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Let DevTools show you the cascade</span></p>\n<p>In Chrome or Firefox, inspect the element and open the Styles panel. Losing declarations are struck through, and the Computed tab shows which rule supplied each final value. Use it to confirm the prediction you made in your head.</p>\n</aside>\n<p>The next lesson takes step 4 apart: how specificity is calculated, why one ID can wreck a component library, and how <code dir=\"ltr\">:is()</code> and <code dir=\"ltr\">:where()</code> let you choose exactly how much weight a selector carries.</p>\n","toc":[{"id":"sec-1","text":"The page you’ll build","level":2},{"id":"sec-2","text":"The cascade, step by step","level":2},{"id":"sec-3","text":"When nobody wins","level":2}],"takeaways":["The cascade sorts competing declarations by origin and importance, then inline styles, then layers, then specificity, and only then by source order.","Source order is the last tie-breaker, not the first, so moving a rule down the file fixes far fewer bugs than people expect.","When no declaration targets a property, the browser falls back to inheritance for inherited properties and to the initial value for the rest.","<code dir=\"ltr\">!important</code> reverses the origin order: a user’s important rule beats an author’s important rule."],"further":[{"title":"Introduction to the CSS cascade (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascade/Introduction"},{"title":"The cascade (web.dev Learn CSS)","url":"https://web.dev/learn/css/the-cascade"}],"quiz":[{"q":"<p>Two rules in the same stylesheet target the same <code dir=\"ltr\">&lt;h2 class=&quot;session-title&quot;&gt;</code>. Which color does it 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:#0550AE;--shiki-dark:#79C0FF\">.session-title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">color</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">teal</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">color</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">crimson</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span></code></pre></div>\n","options":[{"text":"crimson, because it comes later in the file.","why":"Source order only breaks ties between declarations with equal specificity. A class beats a type selector before order is consulted."},{"text":"teal, because a class selector is more specific than a type selector.","why":"Correct. Specificity (0,1,0) beats (0,0,1), and specificity is checked before source order."},{"text":"Whichever color the browser default stylesheet sets for h2.","why":"The user-agent stylesheet is a lower-priority origin than your author styles, so its normal declarations never beat a rule you wrote."}],"answer":1},{"q":"<p>A paragraph inside <code dir=\"ltr\">.card</code> has no <code dir=\"ltr\">color</code> rule of its own, and <code dir=\"ltr\">.card</code> sets <code dir=\"ltr\">color: navy</code>. What does the paragraph get?</p>","options":[{"text":"The initial value of <code dir=\"ltr\">color</code>, which is usually black.","why":"That happens for non-inherited properties. <code dir=\"ltr\">color</code> is inherited, so the paragraph takes its parent’s computed value."},{"text":"Nothing; the property stays undefined until a rule targets it.","why":"Every element has a value for every property. When no rule targets it, the browser defaults through inheritance or the initial value."},{"text":"navy, because <code dir=\"ltr\">color</code> is an inherited property.","why":"Correct. With no cascaded value, inherited properties copy the parent’s computed value."}],"answer":2},{"q":"<p>Which step does the browser consult first when two declarations compete?</p>","options":[{"text":"Origin and importance.","why":"Correct. Whether a declaration is from the browser, the user or you, and whether it is <code dir=\"ltr\">!important</code>, is settled before layers or specificity."},{"text":"Specificity.","why":"Specificity only compares declarations that already tie on origin, importance, inline style and layer."},{"text":"Order of appearance.","why":"Order is the final tie-breaker, used only when everything else is equal."},{"text":"Cascade layers.","why":"Layers come after origin, importance and inline styles in the sort."}],"answer":0}],"exercise":null,"words":636,"readMinutes":3}