web: accessibility audit fixes, slice 12 of M3
Walked the spec section 8 checklist against the running app. Two gaps fixed: the menus now close on Tab instead of leaking focus behind the open menu (the opener restores focus), and the hero's mobile About note restores focus to its trigger on close. Audit results, item by item: - Keyboard: all controls reachable and operable; menus arrow-cycle, Esc/Tab close with focus restore; the only trap is the focus modal. - Focus indicator: global 2 px ink outline with 2 px offset. - Player keys: Space/arrows/Home/End global except text inputs; the scrubber is a native range with aria-valuetext "Round x of y". - Reduced motion: no autoplay, page settles on the final round, discrete swaps, 1000 ms cadence, smooth scrolling and crossfades off. - Contrast: informative text uses --ink-3 or stronger; --ink-4 appears only on text duplicated in the data table or decorative numerals. - Node state is shape-encoded (disc / donut / small dot / halo ring). - Network SVGs aria-hidden; each panel is a labelled region whose text carries the result; the collapsed ResultsTable is a real table with th scope headers behind a keyboard disclosure. - One polite live region announces runs, pauses, scrub releases, and the final state; rounds are not announced during playback. - FocusModal: dialog semantics, trap, Esc, focus restore. ErrorBanner and inline 400 notes use role=alert. lang=en, header/main/footer. - Touch targets 44 px on mobile; node hit halos 24 px. - 200% zoom at 1366 px (683 px viewport) reflows to the mobile stack with nothing lost (verified by screenshot).
This commit is contained in:
parent
5c4a92b5a0
commit
d9b3812306
2 changed files with 10 additions and 2 deletions
|
|
@ -33,6 +33,11 @@ const segments = computed<NarrativeSegment[]>(() =>
|
||||||
|
|
||||||
const aboutOpen = ref(false)
|
const aboutOpen = ref(false)
|
||||||
const noteButton = ref<HTMLButtonElement | null>(null)
|
const noteButton = ref<HTMLButtonElement | null>(null)
|
||||||
|
|
||||||
|
function closeAbout() {
|
||||||
|
aboutOpen.value = false
|
||||||
|
noteButton.value?.focus()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -58,7 +63,7 @@ const noteButton = ref<HTMLButtonElement | null>(null)
|
||||||
</svg>
|
</svg>
|
||||||
{{ preset.disclaimerShort }}
|
{{ preset.disclaimerShort }}
|
||||||
</button>
|
</button>
|
||||||
<AboutPopover v-if="aboutOpen" :anchor="noteButton" @close="aboutOpen = false" />
|
<AboutPopover v-if="aboutOpen" :anchor="noteButton" @close="closeAbout" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,10 @@ function menuButtons(): HTMLButtonElement[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeydown(event: KeyboardEvent) {
|
function onKeydown(event: KeyboardEvent) {
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape' || event.key === 'Tab') {
|
||||||
|
// Tab closes like Esc instead of leaking focus behind the menu; the
|
||||||
|
// opener restores focus and the next Tab continues from there.
|
||||||
|
event.preventDefault()
|
||||||
emit('close')
|
emit('close')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue