Menu


Modal


A centered dialog over a scrim for a short interruption: an icon and title, a body, and a footer of actions.

Customize the preview


Choices
View

Publish changes?

Your changes to Website redesign go live for everyone on the team. You can roll back from the history at any time.

Options


Modal options
ChoiceValuesDefaultWhat it does
sizesmbk-modal--size-<value>smmdlgsm
The greatest width the panel takes.
inlinesmbk-modal--inlineOn or offOff
Shown in place with no scrim, for previews and documentation.

Partssmbk-modal__headersmbk-modal__iconsmbk-modal__titlesmbk-modal__closesmbk-modal__bodysmbk-modal__footer

Examples


Usage

Icon and title, a body, a rule and the actions. Shown in place here.

Publish changes?

Your changes to Website redesign go live for everyone on the team. You can roll back from the history at any time.

Sizes

Three greatest widths.

Publish changes?

Your changes to Website redesign go live for everyone on the team. You can roll back from the history at any time.

Publish changes?

Your changes to Website redesign go live for everyone on the team. You can roll back from the history at any time.

Publish changes?

Your changes to Website redesign go live for everyone on the team. You can roll back from the history at any time.

Without icon

The icon is optional.

Publish changes?

Your changes to Website redesign go live for everyone on the team. You can roll back from the history at any time.

Guidelines


Purpose

Stops the reader for one short thing: a confirmation, a small form, a choice that must be made before going on. The page behind is dimmed and cannot be used until the modal closes. A drawer is for longer tasks beside the page; a modal is for a moment in the middle of it.

When to use

  • Confirming something that cannot be undone.
  • A form of two or three fields that belongs to the page behind.
  • A choice the reader must make before the page can continue.

When not to use

  • Anything the reader may want to read the page during. Use a drawer.
  • Success messages. Use a toast.
  • Long content. Use a page.

Usage rules

Do

  • Name the primary action after what it does: "Delete", not "OK".
  • Put the primary action last and give it the brand tone, or the danger tone for a destructive one.

Don't

  • Stack modals.
  • Use a modal to show a picture. Use the page.

Delete project?

Website redesign and its 24 pages will be removed for everyone on the team.

Do

Name the main button after what it does, so the reader knows the result before they press it.

Delete project?

Website redesign and its 24 pages will be removed for everyone on the team.

Don't

An "OK" button makes the reader reread the question to learn what pressing it will do.

Anatomy

  • The block, a native dialog element centered over a scrim.
  • The header part: an optional icon part and the title part on one line, and the close part in the modal's top corner, the large inset from its top and end edges, overlapping the end of that line; the title keeps clear of it.
  • The body part, which scrolls when tall.
  • A divider and the footer part, optional, holding the actions aligned to the end.

Behavior

Opens modally. Escape, the close button and a click on the scrim all ask to close: a modal that holds its own open state closes itself, and one the page holds waits for the page to close it. A click anywhere on the panel, its padding and edge included, never asks, nor does a press that starts on the panel and ends on the scrim, as when selecting words. With a popover, a dropdown or a list open inside the modal, Escape and a click on the scrim close that first, and the modal stays. The size axis sets the greatest width; the panel shrinks with the window. It fades in and rises a spacing step into place over a scrim that fades in, and fades out a little faster as it closes; under reduced motion it still fades in and out, in place, without rising or dropping. Inline modals do not move.

The inline flag renders the panel in place with no scrim, so it can be seen in the Showroom and in documentation.

The React wrapper opens and closes it with the dialog's own modal open method, as other layers open: given open, the page holds the open state; without it, the modal holds its own, closed unless given defaultOpen. Asking to close calls onOpenChange(false). Do not also set the dialog's open attribute.

Accessibility


What it does for you

  • It is a native dialog opened modally: focus moves inside, is held there, and returns to the opener on close.
  • It is labeled by its title, a second-level heading unless headingLevel gives another, for a modal opened from deeper in the page's outline.
  • The close button is icon-only and named "Close". Every word it says by itself, such as the close button's name, keeps its English unless the page gives its own, so a page in another language can change it.
  • Escape, the close button and a click on the scrim ask to close; the page decides by handling the request.

What you must do

  • Give the modal a title, and set headingLevel where the second level does not fit the page's outline.
  • If the close button is hidden, put an action in the footer that closes the modal.
  • Do not open a modal without the reader having done something.
  • Give the close button's name in the page's own language when the page is not in English.

How to test

  • Press the control that opens it: focus moves into the modal. Tab and Shift + Tab stay inside it, and Escape asks to close it. After it closes, focus is back on the control that opened it.
  • With a screen reader, it is announced as a dialog by its title, and the close button is read as "Close".
  • With text at 200% and the page 320 pixels wide, the panel shrinks with the window and its content stays reachable.
  • In a forced-color mode, the focus ring on its buttons and the panel's edge stay visible.

