{"id":"l-ds-4-1","courseId":"design-systems","lang":"en","kind":"lesson","summary":"Mirror your token tiers in Figma variable collections and modes, publish them through libraries, and run one sync process so design files and code never disagree about a value.","html":"<p>For eight months after we launched tokens, Northwind had two truths. The JSON in the repository said the action blue was <code dir=\"ltr\">#1f5ae0</code>; the Figma library said <code dir=\"ltr\">#2160e8</code>, because a designer had nudged it for a campaign mockup and published. Engineers built from the inspect panel, so the marketing site shipped one blue and the app another. Tokens solve consistency only if design files and code read from the same decisions.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Mapping tiers to Figma variables</h2>\n<p>Figma variables hold reusable values such as colors and numbers, grouped into <strong>collections</strong>. A collection can have several <strong>modes</strong>, each giving every variable in the collection a different value. A variable can also alias another variable. That’s enough to mirror the token architecture almost one to one:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Token concept</th>\n<th>Figma</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Primitive tier</td>\n<td>Collection “Primitives”, one mode</td>\n</tr>\n<tr>\n<td>Semantic tier</td>\n<td>Collection “Semantic”, modes Light and Dark</td>\n</tr>\n<tr>\n<td>Density</td>\n<td>Collection “Density”, modes Comfortable and Compact</td>\n</tr>\n<tr>\n<td>Brand</td>\n<td>An extended collection per brand (Enterprise), or a Brand collection with modes</td>\n</tr>\n<tr>\n<td>Alias <code dir=\"ltr\">{color.blue.600}</code></td>\n<td>Variable aliasing <code dir=\"ltr\">blue/600</code></td>\n</tr>\n</tbody>\n</table></div><p>Variable names use slashes to form groups, so <code dir=\"ltr\">color/action/bg</code> appears as a group path in the picker and corresponds to the token <code dir=\"ltr\">color.action.bg</code>. Figma frames pick modes the way the CSS attribute selectors do: a frame can set a mode explicitly, and anything inside it on Auto inherits that mode, so a dark hero frame on a light page works just like <code dir=\"ltr\">data-theme=&quot;dark&quot;</code> on a section.</p>\n<p>For brands, Figma’s <strong>extended collections</strong>, available on the Enterprise plan, let you create a Tidewater collection that inherits from the Northwind semantic collection and overrides only the brand variables. Updates to variables that Tidewater didn’t override flow through automatically. On other plans, a separate Brand collection with one mode per brand gets you most of the way.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Make the right variable the easy one</h2>\n<p>Two settings matter more than any naming scheme. First, <strong>hide primitives from publishing</strong>. The semantic collection can still alias them, but designers in product files only see semantic variables, which is exactly the rule components follow in code. Second, give every published variable <strong>code syntax</strong>: Dev Mode then shows <code dir=\"ltr\">var(--nw-color-action-bg)</code> instead of a hex value, so what the designer picked is literally what the engineer types. Scoping helps too: restrict color variables to the properties they’re meant for, so a text color doesn’t appear in the fill picker.</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>Two sources of truth</span></p>\n<p>If both the Figma library and the JSON accept direct edits, they will diverge; it’s only a matter of when. Choose one source of truth for values and make the other a mirror that changes only through the sync process. Northwind chose the JSON in the repository, because versioning, review, the contrast checks and releases already lived there. Teams that are design-led sometimes choose Figma as the source; either works if the other side is never edited by hand.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">The sync loop</h2>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 230\" role=\"img\" aria-labelledby=\"l-ds-4-1-t1\"><defs><marker id=\"arrow-l-ds-4-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-ds-4-1-t1\">A loop: a designer proposes a change in a Figma branch, exports JSON into a pull request, CI checks and builds, the release is published, and the released JSON is imported back into the Figma library.</title>\n  <rect class=\"d-box-accent\" x=\"20\" y=\"30\" width=\"150\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"95\" y=\"55\" text-anchor=\"middle\">Figma branch</text>\n  <text class=\"d-label-muted\" x=\"95\" y=\"74\" text-anchor=\"middle\">proposal</text>\n  <rect class=\"d-box-primary\" x=\"265\" y=\"30\" width=\"150\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"340\" y=\"55\" text-anchor=\"middle\">Pull request</text>\n  <text class=\"d-label-muted\" x=\"340\" y=\"74\" text-anchor=\"middle\">tokens JSON</text>\n  <rect class=\"d-box\" x=\"510\" y=\"30\" width=\"150\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"585\" y=\"55\" text-anchor=\"middle\">CI checks</text>\n  <text class=\"d-label-muted\" x=\"585\" y=\"74\" text-anchor=\"middle\">build outputs</text>\n  <rect class=\"d-box-success\" x=\"510\" y=\"150\" width=\"150\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"585\" y=\"183\" text-anchor=\"middle\">Release</text>\n  <rect class=\"d-box-accent\" x=\"20\" y=\"150\" width=\"150\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"95\" y=\"175\" text-anchor=\"middle\">Figma library</text>\n  <text class=\"d-label-muted\" x=\"95\" y=\"194\" text-anchor=\"middle\">import, publish</text>\n  <path class=\"d-arrow\" d=\"M170 58 L261 58\" marker-end=\"url(#arrow-l-ds-4-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M415 58 L506 58\" marker-end=\"url(#arrow-l-ds-4-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M585 86 L585 146\" marker-end=\"url(#arrow-l-ds-4-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M510 178 L174 178\" marker-end=\"url(#arrow-l-ds-4-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"340\" y=\"168\" text-anchor=\"middle\">released JSON</text>\n</svg><figcaption>One source of truth, with Figma as a synced mirror</figcaption></figure>\n<p>Designers still design in Figma; they just don’t publish token values directly. A change starts in a <strong>branch</strong> of the library file (branching is available on Figma’s Organization and Enterprise plans), where the designer can try it in real mockups. When it’s ready, the changed modes are exported as JSON: Figma can export and import a collection’s modes in the DTCG format. The export becomes a pull request against the tokens repository, where CI runs the checks from Section 2 and a core designer and engineer review it. After release, the released JSON is imported into the main library file and the library update is published.</p>\n<p>Some teams automate the last step with a plugin or Figma’s REST API. Northwind does it by hand once per release; it takes ten minutes, and a human glancing at the variables panel has caught two bad imports.</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>Library updates are releases</span></p>\n<p>When you publish a library, people working in files that use it get a notification and choose when to accept the updates. Treat that like a package bump: publish alongside the code release, and paste the same changelog into the publish description so designers see what changed and why.</p>\n</aside>\n<p>The same loop works for components, with Code Connect linking Figma components to their code in Dev Mode. In the exercise, you put the steps of a token change in order. Next, you version those releases so teams know what an update will do to them.</p>\n","toc":[{"id":"sec-1","text":"Mapping tiers to Figma variables","level":2},{"id":"sec-2","text":"Make the right variable the easy one","level":2},{"id":"sec-3","text":"The sync loop","level":2}],"takeaways":["Map token tiers to Figma variable collections, and map themes, brands and density to modes or extended collections.","Pick one source of truth for token values and make the other side a mirror that is updated by a defined process, never by hand.","Give semantic variables code syntax and hide primitives from publishing, so designers pick the same names engineers type.","Library updates are releases too; designers accept them in their files the way engineers bump a package version."],"further":[{"title":"Guide to variables in Figma","url":"https://help.figma.com/hc/en-us/articles/15339657135383-Guide-to-variables-in-Figma"},{"title":"Modes for variables (Figma)","url":"https://help.figma.com/hc/en-us/articles/15343816063383-Modes-for-variables"},{"title":"Extend a variable collection (Figma)","url":"https://help.figma.com/hc/en-us/articles/36346281624471-Extend-a-variable-collection"},{"title":"Guide to libraries in Figma","url":"https://help.figma.com/hc/en-us/articles/360041051154-Guide-to-libraries-in-Figma"}],"quiz":[{"q":"<p>Northwind’s designers can pick any primitive like <code dir=\"ltr\">blue/600</code> from the library, and mockups keep using primitives where semantic tokens belong. What’s the best fix?</p>","options":[{"text":"Delete the primitives collection from Figma.","why":"Semantic variables alias the primitives, so the primitives must exist in the file."},{"text":"Ask designers in a meeting to stop using primitives.","why":"Reminders fade; the library should make the right choice the easy one."},{"text":"Rename primitives with a “do not use” prefix.","why":"It clutters every name and still leaves them in the picker."},{"text":"Hide the primitive variables from publishing, so only semantic variables appear in consuming files.","why":"Correct. Primitives stay available for aliasing inside the library, while designers only see the names they should use."}],"answer":3},{"q":"<p>A designer changed <code dir=\"ltr\">color/action/bg</code> in Figma last week, and an engineer changed the same token in the JSON yesterday, to a different value. What process failure does this show?</p>","options":[{"text":"Two sources of truth that both accept direct edits.","why":"Correct. With one source and a mirror, one of these edits would have been a proposal against the source instead of a competing change."},{"text":"Figma variables don’t support colors from code.","why":"Figma variables hold colors fine; the problem is who is allowed to change them, and where."},{"text":"The engineer should have waited for the next design review.","why":"Timing doesn’t fix the problem; without one source, any two edits can conflict."}],"answer":0},{"q":"<p>How should Northwind represent its two brands in Figma on an Enterprise plan?</p>","options":[{"text":"Duplicate the whole library for Tidewater and maintain both by hand.","why":"Duplicated libraries drift apart, which is the problem the system exists to solve."},{"text":"Put brand colors directly on each component as overrides.","why":"Overrides on components bypass variables, so themes and code mapping stop working."},{"text":"Extend the semantic collection for Tidewater and override only the brand variables.","why":"Correct. An extended collection inherits everything it doesn’t override, so shared changes still reach both brands."},{"text":"Store brand colors as text styles.","why":"Text styles describe typography, not colors, and don’t map to modes or tokens."}],"answer":2}],"exercise":{"kind":"order","items":["propose","export","ci","review","release","sync"],"title":"Ship a token change from Figma to code and back","prompt":"<p>A Northwind designer wants to darken <code dir=\"ltr\">color/text/subtle</code> because it fails contrast in the Tidewater dark theme. The JSON in the tokens repository is the source of truth, and the Figma library is a mirror. Put the steps of the change in order.</p>\n","hints":["Nothing reaches the published Figma library before it has been released from the repository.","Checks run on the pull request, before humans approve it."],"explanation":"<p>The designer explores in a branch, so nothing published changes yet. The exported JSON enters the source of truth through a pull request, where CI validates names, references and contrast and builds every platform. Humans review with that evidence, the release publishes a new version with a changelog, and only then is the released JSON imported into the main library and published, so Figma mirrors exactly what shipped.</p>\n","checkLabels":{},"itemText":{"propose":"Designer tries the darker value in a branch of the Figma library file","export":"Export the changed modes as DTCG JSON and open a pull request in the tokens repository","ci":"CI runs the naming, reference and contrast checks and builds CSS, Swift and Kotlin","review":"A core designer and engineer review the pull request and its visual diffs","release":"Merge and publish a new tokens version with a changelog entry","sync":"Import the released JSON into the main library’s variables and publish the library update"}},"words":725,"readMinutes":3}