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:57—aria-expanded={expanded.value}. This is the default disclosure, used across lab and profile surfaces.islands/ScorecardActions.tsx:263—aria-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.
Open
No activity in this phase yet.
Sign in to post a ripple.