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:
@@ -322,7 +322,13 @@ export function CalculatorWidget({ isEditMode }: WidgetProps) {
|
||||
return (
|
||||
<div
|
||||
className="flex h-full flex-col gap-1 p-1 select-none"
|
||||
tabIndex={0}
|
||||
// a11y/noNoninteractiveTabindex: -1 statt 0 -- die Zahlentasten sind
|
||||
// native <button>-Elemente und damit selbst Teil der Tab-Reihenfolge;
|
||||
// Tastendruecke erreichen handleKeyboard weiterhin per Bubbling, sobald
|
||||
// eine dieser Tasten fokussiert ist. -1 haelt den Container weiterhin
|
||||
// programmatisch fokussierbar, entfernt aber den zusaetzlichen,
|
||||
// wirkungslosen Tab-Stopp auf dem Container selbst.
|
||||
tabIndex={-1}
|
||||
role="application"
|
||||
aria-label={t('calculator.name')}
|
||||
onKeyDown={handleKeyboard}
|
||||
|
||||
@@ -50,7 +50,6 @@ export function ClockWidget({ config }: WidgetProps) {
|
||||
verwirft clamp() im Inline-Style, der Browser bekommt die Klasse
|
||||
ohnehin — und "auto = Klasse, fest = Zahl" ist die einfachere Form. */}
|
||||
<time
|
||||
role="time"
|
||||
data-font-mode={fixedPt === null ? 'auto' : 'fixed'}
|
||||
className="font-semibold tabular-nums leading-none text-foreground text-[clamp(12px,min(20cqw,50cqh),400px)]"
|
||||
style={fixedPt === null ? undefined : { fontSize: `${fixedPt}pt` }}
|
||||
|
||||
@@ -89,7 +89,6 @@ export function SearchWidget({ instanceId, config }: WidgetProps) {
|
||||
<div className="flex h-full items-center gap-2 px-1.5">
|
||||
{/* Provider dropdown (D-14: ~120px left) */}
|
||||
<select
|
||||
role="combobox"
|
||||
className="h-8 w-[120px] shrink-0 rounded border border-border bg-card px-2 text-sm text-foreground"
|
||||
value={selectedProviderId}
|
||||
onChange={(e) => handleProviderChange(e.target.value)}
|
||||
|
||||
@@ -35,10 +35,9 @@ export function WidgetWrapper({ widget, isEditMode, onRemove }: WidgetWrapperPro
|
||||
: 'relative h-full w-full overflow-hidden rounded-lg border border-primary/20 bg-card shadow-sm';
|
||||
|
||||
return (
|
||||
<div
|
||||
<article
|
||||
className={cardClassName}
|
||||
data-widget-id={widget.id}
|
||||
role="article"
|
||||
aria-label={definition ? t(definition.nameKey) : widget.widgetType}
|
||||
>
|
||||
{/* Edit mode: Overlay-Kopfleiste (Griff-Hinweis) mit Loesch-Knopf rechts.
|
||||
@@ -117,6 +116,6 @@ export function WidgetWrapper({ widget, isEditMode, onRemove }: WidgetWrapperPro
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user