The mobile legend's toggles announce their state

Every toggle in this file carried aria-label="aria.toggleLegend" and no
aria-expanded, so a screen-reader user was told the control is a toggle and
never told whether the legend was open or closed. SemanticTreeLegend has had
this right all along; this is the copy that drifted, and it drifted on whether
the control is usable without sight rather than on how it looks.

gospel found it while measuring four implementations of the same control. I
verified it here before changing anything rather than taking the report: zero
occurrences of aria-expanded against four toggle labels.

One computed drives all four buttons, so they cannot disagree with each other
about a state they all describe — which is the same failure, one level down,
as four files disagreeing about the control.

This is the accessibility half of the C11 collapse-caret promotion and does not
wait for it: the promotion is a refactor, this is a defect.
This commit is contained in:
Doxa Maps 2026-09-28 19:41:26 +00:00
parent 16ff7e719b
commit b355594958

View file

@ -539,6 +539,16 @@ const justDragged = () => Date.now() - lastDragEndAt < 400
// Computed properties from stores // Computed properties from stores
const legendState = computed(() => uiStore.legendState) const legendState = computed(() => uiStore.legendState)
/**
* ★ WHAT THE CARET ANNOUNCES. Every toggle in this file carried aria-label="aria.toggleLegend" and
* NO aria-expanded, so a screen-reader user was told the control is a toggle and never told its
* STATE — they could not hear whether the legend was open or closed. SemanticTreeLegend has had
* this right all along; this is the copy that drifted, and it drifted on whether the control is
* usable without sight rather than on decoration. gospel found it while measuring four
* implementations of the same control; I verified it here before changing anything: 0 occurrences
* of aria-expanded against 4 toggle labels.
*/
const legendExpanded = computed(() => legendState.value !== 'collapsed')
const legendHeight = computed(() => uiStore.legendHeight) const legendHeight = computed(() => uiStore.legendHeight)
const caretRotation = computed(() => uiStore.caretRotation) const caretRotation = computed(() => uiStore.caretRotation)
const showPullTab = computed(() => uiStore.showPullTab) const showPullTab = computed(() => uiStore.showPullTab)
@ -796,6 +806,7 @@ onBeforeUnmount(() => {
v-if="legendMode === 'detail' && legendState !== 'collapsed'" v-if="legendMode === 'detail' && legendState !== 'collapsed'"
class="detail-collapse-btn-mobile" class="detail-collapse-btn-mobile"
@click.stop="handleCaretClick" @click.stop="handleCaretClick"
:aria-expanded="String(legendExpanded)"
:aria-label="t('aria.toggleLegend')" :aria-label="t('aria.toggleLegend')"
></button> ></button>
@ -825,7 +836,8 @@ onBeforeUnmount(() => {
> >
<button class="mobile-collapse-caret" <button class="mobile-collapse-caret"
:style="{ transform: `rotate(${caretRotation}deg)` }" :style="{ transform: `rotate(${caretRotation}deg)` }"
:aria-label="t('aria.toggleLegend')" :aria-expanded="String(legendExpanded)"
:aria-label="t('aria.toggleLegend')"
tabindex="-1" tabindex="-1"
> >
<svg width="9" height="9" viewBox="0 0 12 12" fill="none"> <svg width="9" height="9" viewBox="0 0 12 12" fill="none">
@ -875,7 +887,8 @@ onBeforeUnmount(() => {
<button class="mobile-collapse-caret" <button class="mobile-collapse-caret"
:style="{ transform: `rotate(${caretRotation}deg)` }" :style="{ transform: `rotate(${caretRotation}deg)` }"
@click.stop="handleCaretClick" @click.stop="handleCaretClick"
:aria-label="t('aria.toggleLegend')"> :aria-expanded="String(legendExpanded)"
:aria-label="t('aria.toggleLegend')">
<svg width="9" height="9" viewBox="0 0 12 12" fill="none"> <svg width="9" height="9" viewBox="0 0 12 12" fill="none">
<path d="M4.5 3L7.5 6L4.5 9" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/> <path d="M4.5 3L7.5 6L4.5 9" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>
@ -906,7 +919,8 @@ onBeforeUnmount(() => {
<button class="mobile-collapse-caret" <button class="mobile-collapse-caret"
:style="{ transform: `rotate(${caretRotation}deg)` }" :style="{ transform: `rotate(${caretRotation}deg)` }"
@click.stop="handleCaretClick" @click.stop="handleCaretClick"
:aria-label="t('aria.toggleLegend')"> :aria-expanded="String(legendExpanded)"
:aria-label="t('aria.toggleLegend')">
<svg width="9" height="9" viewBox="0 0 12 12" fill="none"> <svg width="9" height="9" viewBox="0 0 12 12" fill="none">
<path d="M4.5 3L7.5 6L4.5 9" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/> <path d="M4.5 3L7.5 6L4.5 9" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>