Raptive · Design Systems · 2026
Design Systems Token Vitals
Auditing what a legacy platform’s code actually does, so a rebrand lands everywhere at once
In-depth case study · the work is ongoing
design-token-vitalsGitHubGrades a codebase’s design token layer, reports what it cannot prove, and ranks the fixes into a next move.Vitals, at a glance
What the audit returns first: eight vitals, four of them unproven. A hatched track is a reading not taken, never a score of zero — the grades, their notes, and why that distinction matters are below.

Every component, ranked by reach
87 canonical components, ranked by how much of the platform each one actually touches.
Every surface was captured at both breakpoints and audited as a screen. Responsive variants are merged and repeated instances inside one screen count once, so the number beside a component is the share of the platform that changes when it does.
Foundational controls and visual primitives.
- Button22%
- Form field16%
- Validation message16%
- Avatar9%
- Badge9%
- Search field9%
- Select / picker9%
- Selection control9%
Small functional groups built from atoms.
- Filter controls34%
- Reaction bar19%
- Author row16%
- Action group13%
- Feed tabs13%
- Profile tabs9%
- Data visualization6%
- Metric card6%
Reusable interface regions and complex modules.
- Top navigation88%
- Bottom nav30%
- Composer16%
- Activity list13%
- Post card13%
- Sidebar11%
- Admin navigation9%
- Review nav9%
Three components are platform-specific and the reach ranking is what proves it rather than assumes it: the bottom nav exists only on mobile, the sidebar and the right rail only on desktop. A contract written from the desktop screens alone would have shipped a component the mobile app cannot use.
Reframing the opportunity
The platform did not have a consistency problem. It had a visibility problem: nobody could say which components a design change would reach until it shipped.
Raptive Community runs on Discourse, a Rails and Ember application whose codebase was started in 2013 — before design tokens were a practice and before CSS custom properties were a browser feature. Typography, color, spacing, radius and elevation decisions had accumulated across Discourse core, Raptive plugins, Sass variables and CSS custom properties. Each one was deliberate when it was made. Together they were fragmented — change a foundational token and some components followed, some reached past it into a primitive palette, and some read a mobile-specific copy of the same value defined in a different file.
Then a rebrand was scheduled. New typography, new palette, new button treatment, and this time the switch had to propagate cleanly across the whole platform. Finding out mid-launch that some of the buttons had updated was not an acceptable way to learn.
- How might we see, before shipping, which components a token change will actually reach?
- How might we order a migration by evidence rather than by whoever asks loudest?
Three questions Figma could not answer
None of the three could be answered from Figma. Figma holds the intent; the browser renders the code.
The audit
So I built the scanner. It reads the production codebase — not a presumed token file — and grades what it finds.
It builds the Sass and CSS import graph, then separates the code Raptive owns from upstream Discourse. It resolves scalars, maps, aliases and custom properties into canonical concepts, and traces which files consume each one. Those consumers are grouped into components; literal colors are audited separately. What comes out is eight graded vitals and a fix queue ranked by impact. It is framework-agnostic and open source: design-token-vitals.
One value, four names
Vital · Single sourceSame value. Four names. No semantic connection between them.
White appears 76 times across 20 files and answers to at least four token names. The brand primary has the same problem. Teams had each named the same decision separately, unaware the others had already named it — so changing one name leaves every other spelling of the same decision untouched, and nothing in the codebase says which ones those are.
- --background-inverse
- --mobile-background
- --mobile-secondary
- --brand-primary
- --icon-brand-primary
- --mobile-primary-blue
- --raptive-primary-blue
Value equality is proven; semantic equivalence is not. The scanner will not merge these on its own — deciding that a Google button and a mobile background are the same decision is a human call, and the report says so rather than quietly collapsing them.
No shared contract
Vital · Tier integrityThe button pulls 26 distinct tokens, 74 times, from more than one source file.
Some buttons consume a semantic layer, some reach directly into the primitive palette, and some read a mobile-specific override. Not because anyone decided that — because there was no shared contract, and nothing standing between a deadline and a local decision.
The button, in evidence
369 rules in the platform paint something button-shaped. Twelve of them define enough to actually draw the button.
So I rendered every one from its own source. Each record carries the selector, the button drawn from the rule that paints it, its resolved geometry, the tokens it reaches for, and the file and line it came from — then sorts by how much the code will admit.
This is a third instrument counting a third thing. The audit counted token references inside the button component; the reach ranking counted screens a component appears on; this counts CSS rules that paint a button anywhere in the platform. 74, 22% and 369 are three different questions with three correct answers.
Most certain first.
The twelve highest-confidence records, and the reason the rebrand could not simply change one color: a neutral pill, a gray icon control, a white surface with a gray outline, a red fill, a red outline, a purple, two greens. Every one is a button. No two agree on fill, radius or type, and each cites a different file.