Keyboard

Modal keys
KeyWhat it does
TaborShift+Tab
Moves focus between the controls inside the modal and holds it there.
Escape
Asks to close the modal. Inside it, an open popover, dropdown or list closes first.
EnterorSpace
On the close button or a footer button, presses it.

Source


The files the modal is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.

Stylesheet
modal.css
/* smbk-modal — Modal. Figma "Modal" 4212:26125 (Size: sm, md, lg).
 * A centered dialog over a scrim, built on the native dialog element. The three
 * widths are the modal size tokens, carried from Figma's frames, used as
 * maximums so the panel shrinks with the window. The sections are spaced with
 * the extra-large stack step, as Figma binds them.
 * The inline flag shows the panel in place, for previews and documentation.
 * The panel fades and rises into place over a fading scrim (motion rules at the end). */

.smbk-modal {
  box-sizing: border-box;
  position: fixed;
  inset: 0;
  z-index: var(--smbk-layer-modal);
  display: none;
  flex-direction: column;
  gap: var(--smbk-space-stack-xl);
  width: 100%;
  max-width: var(--smbk-size-modal-sm);
  max-height: 100%;
  margin: auto;
  padding: var(--smbk-space-inset-2xl);
  border: var(--smbk-border-width-hairline) solid var(--smbk-color-border-rim);
  border-radius: var(--smbk-radius-container-lg);
  background: var(--smbk-color-surface-default);
  color: var(--smbk-color-text-default);
  box-shadow: var(--smbk-elevation-overlay);
}

.smbk-modal[open] {
  display: flex;
}

/* The icon and the title on one line, as Figma draws them. The title keeps
   clear of the close button, which overlaps the row's end (see below). */
.smbk-modal__header {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--smbk-space-inline-lg);
  padding-inline-end: calc(var(--smbk-size-control-md) - var(--smbk-space-inset-2xl) + var(--smbk-space-inset-lg));
}

.smbk-modal__icon {
  flex-shrink: 0;
  font-size: var(--smbk-size-icon-lg);
}

/* The alert dialog (alert-dialog.css, drawn by this stylesheet) warns in the danger color while its confirm action is
   the danger one, as its Figma drawing has it; with a brand confirm action its icon keeps the modal's color. */
.smbk-alert-dialog:has(.smbk-btn--tone-danger) .smbk-modal__icon {
  color: var(--smbk-color-feedback-danger-icon);
}

.smbk-modal__title {
  margin: 0;
  font-family: var(--smbk-typography-heading-2xs-bold-font-family);
  font-size: var(--smbk-typography-heading-2xs-bold-font-size);
  font-weight: var(--smbk-typography-heading-2xs-bold-font-weight);
  line-height: var(--smbk-typography-heading-2xs-bold-line-height);
  letter-spacing: var(--smbk-typography-heading-2xs-bold-letter-spacing);
}

/* The close button sits in the modal's corner, the large inset from its top and end edges, over the
   padding, as Figma places it: the header sits the modal's own padding (and its hairline
   edge) in from those edges, so the button is moved back out by the difference. */
.smbk-modal__close {
  position: absolute;
  top: calc(var(--smbk-space-inset-lg) - var(--smbk-space-inset-2xl) - var(--smbk-border-width-hairline));
  inset-inline-end: calc(var(--smbk-space-inset-lg) - var(--smbk-space-inset-2xl) - var(--smbk-border-width-hairline));
}

.smbk-modal__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  /* The system's scrollbar: thin, in the subtle border color, on a clear track. */
  scrollbar-width: thin;
  scrollbar-color: var(--smbk-color-border-subtle) transparent;
  color: var(--smbk-color-text-subtle);
  font-family: var(--smbk-typography-body-sm-font-family);
  font-size: var(--smbk-typography-body-sm-font-size);
  font-weight: var(--smbk-typography-body-sm-font-weight);
  line-height: var(--smbk-typography-body-sm-line-height);
  letter-spacing: var(--smbk-typography-body-sm-letter-spacing);
}

/* The first thing in it, after the scrollbar the script adds (scrollbar.ts). */
.smbk-modal__body > :first-child,
.smbk-modal__body > .smbk-scrollbar:first-child + * {
  margin-top: 0;
}

.smbk-modal__body > :last-child {
  margin-bottom: 0;
}

.smbk-modal__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--smbk-space-inline-lg);
}

/* ---- sizes ---- */

.smbk-modal--size-md {
  max-width: var(--smbk-size-modal-md);
}

.smbk-modal--size-lg {
  max-width: var(--smbk-size-modal-lg);
}

