facebook / facebook/astryx

Selector: isLabelHidden hides the label without associating it — trigger has no accessible name

Open
#6,318 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
13k
Forks
1.1k
Avg merge
1d 15h
Merged PRs (30d)
690

Description

## Bug: `Selector` with `isLabelHidden` hides the label without associating it — the trigger has no accessible name

**Package:** `@astryxdesign/core@0.5.4`
**Component:** `Selector`

### What happens

`` renders its trigger as:

```html

No project

```

There is no `aria-label` and no `aria-labelledby` pointing back at the visually-hidden label. The trigger's accessible name falls back to its own text content — which is the **currently selected value**, not the field's label.

### Why this is a real-world problem, not a nitpick

In an app with many `Selector` instances on a list (one per row), every trigger with the same current value announces as the *same* accessible name. Concretely: a to-do list with a "Project" selector on every row, where 122 rows have no project selected, produced **122 buttons all named "No project"** to a screen reader — indistinguishable from one another. A "You" default-assignee selector on 118 rows had the same problem.

Passing a more descriptive `label` does **not** help, because `label` is never wired to `aria-label`/`aria-labelledby` on the trigger when `isLabelHidden` is set — only the sighted-visible label element disappears.

### Repro

```tsx
import { Selector } from "@astryxdesign/core/Selector";

{}}
options={[{ value: UNSET, label: "No project" }, { value: "p1", label: "Alpha" }]}
/>
```

Inspect the rendered ``: no `aria-label`, no `aria-labelledby`.

### Expected

When `isLabelHidden` is set, the label should still be associated with the trigger — either:
- render the label element visually-hidden (e.g. `sr-only`) and set `aria-labelledby` to its id, or
- set `aria-label={label}` on the trigger directly.

Either would make `label` do what its name implies even when hidden from sight.

### Current workaround

Pass an explicit `aria-label` prop through `Selector`'s prop spread (it does forward unclaimed props to the trigger, so this works today):

```tsx

```

This works, but it means `label` is effectively decorative/ignored whenever `isLabelHidden` is true, which isn't documented and is easy to miss — we only found it by inspecting the live DOM.

### Version

`@astryxdesign/core@0.5.4`

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.