Skip to main content
← Back to list
01Issue
BugOpenSwamp Club
AssigneesNone

Relationships

#1662 ApiKeyManager's collapsed trigger points aria-controls at an element it never renders

Opened by keeb · 8/14/2026

Problem

islands/ApiKeyManager.tsx renders aria-controls={panelId} on its collapsed trigger (:254), but the collapsed branch returns before the panel exists (:247if (!expanded.value) { return <section><button …/></section> }). The element aria-controls names is only rendered by the expanded branch, so in the state where the attribute actually matters it is a dangling reference to an id that is not in the document.

// islands/ApiKeyManager.tsx:247-273 (collapsed)
if (!expanded.value) {
  return (
    <section …>
      <button
        aria-expanded="false"
        aria-controls={panelId}   // ← nothing in the DOM has this id yet
        …

Why it matters

aria-controls pointing at a missing id is invalid per ARIA and is what assistive technology uses to offer "jump to the controlled region". A dangling reference is silently dropped — the user gets no relationship at all — and validators flag it.

The aria-expanded="false" beside it is correct (it is the literal string, not a boolean — see the sibling issue).

Fix

Keep the panel in the DOM and hide it, rather than returning early — which is what islands/CollapsibleSection.tsx:78-85 already does (class={expanded.value ? "" : "hidden"} on an always-rendered #${id}-panel). That makes aria-controls resolve in both states, and it is the same shape #1657 gave the profile editor.

Note the panel's contents are lazily fetched on expand, so keeping the wrapper rendered does not mean fetching early — the wrapper can render empty.

Environment

swamp-club.com, islands/ApiKeyManager.tsx:247-273. Found while implementing #1657 on the adjacent owner panel.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

8/14/2026, 11:22:54 PM

No activity in this phase yet.

03Sludge Pulse

Sign in to post a ripple.