From 4aec32e3c29e84299c73f0655f8000ad940dd6f8 Mon Sep 17 00:00:00 2001 From: debont80 Date: Wed, 1 Jul 2026 08:02:54 -0400 Subject: [PATCH] a11y: semantic headings (UI33) + command-palette listbox semantics (UI27) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UI33 — render titles as real headings so Narrator heading-navigation works: the shared ScreenHeader title is now (one h1 per screen in one place — Downloads/History/Library/Settings/Terminal); DownloadsView "Queue (N)" and all 11 settings cards are sections; Onboarding "Welcome" is . 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 --- CODE-AUDIT.md | 18 +++++++++-- src/renderer/src/assets/base.css | 18 +++++++++++ .../src/components/CommandPalette.tsx | 32 ++++++++++++------- src/renderer/src/components/DownloadsView.tsx | 2 +- src/renderer/src/components/Onboarding.tsx | 2 +- .../src/components/settings/AboutCard.tsx | 2 +- .../components/settings/AppearanceCard.tsx | 2 +- .../src/components/settings/BackupCard.tsx | 2 +- .../src/components/settings/CookiesCard.tsx | 2 +- .../settings/CustomCommandsCard.tsx | 2 +- .../components/settings/DiagnosticsCard.tsx | 2 +- .../src/components/settings/DownloadsCard.tsx | 2 +- .../src/components/settings/FilenamesCard.tsx | 2 +- .../src/components/settings/NetworkCard.tsx | 2 +- .../settings/PostProcessingCard.tsx | 2 +- .../settings/SoftwareUpdateCard.tsx | 2 +- src/renderer/src/components/ui/Screen.tsx | 2 +- 17 files changed, 69 insertions(+), 27 deletions(-) diff --git a/CODE-AUDIT.md b/CODE-AUDIT.md index 893bfab..23aaab6 100644 --- a/CODE-AUDIT.md +++ b/CODE-AUDIT.md @@ -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 `` 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 `