pnpm dlx @glinui/cli@latest add modalMembers
3 of 10 seats used
- AKAva Kimava@lumen.testOwner
- JRJon Reyesjon@lumen.testAdmin
- SPSara Patelsara@lumen.testEditor
Installation
pnpm dlx @glinui/cli@latest add modalPrefer the package?
Usage
Overlay demos on this page render inside the preview stage so they stay contained. In your app they portal to the document body as usual.
Sizes
Choose sm, md or lg to match the amount of content.
"use client"
import { Button, Input, Modal, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, ModalTrigger} from "@glinui/ui"
export function ModalSizesExample() { return ( <div className="mx-auto flex flex-wrap items-center justify-center gap-3"> {(["sm", "md", "lg"] as const).map((size) => ( <Modal key={size}> <ModalTrigger asChild><Button variant="outline" size="sm">{`Size ${size}`}</Button></ModalTrigger> <ModalContent size={size}> <ModalHeader> <ModalTitle>Rename workspace</ModalTitle> <ModalDescription>Pick a name your team will recognise.</ModalDescription> </ModalHeader> <Input aria-label="Workspace name" defaultValue="Lumen" /> <ModalFooter> <ModalClose asChild><Button variant="ghost">Cancel</Button></ModalClose> <ModalClose asChild><Button>Save name</Button></ModalClose> </ModalFooter> </ModalContent> </Modal> ))} </div> )}Variants
Every look of the shared vocabulary: glinr (the default), plain (shadcn), solid, soft, outline, ghost, gradient and glass. Omit `variant` to follow the ambient design style.
import { Button, Modal, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, ModalTrigger } from "@glinui/ui"
const variants = ["glinr", "plain", "solid", "soft", "outline", "ghost", "gradient", "glass"] as const
export function Demo() { return ( <div className="flex flex-wrap gap-2"> {variants.map((variant) => ( <Modal key={variant}> <ModalTrigger asChild><Button size="sm" variant="outline">{variant}</Button></ModalTrigger> <ModalContent variant={variant} size="sm"> <ModalHeader> <ModalTitle>{variant} modal</ModalTitle> <ModalDescription>Same content, different surface.</ModalDescription> </ModalHeader> <ModalFooter> <ModalClose asChild><Button variant="ghost">Close</Button></ModalClose> <Button>Continue</Button> </ModalFooter> </ModalContent> </Modal> ))} </div> )}Glass (opt-in)
variant="glass" is opt-in. It blurs and tints whatever sits behind the panel, so it needs a colourful or photographic backdrop: switch the stage backdrop to see it.
import { Button, Modal, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, ModalTrigger } from "@glinui/ui"
export function Demo() { return ( <Modal> <ModalTrigger asChild><Button variant="outline">Open glass modal</Button></ModalTrigger> <ModalContent variant="glass"> <ModalHeader> <ModalTitle>Share workspace</ModalTitle> <ModalDescription>The page behind stays visible through the panel.</ModalDescription> </ModalHeader> <ModalFooter> <ModalClose asChild><Button variant="ghost">Cancel</Button></ModalClose> <Button>Share</Button> </ModalFooter> </ModalContent> </Modal> )}With Form Content
Place form fields inside the body area between `ModalHeader` and `ModalFooter`.
import { Button, Modal, ModalTrigger, ModalClose, ModalContent, ModalHeader, ModalFooter, ModalTitle, ModalDescription } from "@glinui/ui"
export function ModalFormDemo() {
return (
<Modal>
<ModalTrigger asChild>
<Button variant="glass">Edit Details</Button>
</ModalTrigger>
<ModalContent variant="matte">
<ModalHeader>
<ModalTitle>Account Settings</ModalTitle>
<ModalDescription>Update your display name and email address.</ModalDescription>
</ModalHeader>
<div className="space-y-3 py-1">
<div className="space-y-1">
<Label htmlFor="modal-name">Display Name</Label>
<Input id="modal-name" variant="glass" placeholder="Your name" />
</div>
<div className="space-y-1">
<Label htmlFor="modal-email">Email</Label>
<Input id="modal-email" type="email" variant="glass" placeholder="you@example.com" />
</div>
</div>
<ModalFooter>
<ModalClose asChild>
<Button variant="ghost">Cancel</Button>
</ModalClose>
<Button variant="liquid">Save changes</Button>
</ModalFooter>
</ModalContent>
</Modal>
)
}Frosted Variant
Use `variant="frosted"` for a heavier frost with stronger blur and inner glow. Great for prominent dialogs.
import { Button, Modal, ModalTrigger, ModalClose, ModalContent, ModalHeader, ModalFooter, ModalTitle, ModalDescription } from "@glinui/ui"
export function ModalFrostedDemo() {
return (
<Modal>
<ModalTrigger asChild>
<Button variant="frosted">Frosted Modal</Button>
</ModalTrigger>
<ModalContent variant="frosted">
<ModalHeader>
<ModalTitle>Frosted Glass</ModalTitle>
<ModalDescription>Heavy frost with 40px blur, 200% saturation, and inner glow.</ModalDescription>
</ModalHeader>
<ModalFooter>
<ModalClose asChild>
<Button variant="ghost">Close</Button>
</ModalClose>
<Button variant="liquid">Continue</Button>
</ModalFooter>
</ModalContent>
</Modal>
)
}Confirmation Dialog
Use a short `ModalDescription` and a destructive action button for irreversible operations.
import { Button, Modal, ModalTrigger, ModalClose, ModalContent, ModalHeader, ModalFooter, ModalTitle, ModalDescription } from "@glinui/ui"
export function ModalConfirmDemo() {
return (
<Modal>
<ModalTrigger asChild>
<Button variant="matte">Delete Project</Button>
</ModalTrigger>
<ModalContent variant="glass" size="sm">
<ModalHeader>
<ModalTitle>Are you sure?</ModalTitle>
<ModalDescription>
This action cannot be undone. The project and all its data will be permanently deleted.
</ModalDescription>
</ModalHeader>
<ModalFooter>
<ModalClose asChild>
<Button variant="ghost">Cancel</Button>
</ModalClose>
<Button variant="liquid">Delete</Button>
</ModalFooter>
</ModalContent>
</Modal>
)
}Accessibility
- Built on Radix Dialog with focus trapping.
- ModalTitle is required for screen readers.
- Background interaction blocked when modal.
- Focus is trapped inside the dialog while open.
- `Escape` closes the dialog and returns focus to the trigger.
- The backdrop overlay blocks interaction with background content.
Keyboard Interactions
| Key | Action |
|---|---|
| Escape | Close the modal. |
| Tab | Cycle focus within the modal. |
ARIA Attributes
- `role="dialog"`
- `aria-modal="true"`
- `aria-labelledby`
- `aria-describedby`
Reduced Motion
Open/close transitions respect `prefers-reduced-motion`.
The overlay fade and content scale animations respect `prefers-reduced-motion` and collapse to instant transitions.
Affected properties
opacitytransformAPI Reference
Modal
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
open | boolean | - | - | Controlled open state. |
defaultOpen | boolean | - | - | Initial open state. |
onOpenChange | (open: boolean) => void | - | - | Open state change handler. |
modal | boolean | - | true | Trap focus and block background interaction. |
container | HTMLElement | null | No | - | Element to portal into. When set, overlay and content use absolute positioning scoped to the nearest positioned ancestor. |
size | "sm" | "md" | "lg" | No | "md" | Variant option from modalSizeVariants. |
variant | PanelVariantProp | No | - | Surface look. Omit for the ambient design style (glinr by default). `glass` is opt-in and needs a colourful or photographic backdrop to read as frosted. |
ModalTrigger
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
asChild | boolean | - | false | Render as child element. |
container | HTMLElement | null | No | - | Element to portal into. When set, overlay and content use absolute positioning scoped to the nearest positioned ancestor. |
size | "sm" | "md" | "lg" | No | "md" | Variant option from modalSizeVariants. |
variant | PanelVariantProp | No | - | Surface look. Omit for the ambient design style (glinr by default). `glass` is opt-in and needs a colourful or photographic backdrop to read as frosted. |
ModalContent
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
variant | PanelVariantProp | No | ambient style (glinr) | Panel look. Omit it for the ambient design style (glinr shell with a gradient hairline ring, elev-3). `plain` is the shadcn border and shadow, `solid` and `soft` are tonal, `glass` is opt-in and needs a backdrop. In the glinr look `ModalHeader` and `ModalFooter` become raised strips that bleed to the dialog edges. Legacy `default` follows the ambient style, `frosted` maps to `glass`, `matte` to `solid`. |
size | "sm" | "md" | "lg" | No | md | Dialog width and padding. |
container | HTMLElement | null | No | - | Portal target. When set, the dialog and its flat dim scrim are positioned inside that element. |
className | string | - | - | Additional class names. |
onEscapeKeyDown | (event: KeyboardEvent) => void | - | - | Called when Escape is pressed. |
onPointerDownOutside | (event: PointerDownOutsideEvent) => void | - | - | Called when clicking outside. |
ModalHeader
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
className | string | - | - | Additional class names. |
container | HTMLElement | null | No | - | Element to portal into. When set, overlay and content use absolute positioning scoped to the nearest positioned ancestor. |
size | "sm" | "md" | "lg" | No | "md" | Variant option from modalSizeVariants. |
variant | PanelVariantProp | No | - | Surface look. Omit for the ambient design style (glinr by default). `glass` is opt-in and needs a colourful or photographic backdrop to read as frosted. |
ModalTitle
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
className | string | - | - | Additional class names. |
container | HTMLElement | null | No | - | Element to portal into. When set, overlay and content use absolute positioning scoped to the nearest positioned ancestor. |
size | "sm" | "md" | "lg" | No | "md" | Variant option from modalSizeVariants. |
variant | PanelVariantProp | No | - | Surface look. Omit for the ambient design style (glinr by default). `glass` is opt-in and needs a colourful or photographic backdrop to read as frosted. |
ModalDescription
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
className | string | - | - | Additional class names. |
container | HTMLElement | null | No | - | Element to portal into. When set, overlay and content use absolute positioning scoped to the nearest positioned ancestor. |
size | "sm" | "md" | "lg" | No | "md" | Variant option from modalSizeVariants. |
variant | PanelVariantProp | No | - | Surface look. Omit for the ambient design style (glinr by default). `glass` is opt-in and needs a colourful or photographic backdrop to read as frosted. |
ModalFooter
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
className | string | - | - | Additional class names. |
container | HTMLElement | null | No | - | Element to portal into. When set, overlay and content use absolute positioning scoped to the nearest positioned ancestor. |
size | "sm" | "md" | "lg" | No | "md" | Variant option from modalSizeVariants. |
variant | PanelVariantProp | No | - | Surface look. Omit for the ambient design style (glinr by default). `glass` is opt-in and needs a colourful or photographic backdrop to read as frosted. |
ModalClose
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
asChild | boolean | - | false | Render as child element. |
container | HTMLElement | null | No | - | Element to portal into. When set, overlay and content use absolute positioning scoped to the nearest positioned ancestor. |
size | "sm" | "md" | "lg" | No | "md" | Variant option from modalSizeVariants. |
variant | PanelVariantProp | No | - | Surface look. Omit for the ambient design style (glinr by default). `glass` is opt-in and needs a colourful or photographic backdrop to read as frosted. |
Source
Import directly from the package or browse the source on GitHub. Click any file to view it.
Generated API Snapshot
Beta
Generated API Snapshot
BetaAuto-extracted from TypeScript source in packages/ui/src/components/modal.tsx. This section is in beta and may lag behind hand-curated docs. Regenerate with pnpm --filter @glinui/docs api:generate.
Generated: 2026-10-08T01:41:21.546Z · Full index: /docs/api-metadata
Primary Props Type
ModalProps
ModalContentProps
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
container | HTMLElement | null | No | - | Element to portal into. When set, overlay and content use absolute positioning scoped to the nearest positioned ancestor. |
size | "sm" | "md" | "lg" | No | "md" | Variant option from modalSizeVariants. |
variant | PanelVariantProp | No | - | Surface look. Omit for the ambient design style (glinr by default). `glass` is opt-in and needs a colourful or photographic backdrop to read as frosted. |
ModalDescriptionProps
No explicit fields extracted for this props type.
ModalFooterProps
No explicit fields extracted for this props type.
ModalHeaderProps
No explicit fields extracted for this props type.
ModalOverlayProps
No explicit fields extracted for this props type.
ModalPortalProps
No explicit fields extracted for this props type.
ModalProps
No explicit fields extracted for this props type.
ModalTitleProps
No explicit fields extracted for this props type.