Skip to content

fix(a11y): make click-only controls keyboard operable - #741

Draft
beruro wants to merge 4 commits into
developfrom
junyu/fix-a11y-keyboard-controls
Draft

fix(a11y): make click-only controls keyboard operable#741
beruro wants to merge 4 commits into
developfrom
junyu/fix-a11y-keyboard-controls

Conversation

@beruro

@beruro beruro commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Problem

Seven visible frontend controls were mouse-only: non-semantic click targets had no focus stop, keyboard activation, or exposed state. Keyboard and assistive-technology users could not operate collapsed diff rows, JSON nodes, the replace disclosure, the date trigger, simulator rows, or the primary BrowseCard action.

Solution

  • Use native buttons for the SearchInput and DateRangeSelector triggers.
  • Retain specialized diff/tree/list row elements only where their layout or nested controls rule out a generic button, and add role, focusability, Enter/Space activation, ARIA state/name, and focus-visible treatment.
  • Make JSON row click and keyboard handlers conditional so primitive rows remain inert.
  • Represent BrowseCard's primary hit area as a named stretched sibling button, keeping the secondary action non-nested and independently operable.
  • Reuse the existing createKeyboardActivationHandler; no new behavior abstraction was introduced.

The frontend UI audit is recorded in docs/frontend-ui-audit-2026-08-07/KeyboardOperability.md: 7 accessibility fixes, 7 raw-element decisions kept with documented design-system reasons, and 0 abstraction candidates.

Potential risks

  • BrowseCard secondary-action clicks no longer bubble into the primary card action. No current caller uses that branch, but future callers must treat the actions as independent.
  • Simulator and expandable JSON rows add intentional tab stops; primitive JSON rows remain inert, and existing virtualization bounds rendered list rows.
  • Two trigger elements changed from div to native button; repository CSS resets cover native defaults, but untracked downstream structure selectors could differ.
  • Focus-visible styling has not yet been captured inside the real Tauri desktop shell. The plain browser dev server cannot initialize getCurrentWindow() without Tauri window metadata, so this PR remains Draft until desktop visual QA/screenshots cover the affected states.
  • No Effect was added or modified. Rollback is a normal revert; there is no data, API, dependency, or persistence migration.

Verification

  • ./node_modules/.bin/vitest run src/util/dom/__tests__/keyboardActivation.test.ts src/engines/ChatPanel/components/DebugJsonViewer/DebugJsonViewer.test.tsPASS, 2 files / 7 tests.
  • git diff --name-only origin/develop...HEAD -- '*.ts' '*.tsx' | xargs ./node_modules/.bin/eslintPASS, no errors or warnings.
  • ./node_modules/.bin/tsc --noEmit --pretty falsePASS.
  • git diff --check origin/developPASS.
  • npm run dev:frontend:light and browser navigation to the local app — frontend compiled, but desktop visual QA was NOT COMPLETED because the non-Tauri browser host fails at Tauri window metadata initialization.
  • Merge of current origin/developPASS, no conflicts; automated checks were run after integration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant