Contrast is what keeps a theme readable. Tonex treats it as part of the model, not a cleanup step at the end: there's a control that sets how strongly colors separate, and an audit that checks every pair against WCAG. Both work from the same seed and variant without changing them.
The contrast level
The contrast level sets how strongly text and on- colors separate from the surfaces behind them. Raise it and the on- pairs shift to hold a larger margin; lower it and they relax toward the baseline. Because Tonex works in a perceptual color model, that shift is a systematic move along tone rather than a guess by eye — see HCT.
The level is per mode, so you can push dark mode harder without touching light. It's the palette-layer remedy: in the web app it lives in Settings; from the CLI it's --contrast 0..1.
What gets checked
The audit reads every foreground/background pair in the theme and sorts each one by how it's used:
- Text pairs — a color carrying readable text. These follow WCAG 1.4.3.
- Non-text pairs — borders, icons, and fills. These follow WCAG 1.4.11.
- Decorative — purely visual, with no contrast requirement, so they're exempt and never evaluated.
Thresholds
Each kind has its own threshold, and you can target either AA or AAA:
| Intent | AA | AAA |
|---|---|---|
| Text | 4.5:1 | 7:1 |
| Non-text | 3:1 | 4.5:1 |
Text is required, the rest is advisory
The two kinds carry different weight. A failing text pair is a real problem — text you can't read. A low-contrast non-text pair is a judgment call: a faint divider or icon is often fine. Tonex encodes exactly that split:
- In the web app, the audit reports everything and blocks nothing — you decide what to fix. See Contrast audit.
- On the CLI,
checkgates the same way an automated step needs to: a failing text pair exits1, non-text only warns, and decorative is exempt. See Check command.
To resolve a flagged pair either way, raise the contrast level or pin the affected role to a value that passes.