Overlays
Content layered above the page: dialogs, drawers, popovers, menus and tooltips.
Modal #
Centered dialog with focus trap, Escape, scroll lock and enter/exit motion.
function Example() {
const open = signal(false);
return (
<>
<Button onClick={() => open.set(true)}>Open modal</Button>
<Modal
open={open()}
onClose={() => open.set(false)}
title="Edit profile"
description="Changes are saved when you press Save."
footer={
<Group gap="0.5rem">
<Button variant="ghost" onClick={() => open.set(false)}>
Cancel
</Button>
<Button onClick={() => open.set(false)}>Save</Button>
</Group>
}
>
Modal body content.
</Modal>
</>
);
}Slots: root backdrop body close description footer header panel title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
onClose | () => void | yes | Called when the user closes it (close button, Escape, backdrop); set open to false. | |
open | boolean | yes | Whether the dialog is shown (controlled). | |
children | content | Body content. | ||
closeOnBackdrop | boolean | Close when the backdrop is clicked (default true). | ||
closeOnEscape | boolean | Close on Escape (default true). | ||
description | content | Text under the title; also the dialog's accessible description. | ||
footer | content | Bottom bar content, usually the action buttons. | ||
hideClose | boolean | Hide the header close button. | ||
label | string | Accessible name when there is no visible title. | ||
mount | Element | Portal target (defaults to document.body). | ||
placement | "center" | "top" | Vertical placement (default center). | ||
role | "dialog" | "alertdialog" | alertdialog for confirmations that interrupt the user. | ||
size | "full" | "sm" | "md" | "lg" | "xl" | Width preset; override freely with --a-modal-width. | ||
title | string | Heading; also the dialog's accessible name. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
ConfirmDialog #
Confirmation built on the shared dialog surface (focus trap, Escape, scroll lock, motion). Initial focus goes to Cancel — the safe choice. Slots match Modal (root backdrop panel …); attributes land on the panel.
function Example() {
const open = signal(false);
return (
<>
<Button variant="danger" onClick={() => open.set(true)}>
Delete project
</Button>
<ConfirmDialog
open={open()}
danger
title="Delete project?"
message="This cannot be undone."
confirmLabel="Delete"
onConfirm={() => open.set(false)}
onCancel={() => open.set(false)}
/>
</>
);
}Slots: root backdrop body close description footer header panel title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
message | string | yes | The question to confirm. | |
onCancel | () => void | yes | Called on cancel, Escape or backdrop click. | |
onConfirm | () => void | yes | Called when the user confirms. | |
open | boolean | yes | Whether the dialog is shown (controlled). | |
cancelLabel | string | "Cancel" | Cancel button text. | |
confirmLabel | string | "Confirm" | Confirm button text. | |
danger | boolean | Destructive action: the confirm button uses the danger style. | ||
title | string | Dialog heading. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Drawer #
Panels that slide in from an edge.
Drawer #
Edge-anchored dialog. Slots match {@link Modal}.
function Example() {
const open = signal(false);
return (
<>
<Button onClick={() => open.set(true)}>Open drawer</Button>
<Drawer open={open()} onClose={() => open.set(false)} title="Filters">
Drawer content.
</Drawer>
</>
);
}Slots: root backdrop body close description footer header panel title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
onClose | () => void | yes | Called when the user closes it (close button, Escape, backdrop); set open to false. | |
open | boolean | yes | Whether the dialog is shown (controlled). | |
children | content | Body content. | ||
closeOnBackdrop | boolean | Close when the backdrop is clicked (default true). | ||
closeOnEscape | boolean | Close on Escape (default true). | ||
description | content | Text under the title; also the dialog's accessible description. | ||
footer | content | Bottom bar content, usually the action buttons. | ||
hideClose | boolean | Hide the header close button. | ||
label | string | Accessible name when there is no visible title. | ||
mount | Element | Portal target (defaults to document.body). | ||
role | "dialog" | "alertdialog" | alertdialog for confirmations that interrupt the user. | ||
side | "top" | "bottom" | "left" | "right" | "right" | Edge the drawer slides in from. | |
size | "full" | "sm" | "md" | "lg" | Width (or height, for top / bottom). | ||
title | string | Heading; also the dialog's accessible name. |
BottomSheet #
Mobile bottom sheet (UIkit / Mantine Drawer bottom). Built on the shared dialog frame: focus trap, Escape (topmost layer), scroll lock, exit motion. Slots match {@link DialogBaseProps} (panel is the host); theme key BottomSheet.
function Example() {
const open = signal(false);
return (
<>
<Button onClick={() => open.set(true)}>Open sheet</Button>
<BottomSheet open={open()} onClose={() => open.set(false)} title="Share">
Sheet content.
</BottomSheet>
</>
);
}Slots: root backdrop body close description footer header panel title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
onClose | () => void | yes | Called when the user closes it (close button, Escape, backdrop); set open to false. | |
open | boolean | yes | Whether the dialog is shown (controlled). | |
children | content | Body content. | ||
closeOnBackdrop | boolean | Close when the backdrop is clicked (default true). | ||
closeOnEscape | boolean | Close on Escape (default true). | ||
description | content | Text under the title; also the dialog's accessible description. | ||
footer | content | Bottom bar content, usually the action buttons. | ||
hideClose | boolean | Hide the header close button. | ||
label | string | Accessible name when there is no visible title. | ||
mount | Element | Portal target (defaults to document.body). | ||
role | "dialog" | "alertdialog" | alertdialog for confirmations that interrupt the user. | ||
title | string | Heading; also the dialog's accessible name. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Popover & hover card #
Floating panels anchored to a trigger.
Popover #
Click-to-toggle panel anchored to a trigger. Escape / outside click close it and return focus to the trigger.
function Example() {
const open = signal(false);
return (
<Popover open={open()} onOpenChange={open.set} label="Share" panelLabel="Share project">
Anyone with the link can view this project.
</Popover>
);
}Slots: root arrow panel trigger
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
onOpenChange | (open: boolean) => void | yes | Called with the next open state (trigger click, Escape, outside click). | |
open | boolean | yes | Whether the panel is open (controlled). | |
arrow | boolean | Show a small arrow pointing at the trigger. | ||
children | content | Panel content. | ||
label | content | "Open" | Label for the built-in trigger button | |
panelLabel | string | Accessible name for the panel (defaults to the trigger label when it's text). | ||
placement | PopoverPlacement | "bottom-start" | Preferred side and alignment; flips and shifts to stay in view. | |
trigger | (api: PopoverTriggerApi) => unknown | Render your own trigger: trigger={(t) => <MyButton {...t.attrs} />}. |
PopoverTriggerApi — Attributes to spread on a custom trigger so it stays wired for a11y.
| Field | Type | Required | Description |
|---|---|---|---|
open | boolean | yes | Whether the panel is open. |
toggle | () => void | yes | Opens or closes the panel. |
attrs | { "aria-expanded": boolean; "aria-controls": string; "aria-haspopup": "dialog"; onClick: () => void; } | yes | ARIA and event attributes to spread onto your trigger element. |
PopoverPlacement
type PopoverPlacement = | "bottom-start" | "bottom-end" | "bottom" | "top-start" | "top-end" | "top";HoverCard #
Hover/focus card panel (Mantine HoverCard).
<HoverCard dropdown={<Text>Ada Lovelace · Analyst of engines</Text>}>
<Anchor href="#ada">@ada</Anchor>
</HoverCard>Slots: root dropdown target
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
dropdown | content | yes | Panel content shown on hover/focus. | |
children | content | The trigger (hovering or focusing it opens the card). | ||
closeDelay | number | 160 | Milliseconds after leaving before the card closes (lets the pointer reach it). | |
openDelay | number | 120 | Milliseconds of hover or focus before the card opens. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Tooltip #
Hover/focus tooltip linked with aria-describedby; Escape dismisses and the tooltip itself is hoverable (WCAG 1.4.13).
<Tooltip content="Copies the deploy URL">
<Button variant="outline" onClick={() => {}}>
Copy link
</Button>
</Tooltip>Slots: root arrow target tooltip
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
content | content | yes | Tooltip text or content. | |
arrow | boolean | true | Show an arrow pointing at the target. | |
children | content | The element the tooltip describes (shown on hover and focus). | ||
closeDelay | number | 80 | Delay before hiding, ms (default 80) — lets the pointer reach the tooltip. | |
disabled | boolean | Never show the tooltip. | ||
openDelay | number | 250 | Delay before showing on hover, ms (default 250). Focus shows immediately. | |
placement | "top" | "bottom" | "left" | "right" | "top" | Preferred side; flips to stay in view. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Menu #
Action menus and context menus.
Menu #
Action menu with roving focus (↑ ↓ Home End, type-ahead), Escape, outside click, focus restore and enter/exit motion.
Nothing chosen yet
function Example() {
const open = signal(false);
const last = signal("");
const pick = (label: string) => () => last.set(label);
return (
<Group gap="0.75rem">
<div style={{ position: "relative" }}>
<Button variant="outline" aria-expanded={open()} onClick={() => open.set(!open())}>
Project actions ▾
</Button>
<Menu
open={open()}
onClose={() => open.set(false)}
label="Project actions"
items={[
{ type: "label", label: "marketing-site" },
{ label: "Rename", onSelect: pick("Rename") },
{ label: "Duplicate", onSelect: pick("Duplicate") },
{ type: "separator" },
{ label: "Delete", onSelect: pick("Delete"), danger: true },
]}
/>
</div>
<Text muted>{last() ? `Chose “${last()}”` : "Nothing chosen yet"}</Text>
</Group>
);
}Slots: root description group icon item label separator shortcut
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
items | MenuItem[] | yes | Actions, separators ({ type: "separator" }) and group labels ({ type: "label" }). | |
open | boolean | yes | Whether the menu is shown (controlled). | |
label | string | Accessible name for the menu. | ||
onClose | () => void | Called on outside click / Escape / selection. Prefer with a wrapping .a-menu-host. | ||
placement | MenuPlacement | "bottom-start" | Preferred position relative to the trigger; flips to stay in view. |
MenuItem
type MenuItem = MenuAction | { type: "separator" } | { type: "label"; label: unknown };MenuAction
| Field | Type | Required | Description |
|---|---|---|---|
type | "item" | Marks a regular action (the default for items without type). | |
label | content | yes | Item text or content. |
onSelect | () => void | yes | Called when the item is chosen (click, Enter or Space); the menu then closes. |
danger | boolean | Destructive action: danger colour. | |
disabled | boolean | Shown but can't be chosen; skipped by arrow keys. | |
icon | content | Leading icon or content. | |
shortcut | string | Right-aligned hint, e.g. ⌘K. | |
description | string | Secondary line under the label. |
MenuPlacement
type MenuPlacement = "bottom-start" | "bottom-end" | "top-start" | "top-end";ContextMenu #
Right-click (or Shift+F10 / ContextMenu key) menu. Clamped to the viewport, focuses the first item, ↑ ↓ Home End navigate, Escape restores focus.
<ContextMenu
items={[
{ id: "open", label: "Open", onSelect: () => {} },
{ id: "rename", label: "Rename", onSelect: () => {} },
{ id: "delete", label: "Delete", danger: true, onSelect: () => {} },
]}
>
<Paper withBorder>Right-click this file card</Paper>
</ContextMenu>Slots: root item menu
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
items | ContextMenuItem[] | yes | Menu items, in order. | |
children | content | The area that opens the menu on right-click, Shift+F10 or the ContextMenu key. |
ContextMenuItem
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Item id. |
label | string | yes | Item text. |
danger | boolean | Destructive action: danger colour. | |
disabled | boolean | Shown but can't be chosen. | |
onSelect | () => void | yes | Called when the item is chosen; the menu then closes. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Spotlight #
Command palette: the search input is a combobox driving a listbox — ↑ ↓ move the active option, Enter runs it, Escape closes.
function Example() {
const open = signal(false);
return (
<>
<Button onClick={() => open.set(true)}>Open command palette</Button>
<Spotlight
open={open()}
onClose={() => open.set(false)}
actions={[
{ id: "new", label: "New file", onSelect: () => open.set(false) },
{ id: "open", label: "Open recent", onSelect: () => open.set(false) },
{ id: "settings", label: "Settings", onSelect: () => open.set(false) },
]}
/>
</>
);
}Slots: root backdrop description empty input label list option panel
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
actions | SpotlightAction[] | yes | Actions to search and run. | |
onClose | () => void | yes | Called on Escape, backdrop click or after an action runs; set open to false. | |
open | boolean | yes | Whether the palette is shown (controlled). | |
placeholder | string | "Search actions…" | Search field hint. |
SpotlightAction
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Action id. |
label | string | yes | Action name; what the search matches. |
description | string | Secondary line under the label. | |
onSelect | () => void | yes | Called when the action is chosen; the palette then closes. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Lightbox #
Fullscreen image viewer: focus trap, Escape, ← → between images.
function Example() {
const index = signal<number | null>(null);
const images = [
{ src: "/images/one.jpg", alt: "Blue placeholder" },
{ src: "/images/two.jpg", alt: "Green placeholder" },
{ src: "/images/three.jpg", alt: "Orange placeholder" },
];
return (
<>
<Button onClick={() => index.set(0)}>Open gallery</Button>
{index() !== null ? (
<Lightbox
images={images}
index={index() ?? 0}
onChange={index.set}
onClose={() => index.set(null)}
/>
) : null}
</>
);
}Slots: root backdrop caption control controls image stage
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
images | LightboxImage[] | yes | The images, in order. | |
index | number | yes | Index of the shown image; null closes the viewer. | |
onClose | () => void | yes | Called on Escape, backdrop click or the close button; set index to null. | |
onChange | (index: number) => void | Called with the next index when the user moves with ← / → or the arrows. |
LightboxImage
| Field | Type | Required | Description |
|---|---|---|---|
src | string | yes | Image URL. |
alt | string | Alternative text (also the viewer's accessible name). | |
caption | string | Caption under the image. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Overlay #
Dimmed layer over its positioned parent.
Content under the overlay
<Box style={{ position: "relative", "min-height": "6rem" }}>
<Text>Content under the overlay</Text>
<Overlay blur />
</Box>| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
blur | boolean | Blur what is underneath. | ||
children | content | Content shown centred on the overlay. | ||
onClick | (e: MouseEvent) => void | Called when the overlay is clicked. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.