Skip to content

Dropdown names the wrapper, not the focusable combobox, so every dropdown has no accessible name #152

Description

@woksin

What happens

Dropdown puts id, aria-label and aria-labelledby on Select.Root
(dist/esm/Dropdown/Dropdown.js):

<Select.Root  id={id} name={name} tabIndex={tabIndex}
             aria-label={ariaLabel} aria-labelledby={ariaLabelledby}  >
    <Select.Trigger>
        <Select.Value placeholder={placeholder} />

Select.Root renders as a plain <div data-part="root"> carrying no role. The focusable element is the inner
<button data-part="trigger" role="combobox">, and it receives none of them.

So the combobox has no accessible name, and an external <label for={id}> cannot name it either, because a
<div> is not a labelable element.

Evidence

@cratis/components 3.4.0, primereact 11.1.0.

Before the workaround, the rendered tree was:

<div data-part="root" aria-label="Select a role">
  <button data-part="trigger" role="combobox">   <!-- aria-label: null -->

Testing Library reported combobox: Name "".

After routing both attributes through pt={{ trigger: { id, 'aria-label': … } }}, the same query reported
combobox: Name "Advisory role".

What it costs a consumer

High. It is an unnamed control for screen-reader users on every dropdown in the application — the failure is
invisible to a type-checker, a linter and a rendering unit test, and visible only to an accessibility audit or a
screen reader.

Our workaround: our own Select wrapper sets id, aria-label, aria-describedby and aria-invalid through
pt.trigger, and deliberately does not pass id to Dropdown, because otherwise two elements claim the
same id.

Suggested fix — the seam

Forward id / aria-label / aria-labelledby / aria-describedby to the trigger button rather than to
Select.Root. The wrapper is not the control.

What is explicitly not being asked for

Not asking for any change to the Select.* composition itself, and not asking Components to invent an
accessible name when the consumer supplies none. Only that the labelling props the component already accepts
land on the element that carries role="combobox".

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions