Token Finder is a small, sharp Figma plugin that closes the gap between design tokens and Tailwind. Tokens are powerful until someone has to use them, and at handoff, translating a layer's values into utility classes is slow, manual and inconsistent. Token Finder does it in place: pick a layer, read the values, get the classes — and let the shape of the class quietly say whether the value was on-token.
The challenge
Design systems lose their speed at the exact moment they should create it: handoff. The designer knows the intent, the engineer needs the class, and the naming layer between them is manual lookup. Worse, nobody can quickly tell whether a value is on-scale or quietly drifting off it.
- Token names are not always implementation-friendly
- Designers and engineers translate values to classes by hand
- Handoff becomes repeated, error-prone lookup work
- It is hard to see, at a glance, what is on-scale vs off-scale
- The system loses speed precisely where it should add it
Discovery & research
Token Finder was scoped from a real handoff tax — hand-translating a layer's 16px into p-4 and a fill into a Tailwind class, and never quite knowing what was on-token. Discovery was about two things: where a design system actually loses speed, and exactly which of a layer's values the tool could honestly audit versus merely translate.
The system loses speed exactly where it should add it
At handoff the designer knows the intent and the engineer needs the class, but the naming layer between them is manual lookup — slow, repetitive and inconsistent. The tool had to live at that seam, reading a single selected node and returning classes in place.
EvidencebuildPayload reads figma.currentPage.selection, groups values into Layout, Radius, Typography and Colour, and returns each with its Tailwind class — all inside Figma, with the manifest's networkAccess set to none.
Drift is only visible where the tool maps a scale
Tailwind's arbitrary bracket can mean two different things, and the tool is honest about which. For spacing and radius a bracket genuinely signals off-scale — but for typography and raw dimensions the tool emits a bracket every time, because it maps no named type or size scale, so there a bracket just means 'no scale', not drift.
EvidencetwPadding/twGap snap to p-4/gap-6 when px÷4 is on-grid and fall to p-[15px] otherwise; twRadius uses a named map (0/2/4/6/8/12/16/24/9999 → rounded-none…rounded-full) or rounded-[Npx]. extractTypographyRows always emits text-[Npx], leading-[Npx], tracking-[…] and font-[500]; width and height are always w-[Npx]/h-[Npx].
Colour on-token means a bound variable, not a matching hex
A named colour class is only truthful when the fill is actually bound to a design-token variable, so the tool never guesses a nearest named colour from a hex. It resolves the bound variable's alias chain and emits a named class only when a token in that chain is marked as a Tailwind colour; an unbound hex stays an honest arbitrary bracket.
EvidenceresolveColorVariableChain walks up to 12 alias hops, reading each collection's defaultModeId (or its first mode key); if any name contains /twcss/, tokenToTwColor produces bg-sky-400, otherwise it falls back to the resolved hex as bg-[#38BDF8]. Raw, unbound paints go straight to the arbitrary form via twColorArbitrary.
The tool never guesses a 'nearest' class
It was tempting to round an off-scale value to the closest utility, but a silent round is exactly how drift hides. The tool either finds the exact scale value or shows the arbitrary bracket — the off-scale value stays visible instead of being smoothed away.
Evidence15px padding renders as p-[15px], not p-4; a 10px radius as rounded-[10px], not rounded-lg; there is no round-to-nearest logic anywhere in twPadding, twGap or twRadius — each is an exact-match-or-bracket branch.
Engineers won't copy output they can't verify
Trust comes from showing the work. Every row puts the raw Figma value beside the translated Tailwind class, with a px→rem readout alongside, so the engineer can sanity-check the mapping before pasting it.
EvidenceEach Row carries both raw and tw; fmtPxRem renders '16px • 1rem' beside the class, and the deduped tailwindAll string is one-tap-copyable for the whole selection.
A handoff tool dies if it becomes a documentation platform
The temptation is to add configuration and docs until nobody opens it. Scope stayed ruthless: read the selection, map four data groups, copy — with only four visibility toggles as configuration. A bridge, not a second system to maintain.
EvidenceThe only settings are colours/typography/layout/radius toggles persisted to clientStorage; the UI is a single 500×720 dark panel with themeColors and no project setup.
How I approached it
I scoped the tool ruthlessly — it is a bridge, not a documentation platform. The plugin reads the selected node, groups its values by data point (layout, radius, typography, colour), and maps each to a Tailwind class. For spacing and radius that means snapping to the scale where the value fits and dropping to an arbitrary bracket where it doesn't; for colour it means resolving a bound Figma variable's alias chain and, if the token is /twcss/-named, emitting a named class instead of a raw hex. Every row shows the raw Figma value beside the class, everything is one-tap-copy, and the whole thing runs offline in a calm dark aesthetic that feels at home next to the code.
Scoped the plugin to one job: value → Tailwind class at handoff
Mapped each data group (spacing, radius, type, colour) to utility output
Snapped spacing and radius to the scale, with arbitrary brackets as the off-scale fallback
Resolved bound colour variables through their alias chain to a named or arbitrary class
Put the raw Figma value beside every class so the mapping stays checkable
Made copy frictionless — per value and for the whole deduped class list
Trade-offs
The tension was simplicity versus usefulness. Too much configuration and it becomes another heavy tool nobody opens; too little information and the output cannot be trusted. It had to stay a lightweight utility while handling the messy reality of Figma colour — raw paints, paint styles, and variables aliased several collections deep — and while being honest about the places it genuinely cannot tell on-token from off.
- Keeping the plugin lightweight and instantly readable, with no config screens
- Resolving colour variables — walking the alias chain up to twelve hops, reading each collection's default mode
- Being honest that typography and dimensions always bracket, so they cannot signal drift
- Making the output trustworthy enough to copy without re-checking
- Serving both designers and engineers in one compact, offline UI
Final direction
The final tool is a compact, Figma-native, dark utility that runs entirely offline: select a layer and see each value mapped to its Tailwind class — named where it lands on the scale, an arbitrary bracket where it doesn't — with colour swatches, the raw Figma value beside every class, and one-tap copy for a single value or the whole deduped list. It supports the design system by making on-token usage the path of least resistance and surfacing off-scale spacing, radius and unbound colour at handoff instead of in production.
Outcomes
Token Finder shows how a small, well-scoped tool can remove repeated handoff friction and surface drift at the same time. The design contribution is the on-scale-vs-arbitrary model — reusing Tailwind's own bracket syntax so the class itself reveals whether a value was on-token, without adding a badge or a rulebook. It is a working, shipped plugin rather than a mockup, and it makes correct usage the path of least resistance exactly where the translation happens. The honest limit is that its value is qualitative — it removes lookups and catches off-scale usage at handoff — rather than a number I have measured in production.
The handoff should not be where the system slows down.
A design system is only useful if people can apply it quickly. Tooling turns system intent into everyday behaviour, and the best place to catch drift is the place work already happens — beside the layer, in the language the engineer will actually paste.