Overlays
Modal
A modal that holds anything: a form, a gallery, a video, a wizard. Optional header and footer, seven sizes, centered or at the top, the body or the whole modal scrolling, edge-to-edge media and three backdrops.
import { Modal } from '@alea/ui';
Examples
Any content
A trigger, a title and whatever goes inside: here a small gallery with a caption. The footer is a function that gets close.
Long content and scrolling
scrollBehavior="inside" keeps the header and footer in place while the body scrolls (forms, terms, lists); "outside" lets the whole modal scroll with the page, for long reading.
Edge-to-edge media
padding="none" lets an image or a video touch the edges; without a title the modal needs an aria-label, and the close button floats over the media. size="auto" fits the content.
Sizes, placement and backdrops
size from xs (320 px) to xl (960 px), full or auto; placement="top" stays put while the content grows (a search); backdrop is dim, blur or transparent. On phones, fullscreenBelow="sm" takes the whole screen.
A flow inside one modal
Change the content instead of stacking modals: a header with Steps, the body for each step, and the footer moving back and forth.
Modal, Dialog or Drawer?
| Use | When |
|---|---|
| Modal | Any content: media, galleries, multi-step flows, long forms, your own layout. |
| Dialog | A title, a sentence, maybe a field, and two buttons. |
| AlertDialog | Confirming something that can't be undone. |
| Drawer or Sheet | Details or filters next to the page, where people keep their place. |
Tokens
The design tokens this component reads, with their value in the current theme. Override any of them to restyle it.
Color
--alea-bg…--alea-border…--alea-surface…--alea-surface-overlay…
Space
--alea-space-2…--alea-space-3…--alea-space-4…
Radius
--alea-radius-lg…
Type
--alea-font-family-display…--alea-font-leading-tight…--alea-font-size-2xl…--alea-font-tracking-display…--alea-font-weight-light…
Shadow
--alea-shadow-card…--alea-shadow-pop…
Layer
--alea-z-modal…
Component
--alea-glass-blur…--alea-glass-rim…--alea-glass-saturate…--alea-glass-tint…
Props
Modal props
A modal with any content: a header and footer if you want them, seven sizes, centered or at the top, the body or the whole modal scrolling, edge-to-edge media, three backdrops. Traps focus, closes on Escape and outside click, and returns focus to what opened it. For a title, text and two buttons, Dialog is quicker; to confirm something destructive, AlertDialog.
| Prop | Type | Default | Description |
|---|---|---|---|
childrenrequired | Render | - | Anything: a form, a gallery, a video, a wizard, a whole page. A function gets close. |
aria-label | string | - | Name for screen readers when there is no title. |
backdrop | "blur" | "dim" | "transparent" | 'dim' | What is behind it: dim (default), blur (dim and blur) or transparent (only for modals that already stand out). |
className | string | - | Extra class names for the modal surface. |
footer | Render | - | Content pinned at the bottom: actions, a summary. A function gets close. |
fullscreenBelow | "sm" | "md" | - | Takes the whole screen below this breakpoint (phones): sm (640 px) or md (768 px). |
header | ReactNode | - | Your own header instead of the title row (it still gets the close button). |
motion | MotionProp | - | How it enters and leaves (default rise): a preset, false, or options. |
padding | "none" | "normal" | 'normal' | normal (default) or none, for media that should touch the edges (an image, a video, a map). |
placement | "center" | "top" | 'center' | center (default) or top (stays put while its content grows, good for search and long forms). |
scrollBehavior | "inside" | "outside" | 'inside' | inside: the header and footer stay, the body scrolls (default). outside: the whole modal scrolls with the page, for long reading. |
showClose | boolean | true | Shows the close button. Default true. |
size | "sm" | "md" | "lg" | "xl" | "xs" | "auto" | "full" | 'md' | xs 320, sm 400, md 560 (default), lg 760, xl 960, full (the whole window less a margin), or auto (as wide as its content). |
title | ReactNode | - | A title in the header, read when it opens. Without one, give the modal an aria-label. |
trigger | ReactNode | - | The button (or any pressable) that opens it. Leave it out and control isOpen yourself. |
Specs
Generated from the source: the variants in the types, the states and tokens in the CSS, and the motion patterns that apply. The grids render the real component in both themes; the numbers are measured from the page.
- Variants
backdropblurdimtransparentfullscreenBelowsmmdpaddingnonenormalplacementcentertopscrollBehaviorinsideoutsidesizesmmdlgxlxsautofull- Motion
- riseTakes a motion prop for its entrance and exit.
- Dark and light
- Every color comes from theme tokens, so it follows data-theme (and presets like Graphite) on the page or any wrapper. Contrast is checked in both themes.
- Styling hooks
.alea-modal-overlay.alea-xmodal-overlay.alea-modal.alea-xmodal.alea-xmodal-dialog.alea-xmodal-head.alea-dialog-title.alea-dialog-x.alea-xmodal-x.alea-xmodal-body.alea-xmodal-footer- Data attributes
data-size=smdata-size=lgdata-size=fulldata-fullscreen-below=smdata-fullscreen-below=mddata-size=xsdata-size=mddata-size=xldata-size=autodata-placement=topdata-backdrop=transparentdata-backdrop=dimdata-backdrop=blurdata-scroll=insidedata-padding=nonedata-scroll=outside
Accessibility
- Focus moves into the modal and stays there; on close it goes back to what opened it.
- The page behind is hidden from screen readers and can't be scrolled.
- It is named by its title, or by aria-label when there is no title (required then).
- The close button is always reachable, also when the content scrolls or is edge-to-edge media.
- Escape and a click on the backdrop close it; set isDismissable={false} or isKeyboardDismissDisabled for work that must not be lost.
Keyboard
| Key | Does |
|---|---|
| Tab | Cycles inside the modal |
| Shift+Tab | Cycles backwards |
| Escape | Closes it |
Do and don't
Do
- Use a title, or an aria-label, so screen readers say what opened.
- Use scrollBehavior="inside" for forms and lists so the actions in the footer stay visible.
- Use placement="top" for content that grows while people type (search, filters).
- Make it full screen on phones (fullscreenBelow="sm") when the content is long.
Don't
- Don't open a modal on page load.
- Don't stack modals: replace the content, or use steps inside one.
- Don't use it for a quick yes or no: AlertDialog is made for that.
- Don't use backdrop="transparent" for content people must focus on.
In Figma
Modal with Size, Placement and Padding, and slots for header, body and footer.