How to find hardcoded colors and spacing in React, Vue and Tailwind
Updated ยท Buoy
Short answer
Search for hex, rgb() and pixel values in components, stylesheets and Tailwind classes, then drop the ones that are correct as literals: icons, email templates, tests, layout math and values that already use a token. What remains is drift; the fastest fix is the literals that exactly match an existing token.
Hardcoded values are the most common kind of design drift. In 20 well-known open-source codebases, Buoy found 2,685 hardcoded-value findings across 35,458 components (see the reports). Here is how to find them in your own code, from a quick search to a check that runs on every pull request.
Quick search with ripgrep
These searches cover the usual places literals hide. Run them from the repository root.
Hex colors in components and stylesheets
rg -n --glob '*.{tsx,jsx,vue,svelte,css,scss}' '#[0-9a-fA-F]{3,8}\b' Tailwind arbitrary values holding a literal
rg -n '\b[a-z-]+-\[(#[0-9a-fA-F]{3,8}|[0-9.]+(px|rem))\]' Pixel values in inline style objects
rg -n 'style=\{\{[^}]*\b[0-9]+px' Why raw search over-reports
A search finds every literal. Many of them are correct, and counting them buries the real problems:
- Artwork.
fillandstrokeon SVG icons and logos are drawings, not theme colors. - Email templates. Most email clients ignore CSS variables, so literals are required.
- Tests, stories, fixtures and Open Graph images hold values on purpose.
- Layout math.
calc(100% - 2rem),100dvhand 1px hairlines are not design decisions. - Values that use a token, such as
bg-[var(--brand)]. - Deliberate choices. A comment such as
// match the OS scrollbaror a 2โ3px optical nudge.
Keep them out with lint rules
- Stylelint:
color-no-hexbans hex colors;declaration-property-value-disallowed-listbans literals for chosen properties; thestylelint-declaration-strict-valueplugin requires variables. - ESLint with Tailwind:
tailwindcss/no-arbitrary-valuefromeslint-plugin-tailwindcssflags arbitrary classes.
Lint rules are strict by design: they do not know which literal equals an existing token, so they cannot suggest the fix, and they fire on the correct literals above unless you add exceptions.
A token-aware scan
A scanner that reads your tokens can tell #2563eb apart from a random blue: if
--color-primary holds exactly that value, the fix is a one-line swap. Buoy does this for CSS,
Sass, Tailwind, React, Vue, Svelte and Angular:
npx @buoy-design/cli drift check # findings, each with the token to use
npx @buoy-design/cli drift check --staged # only the lines you are committing Or paste a public GitHub URL into buoy.design/scan.
Fix in order of payoff
- Literals that exactly equal an existing token: swap them.
- The same literal repeated across files: add a token, then swap.
- One-off values: check for a reason before changing them.
Scan a repository now
Paste a public GitHub URL and get a design health report in under a minute: score, the hardcoded values to fix first, and the path to 100.
Frequently asked questions
- How do I find hardcoded colors in a React codebase?
- Search for hex, rgb() and hsl() values in .tsx, .jsx, .css and .scss files, including inline style objects and Tailwind arbitrary classes like bg-[#2563eb]. Then remove the ones that are fine as literals: SVG icons, email templates, tests and fully transparent values. A token-aware scanner such as Buoy does both steps and names the existing token to use instead.
- How do I find Tailwind arbitrary values?
- Search for classes with square brackets holding a literal, such as p-[13px], text-[#555] or rounded-[3px]. The eslint-plugin-tailwindcss rule no-arbitrary-value flags them in the editor. Arbitrary values that reference a token, like bg-[var(--brand)], are fine.
- Is there a Stylelint rule for hardcoded colors?
- Yes. The built-in color-no-hex rule disallows hex colors, and declaration-property-value-disallowed-list can ban literal values for chosen properties. The stylelint-declaration-strict-value plugin requires variables for properties such as color and margin.
- Why does grep find so many false positives?
- Because many literals are correct: icon paths, brand logos, email templates (email clients do not support CSS variables), tests, 1px borders, calc() expressions and values that already reference a token. Counting them makes a codebase look worse than it is and trains the team to ignore the results.