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

Relationships

#1661 A boolean aria-expanded renders as no aria-expanded: two disclosure widgets report no state to a screen reader

Opened by keeb · 8/14/2026

Problem

Preact serializes a boolean aria-expanded as a plain DOM boolean attribute: false is omitted entirely and true renders valueless. So a disclosure trigger written as aria-expanded={expanded.value} reports no expanded state at all when collapsed, and an empty-string one when open — which is exactly the WCAG 4.1.2 gap the attribute exists to close.

Two live instances, both the site's canonical disclosure widgets:

  • islands/CollapsibleSection.tsx:57aria-expanded={expanded.value}. This is the default disclosure, used across lab and profile surfaces.
  • islands/ScorecardActions.tsx:263aria-expanded={scorecardEditExpanded.value} on the extension scorecard's edit pencil.

How it was found

While implementing #1657 I wrote the same thing (aria-expanded={open}) and the new SSR test caught it immediately. Verbatim from the failure, rendering the trigger collapsed:

<button id="profile-edit-trigger" type="button"   aria-controls="profile-editor" ...>

No aria-expanded at all. And with the signal set true:

<button id="profile-edit-trigger" type="button"  aria-expanded aria-controls="profile-editor" ...>

Valueless, which parses as "", not "true".

islands/ApiKeyManager.tsx:253 gets this right by accident — it hardcodes the string aria-expanded="false" in its collapsed branch.

Fix

aria-expanded={expanded.value ? "true" : "false"} in both files, which is what islands/ProfileEditButton.tsx now does.

A sweep would be better than two edits: any aria-expanded={<boolean expression>} in islands/ or components/ is wrong for the same reason, and the same trap applies to aria-pressed, aria-selected, aria-checked and aria-current — all of which are enumerated string attributes rather than booleans.

Impact

A screen reader user cannot tell whether either control is expanded or collapsed, on every surface those two widgets render. It is invisible to sighted testing and to every render test that does not assert on the attribute specifically.

Environment

swamp-club.com, islands/CollapsibleSection.tsx, islands/ScorecardActions.tsx. Found during #1657.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

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

No activity in this phase yet.

03Sludge Pulse

Sign in to post a ripple.