Modal Curtain
An overlay that contains content and covers the page
Before using this component…
ModalCurtain
is a low-level component that should only be used if the Modal component is too restrictive. Please reach out to the Design Systems team before using it.Examples
The ModalCurtain
component makes it easy to create an accessible overlay that contains content and covers the page. It contains a few powerful features:
- Focus trap: Move the browser's focus to the first interactive element within the
children
. Trap the focus within theModalCurtain
while it is open, preventing users from accidentally tabbing to the page underneath. - Scroll lock: Prevent the page from scrolling while the
ModalCurtain
is open. - Append to the body tag: Move the HTML to end of the DOM, right before the
</body>
tag. This greatly decreases the chance of running into z-index issues. - Listen for Esc key press: Run a function to close the
ModalCurtain
if the user presses Esc. - Close on
ModalCurtain
click: Make it easy to close the modal if theModalCurtain
is clicked on.
The ModalCurtain
component CSS handles positioning, overflow, z-index, visibility, and opacity. It does not include background colors, padding, transitions, or other opinionated styles since the component was designed to be versatile.
ModalCurtain
with a basic modal
function ModalCurtainDemo() { const [isOpen, setIsOpen] = React.useState(false); return ( <div> <Button onClick={() => setIsOpen(true)}>Open Modal</Button> <ModalCurtain stage={isOpen ? 'entered' : 'exited'} onCloseClick={() => setIsOpen(false)} > {({ curtainClassName, curtainOnClick }) => ( <div onClick={curtainOnClick} className={`${curtainClassName} pa4`} style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }} > <div className="bg-white ma-auto pa4 mw7"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum dapibus est nec eros congue, ac dapibus ipsum cursus. Quisque at odio viverra, consequat metus a, commodo ipsum. Donec sodales sapien in luctus sodales. Vivamus ornare mauris in arcu maximus placerat. Cras vitae interdum ipsum. Proin convallis quis elit quis pellentesque. Curabitur a ex eget neque congue tempor sed ut felis. Vivamus in erat ac lacus vehicula condimentum. Nam fringilla molestie facilisis. Etiam eros nisl, mattis et sagittis non, blandit vel nisl. Duis blandit condimentum lorem, sed convallis sapien porttitor vitae. Curabitur molestie, massa et molestie aliquam, odio purus rhoncus sem, a sodales ipsum nisi ac nibh. Nunc in dapibus mauris. Pellentesque rhoncus id arcu at auctor. <div className="mt4"> <Button onClick={() => setIsOpen(false)}>Close Modal</Button> </div> </div> </div> )} </ModalCurtain> </div> ); }
ModalCurtain
with a custom initial focus
function ModalCurtainDemo() { const [isOpen, setIsOpen] = React.useState(false); const [textValue, setTextValue] = React.useState('hello'); const inputEl = React.useRef(); return ( <div> <Button onClick={() => setIsOpen(true)}>Open Modal</Button> <ModalCurtain stage={isOpen ? 'entered' : 'exited'} onCloseClick={() => setIsOpen(false)} initialFocus={inputEl && inputEl.current} > {({ curtainClassName, curtainOnClick }) => ( <div onClick={curtainOnClick} className={`pa4 ${curtainClassName}`} style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }} > <div className="bg-white ma-auto pa4 mw7"> <ModalHeader> <ModalTitle>Add a professional license</ModalTitle> <ModalDescription> Licenses add credibility to your business and provide more trust for customers. </ModalDescription> </ModalHeader> <div className="mt4"> <input value={textValue} onChange={e => setTextValue(e.target.value)} ref={inputEl} className="pv2 ph3 db mb3" /> <Button onClick={() => setIsOpen(false)}>Close Modal</Button> </div> </div> </div> )} </ModalCurtain> </div> ); }
ModalCurtain
with a full screen takeover
function ModalCurtainDemo() { const [isOpen, setIsOpen] = React.useState(false); return ( <div> <Button onClick={() => setIsOpen(true)}>Open Modal</Button> <ModalCurtain stage={isOpen ? 'entered' : 'exited'} onCloseClick={() => setIsOpen(false)} > {({ curtainClassName, curtainOnClick }) => ( <div className={`bg-white pa4 ${curtainClassName}`}> <div className=" ma-auto mw7"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum dapibus est nec eros congue, ac dapibus ipsum cursus. Quisque at odio viverra, consequat metus a, commodo ipsum. Donec sodales sapien in luctus sodales. Vivamus ornare mauris in arcu maximus placerat. Cras vitae interdum ipsum. Proin convallis quis elit quis pellentesque. Curabitur a ex eget neque congue tempor sed ut felis. Vivamus in erat ac lacus vehicula condimentum. Nam fringilla molestie facilisis. Etiam eros nisl, mattis et sagittis non, blandit vel nisl. Duis blandit condimentum lorem, sed convallis sapien porttitor vitae. Curabitur molestie, massa et molestie aliquam, odio purus rhoncus sem, a sodales ipsum nisi ac nibh. Nunc in dapibus mauris. Pellentesque rhoncus id arcu at auctor. <div className="mt4"> <Button onClick={() => setIsOpen(false)}>Close Modal</Button> </div> </div> </div> )} </ModalCurtain> </div> ); }
Props
ModalCurtain
onCloseClick
requiredFunction that fires to close the modal.
Type() => void
children
Content that appears on top of the curtain.
children
is a render prop and expects a function. The function provided receives an object two properties:curtainClassName
: Handles positioning, z-index, opacity, overflow, and visibility. Should be added to theclassName
of the outermost element within thechildren
.curtainOnClick
: You can optionally add this prop to theonClick
of an element withinchildren
. This is typically used on to close a ModalCurtain when clicking on a dark backdrop.
The examples on this page include code samples that demonstrate real use of these props.
Type({ curtainClassName, curtainOnClick, }: { curtainClassName: string; curtainOnClick: (event: React.MouseEvent<HTMLElement>) => void; }) => JSX.Element
stage
The four states that a modal can be in.
exited
– Modal is fully closedentering
– Modal is openingentered
– Modal has fully openexiting
– Modal is closing
Modals that do not have transitions will only use the
entered
andexited
stages.Type'entering' | 'entered' | 'exiting' | 'exited' | null
Default'exited'
accessibilityLabel
Accessibility title used to describe the content of the modal to screen readers.
Typestring
Default'Modal'
shouldCloseOnEscape
Determines if the modal should close when pressing the escape key.
Typeboolean
Defaulttrue
initialFocus
The element that should be focused when the modal opens. If omitted, the entire container element of the modal is focused.
TypeHTMLElement | null