╔════════════════════════════════════════════════════════════════════════════════════════════╗
║ ║
║ ██████╗ █████╗ ████████╗███████╗██╗ ██╗██╗████████╗ ║
║ ██╔════╝ ██╔══██╗╚══██╔══╝██╔════╝██║ ██╔╝██║╚══██╔══╝ ║
║ ██║ ███╗███████║ ██║ █████╗ █████╔╝ ██║ ██║ ║
║ ██║ ██║██╔══██║ ██║ ██╔══╝ ██╔═██╗ ██║ ██║ ║
║ ╚██████╔╝██║ ██║ ██║ ███████╗██║ ██╗██║ ██║ ║
║ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ║
║ ║
║ three gates a linter will not give you ║
║ ║
╚════════════════════════════════════════════════════════════════════════════════════════════╝
Three gates for defects a linter does not have an opinion about, because none of them is a syntax error. Each one exists because a real person read something and could not.
| Gate | Fails the build when |
|---|---|
typefloor |
Type is declared below a readable floor (13px by default) |
contrast-gate |
A declared colour cannot reach 4.5:1 on any surface the file also declares |
deadname-gate |
A retired name reaches anything a human reads |
Install
pip install legible
Use
typefloor ./src # every CSS and inline declaration
contrast-gate ./src # WCAG 4.5:1 against declared surfaces
deadname-gate ./src # a name that should be gone
Each exits non-zero on a finding and prints the file and line. --list
shows every offender rather than the summary.
Why these three
typefloor. Six-point type is not a style choice. This was written after the same reader reported the same thing more than once, and the first time it was pointed at a folder it found every violation immediately — what was missing was never the tool, it was anything that pulled it.
contrast-gate. It reads CSS custom properties — --ink: #hex against
--bg: #hex — and reports the best surface each ink can reach. Dark grey on
navy measured 2.64:1 where body text needs 4.5, and a person found that, not
a build.
It does not read literal color: declarations. A stylesheet using
neither has nothing to compare, and it says so and exits 0 — which in CI
reads as a pass on a site nobody checked. Pass --require-pairs to fail
instead when nothing was found. The Action sets it.
deadname-gate. Fails a build when a name that should be gone reaches a title, a footer, a byline, alt text or a comment — while leaving machine identifiers alone, because renaming a repository path or a published artifact breaks working links or contradicts a deposited record. Both halves matter: a gate that cannot tell those apart gets switched off.
Written for a company rename. It is the same shape as the problem of a person changing their name and finding it in three hundred places, which is the harder version and the one worth getting right.
Exit codes
| Code | Means |
|---|---|
0 |
Clean |
1 |
It found something. The file and line are printed. |
2 |
Misconfigured — it could not check, and will not report clean |
The third one matters. deadname-gate forbids nothing out of the box, so a
fresh install exits 2 with an explanation rather than 0. A gate that
reports a pass while checking nothing is worse than no gate, and every one of
these three shipped a version that did exactly that before it was caught.
Configuration
Each gate reads a JSON file of exceptions, so a deliberate choice is recorded with a reason rather than silently re-flagged.
typefloor ./src --config typefloor.json
contrast-gate ./src --config contrast.json
deadname-gate ./src --config deadname_gate.json
TYPEFLOOR_CONFIG, CONTRAST_CONFIG and DEADNAME_CONFIG do the same.
In CI
- run: pip install legible
- run: typefloor ./src
- run: contrast-gate ./src --require-pairs
- run: deadname-gate ./src
There are Actions too, in .github/actions/ of the source repository.
A note on the argument
An earlier version ignored a bare path and scanned its own directory
instead, then printed OK. A gate that reports a pass on a tree it never
opened is worse than no gate, so a positional path is now the root. If you
pinned an earlier build, check what it is actually reading.
Licence
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file legible-0.1.0.tar.gz.
File metadata
- Download URL: legible-0.1.0.tar.gz
- Upload date:
- Size: 16.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b29774f2edc547dbb54c9f67bcd4061b3deb45b69ec75f284333b4a3d05068b
|
|
| MD5 |
19ffcdf284987c2e9884ecbb5d9d5cae
|
|
| BLAKE2b-256 |
2f6afef8b1d692dd00c1632cbd1304fd9f922ccdcfe3874a041e5f3a374d164d
|
File details
Details for the file legible-0.1.0-py3-none-any.whl.
File metadata
- Download URL: legible-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
37beb99f788884d8fb5b24507f7acc93d68cbc31660f414d2106871f79c11b89
|
|
| MD5 |
7bd130e3d5f9d469a9677049c031979e
|
|
| BLAKE2b-256 |
43cbbb42811847d8704175c802cf0719c4e4b8cdc54dcad0fd6a17d533480a20
|