a11y: semantic headings (UI33) + command-palette listbox semantics (UI27)
UI33 — render titles as real headings so Narrator heading-navigation works: the shared ScreenHeader title is now <Subtitle2 as="h1"> (one h1 per screen in one place — Downloads/History/Library/Settings/Terminal); DownloadsView "Queue (N)" and all 11 settings cards are <Subtitle2 as="h2"> sections; Onboarding "Welcome" is <Title2 as="h1">. A minimal h1–h6 margin reset in base.css zeroes the UA heading margin (Fluent's typography classes out-specify the element selector, so the visual ramp is unchanged) so layout matches the former spans. UI27 — give the command palette proper combobox/listbox semantics: the input is role="combobox" with aria-controls/aria-activedescendant/aria-autocomplete, the list is role="listbox", and each row is role="option" with aria-selected, so the active row is announced to screen readers via active-descendant. Focus stays on the input (standard combobox pattern), so the rows became non-focusable divs; onMouseEnter is kept to unify pointer + keyboard highlight on one sel state. Also corrected UI30's checkbox in the audit (already implemented via the shared SegmentedControl radiogroup; only the marker lagged). typecheck + 253 tests + eslint + prettier + production build all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+16
-2
@@ -952,9 +952,16 @@ are an undefined spacing/radius/type scale and a drift between Fluent components
|
||||
|
||||
### Hover, focus, disabled, keyboard & accessibility
|
||||
|
||||
- [ ] **UI27 — Command palette uses JS hover, not CSS.** Items highlight via `onMouseEnter` setting
|
||||
- [x] **UI27 — Command palette uses JS hover, not CSS.** Items highlight via `onMouseEnter` setting
|
||||
`itemActive` (no `:hover`), unlike every other list, and expose no `aria-selected`/active-descendant,
|
||||
so the highlight is invisible to screen readers. **Standard:** CSS `:hover` + listbox/option semantics.
|
||||
*Fixed in [CommandPalette.tsx](src/renderer/src/components/CommandPalette.tsx): the input is now a
|
||||
`role="combobox"` with `aria-controls`/`aria-activedescendant`/`aria-autocomplete="list"`, the list is a
|
||||
`role="listbox"`, and each row is a `role="option"` with `aria-selected` — so the active row is announced
|
||||
to Narrator via active-descendant (the standard combobox pattern; focus stays on the input, so the rows
|
||||
became non-focusable `div`s rather than `button`s). `onMouseEnter` is kept deliberately: it unifies the
|
||||
pointer and keyboard highlight onto one `sel` state, which — now that `aria-activedescendant` tracks it —
|
||||
is more correct than a pure CSS `:hover` (which wouldn't move the announced selection).*
|
||||
- [x] **UI28 — Command palette input has no focus ring.** The search `<input>` sets `outline: 'none'`
|
||||
with no replacement. **Standard:** a visible focus ring (Fluent stroke token). *(a11y)*
|
||||
- [x] **UI29 — Fragmented focus treatment.** Fluent ring (Fluent controls) vs custom border (Select)
|
||||
@@ -968,9 +975,16 @@ are an undefined spacing/radius/type scale and a drift between Fluent components
|
||||
so that control can't show a disabled state while Fluent controls can. **Standard:** add `disabled` + styling.
|
||||
- [ ] **UI32 — Native-control dark mode is uneven.** The DownloadBar `datetime-local` sets
|
||||
`colorScheme: 'light dark'` explicitly; the `Select` relies on the root `colorScheme`. **Standard:** set `colorScheme` consistently on both.
|
||||
- [ ] **UI33 — No semantic headings.** Titles render as `Subtitle2`/`Title2` (styled spans), so there's
|
||||
- [x] **UI33 — No semantic headings.** Titles render as `Subtitle2`/`Title2` (styled spans), so there's
|
||||
no h1–h6 hierarchy for screen-reader heading navigation (landmarks `<nav>`/`<main>` exist; headings
|
||||
don't). **Standard:** render titles as real headings (`as="h1"`/`"h2"` or `role="heading" aria-level`). *(a11y)*
|
||||
*Fixed by rendering the Fluent typography titles as real headings (the `as` prop keeps the visual ramp):
|
||||
the shared [ScreenHeader](src/renderer/src/components/ui/Screen.tsx) title is now `<Subtitle2 as="h1">`,
|
||||
giving every screen (Downloads, History, Library, Settings, Terminal) one `h1` in one place; the
|
||||
DownloadsView "Queue (N)" and all 11 settings cards are `<Subtitle2 as="h2">` sections beneath it; and the
|
||||
Onboarding "Welcome" title is `<Title2 as="h1">`. A minimal `h1–h6` margin reset in
|
||||
[base.css](src/renderer/src/assets/base.css) zeroes the UA heading margin (Fluent's typography classes
|
||||
out-specify the element selector, so the visual ramp is unchanged) so the layout matches the former spans.*
|
||||
|
||||
### Top cohesion-breakers (the "not one product" shortlist)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user