Back

Back

Token Checker — design system audit tool

Scans any Figma file for raw hex and off-scale values, scores it, and rebinds violations to the right tokens in one click — 1,000+ runs in daily team use.

Role

Design & Build

Industry

Design tooling

status

In daily use

Year

2026

Challenge

Grid is 1,248 tokens across five products. A design system that size only works if designs actually use it, and checking that was a manual job: open a file, click through layers, find the raw hex someone typed instead of picking bg/Error/Base, tell them, repeat.

That job doesn't scale, and it makes the design system owner into a policeman. Nobody enjoys either side of that conversation, and compliance quietly degrades because catching violations depends on someone having time to look.

The tool had to do three things at once: find violations reliably, be trusted enough that designers run it voluntarily, and be conservative enough that it never silently changes someone's work.

Describe this image here

Process

Two-tier validation, because there are two different failures

A property can be wrong in two distinct ways, and conflating them makes the report useless.

Bound to the wrong variable. The layer uses a variable, but its name isn't in Grid's allowlist of 280+ valid token names. Reported as no-token, with the offending variable named so the designer knows what they actually applied.

Not bound at all. Someone typed a hex. Reported as raw-color — and the raw value is looked up in a 110-entry hex→token map to produce a suggestion.

Same for spacing, radius, and typography: is it bound, and is what it's bound to actually ours.


Keyword matching, not exact matching, for spacing names

Exact-match validation looked correct and failed immediately. Figma variable names drift — spacing10, spacing/spacing10, spacing/gap, spacing/horizontal-padding all legitimately exist in Grid.

So spacing validation accepts any variable whose name contains spacing, gap, padding, or margin; radius accepts radius, rounded, or corner. Deliberately loose. A false positive costs the tool its credibility; a rare false negative costs almost nothing.


Never guess a token

Suggestions come from exact value matches only. A padding of 16 maps to spacing/spacing16. A padding of 17 gets no suggestion, and fix-all skips it and counts it as failed rather than rounding to the nearest token.

Nearest-match binding would have been easy and occasionally right. It would also silently change designs, and a tool that alters your work without asking gets uninstalled once. The conservative rule is what makes fix-all safe enough to use on a real file.


Exclude icon fonts

Text nodes set in Font Awesome, Material Symbols, Phosphor, Tabler, Feather, Remix, IcoMoon, or Fontello are skipped for typography checks — they're glyphs, not text, and Grid's Display/Headline/Title/Body/Label styles don't apply.

Mixed-font nodes only skip if every segment is an icon font, so a label with one inline icon still gets checked.

This one class of false positive would have been enough on its own to make designers stop running the tool.


Noisy checks ship off by default

Colour and typography are on out of the box. Spacing and radius default to off, opt-in per designer and persisted in clientStorage.

Spacing violations are numerous and often intentional, and a first run that returns two hundred issues teaches people the tool is broken. Starting narrow and letting designers widen it is how the first run stays credible.


An ignore list, because some violations are correct

Intentional exceptions get added to a persisted ignore list, keyed by node. Ignored nodes are excluded from the violation count and skipped by fix-all — so a designer's decision to override the system survives the next audit instead of being re-flagged forever.


Score only what exists

Each category scores as tokenized ÷ total. The overall score averages only the categories that actually have instances — a frame with no radius properties doesn't get a 0% or a 100% for radius, it's excluded from the mean.

Bands run Perfect at 100, Excellent at 90, Good at 80, Fair at 60, Needs work at 40, Critical below. The gauge turns green above 80, amber above 60, red below.

Grading is the part designers will argue with, so the arithmetic had to be defensible.


Resolving library tokens

Fix-all has to bind to team library variables, which means resolving a token name to a library key. Grid's library paths don't always match the names in the map, so importVarByName normalizes case and slashes, then tries exact match, prefix-in-cache, prefix-in-name, and last-segment in both directions. The library is fetched once and cached per session.


Details that came from real use

  • Debounced auto-recheck, 600ms on selection change, and only after the first manual run. The UI posts a pending state immediately so the spinner appears before the work starts.

  • figma.mixed guards on fills and strokes. Components with mixed overrides return figma.mixed instead of an array, and iterating it throws.

  • Per-node try/catch with a comment that says exactly why: never let one bad node crash the whole frame check.

  • networkAccess: ["none"] in the manifest. The plugin runs on the company's design files and cannot make a network request.

Describe this image here

Outcome

In daily use by Mezorn's design team since May 2026, with 1,000+ audit runs. Compliance checking moved from a manual review job to something any designer runs before they ship.

Describe this image here

What I Learned

  • The hard part wasn't detection, it was earning the right to be run. Every design decision in the tool — icon fonts excluded, spacing off by default, no nearest-match guessing, an ignore list that persists — exists to prevent the one false positive that would have made people stop opening it.

  • Exact matching is the wrong instinct for a system that's alive. My first spacing validation compared against a fixed list of variable IDs and immediately broke on legitimate names like spacing/gap. Keyword matching is less precise and much more correct.

  • Refusing to guess is a feature. Fix-all skips anything without an exact match and reports it as failed. A tool that silently alters your work is worse than no tool, and being conservative is what made "fix all" a button people actually press.

Back

Token Checker — design system audit tool

