audit pass 8: keyboard accessibility and table semantics

CSS:
- Fix light theme input/select/textarea :focus -> :focus-visible

HTML:
- Worker metrics table: convert label <td> to <th scope="row"> for screen readers
- Add aria-label to worker metrics table
- Sticky table: add scope="col" to all column headers
- Keyboard shortcuts modal table: add scope="col" to headers
- Kanban cards: remove tabindex="0" from role="article" (non-interactive)
- Advanced filter: ensure all 3 label/select pairs have for/id associations

JS:
- Lightbox: fix keydown listener leak by storing bound reference for removeEventListener
- Lightbox: save/restore trigger focus on open/close
- Sortable table: add tabindex="0" and Enter/Space keydown handler on sortable <th>
- Split pane: add tabindex="0", role="separator", aria-label, and arrow/Home/End
  keyboard resize support on divider (5% steps)
- Form validation: handle <select multiple> required check via selectedOptions.length

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-26 20:09:29 -04:00
parent b84d71dd7a
commit e8c1197613
3 changed files with 49 additions and 20 deletions
+13 -13
View File
@@ -559,7 +559,7 @@
<div class="lt-section-header">Open</div>
<div class="lt-section-body" id="kanban-col-open" style="min-height:60px">
<div class="lt-card lt-mb-md lt-row-p1" role="article" tabindex="0" aria-label="P1 — Storage array link-down, 5m ago, Unassigned">
<div class="lt-card lt-mb-md lt-row-p1" role="article" aria-label="P1 — Storage array link-down, 5m ago, Unassigned">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-p1" aria-hidden="true">P1</span>
<span class="lt-dot lt-dot-up" aria-hidden="true"></span>
@@ -568,7 +568,7 @@
<div class="lt-text-xs lt-text-muted lt-mt-sm">5m ago · Unassigned</div>
</div>
<div class="lt-card lt-row-p3" role="article" tabindex="0" aria-label="P3 — Update node_exporter on micro1, 1d ago, operator">
<div class="lt-card lt-row-p3" role="article" aria-label="P3 — Update node_exporter on micro1, 1d ago, operator">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-p3" aria-hidden="true">P3</span>
<span class="lt-dot lt-dot-up" aria-hidden="true"></span>
@@ -586,7 +586,7 @@
<span class="lt-frame-br"></span>
<div class="lt-section-header">Pending</div>
<div class="lt-section-body" id="kanban-col-pending" style="min-height:60px">
<div class="lt-card lt-row-p2" role="article" tabindex="0" aria-label="P2 — Scheduled maintenance window, 2d ago, admin">
<div class="lt-card lt-row-p2" role="article" aria-label="P2 — Scheduled maintenance window, 2d ago, admin">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-p2" aria-hidden="true">P2</span>
<span class="lt-dot lt-dot-warn" aria-hidden="true"></span>
@@ -603,7 +603,7 @@
<span class="lt-frame-br"></span>
<div class="lt-section-header">In Progress</div>
<div class="lt-section-body" id="kanban-col-inprogress" style="min-height:60px">
<div class="lt-card lt-row-p2 lt-item-running" role="article" tabindex="0" aria-label="P2 — Switch port flapping on USW-Pro, 2h ago, operator">
<div class="lt-card lt-row-p2 lt-item-running" role="article" aria-label="P2 — Switch port flapping on USW-Pro, 2h ago, operator">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-p2" aria-hidden="true">P2</span>
<span class="lt-dot lt-dot-warn" aria-hidden="true"></span>
@@ -620,7 +620,7 @@
<span class="lt-frame-br"></span>
<div class="lt-section-header">Closed</div>
<div class="lt-section-body" id="kanban-col-closed" style="min-height:60px">
<div class="lt-card lt-row-p4" style="opacity:0.6" role="article" tabindex="0" aria-label="P4 — Update SSL cert on wiki, 3d ago, operator">
<div class="lt-card lt-row-p4" style="opacity:0.6" role="article" aria-label="P4 — Update SSL cert on wiki, 3d ago, operator">
<div class="lt-flex lt-flex-between lt-mb-md">
<span class="lt-p4" aria-hidden="true">P4</span>
<span class="lt-dot lt-dot-idle" aria-hidden="true"></span>
@@ -646,13 +646,13 @@
<span class="lt-status lt-status-online">Online</span>
</div>
<div class="lt-data-table-wrapper">
<table class="lt-data-table">
<table class="lt-data-table" aria-label="pulse-worker-01 metrics">
<tbody>
<tr><td class="lt-text-muted">CPU</td> <td>12%</td></tr>
<tr><td class="lt-text-muted">Memory</td> <td>2.1 GB / 8 GB</td></tr>
<tr><td class="lt-text-muted">Load</td> <td>0.42 / 0.51 / 0.48</td></tr>
<tr><td class="lt-text-muted">Uptime</td> <td>14d 6h</td></tr>
<tr><td class="lt-text-muted">Tasks</td> <td>2 / 5</td></tr>
<tr><th scope="row" class="lt-text-muted">CPU</th> <td>12%</td></tr>
<tr><th scope="row" class="lt-text-muted">Memory</th> <td>2.1 GB / 8 GB</td></tr>
<tr><th scope="row" class="lt-text-muted">Load</th> <td>0.42 / 0.51 / 0.48</td></tr>
<tr><th scope="row" class="lt-text-muted">Uptime</th> <td>14d 6h</td></tr>
<tr><th scope="row" class="lt-text-muted">Tasks</th> <td>2 / 5</td></tr>
</tbody>
</table>
</div>
@@ -1311,7 +1311,7 @@
<div class="lt-section-body">
<div class="lt-table-sticky-wrap">
<table class="lt-table lt-table-responsive">
<thead><tr><th>ID</th><th>Priority</th><th>Title</th><th>Status</th><th>Assignee</th></tr></thead>
<thead><tr><th scope="col">ID</th><th scope="col">Priority</th><th scope="col">Title</th><th scope="col">Status</th><th scope="col">Assignee</th></tr></thead>
<tbody>
<tr><td data-label="ID"><a href="#" class="lt-text-cyan">#001</a></td><td data-label="Priority"><span class="lt-badge lt-badge-p1">P1</span></td><td data-label="Title">Link-down on compute-storage-01</td><td data-label="Status"><span class="lt-badge lt-badge-open">Open</span></td><td data-label="Assignee">jdoe</td></tr>
<tr><td data-label="ID"><a href="#" class="lt-text-cyan">#002</a></td><td data-label="Priority"><span class="lt-badge lt-badge-p2">P2</span></td><td data-label="Title">Switch port flapping USW-Pro-24</td><td data-label="Status"><span class="lt-badge lt-badge-in-progress">In Progress</span></td><td data-label="Assignee">smith</td></tr>
@@ -1645,7 +1645,7 @@ Storage array link-down on `compute-storage-01`.
<div class="lt-modal-body">
<table class="lt-data-table" style="width:100%">
<thead>
<tr><th>Shortcut</th><th>Action</th></tr>
<tr><th scope="col">Shortcut</th><th scope="col">Action</th></tr>
</thead>
<tbody>
<tr><td>Ctrl / ⌘ + K</td><td>Focus search box</td></tr>