HyAccordion
Usage examples on this page are written for Lit / plain HTML (
<hy-accordion>). The same component ships asHyAccordionin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
An accordion component using CSS Grid animation for smooth expand/collapse transitions.
CSS Grid Animation Approach
This implementation uses CSS Grid's grid-template-rows property to create smooth expand/collapse animations:
- Collapsed State:
grid-template-rows: 0fr(zero height) - Expanded State:
grid-template-rows: 1fr(content height) - Browser Handled: Height calculations are managed by the browser
- Content Adaptive: Automatically adjusts when content changes
- Clean Implementation: No JavaScript height measurements required
Technical Benefits
- Eliminates need for ResizeObserver height tracking
- No manual animation frame management
- Automatic content size adaptation
- Smooth CSS transitions
- Reduced code complexity
Accessibility: built in
- Full keyboard navigation (Enter/Space to toggle, Tab to focus)
- Collapsed content is
inert(andaria-hidden): its focusable children leave the Tab order until the accordion expands - ARIA live regions for screen reader announcements
- Proper ARIA attributes and relationships
- High contrast mode support
- Reduced motion support with instant transitions
- Visible focus indicators
Browser Compatibility
CSS Grid animation is supported in:
- Chrome 57+ (March 2017)
- Firefox 52+ (March 2017)
- Safari 10.1+ (March 2017)
- Edge 16+ (October 2017)
Icons
The prefix icon and the expand indicator are names (prefix-icon-name, icon-name) from the design system's icon family. The React HyAccordion takes the same two properties as nodes (HyIcon has no name lookup).
When to use
- Sections people open only when they need them: an FAQ, advanced settings, optional details.
- A long page where showing every section at once would bury the one people want.
Do
- Title each section so people know what is inside without opening it.
- Open the section most people need first with
expanded.
When not to use
- Content every reader needs: show it on the page under headings, or group it in a
hy-card. - A single short section: show it without a disclosure.
- Sections nested more than one level deep:
hy-tree. - Collapsible groups of navigation links:
hy-sidebar-group. - Switching between peer views of the same content:
hy-tab-group.
Examples
Basic Usage
<hy-accordion label="Settings" expanded>
<p>Configure your application settings here.</p>
</hy-accordion>Flat / borderless presentation (emphasis="plain")
<hy-accordion emphasis="plain" label="Neutral">
<p>
Chromeless header — no box, no border, no radius. Add a `border-block-end` rule consumer-side
for a section divider.
</p>
</hy-accordion>With All Features
<hy-accordion
label="Email Settings"
subtitle="Configure notifications and preferences"
optional-text="Last updated today"
prefix-icon-name="mail"
size="medium"
announce-state-changes="true"
animation-duration="400"
>
<div>
<h3>Notification Settings</h3>
<p>Choose how you want to receive notifications.</p>
</div>
</hy-accordion>Programmatic Control
<hy-accordion id="myAccordion" label="API Example">
<p>Content that can be controlled via JavaScript.</p>
</hy-accordion>
<script>
const accordion = document.getElementById('myAccordion');
// Listen for state changes
accordion.addEventListener('toggle', (e) => {
console.log('State changed:', e.detail.expanded);
});
// Programmatic control
accordion.expand(); // Expand with animation
accordion.collapse(); // Collapse with animation
accordion.toggle(); // Toggle current state
</script>Custom Animation Timing
You can customize animation via CSS custom properties:
hy-accordion {
--hy-accordion-motion-transition-expand-duration: 500ms;
--hy-accordion-motion-transition-expand-easing: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
headerId | header-id | string | '' | Auto-generated unique identifier for the header button element. Used to establish ARIA relationships between header and content. |
contentId | content-id | string | '' | Auto-generated unique identifier for the content region element. Used to establish ARIA relationships between header and content. |
label | label | string | 'Accordion Title' | The heading text displayed in the accordion header. Appears alongside prefix and optional text, properly aligned with the expand/collapse icon. |
subtitle | subtitle | string | '' | Secondary descriptive text displayed below the main title. Provides additional context with smaller, lighter typography. |
optionalText | optional-text | string | '' | Brief supplementary text displayed after the main title. Useful for status indicators, timestamps, or brief metadata. Uses smaller, muted styling and prevents line wrapping. |
size | size | 'small' | 'medium' | 'large' | 'medium' | Size variant that controls typography, spacing, and visual hierarchy. - small: Compact presentation with reduced spacing - medium: Standard size for most use cases (default) - large: Prominent display with increased spacing |
emphasis | emphasis | 'solid' | 'outlined' | 'tinted' | 'plain' | 'solid' | Visual chrome weight — solid (default) / outlined / tinted / plain. - solid: surface fill + border + radius (the historic baseline — unchanged) - outlined: transparent fill, keep the border - tinted: subtle surface fill, no border - plain: chromeless (no fill, no border) — the flat / borderless presentation (industry "ghost"; flush also removes the inset); add a border-block-end rule consumer-side for a section divider. |
flush | flush | boolean | false | Removes the accordion's inset: the header's inline padding and the body's padding, so the title and the content line up with the container's edge — for an accordion set inside a panel or a list that already draws its own inset (Carbon's flush alignment, Bootstrap's accordion-flush). Usually with emphasis="plain". |
expanded | expanded | boolean | false | Whether the accordion is currently expanded (content visible) or collapsed (content hidden). Changes trigger CSS Grid-based animations. Reflected as an attribute for CSS styling and external state queries. |
disabled | disabled | boolean | false | Disables all accordion interactions and applies disabled styling. When disabled, interactions are prevented and visual styling indicates disabled state. |
animationDuration | animation-duration | number | 300 | Duration in milliseconds for CSS Grid expand/collapse animations. Controls the transition speed of the grid-template-rows animation. |
prefixIconName | prefix-icon-name | string | undefined | — | Icon name for the prefix indicator displayed before the title, from the design system's icon family. The React HyAccordion takes the same property as a node. |
iconName | icon-name | string | 'chevron-down' | Icon name for the expand/collapse indicator, from the design system's icon family. The icon rotates 180 degrees when the accordion expands. The React HyAccordion takes the same property as a node. |
announceStateChanges | announce-state-changes | boolean | true | Whether to announce state changes to screen readers via ARIA live regions. Uses optimized timing for compatibility with different screen readers. |
announcementPriority | announcement-priority | 'polite' | 'assertive' | 'polite' | ARIA live region politeness level for screen reader announcements. - polite: Announcements wait for current reading to complete (recommended) - assertive: Announcements interrupt current reading |
expandedLabel | expanded-label | string | '{label} expanded' | The announcement read when the accordion expands; {label} is the header text. Use for internationalization. |
collapsedLabel | collapsed-label | string | '{label} collapsed' | The announcement read when the accordion collapses; {label} is the header text. Use for internationalization. |
Events
| Event | Detail | Description |
|---|---|---|
toggle | — | Fired when the accordion state changes (expand or collapse). Detail: { expanded: boolean } |
expand | — | Fired specifically when the accordion expands. Detail: { expanded: boolean } |
collapse | — | Fired specifically when the accordion collapses. Detail: { expanded: boolean } |
transition-end | — | Fired when the CSS Grid animation completes. Detail: { expanded: boolean } |
Slots
| Slot | Description |
|---|---|
default | The main content displayed when the accordion is expanded |
CSS Parts
| Part | Description |
|---|---|
base | The accordion's root container element |
header | The clickable header button element |
prefix | The prefix icon container (only present when prefix content exists) |
icon | The expand/collapse icon container |
content | The CSS Grid animated content container |
live-region | The ARIA live region for screen reader announcements (visually hidden) |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-accordion-border-default-rest | var(--hy-border-default-rest) | Border color at rest |
--hy-accordion-border-subtle-rest | var(--hy-border-subtle-rest) | Subtle border color |
--hy-accordion-stroke-sm | var(--hy-stroke-sm) | Border width |
--hy-accordion-radius-row-list-md | var(--hy-radius-row-list-md) | Border radius where the accordion rounds all four corners at once (the per-corner hooks below shape the expanded header) |
--hy-accordion-radius-row-list-md-top-start | var(--hy-radius-row-list-md-top-start) | Top-start corner radius override. |
--hy-accordion-radius-row-list-md-top-end | var(--hy-radius-row-list-md-top-end) | Top-end corner radius override. See top-start. |
--hy-accordion-header-padding-row-list-md-block | var(--hy-padding-row-list-md-block) | Header block padding |
--hy-accordion-header-padding-row-list-sm-block | var(--hy-padding-row-list-sm-block) | Header block padding |
--hy-accordion-header-padding-row-list-lg-block | var(--hy-padding-row-list-lg-block) | Header block padding |
--hy-accordion-padding-layout-2xl | var(--hy-padding-layout-2xl) | Inline padding |
--hy-accordion-padding-layout-none | var(--hy-padding-layout-none) | Inset when flush: the header's sides and the body's padding |
--hy-accordion-gap-row-list-sm-block | var(--hy-gap-row-list-sm-block) | Gap for small size |
--hy-accordion-gap-row-list-md-block | var(--hy-gap-row-list-md-block) | Gap for medium size |
--hy-accordion-gap-row-list-lg-block | var(--hy-gap-row-list-lg-block) | Gap for large size |
--hy-accordion-gap-row-list-md-inline | var(--hy-gap-row-list-md-inline) | Gap between header elements |
--hy-accordion-text-transform | none | Text transform for accordion title |
--hy-accordion-motion-transition-expand-duration | var(--hy-motion-transition-expand-duration) | Transition duration for the expand/collapse panel animation |
--hy-accordion-motion-transition-expand-easing | var(--hy-motion-transition-expand-easing) | Transition easing for the expand/collapse panel animation |
Methods
toggle()
Toggles the accordion between expanded and collapsed states. Triggers CSS Grid animation and appropriate events.
expand()
Expands the accordion to show its content. Uses CSS Grid animation for smooth transition.
collapse()
Collapses the accordion to hide its content. Uses CSS Grid animation for smooth transition.
focus()
Sets focus on the accordion header button for keyboard navigation.
blur()
Removes focus from the accordion header button.