Scans any Figma file for raw hex and off-scale values, scores it, and rebinds violations to the right tokens in one click — 1,000+ runs in daily team use.

Role

Design & Build

Industry

Design tooling

status

In daily use

Year

2026

Challenge

Grid is 1,248 tokens across five products. A design system that size only works if designs actually use it, and checking that was a manual job: open a file, click through layers, find the raw hex someone typed instead of picking bg/Error/Base, tell them, repeat.

That job doesn't scale, and it makes the design system owner into a policeman. Nobody enjoys either side of that conversation, and compliance quietly degrades because catching violations depends on someone having time to look.

The tool had to do three things at once: find violations reliably, be trusted enough that designers run it voluntarily, and be conservative enough that it never silently changes someone's work.

Describe this image here

Process

Two-tier validation, because there are two different failures

A property can be wrong in two distinct ways, and conflating them makes the report useless.

Bound to the wrong variable. The layer uses a variable, but its name isn't in Grid's allowlist of 280+ valid token names. Reported as no-token, with the offending variable named so the designer knows what they actually applied.

Not bound at all. Someone typed a hex. Reported as raw-color — and the raw value is looked up in a 110-entry hex→token map to produce a suggestion.

Same for spacing, radius, and typography: is it bound, and is what it's bound to actually ours.


Keyword matching, not exact matching, for spacing names

Exact-match validation looked correct and failed immediately. Figma variable names drift — spacing10, spacing/spacing10, spacing/gap, spacing/horizontal-padding all legitimately exist in Grid.

So spacing validation accepts any variable whose name contains spacing, gap, padding, or margin; radius accepts radius, rounded, or corner. Deliberately loose. A false positive costs the tool its credibility; a rare false negative costs almost nothing.


Never guess a token

Suggestions come from exact value matches only. A padding of 16 maps to spacing/spacing16. A padding of 17 gets no suggestion, and fix-all skips it and counts it as failed rather than rounding to the nearest token.

Nearest-match binding would have been easy and occasionally right. It would also silently change designs, and a tool that alters your work without asking gets uninstalled once. The conservative rule is what makes fix-all safe enough to use on a real file.


Exclude icon fonts

Text nodes set in Font Awesome, Material Symbols, Phosphor, Tabler, Feather, Remix, IcoMoon, or Fontello are skipped for typography checks — they're glyphs, not text, and Grid's Display/Headline/Title/Body/Label styles don't apply.

Mixed-font nodes only skip if every segment is an icon font, so a label with one inline icon still gets checked.

This one class of false positive would have been enough on its own to make designers stop running the tool.


Noisy checks ship off by default

Colour and typography are on out of the box. Spacing and radius default to off, opt-in per designer and persisted in clientStorage.

Spacing violations are numerous and often intentional, and a first run that returns two hundred issues teaches people the tool is broken. Starting narrow and letting designers widen it is how the first run stays credible.


An ignore list, because some violations are correct

Intentional exceptions get added to a persisted ignore list, keyed by node. Ignored nodes are excluded from the violation count and skipped by fix-all — so a designer's decision to override the system survives the next audit instead of being re-flagged forever.


Score only what exists

Each category scores as tokenized ÷ total. The overall score averages only the categories that actually have instances — a frame with no radius properties doesn't get a 0% or a 100% for radius, it's excluded from the mean.

Bands run Perfect at 100, Excellent at 90, Good at 80, Fair at 60, Needs work at 40, Critical below. The gauge turns green above 80, amber above 60, red below.

Grading is the part designers will argue with, so the arithmetic had to be defensible.


Resolving library tokens

Fix-all has to bind to team library variables, which means resolving a token name to a library key. Grid's library paths don't always match the names in the map, so importVarByName normalizes case and slashes, then tries exact match, prefix-in-cache, prefix-in-name, and last-segment in both directions. The library is fetched once and cached per session.


Details that came from real use

  • Debounced auto-recheck, 600ms on selection change, and only after the first manual run. The UI posts a pending state immediately so the spinner appears before the work starts.

  • figma.mixed guards on fills and strokes. Components with mixed overrides return figma.mixed instead of an array, and iterating it throws.

  • Per-node try/catch with a comment that says exactly why: never let one bad node crash the whole frame check.

  • networkAccess: ["none"] in the manifest. The plugin runs on the company's design files and cannot make a network request.

Describe this image here

Outcome

In daily use by Mezorn's design team since May 2026, with 1,000+ audit runs. Compliance checking moved from a manual review job to something any designer runs before they ship.

Describe this image here

What I Learned

  • The hard part wasn't detection, it was earning the right to be run. Every design decision in the tool — icon fonts excluded, spacing off by default, no nearest-match guessing, an ignore list that persists — exists to prevent the one false positive that would have made people stop opening it.

  • Exact matching is the wrong instinct for a system that's alive. My first spacing validation compared against a fixed list of variable IDs and immediately broke on legitimate names like spacing/gap. Keyword matching is less precise and much more correct.

  • Refusing to guess is a feature. Fix-all skips anything without an exact match and reports it as failed. A tool that silently alters your work is worse than no tool, and being conservative is what made "fix all" a button people actually press.

Designed and built by Boku.
All rights reserved ©2026

Designed and built by Boku.
All rights reserved ©2026