checkThemeContrast(resolveTheme(theme), { exceptions }) from postcss-uxdsl/ds-runtime, with the exceptions shipped next to the base theme (postcss-uxdsl/theme/base.contrast-exceptions.json) — the same call uxdsl theme --contrast makes in a terminal (its real output is on the CLI page). It checks every text, placeholder and border pair the Surface, Button and Input engines define, in light and dark mode, at every configured breakpoint width, against 4.5:1 for text and 3:1 for borders.
It checks the colors a theme resolves to, not a rendered page: it is not an accessibility certification. And today it fails, for this site's themes and for the packaged base: the failures are real, disclosed and tracked (MIG-B6-29, MIG-B7-01), not hidden behind exceptions. That is what the verdict below says.
| Group | Failing |
|---|---|
| dark · button · text | 19 |
| light · input · text | 18 |
| dark · input · text | 18 |
| light · button · text | 15 |
| dark · input · placeholder | 9 |
| dark · input · border | 9 |
| dark · button · border | 8 |
| light · input · border | 6 |
| dark · surface · text | 6 |
| light · surface · text | 5 |
| light · button · border | 5 |
| dark · surface · border | 3 |
| light · surface · border | 2 |
| Mode | Component | Tone | State | Pair | Width ≥ | Ratio | Needs |
|---|---|---|---|---|---|---|---|
| light | surface.outlined | surface | base | text | 0px | 1.00 | 4.5 |
| light | surface.outlined | surface | base | border | 0px | 1.00 | 3 |
| light | surface.outlined | warning | base | text | 0px | 3.19 | 4.5 |
| light | surface.outlined | light | base | border | 0px | 1.10 | 3 |
| light | surface.flat | surface | base | text | 0px | 1.00 | 4.5 |
| light | surface.flat | warning | base | text | 0px | 3.19 | 4.5 |
| light | surface.flat | light | base | text | 0px | 1.10 | 4.5 |
| light | button.outlined | surface | base | text | 0px | 1.00 | 4.5 |
| light | button.outlined | surface | base | border | 0px | 1.00 | 3 |
| light | button.outlined | surface | hover | text | 0px | 3.13 | 4.5 |
| light | button.outlined | surface | selected | border | 0px | 1.00 | 3 |
| light | button.outlined | warning | base | text | 0px | 3.19 | 4.5 |
| light | button.outlined | warning | hover | text | 0px | 4.29 | 4.5 |
| light | button.outlined | light | base | text | 0px | 1.10 | 4.5 |
| light | button.outlined | light | base | border | 0px | 1.10 | 3 |
| light | button.outlined | light | hover | text | 0px | 1.23 | 4.5 |
| light | button.outlined | light | hover | border | 0px | 1.23 | 3 |
| light | button.outlined | light | selected | border | 0px | 1.10 | 3 |
| light | button.flat | surface | base | text | 0px | 1.00 | 4.5 |
| light | button.flat | surface | hover | text | 0px | 3.13 | 4.5 |
| light | button.flat | surface | selected | text | 0px | 3.13 | 4.5 |
| light | button.flat | warning | base | text | 0px | 3.19 | 4.5 |
| light | button.flat | warning | hover | text | 0px | 4.29 | 4.5 |
| light | button.flat | warning | selected | text | 0px | 4.29 | 4.5 |
| light | button.flat | light | base | text | 0px | 1.10 | 4.5 |
| light | button.flat | light | hover | text | 0px | 1.23 | 4.5 |
| light | button.flat | light | selected | text | 0px | 1.23 | 4.5 |
| light | input.contained | surface | focus | border | 0px | 1.00 | 3 |
| light | input.contained | light | focus | border | 0px | 1.10 | 3 |
| light | input.outlined | surface | base | text | 0px | 1.00 | 4.5 |
| light | input.outlined | surface | base | border | 0px | 1.00 | 3 |
| light | input.outlined | surface | focus | text | 0px | 1.00 | 4.5 |
| light | input.outlined | surface | focus | border | 0px | 1.00 | 3 |
| light | input.outlined | surface | invalid | text | 0px | 1.00 | 4.5 |
| light | input.outlined | warning | base | text | 0px | 3.19 | 4.5 |
| light | input.outlined | warning | focus | text | 0px | 3.19 | 4.5 |
| light | input.outlined | warning | invalid | text | 0px | 3.19 | 4.5 |
| light | input.outlined | light | base | text | 0px | 1.10 | 4.5 |
| light | input.outlined | light | base | border | 0px | 1.10 | 3 |
| light | input.outlined | light | focus | text | 0px | 1.10 | 4.5 |
| light | input.outlined | light | focus | border | 0px | 1.10 | 3 |
| light | input.outlined | light | invalid | text | 0px | 1.10 | 4.5 |
| light | input.underline | surface | base | text | 0px | 1.00 | 4.5 |
| light | input.underline | surface | focus | text | 0px | 1.00 | 4.5 |
| light | input.underline | surface | invalid | text | 0px | 1.00 | 4.5 |
| light | input.underline | warning | base | text | 0px | 3.19 | 4.5 |
| light | input.underline | warning | focus | text | 0px | 3.19 | 4.5 |
| light | input.underline | warning | invalid | text | 0px | 3.19 | 4.5 |
| light | input.underline | light | base | text | 0px | 1.10 | 4.5 |
| light | input.underline | light | focus | text | 0px | 1.10 | 4.5 |
| light | input.underline | light | invalid | text | 0px | 1.10 | 4.5 |
| dark | surface.outlined | surface | base | text | 0px | 1.00 | 4.5 |
| dark | surface.outlined | surface | base | border | 0px | 1.00 | 3 |
| dark | surface.outlined | dark | base | text | 0px | 1.04 | 4.5 |
| dark | surface.outlined | dark | base | border | 0px | 1.04 | 3 |
| dark | surface.outlined | light | base | text | 0px | 1.38 | 4.5 |
| dark | surface.outlined | light | base | border | 0px | 1.38 | 3 |
| dark | surface.flat | surface | base | text | 0px | 1.00 | 4.5 |
| dark | surface.flat | dark | base | text | 0px | 1.04 | 4.5 |
| dark | surface.flat | light | base | text | 0px | 1.38 | 4.5 |
| dark | button.contained | neutral | hover | text | 0px | 2.77 | 4.5 |
| dark | button.contained | neutral | selected | text | 0px | 2.77 | 4.5 |
| dark | button.contained | light | hover | text | 0px | 1.18 | 4.5 |
| dark | button.contained | light | selected | text | 0px | 1.18 | 4.5 |
| dark | button.outlined | surface | base | text | 0px | 1.00 | 4.5 |
| dark | button.outlined | surface | base | border | 0px | 1.00 | 3 |
| dark | button.outlined | surface | hover | text | 0px | 3.21 | 4.5 |
| dark | button.outlined | surface | selected | border | 0px | 1.00 | 3 |
| dark | button.outlined | dark | base | text | 0px | 1.04 | 4.5 |
| dark | button.outlined | dark | base | border | 0px | 1.04 | 3 |
| dark | button.outlined | dark | hover | text | 0px | 1.00 | 4.5 |
| dark | button.outlined | dark | hover | border | 0px | 1.00 | 3 |
| dark | button.outlined | dark | selected | border | 0px | 1.04 | 3 |
| dark | button.outlined | neutral | hover | text | 0px | 2.66 | 4.5 |
| dark | button.outlined | neutral | hover | border | 0px | 2.66 | 3 |
| dark | button.outlined | light | base | text | 0px | 1.38 | 4.5 |
| dark | button.outlined | light | base | border | 0px | 1.38 | 3 |
| dark | button.outlined | light | selected | border | 0px | 1.38 | 3 |
| dark | button.flat | surface | base | text | 0px | 1.00 | 4.5 |
| dark | button.flat | surface | hover | text | 0px | 3.21 | 4.5 |
| dark | button.flat | surface | selected | text | 0px | 3.21 | 4.5 |
| dark | button.flat | dark | base | text | 0px | 1.04 | 4.5 |
| dark | button.flat | dark | hover | text | 0px | 1.00 | 4.5 |
| dark | button.flat | dark | selected | text | 0px | 1.00 | 4.5 |
| dark | button.flat | neutral | hover | text | 0px | 2.66 | 4.5 |
| dark | button.flat | neutral | selected | text | 0px | 2.66 | 4.5 |
| dark | button.flat | light | base | text | 0px | 1.38 | 4.5 |
| dark | input.contained | — | base | placeholder | 0px | 2.66 | 4.5 |
| dark | input.contained | — | focus | placeholder | 0px | 2.66 | 4.5 |
| dark | input.contained | — | invalid | placeholder | 0px | 2.66 | 4.5 |
| dark | input.contained | surface | focus | border | 0px | 1.00 | 3 |
| dark | input.contained | dark | focus | border | 0px | 1.04 | 3 |
| dark | input.contained | light | focus | border | 0px | 1.38 | 3 |
| dark | input.outlined | — | base | placeholder | 0px | 2.66 | 4.5 |
| dark | input.outlined | — | focus | placeholder | 0px | 2.66 | 4.5 |
| dark | input.outlined | — | invalid | placeholder | 0px | 2.66 | 4.5 |
| dark | input.outlined | surface | base | text | 0px | 1.00 | 4.5 |
| dark | input.outlined | surface | base | border | 0px | 1.00 | 3 |
| dark | input.outlined | surface | focus | text | 0px | 1.00 | 4.5 |
| dark | input.outlined | surface | focus | border | 0px | 1.00 | 3 |
| dark | input.outlined | surface | invalid | text | 0px | 1.00 | 4.5 |
| dark | input.outlined | dark | base | text | 0px | 1.04 | 4.5 |
| dark | input.outlined | dark | base | border | 0px | 1.04 | 3 |
| dark | input.outlined | dark | focus | text | 0px | 1.04 | 4.5 |
| dark | input.outlined | dark | focus | border | 0px | 1.04 | 3 |
| dark | input.outlined | dark | invalid | text | 0px | 1.04 | 4.5 |
| dark | input.outlined | light | base | text | 0px | 1.38 | 4.5 |
| dark | input.outlined | light | base | border | 0px | 1.38 | 3 |
| dark | input.outlined | light | focus | text | 0px | 1.38 | 4.5 |
| dark | input.outlined | light | focus | border | 0px | 1.38 | 3 |
| dark | input.outlined | light | invalid | text | 0px | 1.38 | 4.5 |
| dark | input.underline | — | base | placeholder | 0px | 2.66 | 4.5 |
| dark | input.underline | — | focus | placeholder | 0px | 2.66 | 4.5 |
| dark | input.underline | — | invalid | placeholder | 0px | 2.66 | 4.5 |
| dark | input.underline | surface | base | text | 0px | 1.00 | 4.5 |
| dark | input.underline | surface | focus | text | 0px | 1.00 | 4.5 |
| dark | input.underline | surface | invalid | text | 0px | 1.00 | 4.5 |
| dark | input.underline | dark | base | text | 0px | 1.04 | 4.5 |
| dark | input.underline | dark | focus | text | 0px | 1.04 | 4.5 |
| dark | input.underline | dark | invalid | text | 0px | 1.04 | 4.5 |
| dark | input.underline | light | base | text | 0px | 1.38 | 4.5 |
| dark | input.underline | light | focus | text | 0px | 1.38 | 4.5 |
| dark | input.underline | light | invalid | text | 0px | 1.38 | 4.5 |