Accordion is a container that can be expanded and collapsed to show content about a single subject. Its contents can be visible at all items, or expand and collapse as an individual item or a group of items.
also known as Section, Expandable Section, Module, Disclosure, Stack View, Expander
Props
Usage guidelines
- Grouping and organizing content to keep the page clean and digestible.
- Displaying additional related content about a particular subject.
- Enabling users to reveal or hide additional content as necessary (with Expandable variant).
- In a layout that conveys a clear sense of information hierarchy. Use SegmentedControl instead.
- When long content can’t be displayed all at once, and scrolling is necessary.
- When there is insufficient content to condense, as collapsing can increase cognitive load and interaction cost. Consider the static variant of Accordion.
- When the content is crucial to read in full. Consider the static variant instead.
Accessibility
Localization
Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.
Note that accessibilityCollapseLabel
and accessibilityExpandLabel
are optional as DefaultLabelProvider provides default strings. Use custom labels if they need to be more specific.
import { Accordion, Box, DefaultLabelProvider, Text } from 'gestalt'; export default function Example() { return ( <DefaultLabelProvider labels={{ Accordion: { accessibilityCollapseLabel: 'Abschnitt kollabieren.', accessibilityExpandLabel: 'Erweitern Sie den Abschnitt.', }, }} > <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={1} width="100%" > <Box column={12} maxWidth={800} padding={2}> <Accordion.Expandable id="Accordion" items={[ { children: <Text size="200">Inhalt</Text>, summary: [ 'Zusammenfassung # 1', 'Zusammenfassung # 2', 'Zusammenfassung # 3', ], title: 'Titel', }, ]} /> </Box> </Box> </DefaultLabelProvider> ); }
Subcomponents
Accordion.Expandable
Use Accordion.Expandable if your accordion requires expanding and collapsing content.
Accordion.Expandable Props
Variants
Static
An Accordion is a container that can hold any content, and can optionally have a title
that describes the content inside. The default, static Accordion is used to display information that should always be visible.
import { Accordion, Box, Flex, Text } from 'gestalt'; export default function Example() { return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Flex direction="column" flex="grow" gap={{ column: 2, row: 0 }} maxWidth={800} > <Accordion id="accordionExample - default - 1"> <Text size="200">This is example content.</Text> </Accordion> <Accordion id="accordionExample - default - 2" title="Title"> <Text size="200">This is example content.</Text> </Accordion> </Flex> </Box> ); }
Static - Icon
An Icon can be provided to be placed before the title
.
It is recommended that icons be used sparingly to convey additional information, and instead should simply reinforce information in the title. Be sure to provide an iconAccessibilityLabel
.
import { Accordion, Box, Text } from 'gestalt'; export default function Example() { return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Box column={12} maxWidth={800} padding={2}> <Accordion icon="lock" iconAccessibilityLabel="Accordion Locked - check permission settings" id="accordionExample - icon" title="Title" > <Text size="200">This is example content.</Text> </Accordion> </Box> </Box> ); }
Static - IconButton
An IconButton can be provided to be placed after the title
for a supplemental Call To Action (CTA).
import { useRef, useState } from 'react'; import { Accordion, Box, IconButton, Popover, Text } from 'gestalt'; export default function Example() { const [showPopover, setShowPopover] = useState(false); const anchorRef = useRef(null); return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Box column={12} maxWidth={800} padding={2}> <Accordion iconButton={ <IconButton ref={anchorRef} accessibilityLabel="Get help" bgColor="lightGray" icon="question-mark" iconColor="darkGray" onClick={() => { setShowPopover((currVal) => !currVal); }} size="xs" /> } id="accordionExample - iconButton" title="Title" > <Text size="200">This is example content.</Text> </Accordion> {showPopover && ( <Popover anchor={anchorRef.current} idealDirection="right" onDismiss={() => setShowPopover(false)} shouldFocus={false} > <Box padding={3}> <Text weight="bold">Help content!</Text> </Box> </Popover> )} </Box> </Box> ); }
Static - Badge
Badge text can be provided, which will be displayed after the title
. Note that if no title text is provided, the badge will not be displayed.
import { Accordion, Box, Text } from 'gestalt'; export default function Example() { return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Box column={12} maxWidth={800} padding={2}> <Accordion badge={{ text: 'Beta' }} id="accordionExample - badge" title="Title" > <Text size="200">This is example content.</Text> </Accordion> <Accordion badge={{ text: 'Not started', type: 'neutral' }} id="accordionExample - badge neutral" title="Title" > <Text size="200">This is example content.</Text> </Accordion> </Box> </Box> ); }
Static - Error
When using type
as "error"
, be sure to provide an iconAccessibilityLabel
.
import { useState } from 'react'; import { Accordion, Box, Flex, Text, TextField } from 'gestalt'; export default function Example() { const [value, setValue] = useState(''); return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Box column={12} maxWidth={800} padding={2}> <Accordion iconAccessibilityLabel={ !value ? 'This accordion contains an error' : undefined } id="accordionExample - error" title="Personal Info" type={!value ? 'error' : 'info'} > <Flex direction="column" gap={{ column: 4, row: 0 }}> <Text size="200">This is example content.</Text> <TextField errorMessage={!value ? "This field can't be blank!" : null} id="first-name" label="Enter Your Name" onChange={(e) => setValue(e.value)} value={value} /> </Flex> </Accordion> </Box> </Box> ); }
Expandable
Accordions can also allow for expanding and collapsing content. The title
is required and always present. The collapsed state shows optional summary
content, while the expanded state shows any content desired.
import { Accordion, Box, Text } from 'gestalt'; export default function Example() { return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Box column={12} maxWidth={800} padding={2}> <Accordion.Expandable id="ModuleExample - default" items={[ { children: <Text size="200">Children1</Text>, summary: ['summary1', 'summary2', 'summary3'], title: 'Title', }, ]} /> </Box> </Box> ); }
Expandable - Group
Multiple expandable items can be stacked together into an Accordion group. However, only one Accordion will be expanded at any time.
import { Accordion, Box, Text } from 'gestalt'; export default function Example() { return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Box column={12} maxWidth={800} padding={2}> <Accordion.Expandable id="AccordionExample " items={[ { children: <Text size="200">Children1</Text>, summary: ['summary1'], title: 'Title1', }, { children: <Text size="200">Children2</Text>, summary: ['summary2'], title: 'Title2', }, { children: <Text size="200">Children3</Text>, summary: ['summary3'], title: 'Title3', }, ]} /> </Box> </Box> ); }
Expandable - Icon, Badge and IconButton
An Icon can be provided to be placed before the title
.
It is recommended that icons be used sparingly to convey additional information, and instead should simply reinforce information in the title. Be sure to provide an iconAccessibilityLabel
.
Badge text can also be provided, which will be displayed after the title
.
An IconButton can be provided to be placed after the title
for a supplemental Call To Action (CTA).
import { useRef, useState } from 'react'; import { Accordion, Box, IconButton, Popover, Text } from 'gestalt'; export default function Example() { const [showPopover, setShowPopover] = useState(false); const anchorRef = useRef(null); return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Box column={12} maxWidth={800} padding={2}> <Accordion.Expandable id="accordionExample" items={[ { children: <Text size="200">Children1</Text>, icon: 'lock', iconAccessibilityLabel: 'title icon', title: 'Example with icon', }, { badge: { text: 'New' }, children: <Text size="200">Children2</Text>, title: 'Example with badge', }, { children: <Text size="200">Children3</Text>, iconButton: ( <IconButton ref={anchorRef} accessibilityLabel="Get help" bgColor="lightGray" icon="question-mark" iconColor="darkGray" onClick={() => setShowPopover((currVal) => !currVal)} size="xs" /> ), title: 'Example with icon button', }, ]} /> {showPopover && ( <Popover anchor={anchorRef.current} idealDirection="right" onDismiss={() => setShowPopover(false)} shouldFocus={false} > <Box padding={3}> <Text weight="bold">Help content!</Text> </Box> </Popover> )} </Box> </Box> ); }
Expandable - Error
When using type
as "error"
, be sure to provide an iconAccessibilityLabel
.
import { useState } from 'react'; import { Accordion, Box, Text, TextField } from 'gestalt'; export default function Example() { const [value, setValue] = useState(''); const moduleType = !value ? 'error' : 'info'; const summaryInfo = !value ? 'Name is missing' : `Name: ${value}`; const iconAccessibilityLabel = !value ? 'This accordion contains an error' : undefined; return ( <Box alignItems="center" display="flex" height="100%" justifyContent="center" padding={8} > <Box column={12} maxWidth={800} padding={2}> <Accordion.Expandable id="accordionExample" items={[ { children: ( <Text size="200"> <TextField errorMessage={!value ? "This field can't be blank!" : null} id="aboutme" label="Enter Your Name" onChange={(e) => setValue(e.value)} value={value} /> </Text> ), iconAccessibilityLabel, summary: [summaryInfo], title: 'Personal Info', type: moduleType, }, ]} /> </Box> </Box> ); }
Sizes
Module can have different sizes. The default size is large with a padding of 24px ($space-600
). For a dense variant, use the sm
size with a padding of 8px ($space-200
).
import { useState } from 'react'; import { Accordion, Box, Flex, IconButton, SegmentedControl, Text, } from 'gestalt'; export default function Example() { const sizes = ['sm', 'md', 'lg']; const [size, setSize] = useState('sm'); return ( <Box alignItems="center" display="flex" padding={8} width="100%"> <Flex direction="column" gap={{ column: 2, row: 0 }} justifyContent="between" width="100%" > <SegmentedControl items={sizes} onChange={({ activeIndex }) => { if (sizes[activeIndex]) { setSize(sizes[activeIndex]); } }} selectedItemIndex={sizes.indexOf(size)} /> <Accordion badge={{ text: 'Beta' }} icon="lock" iconAccessibilityLabel="Accordion Locked - check permission settings" iconButton={ <IconButton accessibilityLabel="Get help" bgColor="lightGray" icon="question-mark" iconColor="darkGray" onClick={() => {}} size="xs" /> } id="AccordionExample - header" size={size} title="Title" > <Text size="200">This is example content.</Text> </Accordion> <Accordion.Expandable accessibilityCollapseLabel="Collapse section" accessibilityExpandLabel="Expand section" id="AccordionExample - header expandable" items={[ { icon: 'lock', children: <Text size="200">Content here</Text>, summary: ['Summary 1', 'Summary 2'], title: 'Title', }, { badge: { text: 'badge' }, children: <Text size="200">Content here</Text>, summary: ['Summary 1', 'Summary 2', 'Summary 3'], title: 'Title', }, { children: <Text size="200">More content here</Text>, summary: ['Summary 1', 'Summary 2'], title: 'Title', iconButton: ( <IconButton accessibilityLabel="Get help" bgColor="lightGray" icon="question-mark" iconColor="darkGray" onClick={() => {}} size="xs" /> ), }, ]} size={size} /> </Flex> </Box> ); }
Example with external control
import { useState } from 'react'; import { Accordion, Box, Flex, Text } from 'gestalt'; export default function Example() { const [extExpandedId, setExtExpandedId] = useState(null); const mapIds = { 'first-0': 0, 'first-1': 1, 'second-0': 0, 'second-1': 1, }; return ( <Box display="flex" height="100%" justifyContent="center" padding={8}> <Box column={12} maxWidth={800} padding={2}> <Flex direction="column" gap={{ column: 4, row: 0 }}> <Flex direction="column" gap={{ column: 2, row: 0 }}> <Box marginStart={2}> <Text>Step 1</Text> </Box> <Accordion.Expandable expandedIndex={ typeof extExpandedId === 'string' ? mapIds[extExpandedId] : extExpandedId } id="ModuleExampleStep1" items={[ { title: 'Title1', summary: ['summary1'], children: <Text size="200">Children1</Text>, }, { title: 'Title2', summary: ['summary2'], children: <Text size="200">Children2</Text>, }, ]} onExpandedChange={(index) => { if (index) setExtExpandedId( Number.isFinite(index) ? `first-${index}` : index ); }} /> </Flex> <Flex direction="column" gap={{ column: 2, row: 0 }}> <Box marginStart={2}> <Text>Step 2</Text> </Box> <Accordion.Expandable expandedIndex={ typeof extExpandedId === 'string' ? mapIds[extExpandedId] : extExpandedId } id="AccordionExampleStep2" items={[ { title: 'Title1', summary: ['summary1'], children: <Text size="200">Children1</Text>, }, { title: 'Title2', summary: ['summary2'], children: <Text size="200">Children2</Text>, }, ]} onExpandedChange={(index) => { if (index) setExtExpandedId( Number.isFinite(index) ? `second-${index}` : index ); }} /> </Flex> </Flex> </Box> </Box> ); }
Component quality checklist
Quality item | Status | Status description |
---|---|---|
Figma Library | Ready | Component is available in Figma for web and mobile web. |
Responsive Web | Ready | Component responds to changing viewport sizes in web and mobile web. |