fix(quick-260921-bi2): a11y - Rollen, Semantik, Tab-Reihenfolge (32er-Rest, Teil 1/2)

Vier der fuenf noch offenen Regeln aus Teillieferung B bereinigt:

- noRedundantRoles (4): ueberfluessige role-Angaben auf button/time/select
  entfernt (maschineller --unsafe-Fix, gelesen).
- useAriaPropsForRole (1): entfaellt automatisch mit obigem Fix -- das
  <select role="combobox"> in search-widget.tsx verlangte die fehlenden
  ARIA-Attribute nur wegen der ueberfluessigen Rolle.
- useSemanticElements (4): admin-sidebar/settings-sidebar tragen role=
  "navigation" jetzt am <nav> statt am <aside> (kein doppeltes Landmark
  mehr); widget-wrapper.tsx ist jetzt ein echtes <article> statt
  div role="article"; DropZone.tsx trennt die Datei-Entfernen-Schaltflaeche
  als Geschwister ab, damit die Drop-Flaeche selbst ein echtes <button>
  werden kann (ein <button> darf kein zweites <button> verschachteln).
  Die Drop-Flaeche traegt darum jetzt Klick- UND Drag-Handler direkt am
  <button>, sonst waere sie ein "statisches" Element mit Ereignis-Handlern
  geworden (die zurueckgestellten Regeln noStaticElementInteractions /
  noNoninteractiveElementInteractions waeren neu angeschlagen -- geprueft,
  bleiben bei 5/11).
- noNoninteractiveTabindex (1): calculator-widget.tsx traegt jetzt
  tabIndex={-1} statt {0}. Die Zifferntasten sind bereits echte <button>
  und damit selbst Teil der Tab-Reihenfolge; Tastendruecke erreichen
  handleKeyboard weiterhin per Bubbling, sobald eine Taste fokussiert ist.
  Verhalten unveraendert, nur ein wirkungsloser Tab-Stopp auf dem Container
  selbst entfaellt.

Verbleibend: noLabelWithoutControl (22), naechster Schritt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
2026-09-21 09:33:56 +02:00
parent 73ac08af17
commit 21c85a8b88
8 changed files with 64 additions and 54 deletions
@@ -89,17 +89,17 @@ export function AdminSidebar() {
];
return (
<aside
className="flex w-[220px] shrink-0 flex-col border-r border-sidebar-border bg-sidebar"
role="navigation"
aria-label={t('admin.label')}
>
// a11y/useSemanticElements: role="navigation" nicht auf das <aside> gehoben --
// das <nav> darunter trAegt die Navigationssemantik schon; ein zweites
// Navigations-Landmark auf dem Rahmen waere ein doppeltes Landmark fuer
// dieselbe Sache. Der aria-label sitzt darum am <nav>, nicht am <aside>.
<aside className="flex w-[220px] shrink-0 flex-col border-r border-sidebar-border bg-sidebar">
<div className="p-4">
<h2 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
{t('admin.label')}
</h2>
</div>
<nav className="flex flex-col gap-1 px-3">
<nav className="flex flex-col gap-1 px-3" aria-label={t('admin.label')}>
{items
.filter((item) => item.show)
.map((item) => (