/* ---- inline: shown in place, no scrim ---- */

.smbk-modal--inline {
  position: relative;
  inset: auto;
  z-index: auto;
  margin: 0;
}

/* ---- over the page: the scrim, the motion (the panel fades in and rises a spacing step into place over a scrim
   that fades in, and fades out dropping a smaller step; under reduced motion it fades in place) and the page held
   still behind it are the dialogs' own, shared with the drawer and the command palette: dialog/dialog.css. Inline
   modals do not move. ---- */
Tokens it reads
Modal tokens
Token and CSSValuePreviewFoundation
color.overlay.scrim--smbk-color-overlay-scrimlight: #00000080dark: #00000099changes with themeColor
motion.exit.duration--smbk-motion-exit-duration200msMotion
motion.exit.easing--smbk-motion-exit-easingcubic-bezier(0.7, 0, 0.84, 0)Motion
motion.enter.duration--smbk-motion-enter-duration320msMotion
motion.enter.easing--smbk-motion-enter-easingcubic-bezier(0.16, 1, 0.3, 1)Motion
space.8--smbk-space-88pxSpacing
space.16--smbk-space-1616pxSpacing
layer.modal--smbk-layer-modal1300Stacking order
space.stack.xl--smbk-space-stack-xlcomfortable: 24pxcompact: 16pxchanges with densitySpacing
size.modal.sm--smbk-size-modal-sm400pxSize
space.inset.2xl--smbk-space-inset-2xlcomfortable: 24pxcompact: 16pxchanges with densitySpacing
border-width.hairline--smbk-border-width-hairline1pxBorder width
color.border.rim--smbk-color-border-rimlight: #ffffffe6dark: #ffffff0dchanges with themeColor
radius.container.lg--smbk-radius-container-lg16pxRadius
color.surface.default--smbk-color-surface-defaultlight: #f3f5f8dark: #181a1cchanges with themeColor
color.text.default--smbk-color-text-defaultlight: #232527dark: #e7ebf0changes with themeColor
elevation.overlay--smbk-elevation-overlaylight: 0px 12px 16px 2px #373d431a, 0px 2px 8px 0px #373d4326dark: 0px 12px 16px 2px #00000026, 0px 2px 8px 0px #00000033Elevation
space.inline.lg--smbk-space-inline-lgcomfortable: 12pxcompact: 8pxchanges with densitySpacing
size.control.md--smbk-size-control-mdcomfortable: 40pxcompact: 32pxchanges with densitySize
space.inset.lg--smbk-space-inset-lgcomfortable: 12pxcompact: 8pxchanges with densitySpacing
size.icon.lg--smbk-size-icon-lg24pxSize
color.feedback.danger.icon--smbk-color-feedback-danger-iconlight: #cc0d0ddark: #fe7d6dchanges with themeColor
typography.heading-2xs-bold--smbk-typography-heading-2xs-bold-font-family--smbk-typography-heading-2xs-bold-font-size--smbk-typography-heading-2xs-bold-font-weight--smbk-typography-heading-2xs-bold-line-height--smbk-typography-heading-2xs-bold-letter-spacing--smbk-typography-heading-2xs-bold-text-transformfontFamily: Funnel Display, Funnel Display Fallback, system-ui, sans-seriffontSize: min: 18px · max: 24pxfontWeight: 700lineHeight: 1.2letterSpacing: 0px

The quick brown fox

Typography
color.border.subtle--smbk-color-border-subtlelight: #c4cdd8dark: #2d3237changes with themeColor
color.text.subtle--smbk-color-text-subtlelight: #4e555edark: #9ba5b1changes with themeColor
typography.body-sm--smbk-typography-body-sm-font-family--smbk-typography-body-sm-font-size--smbk-typography-body-sm-font-weight--smbk-typography-body-sm-line-height--smbk-typography-body-sm-letter-spacing--smbk-typography-body-sm-text-transformfontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px

The quick brown fox

Typography
size.modal.md--smbk-size-modal-md560pxSize
size.modal.lg--smbk-size-modal-lg800pxSize
Manifest
modal.manifest.json
{
  "name": "modal",
  "block": "smbk-modal",
  "summary": "A centered dialog over a scrim for a short interruption: an icon and title, a body, and a footer of actions.",
  "adapt": "css",
  "axes": {
    "size": {
      "values": [
        "sm",
        "md",
        "lg"
      ],
      "default": "sm",
      "description": "The greatest width the panel takes."
    }
  },
  "flags": {
    "inline": {
      "description": "Shown in place with no scrim, for previews and documentation."
    }
  },
  "parts": [
    "header",
    "icon",
    "title",
    "close",
    "body",
    "footer"
  ],
  "figmaNodeId": "4212:26125"
}