{"id":"l-ds-2-2","courseId":"design-systems","lang":"en","kind":"lesson","summary":"Name design tokens with a written grammar of category, concept, property, variant and state, so names stay true through dark mode, new brands and years of growth.","html":"<p>In Northwind’s old token file there was a token called <code dir=\"ltr\">--color-blue-new</code>. Two years later there was also <code dir=\"ltr\">--color-blue-new-2</code> and <code dir=\"ltr\">--color-blue-newer</code>. Nobody knew which was the brand blue. Naming is the part of a design system that looks like bikeshedding and turns out to be architecture: names are the API every designer and engineer types hundreds of times a week.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">A grammar, not a list</h2>\n<p>Good token names come from a grammar: a fixed set of parts in a fixed order. Northwind’s semantic tokens follow this one:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Text</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>--nw-{category}-{concept}-{property}-{variant}-{state}\n\n--nw-color-action-bg                 category=color concept=action property=bg\n--nw-color-action-bg-hover           … state=hover\n--nw-color-danger-text               concept=danger property=text\n--nw-color-text-subtle               concept=text variant=subtle\n--nw-space-inset-md                  category=space concept=inset variant=md\n--nw-radius-control                  category=radius concept=control</code></pre></div>\n<p>Not every token uses every part, but the parts that appear always appear in that order. That buys you two things. People can guess names they’ve never seen: if <code dir=\"ltr\">--nw-color-action-bg-hover</code> exists, <code dir=\"ltr\">--nw-color-danger-bg-hover</code> probably does too. And sorted alphabetically, related tokens cluster together in every editor and in Figma’s variable panel.</p>\n<p>The <code dir=\"ltr\">nw</code> prefix is a namespace. It stops collisions with third-party CSS and makes system tokens easy to search for, which you’ll use for adoption metrics later.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Name the purpose, not the look</h2>\n<p>At the primitive tier, naming the look is correct: <code dir=\"ltr\">--nw-blue-600</code> <em>is</em> a blue. At the semantic tier, the look is exactly what themes change, so names must describe purpose.</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Fragile name</th>\n<th>Why it breaks</th>\n<th>Durable name</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code dir=\"ltr\">--nw-color-red-bg</code></td>\n<td>Tidewater’s error color is orange</td>\n<td><code dir=\"ltr\">--nw-color-danger-bg</code></td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">--nw-color-text-light</code></td>\n<td>In dark mode it is not light</td>\n<td><code dir=\"ltr\">--nw-color-text-subtle</code></td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">--nw-color-header-bg</code></td>\n<td>Named after its first user</td>\n<td><code dir=\"ltr\">--nw-color-surface-inverse</code></td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">--nw-color-text-2</code></td>\n<td>Says nothing</td>\n<td><code dir=\"ltr\">--nw-color-text-muted</code></td>\n</tr>\n</tbody>\n</table></div><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>Naming a token after the first place it appears</span></p>\n<p>A token named <code dir=\"ltr\">header-bg</code> invites the sidebar team to either misuse it or duplicate it. Before you name a semantic token, ask what <em>role</em> the value plays, and whether another component could play the same role. If the answer is yes, name the role.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">Scales: numbers or sizes</h2>\n<p>Primitive scales need a naming scheme too. There are two common choices:</p>\n<ul>\n<li><strong>Numeric</strong>, like <code dir=\"ltr\">100</code> to <code dir=\"ltr\">900</code> for color lightness or <code dir=\"ltr\">1</code> to <code dir=\"ltr\">12</code> for spacing steps. You can insert <code dir=\"ltr\">150</code> later without renaming anything. The numbers carry little meaning on their own.</li>\n<li><strong>T-shirt sizes</strong>, like <code dir=\"ltr\">xs</code>, <code dir=\"ltr\">sm</code>, <code dir=\"ltr\">md</code>, <code dir=\"ltr\">lg</code>, <code dir=\"ltr\">xl</code>. Easy to read in code review. When designers need a step between <code dir=\"ltr\">sm</code> and <code dir=\"ltr\">md</code>, you are stuck with names like <code dir=\"ltr\">sm-plus</code>.</li>\n</ul>\n<p>Northwind uses numeric scales for primitives and t-shirt sizes for the handful of semantic spacing tokens (<code dir=\"ltr\">--nw-space-inset-sm</code>, <code dir=\"ltr\">-md</code>, <code dir=\"ltr\">-lg</code>), because designers think in sizes and that set rarely grows. Pick one scheme per scale and stick to it. Mixing them inside one scale, such as <code dir=\"ltr\">--nw-space-2</code>, <code dir=\"ltr\">--nw-space-md</code> and <code dir=\"ltr\">--nw-space-24px</code>, is the fastest way to make a scale nobody trusts, because people can no longer tell which values are siblings.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">Enforce the grammar</h2>\n<p>A grammar that lives only in a wiki page lasts until the first busy contributor. Turn it into a check that runs in CI:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"1\" aria-label=\"Run this example\"><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=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><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\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> grammar</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> /</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">^</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">--nw-(color</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">space</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">radius</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">font</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">shadow</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">motion)(-</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">[a-z]</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">)</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(-</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">[0-9]</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">)</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?$</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> banned</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> /-(red</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">blue</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">green</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">light</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">dark</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">new</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">old</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">[0-9]</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">px)(-</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">|$</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">)/</span><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\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> proposed</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  '--nw-color-action-bg-hover'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  '--nw-color-text-light'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  '--nw-color-blue-new'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  '--nw-space-inset-md'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  '--nwColorDangerText'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  '--nw-color-danger-text'</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>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> name</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> of</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> proposed) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> ok</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> grammar.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">test</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(name) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x26;&#x26;</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> !</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">banned.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">test</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(name);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(ok </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'ok    '</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> :</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'reject'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, name);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p>The <code dir=\"ltr\">banned</code> list catches the habits that break first: color words, “light” and “dark”, “new” and “old”, and raw pixel values. Run it against every token file in pull requests, and print the rule that failed so contributors learn the grammar from the error instead of from a wiki page. It’s a blunt tool, and it will occasionally reject a good name. That’s fine; a contributor who hits it asks the core team, and the conversation is the point.</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>Names in token files versus CSS</span></p>\n<p>In a DTCG JSON file, the name parts become nested groups: <code dir=\"ltr\">color</code> → <code dir=\"ltr\">action</code> → <code dir=\"ltr\">bg</code> → <code dir=\"ltr\">hover</code>. The pipeline joins them with dashes and adds the prefix when it writes CSS. The format reserves <code dir=\"ltr\">.</code> for references like <code dir=\"ltr\">{color.action.bg}</code>, which is why token and group names can’t contain periods. You’ll see this in two lessons.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">Renaming without breaking people</h2>\n<p>You will get some names wrong; everybody does. When you rename a token, keep the old name as an alias of the new one for at least one release, mark it deprecated, and announce it in the changelog. Section 4 covers the full deprecation path. In the exercise below, you review a batch of proposed names the way the Northwind core team does every week. Next, you put the semantic tier to work: one set of components, several themes.</p>\n","toc":[{"id":"sec-1","text":"A grammar, not a list","level":2},{"id":"sec-2","text":"Name the purpose, not the look","level":2},{"id":"sec-3","text":"Scales: numbers or sizes","level":2},{"id":"sec-4","text":"Enforce the grammar","level":2},{"id":"sec-5","text":"Renaming without breaking people","level":2}],"takeaways":["Semantic token names describe purpose (<code dir=\"ltr\">danger</code>, <code dir=\"ltr\">subtle</code>), never appearance (<code dir=\"ltr\">red</code>, <code dir=\"ltr\">light</code>), because appearance changes with themes.","A fixed order of name parts, such as category-concept-property-variant-state, makes tokens predictable to guess and easy to sort.","Numeric scales like 100 to 900 leave room to insert values later; t-shirt sizes read better but fill up.","Write the naming grammar down and enforce it with a lint rule, or every contributor invents their own."],"further":[{"title":"Custom properties (--*) (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/--*"},{"title":"Design tokens (Material Design 3)","url":"https://m3.material.io/foundations/design-tokens"},{"title":"Design Tokens Format Module 2025.10 (source on GitHub)","url":"https://github.com/design-tokens/community-group"}],"quiz":[{"q":"<p>Which semantic token name will still be accurate after Northwind adds dark mode?</p>","options":[{"text":"<code dir=\"ltr\">--nw-color-text-subtle</code>","why":"Correct. “Subtle” describes the role (lower emphasis text), which holds in light and dark themes alike."},{"text":"<code dir=\"ltr\">--nw-color-text-light</code>","why":"In dark mode the subtle text is a mid-grey on a dark background, so “light” either lies or gets confused with the theme name."},{"text":"<code dir=\"ltr\">--nw-color-text-gray-500</code>","why":"Embedding a primitive in a semantic name means the name breaks the moment the theme maps it to a different grey."},{"text":"<code dir=\"ltr\">--nw-color-text-2</code>","why":"A bare number says nothing about purpose, so nobody can pick it correctly without opening the docs."}],"answer":0},{"q":"<p>The dispatch team created <code dir=\"ltr\">--nw-color-header-bg</code> for the top bar. Now the sidebar needs the same color. What’s the best move?</p>","options":[{"text":"Use <code dir=\"ltr\">--nw-color-header-bg</code> in the sidebar too.","why":"The sidebar now depends on a token named for the header; when the header changes, the sidebar changes by accident."},{"text":"Copy the value into a new <code dir=\"ltr\">--nw-color-sidebar-bg</code>.","why":"Two tokens for one decision drift apart, which is how the 64 greys happened."},{"text":"Rename it to a purpose-based token like <code dir=\"ltr\">--nw-color-surface-inverse</code> and alias the old name to it during a deprecation period.","why":"Correct. The name now describes the shared role, and the alias gives consumers time to migrate."}],"answer":2},{"q":"<p>Your spacing scale is <code dir=\"ltr\">--nw-space-sm</code>, <code dir=\"ltr\">-md</code>, <code dir=\"ltr\">-lg</code>. Designers now need a value between <code dir=\"ltr\">sm</code> and <code dir=\"ltr\">md</code>. What does this show?</p>","options":[{"text":"T-shirt sizes should never be used for tokens.","why":"They’re readable and work well for small, stable scales; the issue is only that they’re hard to extend in the middle."},{"text":"The new value should be a one-off pixel value in that component.","why":"A hard-coded value bypasses the scale and starts the drift again."},{"text":"Spacing tokens should be component tokens instead.","why":"Spacing is shared across components, so it belongs in the shared scale."},{"text":"Named steps fill up; a numeric scale like 100, 200, 300 leaves room for 150 later.","why":"Correct. Numeric scales trade some readability for room to grow, which is why many systems use them for primitives."}],"answer":3},{"q":"<p>Why does the DTCG format forbid <code dir=\"ltr\">.</code> inside token names?</p>","options":[{"text":"Periods are not valid in JSON keys.","why":"JSON keys can contain any string, including periods."},{"text":"The period separates path segments in references like <code dir=\"ltr\">{color.action.bg}</code>.","why":"Correct. A period inside a name would make the reference path ambiguous."},{"text":"CSS custom properties cannot contain periods.","why":"The rule comes from the token format’s reference syntax, not from CSS."}],"answer":1}],"exercise":{"kind":"choice","multiple":true,"options":["success-border","orange-badge","text-inverse","modal-shadow-v2","focus-ring","dark-surface"],"correct":["success-border","text-inverse","focus-ring"],"title":"Review this week's token proposals","prompt":"<p>Six semantic token names were proposed to Northwind UI this week. The system must support light and dark themes and two brands (Northwind and Tidewater), and follows the grammar <code dir=\"ltr\">--nw-{category}-{concept}-{property}-{variant}-{state}</code>. Select every name you would <strong>approve as written</strong>.</p>\n","hints":["For each name, imagine it in dark mode and in the Tidewater brand. Does it still tell the truth?","Watch for color words, theme words, version numbers and names that only fit one component."],"explanation":"<p>The approved names describe a role: a success border, text on an inverse surface, the focus ring. Each stays accurate in every theme and brand. <code dir=\"ltr\">orange-badge</code> describes a look that Tidewater’s palette will change, <code dir=\"ltr\">modal-shadow-v2</code> encodes history instead of purpose (and the next redesign gives you <code dir=\"ltr\">v3</code>), and <code dir=\"ltr\">dark-surface</code> breaks in dark mode, where most surfaces are dark. A role-based alternative would be <code dir=\"ltr\">--nw-color-surface-raised</code> or <code dir=\"ltr\">--nw-color-surface-inverse</code>.</p>\n","checkLabels":{},"optionText":{"success-border":{"text":"<code dir=\"ltr\">--nw-color-success-border</code>","why":"Approve. It names a role (the border of success messages) and follows the grammar’s order."},"orange-badge":{"text":"<code dir=\"ltr\">--nw-color-orange-badge-bg</code>","why":"Reject. ‘Orange’ is appearance; when the warning color changes for Tidewater, the name lies."},"text-inverse":{"text":"<code dir=\"ltr\">--nw-color-text-inverse</code>","why":"Approve. ‘Inverse’ means text on the opposite surface, which holds in both themes."},"modal-shadow-v2":{"text":"<code dir=\"ltr\">--nw-shadow-modal-v2</code>","why":"Reject. A version number records history, not purpose, and guarantees a <code dir=\"ltr\">v3</code> later."},"focus-ring":{"text":"<code dir=\"ltr\">--nw-color-focus-ring</code>","why":"Approve. Every interactive component shares the focus ring, and the role is stable across themes."},"dark-surface":{"text":"<code dir=\"ltr\">--nw-color-dark-surface</code>","why":"Reject. In dark mode almost every surface is dark, so the name stops meaning anything."}}},"words":686,"readMinutes":3}