Fix M15: replace role=button divs with native button elements in LibraryView

Both interactive headers in LibraryView used role="button" on divs, which is
invalid when they contain child interactive elements (group header had nested
All/None and Download buttons).

- Group header (groupHead): split into an outer flex div + a native <button>
  (groupToggle style) for the toggle/expand action + sibling Fluent Buttons
  for All/None and Download. The toggle button gets aria-expanded and a
  composite aria-label (title + item count). e.stopPropagation() removed
  from the sibling buttons since they are no longer inside the toggle.

- Source card header (cardHead): converted from a role="button" div to a
  native <button> with width:100%, border:none, and background:transparent.
  aria-label set to source.title. tabIndex/onKeyDown removed (native button
  handles keyboard by default).

typecheck + 242 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 14:37:33 -04:00
parent 2f59a6ce63
commit 53aaed5fef
2 changed files with 39 additions and 27 deletions
+1 -1
View File
@@ -263,7 +263,7 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
- [ ] **M14 — Settings search mutates React-owned DOM.** The search toggles each card's
`el.style.display` directly; fragile (breaks if a card ever gets a conditional `style`) and
matches on `textContent` incl. hidden text. Prefer state-driven filtering.
- [ ] **M15 — Nested interactive controls in `role="button"` (a11y).** LibraryView's group header
- [x] **M15 — Nested interactive controls in `role="button"` (a11y).** LibraryView's group header
is a `role="button"` div containing `<Button>`s (All / Download) — invalid ARIA / keyboard
semantics. Make the header a real element with sibling buttons.
- [x] **M16 — No renderer error boundary.** An exception in any view unmounts the whole shell to