Skip to main content

Find hardcoded colors that should be design tokens — the raw #hex / rgb() that escaped your token migration. Comment- and definition-aware, zero dependencies.

Project description

hexsweep

Find the hardcoded colors that should be design tokens. After a token migration, components still have raw background: #3f82f0 buried in them instead of var(--primary-blue) — and you don't find out until dark mode or a rebrand breaks. hexsweep scans your source and flags every raw color literal that escaped, with zero config and zero dependencies.

pipx run hexsweep src/
#   src/components/Button.tsx  (2)
#     14:18  #3f82f0  [hex6]  background
#     27:10  rgb(255, 0, 0)  [rgb]  color
#
#   ✖ 2 hardcoded colors in 1 file (23 files clean)

Exits non-zero when it finds colors, so it drops straight into CI. Pure standard library. Also on npm (npx hexsweep) — the two builds produce byte-for-byte identical output.

Why not grep or stylelint?

grep '#[0-9a-f]{6}' is what people fall back to, and it's noisy: it hits #header id-selectors, url(#gradient) references, colors in comments, and it can't see rgb()/hsl(), 3/4/8-digit hex, or tell a leftover from a token definition.

stylelint can do it, but needs Node + a config + postcss/custom-syntax, it can't see hex inside TSX/CSS-in-JS string literals, and — critically — its color-no-hex flags your tokens.css (the one file that's supposed to hold raw colors) exactly as loudly as a stray hex in a component. The request to exempt token definitions has sat unimplemented for years.

hexsweep sits in between. It's a one-shot, zero-config scanner that is:

  • comment-aware — colors in //, /* */, <!-- --> are skipped (but strings are kept, so it does catch CSS-in-JS like styled.div`color:#3f82f0`);
  • definition-aware--primary: #3f82f0, $brand: …, @brand: … are allowed by default; only usages are flagged (use --strict to flag definitions too);
  • structural#fff { id-selectors and url(#a1b2c3) are not colors;
  • precise — matches hex (3/4/6/8) + rgb()/rgba()/hsl()/hsla(), never a 5- or 7-digit run, with an --allow list for the colors you keep on purpose.

Usage

hexsweep                          # scan the current directory
hexsweep src/ styles/             # scan specific paths
hexsweep --allow "#000,#fff" src  # allowlist colors that are OK to hardcode
hexsweep --strict                 # also flag token definitions (--x/$x/@x)
hexsweep --ext css,scss,vue       # override the scanned extensions
hexsweep --json                   # machine output (byte-identical both builds)

node_modules, .git, dist, build, coverage (and more) are skipped by default; add others with --exclude. The default extensions are css, scss, sass, less, vue, svelte, jsx, tsx, js, ts, html, htm, astro.

Exit codes: 0 clean · 1 hardcoded colors found · 2 error.

In CI

- run: pipx run hexsweep src/   # fails the build on a hardcoded color

The --json shape is sorted by (file, line, column) so it's deterministic regardless of filesystem order:

{
  "version": 1,
  "summary": { "filesScanned": 24, "filesWithFindings": 1, "findingCount": 2 },
  "findings": [
    { "file": "src/components/Button.tsx", "line": 14, "column": 18,
      "literal": "#3f82f0", "category": "hex6", "property": "background", "isDefinition": false }
  ]
}

How it works

It's a line scanner, not a CSS/JS parser — that's what keeps it zero-dependency and lets the Node and Python builds stay byte-identical. It blanks comment spans (keeping string contents and every column position intact), then matches color literals with explicit-ASCII regexes, and applies the id-selector / url() / definition / allowlist gates. Columns are counted in UTF-8 bytes and files are visited in a fixed byte-sorted order, so output never depends on the OS or filesystem.

Scope

MVP matches #hex and rgb()/hsl() functional notation. Named colors (red), modern oklch()/lab(), autofix, and a config file are intentionally left out — the goal is a high-signal, zero-config CI gate, not a parser.

Install

pip install hexsweep  # or pipx run hexsweep
npm i -g hexsweep     # Node build, identical behaviour

Python ≥ 3.8 or Node ≥ 18. No dependencies.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hexsweep-0.1.0.tar.gz (13.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hexsweep-0.1.0-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

Details for the file hexsweep-0.1.0.tar.gz.

File metadata

  • Download URL: hexsweep-0.1.0.tar.gz
  • Upload date:
  • Size: 13.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.7

File hashes

Hashes for hexsweep-0.1.0.tar.gz
Algorithm Hash digest
SHA256 717e7ad880094be2eba073512fbbd022f9d22a9d8e76fd05f77d878105b66fad
MD5 fce1a0e2146f6d69bd37490520d22ec3
BLAKE2b-256 2e45028d5e199d720f2d0a318d7213ae4b0b707d8bb8e7a22708fa6871515a7b

See more details on using hashes here.

File details

Details for the file hexsweep-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: hexsweep-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.7

File hashes

Hashes for hexsweep-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 46e19971b2ef50c5cf156e541597cbafc24f27a4c65cfe722b48dd102237d3e2
MD5 4072a631d5886e8666d4be2a59f06436
BLAKE2b-256 2ecfeded61d1c5984f794890902978e591007c9ddb23cb426dfd32506a36dfe7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page