Linting Design Files: What Code Taught Me About Consistency
Code has ESLint and Prettier. Design files mostly have review comments. What I borrowed from linters, and how it turned into my Figma plugin, Linterly.
If you've ever run ESLint on a codebase for the first time, you know the feeling. A wall of yellow and red. Unused variables, inconsistent quotes, a missing dependency in a hook. None of it is dramatic. All of it is true.
Open almost any mature Figma file and you'll find the design equivalent: a button with a hex value typed in by hand, a heading that looks like the H2 style but isn't attached to it, a card with a 7px radius sitting next to one with 8px. Nobody did it on purpose. It just accumulates.
Code has had tools for this mess for years. Design files mostly have review comments. So what happens when you borrow the linter idea and point it at a canvas?
What a linter actually does
A linter isn't clever. ESLint reads your code, compares it against a set of rules, and tells you where they break. Prettier goes further and just fixes the formatting, so nobody argues about semicolons in a pull request again.
The value isn't in any single rule. It's in three properties:
- It's consistent. The rule fires the same way on Monday morning and Friday evening, for a junior and for a lead.
- It's early. You see the problem while you're still in the file, not days later in a review thread.
- It's impersonal. A warning from a tool is just information. The same note from a colleague can feel like criticism.
That last one matters more than it sounds. Some of the most draining feedback in design teams is the small stuff: "this isn't using the token", "radius should be 8". It's correct, and it's tiring to give and to receive.
The design version of a lint error
When I started listing what I'd want a design linter to catch, the list looked a lot like a style guide written as rules instead of prose:
- Hardcoded hex values. A fill of #3B82F6 that matches your primary blue but isn't linked to the Variable or Style. Change the brand color later and this one stays behind.
- Detached text styles. Text that looks like Body/Regular but has drifted, or was never attached at all.
- Low-contrast text. Grey on lighter grey reads fine on a calibrated monitor and poorly on a phone in sunlight. WCAG gives clear thresholds, so a tool can check them.
- Tiny type. Font sizes under 12px are a quiet accessibility problem, especially in dense dashboards.
- Small touch targets. Anything tappable under 44px is going to frustrate someone's thumb.
- Off-grid radii. A 7px radius in a system built on 4 and 8 isn't a crime. It's just one more decision someone has to make again.
None of these need taste to detect. If a rule can be written down precisely, a human shouldn't be the one enforcing it.
Why automated checks beat review comments
Review comments are great at the things only people can judge: does this flow make sense, is the hierarchy right, does the empty state feel kind. They are bad at being a spellchecker.
When reviewers spend their attention on hex codes, two things happen. The important feedback gets buried, and the trivial still slips through, because people get tired and files get big. A tool doesn't get tired, no matter how deep the layers panel goes.
There's also timing. In code, the linter runs as you type. In design, the "linter" has usually been a lead scrolling through a file right before handoff, when fixing a detached style means hunting across dozens of frames.
Moving the check closer to the work changes the habit. You fix the hex value while your hand is still on it.
The quiet power of a score
One thing code tooling taught me is how motivating a single number can be. Test coverage, Lighthouse scores, a lint count dropping toward zero. A number turns a vague chore into a small game.
Design files rarely give you that. "Is this file healthy?" usually gets answered with a shrug. So when I started building my own linter for Figma, a health score was one of the first things on the list. It isn't a grade on your talent. It's a progress bar for cleanup.
It helps conversations too. "Let's get this file's score up before handoff" is a much easier ask than "the file feels kind of messy".
How this became Linterly
All of this turned into Design Auditor - Linterly, which I released on the Figma Community in late January. It scans a selection or a whole page and does what a good linter does:
- Finds hardcoded hex values and detached text styles, then links them to the closest local Style or Variable.
- Flags low-contrast text and font sizes under 12px, based on WCAG.
- Catches touch targets under 44px and corner radii that sit off a 4/8px grid.
- Rolls everything into a design health score from 0 to 100%.
Then it borrows the best idea from Prettier: you don't just get a list of problems. You get Fix Now for a single issue and Fix All when you trust the suggestions. Linking a stray hex to the right Variable should take one click, not a scavenger hunt.
It also pairs nicely with the other plugins I built over the past few months. The Color Palette Generator creates your color Variables and Paint Styles, and the TypeScale Generator creates your type Variables and text styles. Linterly is the part that checks you're actually using them. Generate the system, then lint against it.
Linters didn't make developers less creative. They took the boring arguments off the table so the interesting ones had room. I think design files deserve the same deal: let the tool worry about whether that blue is really the blue, and spend your review time on whether the design is actually good.
And if you run Linterly on an older file, don't panic at the first score. Every codebase looks rough the first time you lint it, too.