fix: address a11y code review findings

Bugs fixed:
- Space key was hijacked in tag input when a suggestion was
  highlighted, preventing users from typing spaces. Removed
  Space as a selection key (Enter is sufficient per combobox
  pattern).
- ArrowUp was clamped to index 0, making it impossible to
  deselect all suggestions and return to free typing. Now
  allows arrowing back to -1 which clears aria-activedescendant.

Cleanup:
- Remove dead inline onkeydown handlers from tag suggestion
  <li> elements (tabindex="-1" means they never receive focus,
  so the handlers never fire; the global keydown listener in
  app.js handles keyboard navigation).
- Add outline to aria-selected="true" state for visual parity
  with hover (keyboard users now see the same indicator).
- Announce "Ingen forslag" in live region when suggestions are
  empty (screen readers previously got silence).
- Add responsive table wrapper to admin tags and admin requests
  tables (was only on admin users).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-03-29 17:58:40 +02:00
commit a64d0c5dff
5 changed files with 22 additions and 13 deletions

View file

@ -164,6 +164,8 @@
.tag-suggestion[aria-selected="true"] { .tag-suggestion[aria-selected="true"] {
background: var(--pico-primary-focus); background: var(--pico-primary-focus);
outline: 2px solid var(--pico-primary);
outline-offset: -2px;
} }
/* Fave detail actions */ /* Fave detail actions */

View file

@ -48,7 +48,7 @@
if (status) { if (status) {
status.textContent = count > 0 status.textContent = count > 0
? count + " forslag tilgjengelig" ? count + " forslag tilgjengelig"
: ""; : "Ingen forslag";
} }
// Reset active descendant tracking. // Reset active descendant tracking.
@ -88,21 +88,19 @@
break; break;
case "ArrowUp": case "ArrowUp":
event.preventDefault(); event.preventDefault();
activeIndex = Math.max(activeIndex - 1, 0); // Allow arrowing back to -1 to deselect all options.
setActiveDescendant(items); activeIndex = Math.max(activeIndex - 1, -1);
if (activeIndex === -1) {
clearActiveDescendant();
clearAriaSelected(items);
} else {
setActiveDescendant(items);
}
break; break;
case "Enter": case "Enter":
if (activeIndex >= 0 && activeIndex < items.length) { if (activeIndex >= 0 && activeIndex < items.length) {
event.preventDefault(); event.preventDefault();
var tagName = items[activeIndex].textContent.trim(); addTag(null, items[activeIndex].textContent.trim());
addTag(null, tagName);
}
break;
case " ":
if (activeIndex >= 0 && activeIndex < items.length) {
event.preventDefault();
var tagName2 = items[activeIndex].textContent.trim();
addTag(null, tagName2);
} }
break; break;
case "Escape": case "Escape":
@ -133,6 +131,12 @@
} }
} }
function clearAriaSelected(items) {
for (var i = 0; i < items.length; i++) {
items[i].setAttribute("aria-selected", "false");
}
}
function closeSuggestions() { function closeSuggestions() {
var listbox = document.getElementById("tag-suggestions"); var listbox = document.getElementById("tag-suggestions");
if (listbox) { if (listbox) {

View file

@ -7,6 +7,7 @@
{{with .Data}} {{with .Data}}
{{if .Requests}} {{if .Requests}}
<div class="table-responsive" role="region" aria-label="Forespørsler" tabindex="0">
<table role="grid"> <table role="grid">
<thead> <thead>
<tr> <tr>
@ -37,6 +38,7 @@
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
</div>
{{else}} {{else}}
<p>Ingen ventende forespørsler.</p> <p>Ingen ventende forespørsler.</p>
{{end}} {{end}}

View file

@ -7,6 +7,7 @@
{{with .Data}} {{with .Data}}
{{if .Tags}} {{if .Tags}}
<div class="table-responsive" role="region" aria-label="Merkelapp-tabell" tabindex="0">
<table role="grid"> <table role="grid">
<thead> <thead>
<tr> <tr>
@ -35,6 +36,7 @@
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
</div>
{{else}} {{else}}
<p>Ingen merkelapper ennå.</p> <p>Ingen merkelapper ennå.</p>
{{end}} {{end}}

View file

@ -3,6 +3,5 @@
class="tag-suggestion" class="tag-suggestion"
aria-selected="false" aria-selected="false"
onclick="addTag(this, '{{$tag.Name}}')" onclick="addTag(this, '{{$tag.Name}}')"
onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();addTag(this, '{{$tag.Name}}')}"
tabindex="-1">{{$tag.Name}}</li> tabindex="-1">{{$tag.Name}}</li>
{{end}} {{end}}