The grayed and outlined placeholders are a rule, not a style: they mean the evidence is incomplete, never that the button is gray. A record only gets a color once the source or a runtime observation defines one. It is the same refusal the eight vitals make — the tool would rather show a blank than a color it cannot prove.

That is fifteen of 294, and the proportion is the point rather than the parade — the remaining 279 look the same. Below them sit 68 more that are selector-only: a real class, in a real file, defining no visual properties at all. Together they are why “change the button color” was never a one-line job. Twelve rules can be edited with confidence; the rest have to be resolved before anyone can say what editing them would do.
Two rankings
By token footprint the button ranks first. By platform presence it ranks fourth, behind a navigation bar that touches 88% of the product.
Both numbers are correct, and they answer different questions. Token footprint counts references in the codebase — it says where the migration work is. Platform presence counts screens — it says where a mistake would be seen. The button carries 8.7% of the token references and appears on 22% of the screens. The top navigation appears on 88% — I have not measured its token footprint, so I cannot tell you whether it is the cheaper fix, only that it is the more visible one.
The scopes differ too, which is easy to miss and expensive to conflate. The code audit resolved 479 token concepts across everything the platform loads, Discourse core included. The design-side inventory scopes to the 118 tokens Raptive actually owns, with the runtime Discourse palette feeding the rest. Neither number is the “real” one. Quoting either without saying which instrument produced it is how a slide deck becomes wrong.
Three groups, three different needs
The same 87 components group a second way — not by how complex they are, but by what each group needs from the token layer.
Operational controls is the group that matters most and reads least urgent. Those components run almost entirely on runtime Discourse theme variables, so they cannot be represented in a Figma library at all until they are aliased into the same semantic color, spacing, radius and type families as everything else. Building the library before that aliasing would produce a design system that quietly excludes the admin, review and preferences surfaces.
Eight vitals, four unproven
The audit reports what it can and cannot prove about itself.
- Naming coherenceFour grammars active; needs consolidation.fail
- Tier integritySome consumers reach past semantics into primitives.attention
- Coverage14 of 19 families confirmed; 5 unmeasured.attention
- Single sourceMultiple names still resolve to one value.attention
- Leakage26 exact-match and 163 uncovered candidates await semantic review.blocked
- Mode completenessNo compiled light and dark output audited yet.blocked
- OrphansReachability analysis incomplete.blocked
- EnforcementNo token-aware CI gate proven.blocked
Blocked is not a failing grade. It is the tool refusing to grade what it has not measured — four of the eight say “no evidence yet” instead of inventing one. An audit that grades everything is not measuring; it is guessing with a rubric.
Design, approve, migrate
Design somewhere the framework cannot argue back, approve on clean evidence, then migrate in the order the audit already proved.
One contract, two implementations
One canonical contract. Two framework-native implementations. Every delivery format generated, never hand-maintained in parallel.
React consumes the contract and produces React components. Discourse consumes it and produces Ember and Glimmer components. Both implement the same visual and behavioral contract without sharing a line of source. CSS custom properties, Sass aliases and the Figma JSON are all generated from it, which is what prevents the split brain that created the fragmentation in the first place.
Button / Primary
background color.action.primary
foreground color.text.on-action
height size.control.medium
padding-inline space.md
radius radius.control
focus-ring color.focus
states default · hover · pressed
focus · disabled · loadingThe library, drafted
The unified library is drafted: 909 tokens across 14 groups, in one export meant to become the single source Figma and code both read.
It carries a primitive layer and a Discourse-facing bridge layer in the same file, which is what will let the migration run component by component instead of as a single flip. The bridge is temporary by design — every token in it is one a component still has to be moved off. It is not wired to production yet; counting it as delivered would be counting the plan as the work.
Migration roadmap
| Phase | Why it sits here | Status |
|---|---|---|
| 1 · Buttons | Highest token footprint at 8.7%. Not the most visible component — see Two rankings. | Roadmapped |
| 2 · Typography | Foundational to everything after it. The DM Sans asset is already source-verified. | Planned |
| 3 · Spacing & layout | Grid, component gaps, surface padding — audit the ladder, then fill its missing steps. | Planned |
| 4 · Color harmonization Vital · Leakage | Resolve 26 exact matches, classify 163 uncovered values, retire the duplicate names. | In discussion |
| 5 · Mode & accessibility Vital · Mode completeness | Compile every light and dark bundle pair, render in real contexts, verify WCAG 2.2 AA. | Next gate |
Buttons are the critical path: typography and spacing both depend on the contract shape that migrating buttons settles. The order is not a preference — it is the token footprint, ranked.
What changes
Locating a decision
Search 21 files, guess which components consume the change, ship it and find out.
A ranked list of 105 components, with file and line evidence for every reference.
Shipping a visual change
The change reaches some components and not others. Discovered in QA, or after launch.
Components read one semantic layer, so a color change propagates for one reason rather than four.
Design against production
Figma and production drift apart until something ships wrong.
The audit is an evidence boundary. Documented differences are tracked, not discovered.
What transfers
None of this is specific to Discourse, or to a rebrand. It needs only a codebase where one decision answers to more than one name.
Any change that has to land everywhere at once asks the same question first — a rebrand, an acquisition, a design system’s second version. The scanner reads Sass and CSS, so it fits most front ends built on them, and it is open source. What it cannot do is read a value that does not exist until the page runs: a custom property a theme overrides after load is invisible to anything that only reads files. That limit is the same on every codebase, and knowing where it falls is part of using the instrument rather than a flaw in it.
The part that transfers without any tool at all is the order of operations. Measure the code rather than the design file, because the design file holds the intent and the browser holds what shipped. Rank the work by something you measured rather than by whoever asks loudest. And write down what you could not prove, so the next person knows where to look instead of inheriting a scorecard that graded everything.
Role & scope
Strategy
- Framed the rebrand as an architecture problem, not a visual one
- Defined the framework-neutral token contract
- Set migration order from measured token footprint
Execution
- Built the audit tool that scans the production codebase
- Built the 0→1 React and Vite environment: 224 tokens, 45 components, CI-integrated
- Drafted the unified 909-token library, the intended single source for Figma and code
- Specified the button contract across every state and mode
Leadership
- Ran rebrand phasing with product managers against audit evidence
- Gave engineering shared vocabulary for tokens, tiers and leakage
- Wrote down what the audit cannot prove, so nobody over-claims it
What is proven, what is not
Proven
- 479 token concepts mapped across 21 definition sources
- 105 components ranked by confirmed token footprint
- 26 exact-match and 163 uncovered color candidates located with file and line
- DM Sans and 17 brand colors verified from source, not from a spec
- Migration order grounded in footprint rather than in preference
Still open
- Compiled light and dark bundle pairs are not audited yet
- No token-aware CI gate has been proven
- Semantic equivalence for the 163 literal matches needs human review
- 70 concepts remain unclassified
- Delivery-speed impact is unmeasured; the baseline is being captured during rollout
The audit fixes nothing. It shows the shape of what needs fixing and puts it in order — which is the part a rebrand cannot be planned without. A rebrand is normally a visual change. Making this one architectural is what makes the next one cheap.