Module is a container that holds content about one subject. Its contents can be visible at all times, or expand and collapse as individual modules or a group of modules.
also known as Accordion, Section, Expandable Section, 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 Module.
- When the content is crucial to read in full. Consider the static variant instead.
Accessibility
Subcomponents
Module.Expandable
Use Module.Expandable if your module requires expanding and collapsing content.
Module.Expandable Props
Variants
Static
A Module is a container that can hold any content, and can optionally have a title
that describes the content inside. The default, static Module is used to display information that should always be visible.
import { Box, Flex, Module, Text } from 'gestalt'; export default function Example() { return ( <Box padding={8} height="100%" display="flex" alignItems="center" justifyContent="center" > <Flex direction="column" gap={{ column: 2, row: 0 }} maxWidth={800} flex="grow" > <Module id="ModuleExample - default - 1"> <Text size="200">This is example content.</Text> </Module> <Module id="ModuleExample - default - 2" title="Title"> <Text size="200">This is example content.</Text> </Module> </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 { Box, Module, Text } from 'gestalt'; export default function Example() { return ( <Box padding={8} height="100%" display="flex" alignItems="center" justifyContent="center" > <Box column={12} maxWidth={800} padding={2}> <Module icon="lock" iconAccessibilityLabel="Module Locked - check permission settings" id="ModuleExample - icon" title="Title" > <Text size="200">This is example content.</Text> </Module> </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 { Box, IconButton, Module, Popover, Text } from 'gestalt'; export default function Example() { const [showPopover, setShowPopover] = useState(false); const anchorRef = useRef(null); return ( <Box padding={8} height="100%" display="flex" alignItems="center" justifyContent="center" > <Box column={12} maxWidth={800} padding={2}> <Module iconButton={ <IconButton bgColor="lightGray" icon="question-mark" iconColor="darkGray" accessibilityLabel="Get help" size="xs" onClick={() => { setShowPopover((currVal) => !currVal); }} ref={anchorRef} /> } id="ModuleExample - iconButton" title="Title" > <Text size="200">This is example content.</Text> </Module> {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 { Box, Module, Text } from 'gestalt'; export default function Example() { return ( <Box padding={8} height="100%" display="flex" alignItems="center" justifyContent="center" > <Box column={12} maxWidth={800} padding={2}> <Module badge={{ text: 'Beta' }} id="ModuleExample - badge" title="Title" > <Text size="200">This is example content.</Text> </Module> <Module badge={{ text: 'Not started', type: 'neutral' }} id="ModuleExample - badge neutral" title="Title" > <Text size="200">This is example content.</Text> </Module> </Box> </Box> ); }
Static - Error
When using type
as "error"
, be sure to provide an iconAccessibilityLabel
.
import { useState } from 'react'; import { Box, Flex, Module, Text, TextField } from 'gestalt'; export default function Example() { const [value, setValue] = useState(''); return ( <Box padding={8} height="100%" display="flex" alignItems="center" justifyContent="center" > <Box column={12} maxWidth={800} padding={2}> <Module id="ModuleExample - error" title="Personal Info" iconAccessibilityLabel={ !value ? 'This module contains an error' : undefined } 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> </Module> </Box> </Box> ); }
Expandable
Modules 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 { Box, Module, Text } from 'gestalt'; export default function Example() { return ( <Box padding={8} height="100%" display="flex" alignItems="center" justifyContent="center" > <Box column={12} maxWidth={800} padding={2}> <Module.Expandable accessibilityExpandLabel="Expand the module" accessibilityCollapseLabel="Collapse the module" 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 a Module group. However, only one Module will be expanded at any time.
import { Box, Module, Text } from 'gestalt'; export default function Example() { return ( <Box padding={8} height="100%" display="flex" alignItems="center" justifyContent="center" > <Box column={12} maxWidth={800} padding={2}> <Module.Expandable id="ModuleExample " accessibilityExpandLabel="Expand the module" accessibilityCollapseLabel="Collapse the module" 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 { Box, IconButton, Module, Popover, Text } from 'gestalt'; export default function Example() { const [showPopover, setShowPopover] = useState(false); const anchorRef = useRef(null); return ( <Box padding={8} height="100%" display="flex" alignItems="center" justifyContent="center" > <Box column={12} maxWidth={800} padding={2}> <Module.Expandable accessibilityExpandLabel="Expand the module" accessibilityCollapseLabel="Collapse the module" id="ModuleExample " 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 bgColor="lightGray" icon="question-mark" iconColor="darkGray" accessibilityLabel="Get help" size="xs" onClick={() => setShowPopover((currVal) => !currVal)} ref={anchorRef} /> ), 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 { Box, Module, 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 module contains an error' : undefined; return ( <Box padding={8} height="100%" display="flex" alignItems="center" justifyContent="center" > <Box column={12} maxWidth={800} padding={2}> <Module.Expandable accessibilityExpandLabel="Expand the module" accessibilityCollapseLabel="Collapse the module" id="ModuleExample " 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> ); }
Example with external control
import { useState } from 'react'; import { Box, Flex, Module, 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 padding={8} height="100%" display="flex" justifyContent="center"> <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> <Module.Expandable accessibilityExpandLabel="Expand the module" accessibilityCollapseLabel="Collapse the module" 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> <Module.Expandable id="ModuleExampleStep2" accessibilityExpandLabel="Expand the module" accessibilityCollapseLabel="Collapse the module" expandedIndex={ typeof extExpandedId === 'string' ? mapIds[extExpandedId] : extExpandedId } onExpandedChange={(index) => { if (index) setExtExpandedId( Number.isFinite(index) ? `second-${index}` : index ); }} items={[ { title: 'Title1', summary: ['summary1'], children: <Text size="200">Children1</Text>, }, { title: 'Title2', summary: ['summary2'], children: <Text size="200">Children2</Text>, }, ]} /> </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. |