Usage
import { Modal } from '@loomhq/lens'
<Modal title="Title">Content</Modal>
Accessibility
Please verify that your component supports the following accessibility features that come out of box and none of your modifications negate these features.
Modal
should:
- Be focusable and togglable (open and closeable) by every input device (mouse, keyboard, switch, etc).
- Have a clear trigger to open: Focusable element (i.e.,
<Button type="button" ...>
)
- Have a clear trigger to close: Natively, we provide the
X
button. If you create your own, please ensure your close trigger supports the below features.
- Prevent the body from scrolling in the background when it is open.
Keyboard navigability
When the Modal
(dialog
HTML element) is open
, these are the expected behaviours:
-
Autofocus: The focus should shift from the trigger that opened the Modal
to the first focusable element (usually the close button
).
-
Tab: The user should be able to Tab
to the next focusable element, and Shift + Tab
to the previous focusable element within the Modal
.
-
Close Modal: The user should be able to close the modal with Esc
-
Trap Focus: The user should not be able to focus outside of the Modal
. The last focusable item should loop back around to the first focusable item.
-
Return Focus: When the Modal
is closed, the focus should return to the trigger that opened the Modal
(usually a button).
Screenreader support
Screenreader support is still [WIP]. In-code comments outlines the remaining work with Linear ticket references.
Modal with all elements
() => {
const [isOpen, setIsOpen] = React.useState(false)
return (
<>
<Button onClick={() => setIsOpen(!isOpen)} variant="primary">
Open Modal
</Button>
<Modal
mainButton={<Button variant="primary">Confirm</Button>}
secondaryButton={<Button>Cancel</Button>}
alternativeButton={<TextButton icon={<SvgAdd />}>Add</TextButton>}
title={demoText.short}
isOpen={isOpen}
onCloseClick={() => setIsOpen(!isOpen)}
>
{demoText.medium}
</Modal>
</>
)}
With dividers
() => {
const [isOpen, setIsOpen] = React.useState(false)
return (
<>
<Button onClick={() => setIsOpen(!isOpen)} variant="primary">
Open Modal
</Button>
<Modal
hasDividers
mainButton={<Button variant="primary">Confirm</Button>}
secondaryButton={<Button>Cancel</Button>}
alternativeButton={<TextButton icon={<SvgAdd />}>Add</TextButton>}
title={demoText.short}
isOpen={isOpen}
onCloseClick={() => setIsOpen(!isOpen)}
>
{demoText.alphabet.map((letter, index) => (
<Container
paddingTop="small"
paddingBottom="small"
borderSide="bottom"
key={index}
>{letter}</Container>
))}
</Modal>
</>
)}
Bottom alignment
Best for mobile modals. Recommend setting a max width for your internal components.
() => {
const [isOpen, setIsOpen] = React.useState(false)
return (
<>
<Button onClick={() => setIsOpen(!isOpen)} variant="primary">
Open Modal
</Button>
<Modal
placement="bottom"
maxWidth="unset"
isOpen={isOpen}
onCloseClick={() => setIsOpen(!isOpen)}
>
<Container maxWidth={60} width="100%" marginX="auto">
{demoText.alphabet.map((letter, index) => (
<Container
paddingTop="small"
paddingBottom="small"
borderSide="bottom"
key={index}
>{letter}</Container>
))}
</Container>
</Modal>
</>
)}
Custom Modal with Bottom Drawer
With scrollable section
() => {
const [isOpen, setIsOpen] = React.useState()
return (
<>
<Button onClick={() => setIsOpen(!isOpen)} variant="primary">
Open Modal
</Button>
<Backdrop
isOpen={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
<ModalCard
onCloseClick={() => setIsOpen(!isOpen)}
isOpen={isOpen}
maxWidth={72}
>
<Arrange rows={['1fr', 'auto']}>
<Container
overflow="auto"
maxHeight="100%"
padding="xlarge"
>
<Text size="large">
{demoText.veryLong}
</Text>
</Container>
<Container backgroundColor="highlight" padding="xlarge">
{demoText.medium}
</Container>
</Arrange>
</ModalCard>
</Backdrop>
</>
)}
Props
Modal
maxHeight
number | string
'70vh'
maxWidth
number | string
60
placement
'center' | 'bottom'
'center'
mainButton
React.ReactNode
secondaryButton
React.ReactNode
alternativeButton
React.ReactNode
onCloseClick
React.ReactEventHandler
onBackgroundClick
React.ReactEventHandler
onKeyDown
React.ReactEventHandler
ModalCard
maxWidth
number | string
60
maxHeight
number | string
'70vh'
onKeyDown
React.ReactEventHandler
onCloseClick
React.ReactEventHandler