Badge: full label should be reachable by focus and on touch, and only when it is actually clipped
- Dominant language
- TypeScript
- Stars
- 13.1k
- Forks
- 1.1k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 669
Description
### Context
#5558 stops a long `Badge` label escaping its container by clipping it with an
ellipsis, and — per review — carries a string or number label in the badge's
native `title` so the tail is not simply lost.
`title` is deliberately the cheap half of that answer: it costs no measurement
and no hook, so `Badge` keeps rendering identically on the server. It leaves
three things unsolved.
### Gaps
1. **It is set whether or not the label fits.** Knowing whether the text is
actually clipped requires measuring the rendered element, which `title`
avoids by design. A badge whose label fits comfortably still carries a
redundant tooltip.
2. **It is pointer-only.** A native `title` answers hover. It does not answer
keyboard focus, and on touch it does not appear at all — so the full text is
unreachable for anyone not using a mouse. This is the part of
@rubyycheung's review that #5558 does not close.
3. **A rich `label` gets nothing.** A `ReactNode` label would have to be
flattened to a string, and flattening renders a guess — an icon, a
``, a nested element all read differently — so #5558 leaves it
alone.
### What would close it
A tooltip shown only when the text is really cut:
- measure the label (`scrollWidth > clientWidth`, or a `ResizeObserver`) and
attach the affordance only when it overflows;
- reachable by hover **and** keyboard focus, which means the badge needs to be
focusable when — and only when — it carries recoverable text;
- a touch path, since neither `title` nor hover exists there;
- keep the `title` fallback for the server-rendered pass so the text is never
unreachable before hydration.
That is a client component, which is why #5558 does not do it: it is a real
trade-off (every `Badge` on a page becomes a client boundary, or the tooltip
becomes opt-in) and deserves its own discussion rather than riding along with a
containment fix.
### Not blocking
`Badge` is not interactive today, so making it focusable is a behaviour change,
not just an addition — that is the first thing to settle.
Contributor guide
Research direction
Start by reviewing the Badge implementation and the changes from #5558, then resolve whether the client tooltip belongs in every Badge or is opt-in. Done means overflow-only full-label access works on hover, focus, and touch, rich labels are handled appropriately, and the server-rendered title fallback remains.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- accessibility, design, frontend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100