Skip to content

Commit cf33ac8

Browse files
committed
Docs: document the APCA Lc-45 floor and brag the dual WCAG-2 + APCA verification
- README: a "Easy on the eyes" feature line — every text color is verified against both WCAG 2.2 AA and APCA, with nothing below APCA Lc 45. - `scripts/check-a11y-contrast/README.md`: a new "APCA floor" section (the enforced Lc-45 bar, the advisory `-verbose` detail, why we gate the floor and not the full ladder, and APCA's draft status), plus `apca.go` / `apca_test.go` in the architecture and tests maps and the threshold-tuning note.
1 parent e28b9a0 commit cf33ac8

2 files changed

Lines changed: 31 additions & 5 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ Core features:
4242
- **Keyboard-first**: do anything without touching your mouse, using your familiar shortcuts.
4343
- **Blazing fast file operations**: copy, move, rename, and delete with a few keystrokes.
4444
- **Optional, privacy-first AI**: search and select with natural language, all on your Mac.
45+
- **Easy on the eyes**: real dark and light modes, and every text color is verified against both WCAG 2.2 AA and APCA,
46+
the modern perceptual contrast method, so nothing falls below APCA Lc 45.
4547

4648
## Installation
4749

scripts/check-a11y-contrast/README.md

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# check-a11y-contrast
22

3-
Design-time WCAG 2.2 contrast checker for the Cmdr desktop app.
3+
Design-time contrast checker for the Cmdr desktop app: WCAG 2.2 AA (the primary gate) plus an enforced APCA Lc-45
4+
perceptual floor.
45

56
## Why
67

@@ -23,11 +24,11 @@ go run ./scripts/check-a11y-contrast
2324
# Via check runner
2425
pnpm check a11y-contrast
2526

26-
# Verbose (show warnings from unresolvable values)
27+
# Verbose (warnings from unresolvable values + the full APCA advisory detail)
2728
go run ./scripts/check-a11y-contrast -- --verbose
2829
```
2930

30-
Exit code 0 on clean, 1 on any violation.
31+
Exit code 0 on clean, 1 on any violation (a WCAG pair below threshold OR an APCA pair below the Lc-45 floor).
3132

3233
## What it checks
3334

@@ -71,6 +72,24 @@ stripped (those carry old-WebKit hex fallbacks that would otherwise overwrite th
7172
more than one — for example a small block carrying the selection-fg fallback rule sits before the main dark token
7273
table).
7374

75+
## APCA floor (perceptual second opinion)
76+
77+
On top of the WCAG gate, the tool runs every evaluated pair through APCA (the Accessible Perceptual Contrast Algorithm,
78+
the method explored for WCAG 3), using the canonical `apca-w3` 0.1.9 (W3) constants. APCA predicts real readability
79+
better than WCAG 2: it's polarity-aware (dark-on-light and light-on-dark differ, which WCAG 2 treats as identical) and
80+
accounts for font size and weight. Output is `Lc` ("lightness contrast"), signed, |Lc| ~0..108. See `apca.go`.
81+
82+
- **Enforced**: any pair below **APCA Lc 45** (APCA's "absolute minimum for any text") fails the check, alongside WCAG.
83+
- **Advisory** (printed only with `-verbose`): the full `|Lc|` distribution, a "blast radius if bar X were the gate"
84+
table, a zoom sweep (zoom relaxes the size-based target; it does not change `Lc`), and every pair below its
85+
font-size/weight-aware target. Non-verbose runs print a one-line summary plus the floor verdict.
86+
- **Why a floor, not the full APCA ladder**: APCA's preferred body level (Lc 75–90) would flag most of the app's
87+
14px/400 text — that 45–60 band is design intent (de-emphasized placeholders, hints, disabled), not a bug. We gate
88+
only the hard floor and keep the muted band advisory.
89+
- **Status** (verified 2026-06-30): APCA was removed from the WCAG 3 draft (2023) and now develops independently (ARC);
90+
its core math has been stable since 2022, which is why we enforce one conservative bar and keep the rest advisory.
91+
WCAG 2.2 AA stays the primary, legally-recognized gate.
92+
7493
## Output
7594

7695
```
@@ -117,7 +136,9 @@ analyzer.go Walks parsed rules per mode, tracks cascade state by
117136
compound class set, emits Finding per (selector, mode) pair.
118137
Uses `evaluateAt` to run worst-case across accent variants
119138
when the pair is accent-sensitive.
120-
reporter.go Pretty prints violations and optional warnings.
139+
reporter.go Pretty prints WCAG violations and optional warnings.
140+
apca.go APCA 0.1.9 (W3) Lc math, the font-size/weight target
141+
ladder, the enforced Lc-45 floor, and the advisory report.
121142
accent_matrix.go Runtime accent variants (the 8 macOS system accents +
122143
Cmdr gold) and the per-variant VarTable override.
123144
size_tiers.go `.size-*` utility classes × known container bgs, since
@@ -140,6 +161,8 @@ Tests:
140161
- `parser_test.go`: app.css + Svelte parsing, selector extraction.
141162
- `analyzer_test.go`: cascade inheritance, known false-positive cases.
142163
- `accent_matrix_test.go`: variant sweep + per-variant resolution.
164+
- `apca_test.go`: APCA reference values (black-on-white ≈ Lc 106, white-on-black ≈ −108), polarity asymmetry, target
165+
ladder.
143166

144167
Diagnostic helpers (skipped by default; gated on env vars):
145168

@@ -163,7 +186,8 @@ Edit `namedColors` map in `contrast.go`.
163186

164187
### Tune thresholds
165188

166-
WCAG AA (current) uses 4.5:1 / 3:1. For AAA, change the constants in `analyzer.evaluate` (7:1 / 4.5:1).
189+
WCAG AA (current) uses 4.5:1 / 3:1. For AAA, change the constants in `analyzer.evaluate` (7:1 / 4.5:1). The enforced
190+
APCA floor is `apcaFloor` in `apca.go` (Lc 45); the advisory size/weight target ladder is `apcaTiers` there.
167191

168192
### Add an allowlist for intentional violations
169193

0 commit comments

Comments
 (0)