Skip to content

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 NameTypeDefaultDescription
styleJSX.CSSProperties{}Inline styles to apply directly to the Accordion’s root element.
classstring""Additional CSS classes to apply to the Accordion.
refAccordionRef | undefinedundefinedReference to the Accordion instance, providing access to its methods for programmatic control.
multiplebooleanfalseIf true, allows multiple panels to be expanded at the same time.
disabledbooleanfalseDisables the Accordion when set to true.
class-disabledstring""Additional CSS classes to apply when the Accordion is disabled.
onChange(title: string) => voidundefinedCallback triggered whenever the expanded panels change.

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.

PropertyTypeDescription
elementHTMLDivElementA reference to the Accordion’s root HTML element, useful for DOM access or styling.
MethodParametersReturn ValueDescription
expandtitle: stringvoidExpands the panel with the given title.
collapsetitle: stringvoidCollapses the panel with the given title.
expandAllnonevoidExpands all panels. Only works if the Accordion has the multiple prop enabled.
collapseAllnonevoidCollapses all panels.

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).

Prop NameTypeDefaultDescription
expandedbooleanfalseIf true, the panel is expanded by default.
titlestring""The title or label for the panel. It is used for programmatic manipulation.
disabledbooleanfalseDisables the panel, preventing user interaction.
class-disabledstring""Additional CSS classes applied when the panel is disabled.
class-expandedstring""Additional CSS classes applied when the panel is expanded.
styleJSX.CSSProperties{}Inline styles to apply to the panel container.
classstring""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;

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.

Prop NameTypeDefaultDescription
styleJSX.CSSProperties{}Inline styles to apply to the heading element.
classstring""Additional CSS classes for the heading element.
childrenJSX.Element""Content of the heading, used to render text, HTML, or JSX elements within the heading.
onActionRecord<string, (scope: string) => void>undefinedExtends or overrides the heading’s default navigation action handlers. See Implemented Navigation Actions for details.
anchorstring | HTMLElementundefinedLinks navigation to another element. When the anchor element is focused, the heading’s actions will execute. Can be a CSS selector or HTMLElement.

The Accordion.Icon slot allows you to customize or change the icon displayed inside the accordion’s heading.

Prop NameTypeDefaultDescription
styleJSX.CSSProperties{}Inline styles to apply to the heading element.
classstring""Additional CSS classes for the heading element.
childrenJSX.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;

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.

Prop NameTypeDefaultDescription
styleJSX.CSSProperties{}Inline styles to apply to the body element.
classstring""Additional CSS classes for the body element.
childrenJSX.Element""Content of the body, used to render text, HTML, or JSX elements within the body.

The Accordion.Heading component slot implements the following navigation action by default:

Action NameBehavior
selectToggles 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 onChange prop to handle changes in the Accordion’s panels.
  1. Pass a callback function to the onChange prop.
  2. The callback receives the title of 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;

The Accordion component allows you to programmatically toggle a panel by providing its title as the argument.

  1. Create a ref variable of type accordionRef and assign it to the Accordion component.
  2. Use the ref.expand(title) method to programmatically expand the panel, such as in response to a button click or keyboard event.
  3. 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;

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().

  1. Wrap your Accordion inside a Navigation component
  2. Assign a CSS class to each Accordion.Heading you want to be navigable
  3. Register the headings as a navigation area using either:
    • navigationRef.registerArea() with the class selector
    • Or wrap the accordion in Navigation.Area with 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;

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.

  1. Wrap the elements you wish to be navigable inside Accordion.Body with Navigation.Area
  2. Use the onChange callback to detect when the panel expands
  3. Call navigationRef.focusFirst() or navigationRef.switchArea() to focus the body’s navigation area
  4. Optionally set up a back action 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;
}