Skip to content

fix(fluid-select): name the listbox from @label - #195

Merged
mnutt merged 1 commit into
mainfrom
mn/select-listbox-label
Sep 15, 2026
Merged

mnutt merged 1 commit into
mainfrom
mn/select-listbox-label

Conversation

@mnutt

@mnutt mnutt commented Sep 11, 2026

Copy link
Copy Markdown
Member

Shortcut: [sc-219839]

Problem

fluid-select/list.hbs renders role="listbox" with an id and tabindex="-1" but no aria-label or aria-labelledby. On open, fluid-select.js calls element.focus({ preventScroll: true }) on that list — so the element a screen reader lands on is unnamed, even though the trigger beside it is named correctly from @label.

Measured in the computed accessibility tree: button "Fruit" → listbox (UNNAMED).

Neither template lint nor axe catches this: the markup is individually valid, and axe doesn't require a name on a listbox. It only shows up when you read the computed tree.

Two smaller items in the same component:

  • select.search renders a placeholder and has an aria-label={{or @label "Search options"}} fallback, but @label was never passed down — so a searchable select's input is always named the generic "Search options".
  • The trigger's caret svg reports as an unnamed image node — noise rather than a failure.

Fix

Fall back to @label on the listbox when the caller passes nothing. ...attributes already reaches the list, so placing aria-label={{@label}} above it keeps callers able to override. This clears all 62 canvas call sites without a single call-site change, and makes the local aria-label workarounds in select-field and the timezone picker redundant (they still win via ...attributes).

  • list.hbs — aria-label={{@label}} above ...attributes
  • fluid-select.hbs — pass label=@label to the curried list and search components
  • trigger.hbs — aria-hidden="true" on the caret svg

Testing

Computed tree now reads listbox "Fruit". Two new integration tests cover the default name and the ...attributes override; the existing search-combobox test now asserts the @label pass-through instead of the fallback string. 30/30 fluid-select tests pass.

Related

Same shape as the two FluidCheckbox asks sitting unraised in the plan's Upstream status — a component emitting an ARIA structure it doesn't finish wiring.

The listbox is what focus lands on when the popup opens, but it carried no
aria-label, so screen readers announced an unnamed element even though the
trigger beside it was named. Default it from @Label, above ...attributes so
callers can still override.

Also pass @Label through to the search input, whose aria-label fallback never
received it, and hide the trigger's caret svg from the accessibility tree.
@mnutt
mnutt requested a review from nicksteffens September 11, 2026 18:27
@mnutt
mnutt merged commit 82d2ec4 into main Sep 15, 2026
5 checks passed
@mnutt
mnutt deleted the mn/select-listbox-label branch September 15, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants