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 likestyled.div`color:#3f82f0`); - definition-aware —
--primary: #3f82f0,$brand: …,@brand: …are allowed by default; only usages are flagged (use--strictto flag definitions too); - structural —
#fff {id-selectors andurl(#a1b2c3)are not colors; - precise — matches hex (3/4/6/8) +
rgb()/rgba()/hsl()/hsla(), never a 5- or 7-digit run, with an--allowlist 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
Metadata
Release files for hexsweep 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| hexsweep-0.1.0.tar.gz | 13.9 kB | Without provenance | Details for hexsweep-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| hexsweep-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for hexsweep-0.1.0-py3-none-any.whl |
Total release size: 25.7 kB
Release files / hexsweep-0.1.0.tar.gz
| Download URL | hexsweep-0.1.0.tar.gz |
|---|---|
| Size | 13.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
717e7ad880094be2eba073512fbbd022f9d22a9d8e76fd05f77d878105b66fad
|
|
BLAKE2b-256 checksum How to use checksums |
2e45028d5e199d720f2d0a318d7213ae4b0b707d8bb8e7a22708fa6871515a7b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.7
|
Release files / hexsweep-0.1.0-py3-none-any.whl
| Download URL | hexsweep-0.1.0-py3-none-any.whl |
|---|---|
| Size | 11.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
46e19971b2ef50c5cf156e541597cbafc24f27a4c65cfe722b48dd102237d3e2
|
|
BLAKE2b-256 checksum How to use checksums |
2ecfeded61d1c5984f794890902978e591007c9ddb23cb426dfd32506a36dfe7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.7
|
Security for hexsweep 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.