fix: give each kanban board tab a persistent tabpanel for ARIA compliance

Inactive tabs' aria-controls pointed at an ID that only existed for the
active board, and the one existing tabpanel wrapped just the breadcrumb
text instead of the actual columns/cards. Render all four board panels
always in the DOM with v-show instead of v-if so every tab's
aria-controls resolves to a real, persistent panel wrapping its real
content, per the WAI-ARIA Tabs pattern.
This commit is contained in:
pyr0ball 2026-07-14 13:18:11 -07:00
parent 634da8c309
commit cee0dcd36a

View file

@ -79,7 +79,7 @@ function labelFor(stage) {
</button>
</div>
<div class="mobile-breadcrumb" role="tabpanel" :id="`board-panel-${activeBoardKey}`">
<div class="mobile-breadcrumb">
{{ activeBoard.label }} &rsaquo; {{ labelFor(activeStage) }}
</div>
@ -97,16 +97,24 @@ function labelFor(stage) {
</button>
</div>
<div class="columns">
<div
v-for="board in BOARDS"
:key="board.key"
v-show="board.key === activeBoardKey"
role="tabpanel"
:id="`board-panel-${board.key}`"
:aria-labelledby="`board-tab-${board.key}`"
class="columns"
>
<div
v-for="stage in activeBoard.stages"
v-for="stage in board.stages"
:key="stage"
class="column"
:class="{ 'is-active-mobile': stage === activeStage }"
:class="{ 'is-active-mobile': stage === activeStage && board.key === activeBoardKey }"
>
<h3>{{ labelFor(stage) }} ({{ itemsForStage(activeBoardKey, stage).length }})</h3>
<h3>{{ labelFor(stage) }} ({{ itemsForStage(board.key, stage).length }})</h3>
<button
v-for="item in itemsForStage(activeBoardKey, stage)"
v-for="item in itemsForStage(board.key, stage)"
:key="item.id"
type="button"
class="card"
@ -117,7 +125,7 @@ function labelFor(stage) {
<p class="card-snippet">{{ item.raw_content.slice(0, 80) }}</p>
<p v-if="item.follow_up_date" class="card-followup">Follow up: {{ item.follow_up_date }}</p>
</button>
<p v-if="itemsForStage(activeBoardKey, stage).length === 0" class="column-empty">Nothing here</p>
<p v-if="itemsForStage(board.key, stage).length === 0" class="column-empty">Nothing here</p>
</div>
</div>
</div>