Accordion Menu
The Accordion component is a vertically stacked set of interactive headings that each reveal an associated section of content.
It allows users to expand and collapse sections to show or hide their content.
To include an accordion in your UI, wrap one or more Accordion.Panel components within the Accordion component.
Each panel contains an Accordion.Heading and an Accordion.Body:
import Accordion from '@components/Basic/Accordion/Accordion';
const App = () => { return ( <Accordion> <Accordion.Panel> <Accordion.Heading>Heading 1</Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel> <Accordion.Panel> <Accordion.Heading>Heading 2</Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel> </Accordion> );};
export default App;| Prop Name | Type | Default | Description |
|---|---|---|---|
style | JSX.CSSProperties | {} | Inline styles to apply directly to the Accordion’s root element. |
class | string | "" | Additional CSS classes to apply to the Accordion. |
ref | AccordionRef | undefined | undefined | Reference to the Accordion instance, providing access to its methods for programmatic control. |
multiple | boolean | false | If true, allows multiple panels to be expanded at the same time. |
disabled | boolean | false | Disables the Accordion when set to true. |
class-disabled | string | "" | Additional CSS classes to apply when the Accordion is disabled. |
onChange | (title: string) => void | undefined | Callback triggered whenever the expanded panels change. |
Ref API
Section titled “Ref API”To interact with the Accordion programmatically, you can use the AccordionRef interface. This interface provides properties and methods to access and manipulate the component’s state.
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
element | HTMLDivElement | A reference to the Accordion’s root HTML element, useful for DOM access or styling. |
Methods
Section titled “Methods”| Method | Parameters | Return Value | Description |
|---|---|---|---|
expand | title: string | void | Expands the panel with the given title. |
collapse | title: string | void | Collapses the panel with the given title. |
expandAll | none | void | Expands all panels. Only works if the Accordion has the multiple prop enabled. |
collapseAll | none | void | Collapses all panels. |
Accordion.Panel
Section titled “Accordion.Panel”The Accordion.Panel slot represents an individual section within the Accordion and must be included to render a panel on the Accordion.
It contains a heading (rendered with Accordion.Heading) and a body (with Accordion.Body).
Properties
Section titled “Properties”| Prop Name | Type | Default | Description |
|---|---|---|---|
expanded | boolean | false | If true, the panel is expanded by default. |
title | string | "" | The title or label for the panel. It is used for programmatic manipulation. |
disabled | boolean | false | Disables the panel, preventing user interaction. |
class-disabled | string | "" | Additional CSS classes applied when the panel is disabled. |
class-expanded | string | "" | Additional CSS classes applied when the panel is expanded. |
style | JSX.CSSProperties | {} | Inline styles to apply to the panel container. |
class | string | "" | Additional CSS classes for the panel container. |
Creating an accordion menu with an expanded panel by default.
import Accordion from '@components/Basic/Accordion/Accordion';
const App = () => { return ( <Accordion> <Accordion.Panel expanded title="panel1"> <Accordion.Heading>Heading 1</Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel> <Accordion.Panel title="panel2"> <Accordion.Heading>Heading 2</Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel> </Accordion> );};
export default App;Accordion.Heading
Section titled “Accordion.Heading”The Accordion.Heading slot is used inside an Accordion.Panel to define the clickable heading for that panel.
It displays the panel’s title or label and handles user interaction for expanding or collapsing the panel.
Properties
Section titled “Properties”| Prop Name | Type | Default | Description |
|---|---|---|---|
style | JSX.CSSProperties | {} | Inline styles to apply to the heading element. |
class | string | "" | Additional CSS classes for the heading element. |
children | JSX.Element | "" | Content of the heading, used to render text, HTML, or JSX elements within the heading. |
onAction | Record<string, (scope: string) => void> | undefined | Extends or overrides the heading’s default navigation action handlers. See Implemented Navigation Actions for details. |
anchor | string | HTMLElement | undefined | Links navigation to another element. When the anchor element is focused, the heading’s actions will execute. Can be a CSS selector or HTMLElement. |
Accordion.Icon
Section titled “Accordion.Icon”The Accordion.Icon slot allows you to customize or change the icon displayed inside the accordion’s heading.
Properties
Section titled “Properties”| Prop Name | Type | Default | Description |
|---|---|---|---|
style | JSX.CSSProperties | {} | Inline styles to apply to the heading element. |
class | string | "" | Additional CSS classes for the heading element. |
children | JSX.Element | "" | Custom content, such as text, HTML, or JSX, to render as the accordion icon. |
To use a custom icon, provide your desired content as children of the Accordion.Icon slot:
import Accordion from '@components/Basic/Accordion/Accordion';import CustomIcon from '@assets/CustomIcon';
const App = () => { return ( <Accordion> <Accordion.Panel expanded title="panel1"> <Accordion.Heading> Heading 1 <Accordion.Icon> <CustomIcon /> </Accordion.Icon> </Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel> <Accordion.Panel title="panel2"> <Accordion.Heading>Heading 2</Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel> </Accordion> );};
export default App;Accordion.Body
Section titled “Accordion.Body”The Accordion.Body slot is used inside an Accordion.Panel to define the content area that is shown or hidden when the panel is expanded or collapsed.
It displays the main content associated with the panel.
Properties
Section titled “Properties”| Prop Name | Type | Default | Description |
|---|---|---|---|
style | JSX.CSSProperties | {} | Inline styles to apply to the body element. |
class | string | "" | Additional CSS classes for the body element. |
children | JSX.Element | "" | Content of the body, used to render text, HTML, or JSX elements within the body. |
Implemented Navigation Actions
Section titled “Implemented Navigation Actions”The Accordion.Heading component slot implements the following navigation action by default:
| Action Name | Behavior |
|---|---|
select | Toggles the accordion panel (expands if collapsed, collapses if expanded) |
You can extend the Accordion.Heading with additional navigation actions or override the default behavior using the onAction prop:
import Accordion from '@components/Basic/Accordion/Accordion';
<Accordion> <Accordion.Panel> <Accordion.Heading class="navigable" onAction={{ 'select': () => console.log('Custom select behavior!'), 'info': () => console.log('Show panel info') }} > Heading 1 </Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel></Accordion>For more information about navigation actions, see the Navigation component documentation.
Retrieve the modified panel’s title on change
Section titled “Retrieve the modified panel’s title on change”To detect which panel was last expanded or collapsed:
- Use the
onChangeprop to handle changes in the Accordion’s panels.
- Pass a callback function to the
onChangeprop. - The callback receives the
titleof the panel that was most recently expanded or collapsed.
import Accordion from '@components/Basic/Accordion/Accordion';
const App = () => { const handleChange = (title: string) => { console.log(title); };
return ( <Accordion onChange={handleChange}> <Accordion.Panel> <Accordion.Heading>Heading 1</Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel> <Accordion.Panel> <Accordion.Heading>Heading 2</Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel> </Accordion> );};
export default App;Programmatically toggle a panel
Section titled “Programmatically toggle a panel”The Accordion component allows you to programmatically toggle a panel by providing its title as the argument.
- Create a
refvariable of typeaccordionRefand assign it to theAccordioncomponent. - Use the
ref.expand(title)method to programmatically expand the panel, such as in response to a button click or keyboard event. - Use the
ref.collapse(title)method to programmatically collapse the panel, such as in response to a button click or keyboard event.
import Accordion, {accordionRef} from '@components/Basic/Accordion/Accordion';
const App = () => { let accordion!: accordionRef;
const handleKeyPress = (e: KeyboardEvent) => { if (e.keyCode === 13) { accordionRef.expand('heading-1'); } else if (e.keyCode === 27) { accordionRef.collapse('heading-1'); } };
return ( <Accordion ref={accordion} keypress={handleKeyPress}> <Accordion.Panel title="heading-1"> <Accordion.Heading>Heading 1</Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel> <Accordion.Panel> <Accordion.Heading>Heading 2</Accordion.Heading> <Accordion.Body>Accordion content</Accordion.Body> </Accordion.Panel> </Accordion> );};
export default App;Making accordion headings navigable
Section titled “Making accordion headings navigable”The Accordion.Heading needs to be focused in order to respond to the select action.
To enable gamepad/keyboard navigation for accordion headings, add them to a navigation area by assigning a CSS class and registering them with Navigation.Area or navigationRef.registerArea().
- Wrap your
Accordioninside aNavigationcomponent - Assign a CSS class to each
Accordion.Headingyou want to be navigable - Register the headings as a navigation area using either:
navigationRef.registerArea()with the class selector- Or wrap the accordion in
Navigation.Areawith the appropriate selector
import Navigation, { NavigationRef } from '@components/Utility/Navigation/Navigation';import Accordion from '@components/Basic/Accordion/Accordion';
const App = () => { let navigationRef: NavigationRef | undefined;
// Register accordion headings as a navigation area onMount(() => { navigationRef?.registerArea('accordion-headers', ['.accordion-heading']); });
return ( <Navigation ref={navigationRef}> <Accordion> <Accordion.Panel title="panel-1"> <Accordion.Heading class="accordion-heading"> Settings </Accordion.Heading> <Accordion.Body>Settings content</Accordion.Body> </Accordion.Panel> <Accordion.Panel title="panel-2"> <Accordion.Heading class="accordion-heading"> Audio </Accordion.Heading> <Accordion.Body>Audio content</Accordion.Body> </Accordion.Panel> </Accordion> </Navigation> );};
export default App;Navigating accordion body content
Section titled “Navigating accordion body content”For more complex accordion implementations where the body contains navigable elements (like form inputs, buttons, or lists), you can create a Navigation.Area inside the Accordion.Body and automatically focus it when the panel expands.
- Wrap the elements you wish to be navigable inside
Accordion.BodywithNavigation.Area - Use the
onChangecallback to detect when the panel expands - Call
navigationRef.focusFirst()ornavigationRef.switchArea()to focus the body’s navigation area - Optionally set up a
backaction to return focus to the accordion headings
import Navigation, { NavigationRef } from '@components/Utility/Navigation/Navigation';import Accordion from '@components/Basic/Accordion/Accordion';
const App = () => { let navigationRef: NavigationRef | undefined;
// Handle accordion panel changes const handleAccordionChange = (title: string) => { if (title === 'settings-panel') { // Focus the navigation area inside the expanded panel navigationRef?.focusFirst('settings-area'); } };
onMount(() => { // Register accordion headings as an area and focus it. navigationRef?.registerArea('accordion-headers', ['.accordion-heading'], true);
// Configure back action to return to accordion headers navigationRef?.updateAction('back', { key: { binds: ['ESC'], type: ['press'] }, button: { binds: ['face-button-right'] }, callback: (scope) => { if (scope === 'settings-area') { navigationRef?.focusFirst('accordion-headers'); } } }); });
return ( <Navigation ref={navigationRef}> <Accordion onChange={handleAccordionChange}> <Accordion.Panel title="settings-panel"> <Accordion.Heading class="accordion-heading"> Settings </Accordion.Heading> <Accordion.Body> <Navigation.Area name="settings-area"> <div class="menu-item">Graphics Quality</div> <div class="menu-item">Resolution</div> <div class="menu-item">V-Sync</div> </Navigation.Area> </Accordion.Body> </Accordion.Panel> <Accordion.Panel title="audio-panel"> <Accordion.Heading class="accordion-heading"> Audio </Accordion.Heading> <Accordion.Body>Audio settings content</Accordion.Body> </Accordion.Panel> </Accordion> </Navigation> );};
export default App;CSS for .menu-item:
.menu-item { padding: 1vmax; border-bottom: 1px solid #232323; background-color: #cecece;}
.menu-item:focus { background-color: #b1b1b1;}