diff --git a/.changeset/fix-checkbox-bubble-input-a11y-3167.md b/.changeset/fix-checkbox-bubble-input-a11y-3167.md new file mode 100644 index 0000000000..2bd8117e68 --- /dev/null +++ b/.changeset/fix-checkbox-bubble-input-a11y-3167.md @@ -0,0 +1,5 @@ +--- +'@radix-ui/react-checkbox': patch +--- + +Fix bubble input accessibility by using the native `hidden` attribute instead of `aria-hidden`, and mirror label association from the control to the hidden input. diff --git a/packages/react/checkbox/src/checkbox.test.tsx b/packages/react/checkbox/src/checkbox.test.tsx index 34147891c8..6d9e9d40c4 100644 --- a/packages/react/checkbox/src/checkbox.test.tsx +++ b/packages/react/checkbox/src/checkbox.test.tsx @@ -296,6 +296,27 @@ describe('Checkbox', () => { expect(onChange).toHaveBeenCalledWith(false); }); }); + + // Regression test for https://github.com/radix-ui/primitives/issues/3167 + describe('given a Checkbox with label association in a form', () => { + it('should hide the bubble input with the native hidden attribute', async () => { + const rendered = render( +
+ +
, + ); + + const input = rendered.container.querySelector('input[type="checkbox"]'); + expect(input).toHaveAttribute('hidden'); + expect(input).not.toHaveAttribute('aria-hidden'); + expect(await axe(rendered.container)).toHaveNoViolations(); + }); + }); }); describe('Legacy Checkbox', () => { @@ -478,21 +499,9 @@ describe('Legacy Checkbox', () => { }); function LegacyCheckbox(props: React.ComponentProps) { - const containerRef = React.useRef(null); - React.useEffect(() => { - // We use the `hidden` attribute to hide the nested input from both sighted users and the - // accessibility tree. This is perfectly valid so long as users don't override the display of - // `hidden` in CSS. Unfortunately axe doesn't recognize this, so we get a violation because the - // input doesn't have a label. This adds an additional `aria-hidden` attribute to the input to - // get around that. - // https://developer.paciellogroup.com/blog/2012/05/html5-accessibility-chops-hidden-and-aria-hidden/ - containerRef.current?.querySelector('input')?.setAttribute('aria-hidden', 'true'); - }, []); return ( -
- - - -
+ + + ); } diff --git a/packages/react/checkbox/src/checkbox.tsx b/packages/react/checkbox/src/checkbox.tsx index f0e82f02e8..b8944c123c 100644 --- a/packages/react/checkbox/src/checkbox.tsx +++ b/packages/react/checkbox/src/checkbox.tsx @@ -341,16 +341,21 @@ const CheckboxBubbleInput = React.forwardRef