ui/CollapsibleCard: support rendering Header as a heading element#77962
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: +249 B (0%) Total Size: 7.93 MB 📦 View Changed
ℹ️ View Unchanged
|
|
Flaky tests detected in 6d396f8. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/25745235412
|
ciampo
left a comment
There was a problem hiding this comment.
Thank you for working on this!
We'll need to add a CHANGELOG entry, too
|
@ciampo Thanks for the update!
Consumers may not necessarily place this component after an |
|
@t-hamano fair point! I updated the PR , |
|
@ciampo Thanks for working on this! As far as I've tested, everything is working well. |
3e7daf4 to
9749fbd
Compare
…vigation Provide proper document outline by rendering the CollapsibleCard.Header as a <h2> via the render prop, so assistive technologies can navigate between guideline sections by heading. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adopters need to make the card contribute to the document outline, but the `render` prop's function form is not obvious from the type alone. Show the recommended snippet in JSDoc and add a Storybook story so the pattern is discoverable from both the editor and Storybook. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restructure `CollapsibleCard.Header` to render an `<h3>` wrapper element by
default via `useRender`, with the inner `Collapsible.Trigger` + `Card.Header`
click target nested inside. This follows the W3C APG accordion pattern
(heading wraps button) so each card contributes to the document outline
without consumers having to compose the wrapping themselves.
The wrapper element is controlled by the `render` prop:
- default: `<h3>`
- `render={ <h2 /> }` (or any h1–h6) changes the heading level
- `render={ <div /> }` opts out of heading semantics
Forwarded props (`className`, `style`, `aria-*`, `data-*`) and `ref` now
land on the outer heading wrapper instead of the inner click-target div;
`ref` widens from `HTMLDivElement` to `HTMLHeadingElement`.
Internal CSS resets combine `defenseStyles.heading` with a CollapsibleCard-
local `.heading-wrapper` class that overrides the `--_gcd-heading-*` custom
properties to `inherit` and adds `font-family`/`line-height: inherit` —
keeping the wrapper visually transparent so UA and wp-admin styles can't
bleed into the inner trigger.
…rd.Header
`CollapsibleCard.Header` now renders the heading wrapper internally and
exposes the level via the `render` prop. Drop the inline render callback
and the `style={ { margin: 0 } }` workaround — passing `<h2 />` to the
`render` prop is enough to override the default `<h3>` for the
Guidelines page.
The right heading level depends on the surrounding document outline, so
let consumers opt into heading semantics via render={ <h2 /> } instead
of defaulting to h3. Addresses review feedback.
9749fbd to
6d396f8
Compare
See #77903 (comment)
What?
CollapsibleCard.Headernow wraps its trigger in an outer element. Default is a<div>, but consumers can opt into heading semantics viarender={ <h2 /> }(or any of<h1>–<h6>) so the card contributes to the document outline. The Guidelines accordion is migrated to the new API.Why?
For accessibility — the right heading level depends on the surrounding outline, so
CollapsibleCard.Headerlets the consumer choose. Each Guidelines section now contributes an<h2>to the outline, following the W3C ARIA APG accordion pattern (heading wraps button).Testing Instructions
<h2>. No visual changes.<h2>,<h3>, and<div>(default) wrappers respectively, with no visual differences.CollapsibleCardstories should still look identical (the wrapper is now an outer<div>instead of the trigger directly — visual output unchanged).How / implementation
useRender(default<div>); the existingCollapsible.Trigger+Card.Headerclick target is nested as the wrapper's children.role="button"directly on a heading: per ARIA, an explicit role overrides the host's implicit role completely, so the heading would disappear from the accessibility tree. Two elements (heading wraps button) is the only structure that satisfies the APG pattern, hence the always-present outer wrapper.defenseStyles.headingclass with a CollapsibleCard-local.heading-wrapperclass that overrides the--_gcd-heading-*custom properties toinheritand addsfont-family: inherit; line-height: inherit;. When the wrapper renders as an h-tag, this protects the inner trigger from UA / wp-admin h-tag styles bleeding in via inheritance. When it renders as a div, the heading-targeted defense is a no-op.API change summary
<div>(the trigger itself)<div>wrapper around the triggerrendercallback wrapping the triggerrender={ <h2 /> }(or any<h1>–<h6>)renderprop targetCard.Header)className,style,aria-*,data-*) targetrefelement typeHTMLDivElementHTMLElementaria-describedby(auto-wired byHeaderDescription)Keyboard testing
Enter/Space— the panel expands and collapses as before.Screenshots or screencast
There shouldn't be any visual changes
TODO / Follow-ups
Use of AI Tools
The implementation and documentation were drafted with Claude Code (and the latest iterations with Cursor + Claude). All output was reviewed before commit.