Default Modal
<Modal
open
focusTrapOptions={{ disabled: true }}
onClose={() => alert("closing")}
title="Grand Moff Tarkin"
footer={<Button primary>Close</Button>}
portal={false}
>
Don't worry, we'll deal with your rebel friends soon enough!
</Modal>
Size
Modals have four sizes depending on the width of content that needs to be displayed. Medium is the default size.
<State
initial={{
open: false,
}}
>
{([state, setState]) => (
<div>
<ButtonGroup>
{[
Modal.Sizes.XS,
Modal.Sizes.S,
Modal.Sizes.M,
Modal.Sizes.L,
].map((width, index) => (
<Button
key={index}
onClick={() =>
setState({ modalWidth: width, open: true })
}
>
<span className="tt-uppercase m-r-half">{width}</span>{' '}
Modal
</Button>
))}
</ButtonGroup>
<Modal
open={state.open}
size={state.modalWidth}
onClose={() => setState({ open: false })}
title={`${state.modalWidth} Modal`}
footer={
<Button primary onClick={() => setState({ open: false })}>
Close
</Button>
}
>
Don't worry, we'll deal with your rebel friends soon enough!
</Modal>
</div>
)}
</State>
Scrolling Content
When the content of the modal is larger than the screen supports, it becomes scrollable with consistent spacing between the screen edge and the modal.
<Modal
open
focusTrapOptions={{ disabled: true }}
onClose={() => alert("closing")}
title="Grand Moff Tarkin"
footer={<Button primary>Close</Button>}
portal={false}
>
<p style={{marginTop: 0}}>
Chia cillum etsy pabst, in paleo fashion axe.
Gastropub pariatur tilde wayfarers chia laboris.
Salvia cred franzen chambray cillum slow-carb.
Beard letterpress distillery, knausgaard readymade YOLO in.
Aesthetic ea aliqua, blog vaporware kombucha gluten-free art party VHS pariatur cray raclette.
</p>
<p>
Pop-up kale chips four dollar toast gastropub you probably haven't heard of them prism tote bag.
Paleo thundercats godard glossier +1, iceland anim.
Readymade sriracha occaecat, crucifix bicycle rights retro seitan exercitation craft beer kale
chips minim.
Do post-ironic wayfarers, seitan etsy small batch hammock green juice hexagon whatever hoodie
ipsum fashion axe copper mug.
Fingerstache put a bird on it palo santo craft beer.
</p>
<p>
Adaptogen officia cred ut enamel pin.
Man bun fixie blue bottle minim proident franzen raw denim fanny pack, church-key edison bulb
butcher lumbersexual vaporware ethical YOLO.
Mlkshk elit austin succulents live-edge poke esse pork belly williamsburg consectetur
helvetica craft beer put a bird on it pop-up aliqua.
Viral irure synth laboris laborum.
</p>
<p>
Ut blue bottle activated charcoal pickled meh.
Forage esse occupy artisan aliquip scenester biodiesel street art actually truffaut succulents
art party yr nulla ethical.
Hell of waistcoat aliquip gochujang sunt, fashion axe sed ut photo booth cronut.
Kogi tattooed semiotics cold-pressed adipisicing taiyaki.
</p>
</Modal>
Non-closable Modals
For mandatory workflows and notifications, we can also remove the close functionality. The close button in the Modal Header does not appear and clicking on the backdrop doesn't do anything.
<State initial={false}>
{([open, setOpen]) => (
<>
<Button onClick={() => setOpen(true)}>Open</Button>
<Modal
open={open}
title="Accept Estimate"
footer={
<Button primary onClick={() => setOpen(false)}>
I Accept
</Button>
}
>
<Form>
<Form.Input value="John Smith" />
</Form>
<p>
I understand this is a legal representation of my signature.
</p>
</Modal>
</>
)}
</State>
The Modal Footer can have a few layouts depending on the needs of the Modal. Usually they fall into one of these four layouts.
<Modal
open={open}
focusTrapOptions={{ disabled: true }}
footer={<Button primary>Save</Button>}
portal={false}
>
Single Right Aligned Button
</Modal>
<Modal
open={open}
focusTrapOptions={{ disabled: true }}
footer={<ButtonGroup>
<Button>Cancel</Button>
<Button primary>Save</Button>
</ButtonGroup>}
portal={false}
>
Double Right Aligned Buttons
</Modal>
<Modal
open={open}
focusTrapOptions={{ disabled: true }}
footer={<Button>Close</Button>}
footerAlign="center"
portal={false}
>
Centered Button
</Modal>
<Modal
open={open}
focusTrapOptions={{ disabled: true }}
footer={<React.Fragment>
<Button>Cancel</Button>
<Button primary>Save</Button>
</React.Fragment>}
footerAlign="space-between"
portal={false}
>
Split Buttons
</Modal>
Best Practices
A Modal should:
- Be used to display critical information or workflows that do not merit full pages
- Include clear call-to-actions for the user to proceed, cancel, or close
- Be used sparingly because they are purposefully interruptive
Content Guidelines
Write titles as verbs
Titles should have a clear verb + noun question or statement.
Follow the content guidelines for headings and subheadings.
- Edit customer information
- Delete message?
- Discard unsaved changes?
- Edit the service agreement for this customer
- Are you sure you want to remove the invoice?
- Delete?
Make body content concise and actionable
Use imperative verbs when telling users what they need to know or do. Don’t use permissive language like "you can".
- Notification emails will be sent to this address.
- This cannot be undone.
- You can edit the email address where emails will be sent.
- Are you sure you want to delete this job? You can’t reverse this.
Users should be able to predict what will happen when they click a button.
Lead with a strong verb that encourages action. Use a verb/noun pair on actions in most cases. Common actions like Save, Close, or Cancel do not require an accompanying noun.
- To completely hide the context of the page, use a Takeover component.
- For critical information that requires a specific decision, use a Dialog component.
- For informative or basic actions that do not interrupt the user flow, use a Popover.
Importing
import { Modal } from '@servicetitan/design-system';