Skip to content

Commit a22175b

Browse files
Updated per CodeRabbit suggestions
1 parent b9ccb5e commit a22175b

3 files changed

Lines changed: 25 additions & 13 deletions

File tree

‎app/globals.css‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,5 +781,8 @@ main#app .rf-page-btn:focus-visible {
781781
}
782782

783783
main#app .rf-data-table td[data-rf-copy]:focus-visible {
784-
outline-color: var(--brand-color);
784+
/* --brand-accent (the focus-indicator role), not --brand-color (the
785+
accent-background role): #2a6f97 is only 2.9:1 against the dark
786+
--surface-color, below the 3:1 of SC 2.4.11. */
787+
outline-color: var(--brand-accent);
785788
}

‎plans/css-only-dark-mode.md‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ split because #2a6f97 as *text* on a dark page fails AA (3.3:1), while as a
7575
|---|---|---|---|---|
7676
| `--brand-color` | accent **backgrounds** (CTA, hover fills) | #2a6f97 | #2a6f97 | white text 5.5:1 |
7777
| `--link-color` (new) | accent **text** (links, wordmark, hero h1) | #2a6f97 | #6fb1d9 | 7.7:1 on dark bg |
78-
| `--brand-accent` | hover bg + focus indicator | #1f5273 | #2a6f97 | 3.3:1 on dark bg, 5.5:1 vs white |
78+
| `--brand-accent` | hover bg + focus indicator | #1f5273 | #3178a6 | 3.8:1 on dark bg, 3.3:1 vs surface, 4.8:1 vs white |
7979
| `--bg-color` / `--surface-color` | page / panels | #ffffff / #f5f8fa | #10161d / #1a2330 | — |
8080
| `--text-color` / `--muted-color` | text | #1f2d3d / #5c6b7a | #e6edf3 / #9fb0c0 | 13:1 / 8:1 |
81-
| `--border-color`, `--input-border`, `--card-bg` | chrome (was #dde4ea / #c6d2dc / #fff hardcoded) | as today | #2c3a4d / #566d8f / #1a2330 | input border ≥3:1 vs surface |
81+
| `--border-color`, `--input-border`, `--card-bg` | chrome (was #dde4ea / #c6d2dc / #fff hardcoded) | as today | #2c3a4d / #5b7396 / #1a2330 | input border ≥3:1 vs surface |
8282
| `--error-color` / `--success-color` | status text | #c0392b / #1e8449 | #f0928c / #5bbd8b | ~8:1 on dark bg |
8383
| `--error-bg` / `--error-border` | error page panel | #fdf6f5 / #f0c6c0 | #2b1d1f / #7a4a48 | — |
8484

@@ -132,12 +132,14 @@ absolute positioning, no magic numbers.
132132

133133
- Container: `<div role="radiogroup" aria-label="Color theme">` — three
134134
labelled, native radios (`aria-label` "Light theme" / "Dark theme" /
135-
"System preference"), visually hidden via
136-
`position: absolute; opacity: 0` (still keyboard-focusable).
135+
"System preference"), each visually replaced by its icon pill via
136+
`appearance: none; position: absolute; inset: 0` (still keyboard-focusable;
137+
the checked input itself renders the brand pill).
137138
- Each pill: 28×28px label (≥ 24px, SC 2.5.8), inline `stroke="currentColor"`
138139
SVG icon (`aria-hidden="true"` — the radio label carries the name).
139-
- Selected state: `:checked + svg` (brand pill, white icon). Focus:
140-
`:focus-visible + svg` outline (SC 2.4.7/2.4.11).
140+
- Selected state: `:checked` background on the input plus
141+
`:checked + svg` icon recolor. Focus: the global `:focus-visible` rule
142+
outlines the input (SC 2.4.7/2.4.11).
141143
- `color-scheme` per palette + `<meta name="color-scheme" content="light dark">`
142144
for native controls, scrollbars and the pre-CSS canvas.
143145
- No color-transition animation (instant switch; nothing for
@@ -165,9 +167,9 @@ CSS variables in `lib/report-view.js` at render time).
165167
3. `tests/unit/css-a11y.test.js` — extend the resolver to be palette-aware
166168
(light values from the base block, dark values from the dark blocks) and
167169
assert the existing light guarantees **and** their dark equivalents. The
168-
palette values are chosen so the file's last-occurrence resolution (used
169-
by the unchanged light tests) still passes: dark `--brand-accent`
170-
(#2a6f97) must keep ≥3:1 vs white as well as vs the dark background.
170+
palette values are chosen so the file's last-occurrence resolution (used
171+
by the unchanged light tests) still passes: dark `--brand-accent`
172+
(#3178a6) must keep ≥3:1 vs white as well as vs the dark background.
171173
4. `tests/e2e/dark-mode.spec.js` (against the built export, as always):
172174
- toggle right edge == `.menu-bar` content right edge (±1px, incl. mobile);
173175
- three options render with icons, system preselected;
@@ -228,8 +230,8 @@ mustache-rendered).
228230
`css-a11y.test.js`/`layout-styles.test.js` parse globals.css with a
229231
flat regex; the plan deliberately keeps single-level `@media` nesting and
230232
chooses dark values that also satisfy the light assertions under the
231-
parser's last-occurrence variable resolution (e.g. dark `--brand-accent`
232-
#2a6f97 is ≥3:1 vs white). The new `theme-css.test.js` then adds a
233+
parser's last-occurrence variable resolution (e.g. dark `--brand-accent`
234+
#3178a6 is ≥3:1 vs white). The new `theme-css.test.js` then adds a
233235
palette-aware resolver so this fragile coincidence becomes an enforced
234236
property instead of luck.
235237
6. **Duplication risk** — the dark block exists twice (media + explicit).
@@ -246,7 +248,8 @@ mustache-rendered).
246248
9. **Contrast choices are computed, not eyeballed** — every dark palette pair
247249
is asserted in tests using the same WCAG math as the existing suite; values
248250
were pre-verified (dark bg/text ≈13:1, muted ≈8:1, links ≈7.7:1, focus
249-
indicator 3.3:1, brand-bg+white 5.5:1, error/success ≈8:1).
251+
indicator 3.8:1 on the page bg (3.3:1 on surfaces), brand-bg+white 5.5:1,
252+
error/success ≈8:1).
250253
10. **Alignment is structural, not geometric** — placing the toggle inside
251254
`.menu-bar` (like the search before it) guarantees right-edge alignment
252255
at every viewport with zero positioning code; the E2E test pins it with a

‎tests/unit/theme-css.test.js‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,12 @@ describe('dark mode cascade structure', () => {
196196
// backgrounds; the override must come from the palette.
197197
const focus = rules.get('main#app .rf-page-btn:focus-visible');
198198
expect(focus).toMatch(/outline-color:\s*var\(--brand-accent\)/);
199+
200+
// The cell-copy focus ring must use the focus-indicator role too:
201+
// --brand-color (the accent *background*) is only 2.9:1 against the
202+
// dark --surface-color, below the 3:1 of SC 2.4.11.
203+
const cellCopy = rules.get('main#app .rf-data-table td[data-rf-copy]:focus-visible');
204+
expect(cellCopy).toMatch(/outline-color:\s*var\(--brand-accent\)/);
199205
});
200206
});
201207

0 commit comments

Comments
 (0)