You asked Claude Code to build a dashboard. It looks pretty good! Except it has six slightly different shades of gray, four border radii, and a button that's actually a div with an onClick.
You could add another paragraph to your AGENTS.md asking it to follow your design system. Maybe put IMPORTANT in all caps this time.
Or you could make it follow some actual rules.
Meet your new best friend: the linter
A linter is a tool that checks your code against a set of rules and tells you when something breaks them.
Unlike the instructions you give your coding agent, these rules are enforced mechanically. The agent doesn't get to decide whether it feels like following them today.
For example, you can tell it not to use arbitrary spacing values, require semantic HTML, or flag certain accessibility mistakes.
And the best part? You don't have to fix everything yourself. Your agent can run the linter, read the errors, and fix its own mess.
AGENTS.md
Please use our design tokens.
IMPORTANT.
×Ignored by your agent
$ npm run lint
error Unapproved spacing value
p-[17px]
✓A rule your agent has to follow
A linter won't make your designs good. But it can stop your agent from making the same avoidable mistakes over and over.
Stop inventing design tokens
If you're using Tailwind, you've probably seen classes like these:
<div className="p-[17px] rounded-[13px] bg-[#F8F9FB]">
There's nothing inherently wrong with any of those values. But if your agent invents new ones for every component, your carefully considered design system quickly becomes a collection of suspiciously similar numbers.
Tailwind already provides a spacing scale and utilities that can reference your design tokens. You can also configure ESLint rules to flag arbitrary values or restrict specific classes.
For example, tailwindcss/no-unnecessary-arbitrary-value flags arbitrary values when an equivalent standard utility exists. For stricter enforcement, eslint-plugin-better-tailwindcss provides configurable restrictions on classes.
Project overview
Everything in one place.
Invented values
p-[17px]Adds a spacing value outside the shared scale.
rounded-[13px]Introduces another nearly identical corner radius.
bg-[#F8F9FB]Hard-codes a color instead of reusing a token.
Project overview
Everything in one place.
Shared tokens
p-4Reuses the spacing scale.
rounded-xlKeeps corners consistent across components.
bg-whiteUses an existing color utility.
The goal isn't to ban every arbitrary value. Sometimes you really do need an unusual measurement.
It's to make the agent reuse decisions you've already made instead of reinventing your spacing system every time you ask for a modal.
Your clickable div is not a button
Here's a classic:
<div onClick={handleSubmit}>
Submit
</div>
Looks like a button. Works when you click it. Ship it?
Not quite.
A real <button> comes with keyboard interaction, focus behavior, and semantics that browsers and assistive technologies already understand.
<button type="button" onClick={handleSubmit}>
Submit
</button>
The eslint-plugin-jsx-a11y package catches common accessibility mistakes in React, including non-interactive elements being used as controls, missing image descriptions, and invalid accessibility attributes. (If you’re using React Native, try eslint-plugin-react-native-a11y.)
Use Tab to reach the button, then Enter or Space to activate it.
Submit
A plain div. Skipped by Tab.
0 activations
A native button. Keyboard ready.
0 activations
Of course, linting your JSX isn't enough to make a site accessible.
Tools like axe-core can check the rendered page for additional issues, including detectable color-contrast failures and missing accessible names. You can run axe through browser developer tools or automated tests.
Neither tool catches everything. You'll still want to test with a keyboard, and sometimes a screen reader.
But at least you can stop shipping clickable divs and calling it a day.
A few more rules worth stealing
Once you've got the basics, there are other checks that can save you from surprisingly annoying problems.
Conflicting Tailwind classes. Tools such as eslint-plugin-better-tailwindcss can flag duplicate or conflicting utilities before you spend twenty minutes wondering why a style isn't applying.
Unrestricted CSS values. If you're writing regular CSS instead of Tailwind, Stylelint can enforce approved property values. Your design tokens don't have to disappear just because you're not using utility classes.
You don't need fifty rules on day one. Start with the mistakes you actually keep seeing.
Make the agent clean up after itself
Installing a linter is only half the job. You also need to make sure it actually runs.
A simple workflow looks like this:
Generate UI
Claude Code~/portfolio
❯Add a project overview card using our existing UI components.
Enter to send
Run lint
$ npm run lint
error p-[17px]: use the spacing scale
error rounded-[13px]: use a radius token
error bg-[#F8F9FB]: use a color token
3 violations
Agent fixes the code
Claude Code
●Fixing 3 violations. Replacing the custom values with existing design tokens.
Update(component.tsx)
− p-[17px] rounded-[13px] bg-[#F8F9FB]
+ p-4 rounded-xl bg-white
Check again
$ npm run lint
✓ No violations. Ready to review.
Configure your rules, add a lint command to your project, and tell your agent to run it after making changes.
For example, your AGENTS.md might include:
After modifying frontend code:
- Run `npm run lint`.
- Fix any lint errors introduced by your changes.
- Don't disable rules to make errors disappear.
If you're working with a team, run the same checks in CI so violations can block a merge rather than quietly sneaking into production.
One important detail: ESLint doesn't magically prevent bad code from being written. It detects violations when it runs. Your agent still needs to respond to the errors, and your workflow needs to enforce the result.
The difference is that you're no longer relying entirely on the agent remembering your preferences.
Instead of asking nicely for the fourteenth time, you've given it a test it needs to pass.
Unfortunately, taste is still your problem
None of this will tell you that your dashboard has too many cards, your onboarding flow makes no sense, or that the entire interface looks like every other AI-generated SaaS product.
That's still your job.
But if the tooling can enforce the decisions you've already made, you get to spend less time fixing inconsistent padding and more time making the decisions that actually require a designer.
And maybe, just maybe, your next prototype will only have one shade of almost-white.