Fix invalid HTML nesting in ActionList.Heading#8066
Draft
janmaarten-a11y wants to merge 1 commit into
Draft
Conversation
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 detectedLatest commit: 0c52807 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
ActionList.Headingrenders the section heading for anActionList(e.g. anh1–h6that labels the list). Until now it wrapped that heading in aVisuallyHiddencomponent:VisuallyHiddenalways renders a<span>around its children — it only toggles a CSS class to hide/show. That means everyActionList.Headingended up as a heading nested inside a<span>, regardless of whethervisuallyHiddenwas set.That nesting is invalid HTML: a heading (
<h1>–<h6>) is flow content, and a<span>may only contain phrasing content. So we were emitting a<span><h2>…</h2></span>structure that browsers technically have to error-correct.ActionListis used very widely (including indirectly byNavList), so this affected a lot of pages.This is the same problem — and the same fix — we just landed for
NavList.Headingin #8031. This PR bringsActionList.Headingin line.What changed
Instead of wrapping the heading in a
VisuallyHiddenspan, we apply the visually-hidden CSS class directly to the heading element usingclsx:The wrapping
<span>is gone, so the heading is now a direct child of the list container — valid HTML.One detail worth calling out: unlike
NavList.Heading, theActionListHeaderclass (which provides the heading's margins and thedata-list-variant-based spacing) must stay on the heading in all states. So the visually-hidden class is added on top ofActionListHeaderonly whenvisuallyHiddenis true — they are not mutually exclusive here.Changed
ActionList.Headingno longer wraps its heading in aVisuallyHidden<span>; the visually-hidden styles are applied directly to the heading element.Removed
<span>wrapper around theActionList.Headingelement.Why this is safe
ActionListis high-traffic, so we checked the things that could plausibly depend on the old structure:List.tsxandGroup.tsxderivearia-labelledbyfrom the heading slot's React props (id ?? headingId), not from the DOM. Removing the wrapper span doesn't change the id wiring.List.tsxrenders{slots.heading}directly; only the wrapper element disappears, the heading itself is still emitted.ActionListHeader(margins) stays on the heading in every state. WhenvisuallyHiddenis set,InternalVisuallyHiddenis added (absolute position, 1px clip), matching the previous behaviour.ActionList.GroupHeadingis not affected. It uses its own local heading component and only imports the type fromHeading.tsx.Testing & Reviewing
ActionList/Heading.test.tsxcovering: the heading is no longer wrapped in a<span>,visuallyHiddenappliesInternalVisuallyHiddento the heading, and the default (visible) case keeps onlyActionListHeader.ActionListandNavListunit suites together (NavList consumes ActionList.Heading indirectly): all passing.This branch is rebased on top of #8031 (NavList.Heading), since the two changes are closely related.
Rollout strategy
Merge checklist