[Menu] [Select] [Checkbox] [Switch] [RadioItem] provide focusableWhenDisabled
- Dominant language
- TypeScript
- Stars
- 10.9k
- Forks
- 543
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 101
Description
# Feature request
## Summary
Expose `focusableWhenDisabled` as a public prop on `Menu.Trigger`, `Select.Trigger`, `Checkbox.Root`, `Switch.Root`, and `Radio.Root` (and any other standalone trigger/input components that currently render with the native `disabled` attribute or `tabIndex={-1}` when disabled).
The mechanism already exists internally - `useFocusableWhenDisabled` is used by `Tab`, `Menu.Item`, and `Select.Item`, where it is hardcoded to `true` because composite items need to remain navigable. We would like the same toggle available on top-level controls, so consumers can opt into the same behavior.
When enabled, the component should render `aria-disabled="true"` instead of the native `disabled` attribute (for native button variants), and keep `tabIndex` at its default value of `0` (for non-native variants like `` in Checkbox/Switch/Radio). Click and keyboard activation should remain blocked by `useButton`, exactly as today.
## Motivation
Browsers skip elements with the native `disabled` attribute entirely in the tab order. This is technically spec-compliant, but it has a real accessibility cost:
- Screen reader users cannot discover disabled controls via keyboard.
- WCAG 4.1.2 (Name, Role, Value) requires that state changes be exposed to assistive technologies. The `disabled` attribute exposes the state - but only if the user can actually focus the element.
The current workaround is to use the `render` prop and strip the `disabled` attribute manually:
```tsx
{
const { disabled: nativeDisabled, ...rest } = props;
return ;
}}
/>
```
This works but is fragile, easy to forget, and requires every consumer to know the internal DOM output. Exposing `focusableWhenDisabled` as an opt-in prop would let teams adopt the modern ARIA practice.
A reasonable API:
```tsx
...
```
Default could stay false to avoid a breaking change.
Thanks for considering.
Contributor guide
Research direction
Start by tracing useFocusableWhenDisabled in Tab, Menu.Item, and Select.Item, then inspect Menu.Trigger, Select.Trigger, Checkbox.Root, Switch.Root, and Radio.Root alongside useButton. Add the opt-in behavior while preserving the current default: native variants should expose aria-disabled, non-native variants should remain focusable, and activation must stay blocked when disabled.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- accessibility, frontend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100