Modal
How to build a Modal using React. For code usage with other frameworks, please follow the links in the live demo on the usage tab.
- Overview
- Modal
- ComposedModal
- Opening/closing modal
- Modal sizes
- Modal button variants
- Using modal title as message
- Focus management
- Component API
- Feedback
Overview
There are two choices for you to use Carbon modals.
Modal type | Purpose |
---|---|
<Modal> | For putting simple contents in |
<ComposedModal> | For more flexibility in the contents |
Modal
<Modal>
allows you to use its bespoke set of contents. children
prop maps to the modal body content.
You can also use modalLabel
, modalHeading
, secondaryButtonText
and primaryButtonText
props to change the corresponding text.
ComposedModal
<ComposedModal>
provides you with more flexibility in the contents. You can provide the contents via <ModalHeader>
, <ModalBody>
and <ModalFooter>
with your contents in them.
Opening/closing modal
For both modal variants, you can open/close the modal by changing the open
prop. For example, you can implement a launcher button with a React state and a <Button>
that changes the state:
function ModalStateManager() {const [open, setOpen] = useState(false);return (<>{typeof document === "undefined"? null: ReactDOM.createPortal(<ComposedModal open={open} onClose={() => setOpen(false)}>...
You can create an abstract version of such state manager, as shown below.
const ModalStateManager = ({renderLauncher: LauncherContent,children: ModalContent,}) => {const [open, setOpen] = useState(false);return (<>{!ModalContent || typeof document === "undefined"? null
Modal sizes
There are four responsive modal sizes: xs
, small
, default, and large
. You can set it via size
prop.
<ComposedModal size="lg"><ModalHeader /><ModalBody><p className="bx--modal-content__text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendissecursus fermentum risus, sit amet fringilla nunc pellentesque quis. Duisquis odio ultrices, cursus lacus ac, posuere felis. Donec dignissim liberoin augue mattis, a molestie metus vestibulum. Aliquam placerat felisultrices lorem condimentum, nec ullamcorper felis porttitor.
Alignment
Depending on the modal size of your choice and the viewport size,
<Modal>
/<ComposedModal>
adds 20% padding at the right of the modal
body content. Carbon design specifies that such 20% padding shouldn’t be
applied to form elements. You can set hasForm
prop to <Modal>
/<ModalBody>
to remove the padding,
and use bx--modal-content__regular-content
class to apply the 20%
padding to non-form contents, as shown below.
<ComposedModal><ModalHeader /><ModalBody hasForm><TextInput data-modal-primary-focus labelText="Enter something" /><p className="bx--modal-content__text bx--modal-content__regular-content">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendissecursus fermentum risus, sit amet fringilla nunc pellentesque quis. Duisquis odio ultrices, cursus lacus ac, posuere felis. Donec dignissim liberoin augue mattis, a molestie metus vestibulum. Aliquam placerat felis
Overflow content
In cases where the largest modal size cannot fit your modal body content in, Carbon
design specifies to have a “visual fade” look at the end of the modal body area
to indicate there is additional content out of view. You can set
hasScrollingContent
prop to <Modal>
/<ModalBody>
to do that, as shown below.
<ComposedModal size="large"><ModalHeader /><ModalBody hasScrollingContent><p className=".bx--modal-content__text">Some very large contents...</p></ModalBody></ComposedModal>
Modal button variants
With <Modal>
, you have limited control over the set of buttons. The following table shows the supported patterns.
Button group variant | Usage |
---|---|
No button (passive modal) | Use passiveModal prop in <Modal> |
One button | Use <ComposedModal> |
Two buttons | Use <Modal> without passiveModal prop |
Two buttons with danger button | Use danger prop in <Modal> |
<Modal danger><ModalHeader /><p className="bx--modal-content__text">The modal body content</p></Modal>
With <ComposedModal>
, you can control the buttons with the following code.
Button group variant | Usage |
---|---|
No button (passive modal) | Use <ComposedModal> without <ModalFooter> |
One button | Use primaryButtonText in <ModalFooter> |
Two buttons | Use primaryButtonText and secondaryButtonText in <ModalFooter> |
Three buttons | Put three buttons as children of <ModalFooter> , instead of using primaryButtonText or secondaryButtonText . Using this option requires style adjustment defined in application-level CSS for <ModalFooter> , e.g. .bx--modal-footer { padding: 25%; } . |
Two buttons with danger button | Set danger prop in <ModalFooter> (like below) |
<ComposedModal>...<ModalFooter danger primaryButtonText="OK" secondaryButtonText="Cancel" /></ComposedModal>
<ComposedModal>...<ModalFooter><Buttonkind="secondary"onClick={() => { (Run some action...) setOpen(false); }}>Another button</Buttton><Button
Using modal title as message
For short, direct messages the title can include the whole message to add visual clarity to an otherwise repetitive title and body message.
To use this pattern with <Modal>
, you can omit the children
prop.
const modalHeadding ='Are you sure you want to add the "Speech to Text" service ' +'to the node-test app?';...<ModalmodalHeading={modalHeading}secondaryButtonText="Cancel"primaryButtonText="Add"/>
To use this pattern with <ComposedModal>
, you can omit <ModalBody>
.
<ComposedModal><ModalHeader label="Modla label"><h1>Are you sure you want to add the "Speech to Text" service to the node-testapp?</h1></ModalHeader><ModalFooter primaryButtonText="OK" secondaryButtonText="Cancel" /></ComposedModal>
Focus management
For both modal variants, once the modal is open, keyboard focus will be restricted inside modal. This means:
- If you press
Tab
at the last keyboard-focusable element in modal, the first keyboard-focusable element in modal will get focus. - If you press
Shift-Tab
at the first keyboard-focusable element in the modal, the last keyboard-focusable element in the modal will get focus.
We take extra step here to ensure such behavior works with floating menus,
given floating menu is placed outside of modal in DOM. If you use any
non-Carbon floating menus in your application, set selectorsFloatingMenus={['.bx--overflow-menu-options', '.bx--tooltip', '.flatpickr-calendar', '.your-floating-menu-foo', '.your-floating-menu-bar']}
to <Modal>
/<ComposedModal>
.
Also for both modal variants, you can set the DOM element that gets focus when the modal gets open, by either of the following ways:
Setting data-modal-primary-focus
attribute to the target element
<ComposedModal><ModalBody hasForm><TextInput data-modal-primary-focus labelText="Enter something" /></ModalBody></ComposedModal>
Specifying a query selector to the target element
{/* `.bx--text-input` selects the `<input>` in `<TextInput>` */}<ComposedModal selectorPrimaryFocus=".bx--text-input"><ModalBody hasForm><TextInput labelText="Enter something" /></ModalBody></ComposedModal>;
Component API
Modal props
Prop | Type | Required | Default | Description |
---|---|---|---|---|
aria-label | string | required if hasScrollingContent is provided | – | Required props for the accessibility label of the header |
children | node | – | – | Provide the contents of your Modal |
className | string | – | – | Specify an optional className to be applied to the modal root node |
danger | boolean | – | – | Specify whether the Modal is for dangerous actions |
hasForm | boolean | – | – | Provide whether the modal content has a form element. If true is used here, non-form child content should have bx--modal-content__regular-content class. |
hasScrollingContent | boolean | – | false | Specify whether the modal contains scrolling content |
iconDescription | string | – | ‘Close’ | Provide a description for “close” icon that can be read by screen readers |
id | string | – | – | Specify the DOM element ID of the top-level node |
modalAriaLabel | string | – | – | Specify a label to be read by screen readers on the modal root node |
modalHeading | node | – | – | Specify the content of the modal header title |
modalLabel | node | – | – | Specify the content of the modal header label |
onKeyDown | function | – | – | Specify a handler for keypresses |
onRequestSubmit | function | – | – | Specify a handler for “submitting” modal. The handler should care of closing modal, e.g. changing open prop, if necessary. |
onRequestClose | function | – | – | Specify a handler for closing modal. The handler should care of closing modal, e.g. changing open prop. |
open | boolean | – | – | Specify whether the Modal is currently open |
onSecondarySubmit | function | – | – | Specify a handler for the secondary button. Useful if separate handler from onRequestClose is desirable |
passiveModal | boolean | – | false | Specify whether the modal should be button-less |
primaryButtonDisabled | boolean | – | false | Specify whether the Button should be disabled, or not |
primaryButtonText | string | – | – | Specify the text for the secondary button |
secondaryButtonText | string | – | – | Specify the text for the secondary button |
selectorPrimaryFocus | string | – | ’[data-modal-primary-focus]’ | Specify a CSS selector that matches the DOM element that should be focused when the Modal opens |
selectorsFloatingMenus | string[] | – | – | Specify CSS selectors that match DOM elements working as floating menus. Focusing on those elements won’t trigger “focus-wrap” behavior |
shouldSubmitOnEnter | boolean | – | – | Specify if Enter key should be used as “submit” action |
size | 'xs' | 'md' | 'lg' | – | – | Specify the size variant |
Click the Show Info
button in our storybook, or follow the links in the component live demo for other frameworks.
Feedback
Help us improve this component by providing feedback, asking questions, and leaving any other comments on GitHub.