From 0c52807e9ecb668ed058c9c6c3191707a690a45c Mon Sep 17 00:00:00 2001 From: Jan Maarten <83665577+janmaarten-a11y@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:35:44 -0700 Subject: [PATCH] Fix invalid HTML nesting in ActionList.Heading Apply the visually-hidden styles directly to the heading element instead of wrapping it in a VisuallyHidden span. A heading is not valid phrasing content inside a span, so the wrapper produced invalid HTML for every ActionList.Heading instance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/actionlist-heading-span-fix.md | 5 +++ .../react/src/ActionList/Heading.test.tsx | 36 +++++++++++++++++++ packages/react/src/ActionList/Heading.tsx | 34 +++++++++--------- 3 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 .changeset/actionlist-heading-span-fix.md diff --git a/.changeset/actionlist-heading-span-fix.md b/.changeset/actionlist-heading-span-fix.md new file mode 100644 index 00000000000..0ef051b14a1 --- /dev/null +++ b/.changeset/actionlist-heading-span-fix.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +Fix invalid HTML nesting in `ActionList.Heading` by applying the visually-hidden styles directly to the heading element instead of wrapping it in a `span`. diff --git a/packages/react/src/ActionList/Heading.test.tsx b/packages/react/src/ActionList/Heading.test.tsx index 2e32f776988..2bd6b5f72c5 100644 --- a/packages/react/src/ActionList/Heading.test.tsx +++ b/packages/react/src/ActionList/Heading.test.tsx @@ -5,6 +5,7 @@ import {ActionList} from '.' import {ActionMenu} from '../ActionMenu' import {implementsClassName, withExpectedConsoleError} from '../utils/testing' import classes from './Heading.module.css' +import visuallyHiddenClasses from '../_VisuallyHidden.module.css' describe('ActionList.Heading', () => { implementsClassName( @@ -29,6 +30,41 @@ describe('ActionList.Heading', () => { expect(heading).toHaveTextContent('Heading') }) + it('should not wrap the heading in a span', async () => { + const {getByRole} = HTMLRender( + + Heading + , + ) + const heading = getByRole('heading', {level: 1}) + expect(heading.parentElement?.tagName).not.toBe('SPAN') + expect(heading).toHaveClass(classes.ActionListHeader) + }) + + it('should apply the visually-hidden class to the heading when visuallyHidden is set', async () => { + const {getByRole} = HTMLRender( + + + Heading + + , + ) + const heading = getByRole('heading', {level: 1}) + expect(heading).toHaveClass(visuallyHiddenClasses.InternalVisuallyHidden) + expect(heading).toHaveClass(classes.ActionListHeader) + }) + + it('should not apply the visually-hidden class to the heading by default', async () => { + const {getByRole} = HTMLRender( + + Heading + , + ) + const heading = getByRole('heading', {level: 1}) + expect(heading).not.toHaveClass(visuallyHiddenClasses.InternalVisuallyHidden) + expect(heading).toHaveClass(classes.ActionListHeader) + }) + it('should label the action list with the heading id', async () => { const {container, getByRole} = HTMLRender( diff --git a/packages/react/src/ActionList/Heading.tsx b/packages/react/src/ActionList/Heading.tsx index 3ffcd843468..b8dd90e305d 100644 --- a/packages/react/src/ActionList/Heading.tsx +++ b/packages/react/src/ActionList/Heading.tsx @@ -3,11 +3,11 @@ import {useMergedRefs} from '../hooks' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import {default as HeadingComponent} from '../Heading' import {ListContext} from './shared' -import VisuallyHidden from '../_VisuallyHidden' import {ActionListContainerContext} from './ActionListContainerContext' import {invariant} from '../utils/invariant' import {clsx} from 'clsx' import classes from './Heading.module.css' +import visuallyHiddenClasses from '../_VisuallyHidden.module.css' type HeadingLevels = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' type HeadingVariants = 'large' | 'medium' | 'small' @@ -33,21 +33,23 @@ export const Heading = forwardRef(({as, size, children, visuallyHidden = false, ) return ( - - - {children} - - + + {children} + ) }) as PolymorphicForwardRefComponent