Skip to content

RadioListItem and CheckboxListItem accept rich label and description content - #5257

Draft
cixzhang wants to merge 1 commit into
mainfrom
radiolist-node-label
Draft

RadioListItem and CheckboxListItem accept rich label and description content#5257
cixzhang wants to merge 1 commit into
mainfrom
radiolist-node-label

Conversation

@cixzhang

Copy link
Copy Markdown
Contributor

Why

RadioListItem types label and description as string. Its sibling CheckboxListItem already types label as ReactNode, so radio and checkbox disagree about the same slot — and an app whose option descriptions carry a link can type one on neither.

The runtime was never the obstacle. Item and ListItem have taken ReactNode for both slots all along, so a rich node already rendered and the label element already named the radio from its own text. Only the types refused.

What

label on RadioListItem, and description on both items, are now ReactNode. RadioListItem picks up the aria-label escape hatch CheckboxListItem established, with the same meaning: a plain-text accessible name applied to the control, not the row.

The two controls reach that name by different routes, and the prop docs say so rather than pretending they match:

  • CheckboxListItem's checkbox carries its own visually hidden label, so a rich label leaves it named "Checkbox" — it warns in dev until you pass aria-label.
  • RadioListItem's <label htmlFor> wraps the visible content and is tied to the input, so a rich label still computes a name from its text. aria-label narrows a name that reads badly, rather than rescuing a missing one, so there is no dev warning here — it would fire on labels that are already named correctly.

One caveat now documented on the prop: label sits inside the <label> element, so interactive content there would also select the radio. Links belong in description or endContent, both outside it — which is what the motivating case wants anyway.

Left alone deliberately. RadioList and CheckboxList keep label: string and description?: string. Those are the group's field-level label and helper text, owned by Field and shared with every input that composes it; widening them is a change to Field's contract alongside isLabelHidden and labelTooltip, with its own accessible-name story. Different change.

Risk

One behavior change: aria-label on RadioListItem used to land on the row <div>, where ARIA ignores it on a role-less element. It now names the radio. Every other aria-* still forwards to the row, matching CheckboxListItem.

Widening description also made a latent guard mismatch reachable, so it is fixed here: the description element was guarded by != null while its aria-describedby used truthiness, and the ordinary flag && <span>…</span> idiom yields false — enough to render an empty described-by target. Both now go through isRenderable.

Testing

Core suite (7057 tests), typecheck, typecheck:docs and lint all green. New unit tests cover the name computed from a rich label, aria-label overriding it, a rich label still selecting on click, a link in a description staying outside the <label>, and an empty description leaving aria-describedby off.

jsdom cannot settle accessible names, so the a11y half was read out of Chromium's own accessibility tree against the new stories:

Control Accessible name Accessible description
radio, label Pro <Badge label="Popular" />, no aria-label Pro Popular
same, with aria-label="Pro" Pro $12 per seat. See what is included
radio, string label, rich description Starter Free forever. Compare plans
checkbox, rich description Personalization

Interaction in the same browser: clicking a rich label selects its radio; clicking a link inside a description selects nothing, on radio and checkbox alike, while a click on the rest of the checkbox row still toggles it.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Aug 20, 2026
@vercel

vercel Bot commented Aug 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
astryx Ready Ready Preview Aug 20, 2026 5:34pm

Request Review

RadioListItem typed label and description as string while CheckboxListItem
already typed label as ReactNode, so the two disagreed about the same slot and
neither could hold a description with a link in it. The runtime rendered
ReactNode fine; only the types refused.

RadioListItem also gains the aria-label escape hatch CheckboxListItem
established, applied to the radio control rather than the row.
@cixzhang
cixzhang force-pushed the radiolist-node-label branch from 5f03b74 to 10009ff Compare August 20, 2026 17:30
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

🧪 Sandbox Preview

View Sandbox for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

Modified Components

CheckboxList (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Bundle Size (ESM) N/A N/A N/A
Lines of Code N/A 334 -
Complexity N/A Very High (61) -
RadioList (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Bundle Size (ESM) N/A N/A N/A
Lines of Code N/A 448 -
Complexity N/A Very High (43) -

Bundle Size Summary

Package Size (ESM) Size (CJS) Gzipped
@astryxdesign/core N/A 4.8KB 1.2KB

Accessibility Audit

Status: 2 accessibility violation(s) found — 2 serious.

CheckboxList - 1 issue(s)
  • 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
    • Rule: color-contrast · Affects 4/17 stories · Learn more
    • WCAG: 1.4.3 (Level AA)
RadioList - 1 issue(s)
  • 🟠 serious: Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds
    • Rule: color-contrast · Affects 3/13 stories · Learn more
    • WCAG: 1.4.3 (Level AA)

Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

github-actions Bot added a commit that referenced this pull request Aug 20, 2026
github-actions Bot added a commit that referenced this pull request Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant