{"id":"l-ds-4-2","courseId":"design-systems","lang":"en","kind":"lesson","summary":"Apply semantic versioning to a design system by defining its public API precisely, classifying changes to props, tokens and visuals, and writing changelogs that tell consumers what to do.","html":"<p>When Northwind released version 3.4.0 of its UI package, eleven teams’ builds failed the next morning. The release notes said “minor improvements”. One of those improvements renamed the <code dir=\"ltr\">--nw-color-text-light</code> token, because the team had (correctly) decided the name was misleading. It was a good change released under the wrong number, and it cost us more trust than any bug that year. Versioning is how a design system makes promises to its consumers, and broken promises are expensive.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Semver in one paragraph</h2>\n<p>Semantic versioning gives every release a number <code dir=\"ltr\">MAJOR.MINOR.PATCH</code>. A <strong>patch</strong> (4.7.2 → 4.7.3) fixes bugs without changing the public API. A <strong>minor</strong> (4.7.2 → 4.8.0) adds functionality in a backward-compatible way. A <strong>major</strong> (4.7.2 → 5.0.0) contains changes that can break existing usage. Package managers rely on this: a dependency written as <code dir=\"ltr\">^4.7.2</code> accepts any 4.x.x from 4.7.2 upward, but never 5.0.0. Before 1.0.0, anything may change at any time, so a system that teams depend on should reach 1.0 early.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Define your public API first</h2>\n<p>Semver means nothing until you’ve written down what “the public API” is. For Northwind UI it is:</p>\n<ul>\n<li>every exported component and its props, including their default values;</li>\n<li>every token name in the tokens package, and the role each one describes;</li>\n<li>documented CSS hooks: <code dir=\"ltr\">data-variant</code>, <code dir=\"ltr\">data-size</code> and the component tokens;</li>\n<li>documented behavior, such as “Dialog returns focus to the element that opened it”.</li>\n</ul>\n<p>Anything not on that list, such as internal class names, the DOM nesting inside a component, or private helpers, can change in any release. Write the list in your docs, and tell teams plainly that depending on undocumented internals is at their own risk.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Classifying real changes</h2>\n<p>With the API defined, most decisions become mechanical:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Change</th>\n<th>Bump</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Fix Dialog not returning focus on close</td>\n<td>Patch</td>\n</tr>\n<tr>\n<td>Add a <code dir=\"ltr\">loading</code> prop to Button</td>\n<td>Minor</td>\n</tr>\n<tr>\n<td>Add a new Tabs component or a new token</td>\n<td>Minor</td>\n</tr>\n<tr>\n<td>Refine a token’s value within the same role</td>\n<td>Minor, with a note and visual diffs</td>\n</tr>\n<tr>\n<td>Rename or remove a token, prop or component</td>\n<td>Major</td>\n</tr>\n<tr>\n<td>Change a default, such as Button’s default variant</td>\n<td>Major</td>\n</tr>\n</tbody>\n</table></div><p>Two rows deserve a comment. Changing a default is major because code that relied on the default now renders differently without a single line changing. And value refinements are minor because the token’s contract is its role, not its exact color; if “subtle text” gets slightly darker to pass contrast, every screen that used it still gets subtle text. Treat those as design updates: call them out in the changelog with before-and-after images, because teams with screenshot tests will see their baselines change.</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>“It’s only a rename”</span></p>\n<p>Renames feel harmless because nothing about the design changed. To the build of a team that uses the old name, a rename is identical to a deletion. Ship the new name in a minor release, keep the old one as a deprecated alias, and remove it only in the next major. The next lesson builds that path step by step.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">Changesets and changelogs</h2>\n<p>Remembering the right bump at release time doesn’t scale across many contributors. Northwind uses Changesets: every pull request that changes a package adds a small file stating the bump type and a summary written for consumers.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"markdown\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">.changeset/quiet-trucks-dance.md</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\">---</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"@northwind/ui\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">minor</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:#1F2328;--shiki-dark:#E6EDF3\">Button: add a </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">`loading`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> prop. While loading, the button keeps its width, sets </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">`aria-busy`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, and ignores clicks.</span></span></code></pre></div>\n<p>When a release is cut, the tool takes the highest bump among pending changesets, updates the version, and turns the summaries into <code dir=\"ltr\">CHANGELOG.md</code>. Here is the same logic in miniature:</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\"> order</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { patch: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, minor: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, major: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</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\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> nextVersion</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">current</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">changes</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:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">major</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">minor</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">patch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> current.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">split</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'.'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">map</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(Number);</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> bump</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> changes.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">reduce</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">top</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (order[c.bump] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> order[top] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> c.bump </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> top), </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'patch'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (bump </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'major'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">return</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">major</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}.0.0`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (bump </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'minor'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">return</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">major</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}.${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">minor</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}.0`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">major</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}.${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">minor</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}.${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">patch</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</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:#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\"> pending</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:#1F2328;--shiki-dark:#E6EDF3\">  { bump: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'patch'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, summary: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Dialog returns focus to its trigger on close.'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { bump: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'minor'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, summary: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Button: add a `loading` prop.'</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\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">nextVersion</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'4.7.2'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, pending));</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\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">nextVersion</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'4.7.2'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, [</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">pending, { bump: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'major'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, summary: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Remove Button `kind`.'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }]));</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p>A good changelog groups entries by impact (breaking, added, fixed), is written for the people upgrading, and gives every breaking entry its migration steps. “Refactored Button internals” tells a product team nothing; “Button <code dir=\"ltr\">kind</code> removed, use <code dir=\"ltr\">variant</code>, a codemod is available” tells them exactly what to do.</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>One version for the whole package</span></p>\n<p>Some systems version every component separately. Northwind tried it and ended up with a compatibility matrix nobody could read: Dialog 2.x needed Button 3.x but Tabs 1.x needed Button 2.x. One version for <code dir=\"ltr\">@northwind/ui</code> and one for <code dir=\"ltr\">@northwind/tokens</code> is easier for everyone to reason about.</p>\n</aside>\n<p>In the exercise, you assign version numbers to a batch of changes. Next, you plan how to remove things without breaking forty teams in one morning.</p>\n","toc":[{"id":"sec-1","text":"Semver in one paragraph","level":2},{"id":"sec-2","text":"Define your public API first","level":2},{"id":"sec-3","text":"Classifying real changes","level":2},{"id":"sec-4","text":"Changesets and changelogs","level":2}],"takeaways":["Semver only works when you have written down what your public API is; for a design system that includes component props, exported names, token names and documented CSS hooks.","Removing or renaming anything public, or changing a default, is a major change, even when it feels small.","Adding props, components or tokens is minor; fixing behavior to match the documentation is a patch.","A changelog is written for consumers, grouped by impact, and every breaking entry includes migration steps."],"further":[{"title":"About semantic versioning (npm Docs)","url":"https://docs.npmjs.com/about-semantic-versioning"},{"title":"Semantic Versioning specification (source)","url":"https://github.com/semver/semver/blob/master/semver.md"},{"title":"Changesets (official repository)","url":"https://github.com/changesets/changesets"}],"quiz":[{"q":"<p><code dir=\"ltr\">@northwind/ui</code> is at 4.7.2. A release removes the deprecated <code dir=\"ltr\">kind</code> prop from Button (replaced by <code dir=\"ltr\">variant</code> in 4.3.0). What’s the next version?</p>","options":[{"text":"4.7.3, because the prop was already deprecated.","why":"Deprecation is a warning, not a removal; code that still passes <code dir=\"ltr\">kind</code> breaks when it disappears."},{"text":"4.8.0, because only one prop changed.","why":"The number of changes doesn’t matter; any removal from the public API is breaking."},{"text":"5.0.0, because removing a public prop breaks code that still uses it.","why":"Correct. Removals belong in a major release, ideally batched with other planned removals."}],"answer":2},{"q":"<p>Northwind darkens <code dir=\"ltr\">--nw-color-text-subtle</code> slightly so it passes contrast in dark mode. The name and role stay the same. How should it be released?</p>","options":[{"text":"As a major version, because every screen with subtle text will look different.","why":"Treating every value adjustment as major would mean a major release every few weeks and teach people to ignore majors."},{"text":"As a minor version with a clear changelog note and visual diffs, since the token’s contract (its role) is unchanged.","why":"Correct. The name and meaning are the contract; a value refinement within that role is a documented design update."},{"text":"As a patch with no changelog entry.","why":"Visible changes deserve a note even when they’re compatible; teams with screenshot tests need to know why their baselines changed."},{"text":"It shouldn’t be released; token values must never change.","why":"Values are expected to evolve; that’s why products reference names instead of hard-coding them."}],"answer":1},{"q":"<p>Which changelog entry is most useful to a product team?</p>","options":[{"text":"“Refactored Button internals and cleaned up styles.”","why":"It describes the maintainers’ work, not what changes for the consumer or whether they need to act."},{"text":"“Bumped dependencies.”","why":"Accurate but empty; it doesn’t say whether anything visible or behavioral changed."},{"text":"“Various fixes and improvements.”","why":"It forces every team to read the diff to find out whether they’re affected."},{"text":"“Breaking: Button <code dir=\"ltr\">kind</code> prop removed. Replace <code dir=\"ltr\">kind=&quot;primary&quot;</code> with <code dir=\"ltr\">variant=&quot;primary&quot;</code>, or run the codemod <code dir=\"ltr\">nw-codemods button-kind</code>.”","why":"Correct. It says what changed, who is affected, and exactly how to migrate."}],"answer":3}],"exercise":{"kind":"fill","language":"text","template":"@northwind/ui is at 4.7.2. Each line is a separate release from 4.7.2.\n\nFix: Dialog now returns focus to its trigger on close        -> {{0}}\nAdd a `loading` prop to Button                               -> {{1}}\nRemove the old token name --nw-color-text-light              -> {{2}}\nChange Button's default variant from \"primary\" to \"secondary\" -> {{3}}\nA Dialog focus fix plus a new Tabs component                 -> {{4}}\nDarken --nw-color-text-subtle slightly (same name and role)  -> {{5}}","blanks":[{"answers":["4.7.3"]},{"answers":["4.8.0"]},{"answers":["5.0.0"]},{"answers":["5.0.0"]},{"answers":["4.8.0"]},{"answers":["4.8.0"]}],"title":"Number the releases","prompt":"<p>Apply Northwind’s versioning policy from the lesson. Each line is its own release starting from <strong>4.7.2</strong>. Write the full next version number for each one, such as <code dir=\"ltr\">4.7.3</code>.</p>\n","hints":["Ask of each change: does it fix, add, or break something in the public API (props, defaults, token names)?","A release takes the highest bump among its changes.","Token values may evolve within their role; token names are the contract."],"explanation":"<p>The focus fix restores documented behavior, so it’s a patch (4.7.3). A new prop is a backward-compatible addition (4.8.0). Removing a token name breaks anyone still using it, and changing a default changes rendering without any code change, so both are majors (5.0.0). A release mixing a fix and a new component takes the higher bump, minor (4.8.0). A value refinement that keeps the token’s name and role is a minor design update (4.8.0), shipped with a changelog note and visual diffs.</p>\n","checkLabels":{}},"words":689,"readMinutes":3}