Three Brand-System Variants From a Single tokens.json
A brand that exists only in light mode is a brand that breaks the moment a real product needs dark mode, and breaks again the moment a low-vision user needs high contrast. The amateur move is to hand-build three parallel design files and pray they stay in sync; they never do, and within a quarter the dark theme has drifted a shade off the light one and the high-contrast variant is a forgotten afterthought that fails the very users it was supposed to serve. The senior move - the one this lesson teaches - is to generate all three variants from one source of truth, so light, dark, and high-contrast are not three brands to maintain but three resolutions of one. You will derive the variants from the semantic layer of the tokens.json you have governed, produce a Figma library that reads from the same tokens through Figma Variables, and run an AI accessibility audit at WCAG 2.2 on each. The artifact is three branded variants plus a passing a11y audit per variant - one system, three faces, all provably accessible.
Why One Source, Three Variants, Is the Only Sane Architecture
The reason to generate variants rather than hand-build them is the same reason you built a semantic token layer in the first place: a single point of change that propagates everywhere. Consider what a hand-built three-theme system actually requires. Every time the brand adds a color, a contributor must add it to the light file, the dark file, and the high-contrast file, getting the mapping right in each, three separate times, forever. Every time a designer tweaks a surface shade, they must remember to tweak it in all three. The system does not enforce consistency; it relies on human memory to maintain it, and human memory is exactly what fails under deadline. Drift is not a risk in a hand-built multi-theme system. It is the default state the system decays toward.
Generation inverts this. There is one set of semantic tokens - color.surface.default, color.text.primary, color.action.primary - and each variant is a different resolution of those same semantics. In light mode color.surface.default resolves to a near-white; in dark mode it resolves to a near-black; in high contrast it resolves to pure white or pure black with the contrast cranked to maximum. The component that references color.surface.default does not change at all. It asks for "the default surface" and gets whatever the active theme says that is. Add a new semantic and you add it once with its three resolutions; the component picks it up in every theme automatically. The system enforces consistency structurally, because there is only one place each role is defined and only the resolution changes.
How the Semantic Layer Makes This Mechanically Possible
This is where the architecture from the first two lessons pays off concretely. Recall the three layers: primitives (the raw palette), semantics (roles that alias primitives), and component tokens. Variant generation operates almost entirely at the boundary between primitives and semantics. The primitives stay largely shared - your full color ramps exist once - and each theme is a mapping from semantic roles to different points in those ramps. Light mode maps color.text.primary to color.gray.950 and color.surface.default to color.gray.50; dark mode maps color.text.primary to color.gray.50 and color.surface.default to color.gray.950. Same primitives, inverted semantic mapping.
Mechanically, the DTCG and Figma Variables worlds both express this as modes or theme sets. In Figma Variables, a single variable collection has multiple modes - Light, Dark, High Contrast - and each variable holds a different value per mode. The semantic variable color/surface/default has three values, one per mode, and any component bound to that variable renders correctly in whichever mode is active. In the token file, the same idea is expressed as themed semantic sets that resolve against shared primitives. Either way, the unit of variation is the semantic-to-primitive mapping, and the unit of stability is the semantic name the component depends on. A component never knows which theme it is in; it only knows the role it asked for, which is exactly the indirection that makes one source serve three faces.
Light, dark, and high-contrast are not three brands to maintain. They are three resolutions of one. The component asks for "the default surface" and gets whatever the active theme says that is - so adding a color is one change in three resolutions, never three changes in three files.
Generating the Three Variants, in Order of Difficulty
The three variants are not equally hard, and treating them as if they were is how the high-contrast one ends up half-baked. Generate them in order of increasing difficulty, because each one teaches you something the next one needs.
Light: The Baseline You Already Have
Light mode is usually your existing brand - the one the guidelines PDF described and the one your semantic tokens were first authored against. It is the baseline, and the work here is mostly making sure the semantic mapping is clean and complete: every role that a component needs has a light resolution, and none of those resolutions is a primitive referenced directly. If your light theme is solid and fully semantic, the other two become a matter of re-resolution rather than redesign. If your light theme has gaps or off-system colors, fix them here, because every gap you leave will multiply across the variants you generate from it.
Dark: The Inversion That Is Not a Simple Inversion
The naive approach to dark mode is to invert every color, and it produces a bad dark theme every time. Pure inversion turns your brand blue into an ugly orange and makes large white surfaces into pure-black voids that strain the eye. Real dark mode is a re-mapping, not an inversion: surfaces become dark grays (rarely pure black, because pure black with white text causes halation and eye strain), text becomes light grays (rarely pure white, for the same reason), and brand colors are often slightly desaturated and lightened so they hold their identity against a dark surface without vibrating. You author dark mode as a deliberate second resolution of each semantic, using your existing primitive ramps, and AI can genuinely help here by proposing dark-surface mappings that maintain contrast - but you judge whether the brand still reads as itself, because "technically sufficient contrast" and "still looks like our brand" are different bars and only you hold the second one.
High Contrast: The One Everyone Half-Bakes
High contrast is the variant teams skip or rush, and it is the one that matters most to the users who need it - people with low vision for whom the standard theme's tasteful, slightly-desaturated palette is literally unreadable. A high-contrast theme is not just "more contrast"; it is a deliberate resolution that pushes every text-on-surface pairing well past the WCAG minimums (toward the 7:1 of AAA rather than the 4.5:1 floor), strengthens every border so component boundaries are unmistakable, and ensures the focus indicator is unmissable. It often means near-pure foreground and background and a reduced, high-distinction palette. Because it is generated from the same semantics, you build it as a third resolution: re-map each role to its highest-contrast primitive. The discipline is to actually do it rather than ship a "high contrast" theme that is just the dark theme with a different name, which fails an audit and, worse, fails the user.
The Figma Library Reading From the Same Tokens
A token file the engineers build from and a Figma library the designers work in are only a system if they read from the same source. The failure mode is two sources of truth: the tokens.json says one thing, the Figma library says another, and the brand fragments along the seam between design and code. Figma Variables is the mechanism that closes that seam. You import the token file's semantic sets into a Figma Variables collection with one mode per variant, so the Figma library's color/surface/default variable holds the same three values your token file's semantic resolves to, sourced from the same place.
The payoff is that a designer switching a frame from Light to Dark mode in Figma sees exactly what an engineer's build produces when it switches themes, because both are resolving the same semantic against the same per-mode values. The design file and the codebase stop being two interpretations of the brand and become two views of one source. When the brand changes a surface shade, it changes in the token file, syncs to Figma Variables, and updates every frame and every component at once, in both worlds. This is the concrete realization of brand-system-as-code: the designers and the engineers and the agents are all reading the same tokens, and the three variants are coherent across all of them because they are generated, not transcribed.
The AI Accessibility Audit Per Variant
Three variants means three audits, because a variant that passes WCAG 2.2 in light mode tells you nothing about whether it passes in dark or high contrast. Each theme is a different set of color pairings and therefore a different contrast surface. A text-on-surface pair that hits 4.5:1 in light might drop to 4.1:1 in dark because the dark surface and dark-mode text were mapped to a pairing that looks fine but measures short. The only way to know is to test each variant's resolved pairings, and this is exactly the high-volume, deterministic contrast math you hand to AI while you own the judgment and the targets.
The audit per variant tests the same things the token-level audit taught you: every semantic text-on-surface pairing against 4.5:1 (normal text) and 3:1 (large text), every meaningful non-text token against 3:1, the focus-appearance token against 2.4.11's 3:1 minimum, and interactive sizing tokens against the 2.5.8 24-by-24 floor. The difference is you run it three times, once per resolved theme, because the sizing and focus criteria are usually theme-invariant but the contrast criteria are entirely theme-dependent. AI computes the ratios across every pairing in each theme and returns the failures with their token paths; you decide whether a near-miss gets re-mapped to a darker primitive or whether the role itself needs rethinking. The high-contrast variant should pass with room to spare by design; if it barely passes, it was not actually built as a high-contrast theme.
A passing audit per variant is the deliverable that makes the whole thing real. It is the difference between "we have a dark mode" and "we have a dark mode that a low-vision user can actually read, proven, with the failing pairings found and fixed." Three variants without three passing audits is three ways to fail an accessibility review instead of one. With the audits, you have shipped a brand that serves every user in every theme, generated from one source, coherent across design and code, and provably compliant - which is senior-IC work in the exact sense this level means it.
A Worked Example: One Semantic, Three Resolutions
Take a single semantic and follow it through all three variants so the mechanism is concrete rather than abstract. The semantic is color.text.secondary - the muted body text used for captions, helper text, and metadata. In your light theme it resolves to color.gray.600, a medium gray that sits at roughly 4.6:1 against the near-white color.surface.default, which clears the 4.5:1 floor for normal text with almost no margin. That thin margin is your first warning sign, and it is exactly the kind of thing the per-variant audit will surface.
Now generate dark mode. The naive move is to flip color.gray.600 to its mirror in the ramp, color.gray.400, against the dark surface color.gray.950. AI computes the pairing and reports 4.2:1 - a failure. The secondary text that was barely legible in light mode is now illegibly muted in dark mode, because the symmetric mirror did not preserve the contrast relationship. The fix is a deliberate re-mapping: resolve dark-mode color.text.secondary to a lighter color.gray.350, which the audit confirms clears 4.5:1 against the dark surface. Same semantic, same shared ramp, a different and deliberate primitive chosen because the audit told you the obvious one failed. The component rendering caption text never changes; it asks for color.text.secondary and gets the right value in each theme.
Finally, high contrast. Here you do not nudge - you push. color.text.secondary in the high-contrast theme resolves to color.gray.100 against a near-black surface, landing well past 7:1, because the entire point of the variant is that the user who needs it cannot read a tasteful 4.6:1 muted gray. Notice the arc: the same role traveled from a barely-passing medium gray, to a deliberately-chosen lighter gray, to a near-white that clears AAA with room to spare, and at no point did the caption component change. That is the whole pattern in one token, and it is why the audit runs three times - the same semantic measured 4.6:1, then 4.2:1, then well past 7:1, across the three resolutions, and only testing each one catches the dark-mode failure that the light audit would have sworn was fine.
Theme Switching and the Honest Edge Cases
Generating three coherent variants is most of the work, but two edge cases separate a system that demos well from one that ships well, and a senior IC handles both rather than discovering them in production. The first is the moment of switching. When a user toggles from light to dark, every surface and text color changes at once, and if you animate that transition you are animating dozens of color tokens simultaneously - which can be tasteful or can be a seizure-adjacent flash depending on duration and easing. The honest move is to keep theme-switch transitions short and to respect prefers-reduced-motion, so a user who has asked the system for less motion gets an instant switch rather than a sweeping animation. The transition is itself a thing your tokens should govern (a theme-switch duration token), not an ad-hoc value an engineer picks.
The second edge case is the system theme that follows the operating system. Many products respect the OS-level light or dark preference, which means your theme is chosen for you by the user's environment, and high contrast may also be triggered by an OS-level accessibility setting (forced-colors or an equivalent). A system generated from one source handles this gracefully because the theme is just a mode to select, and selecting it by OS preference is the same mechanism as selecting it by a toggle. The thing to verify is that the OS-triggered high-contrast path actually lands on your deliberately-built high-contrast variant and not on a browser's forced-colors override that ignores your tokens entirely. This is a real-world detail that a hand-built three-file system tends to miss and a generated, audited system is positioned to get right, because the high-contrast resolution already exists and already passed its audit - you are wiring an existing, proven variant to an existing trigger, not scrambling to build one when a user's OS demands it.
What AI Does, and What You Decide
Keep the division of labor sharp, because it is tempting to let the model do more than it should once it is computing ratios competently. AI is genuinely good at three things here: proposing dark and high-contrast mappings that maintain contrast (a useful first pass), computing every contrast ratio across every pairing in every theme (tedious and error-prone by hand), and flagging the specific failures with their paths. Those are real accelerations and you should take them.
What AI cannot do is the judgment that the variants are still your brand. It can tell you a dark-mode blue passes 4.5:1; it cannot tell you that the blue it chose now reads as corporate-generic and lost the warmth that made your brand recognizable. It can crank a high-contrast theme to pass AAA; it cannot tell you the palette it produced is harsh in a way that contradicts the brand's calm, careful character, or that a particular high-contrast mapping breaks a meaning your brand encodes in color. The model resolves for measurable contrast; you resolve for contrast-and-identity-and-meaning together. Take the model's ratio math and its first-pass mappings, then judge each variant against the question only you can answer: does this still look and feel like us, and does it serve the user it is for? That judgment, applied across three coherent variants with three passing audits, is the work.
Key Takeaways
- Hand-building light, dark, and high-contrast as three parallel files guarantees drift, because consistency depends on human memory across three places. Generating all three from one semantic token layer makes consistency structural: each variant is a different resolution of the same semantics, so adding a color is one change in three resolutions, not three changes in three files.
- Variant generation operates at the primitive-to-semantic boundary. Primitives stay shared; each theme is a different mapping from semantic roles to points in those ramps. Figma Variables expresses this as modes on a collection; the token file expresses it as themed semantic sets. The component only ever knows the role it asked for, never the theme.
- Generate in order of difficulty. Light is your baseline - make the semantic mapping clean and complete here, because every gap multiplies. Dark is a re-mapping, not an inversion (dark grays not pure black, light grays not pure white, brand colors lightened and desaturated to hold identity). High contrast is the one teams half-bake; build it deliberately past the minimums, not as the dark theme renamed.
- Close the design-code seam with Figma Variables. Import the token file's semantic sets as a Variables collection with one mode per variant, so the Figma library and the codebase resolve the same semantics against the same per-mode values - two views of one source, not two interpretations of the brand.
- Run the WCAG 2.2 audit once per variant, because contrast is entirely theme-dependent even when sizing and focus are not. A pair that passes in light can fail in dark. AI computes every ratio across every pairing in each theme and returns failures with paths; you decide the re-mapping. The high-contrast variant should pass with room to spare or it was not really built as one.
- AI proposes mappings and computes ratios; you judge whether each variant still reads as your brand and serves the user it is for. The model resolves for measurable contrast; you resolve for contrast, identity, and meaning together. Three coherent variants with three passing audits is the senior-IC deliverable.
Skill.re