Overlays

Dialog

Show a focused task, form, or details above the current page.

Installation

pnpm dlx @mielui/svelte add dialog

Usage

Bind open on Root to open or close the dialog from another control. onOpenChange runs when the dialog opens or closes itself; setting the bound value does not call it. Each Root keeps its own state.

Include a Title, or give Content an aria-label. Description is optional. Focus, Escape, and outside presses are coordinated with nested overlays, so the topmost layer closes first.

The theme's chrome.borders setting chooses single framing, the default, or double framing around the panel.

<script lang="ts">
    import * as Dialog from '@mielui/svelte/components/dialog';
    import Kbd from '@mielui/svelte/components/kbd';

    let open = $state(false);
</script>

<Dialog.Root bind:open>
    <Dialog.Trigger>Open</Dialog.Trigger>
    <Dialog.Content>
        <Dialog.Header>
            <Dialog.Title>Title</Dialog.Title>
        </Dialog.Header>
        <Dialog.Footer>
            <Dialog.Close>Cancel <Kbd shortcut="esc" /></Dialog.Close>
            <Dialog.Confirm>Save <Kbd shortcut="enter" /></Dialog.Confirm>
        </Dialog.Footer>
    </Dialog.Content>
</Dialog.Root>

Examples

Form in a dialog

A compact dialog with an icon beside the title, one field, and shortcut hints on the actions.

Basic

Nested

With select

A Select inside a dialog keeps its own layer: Escape closes the menu first and only then the dialog.

Compact

Large

Wide

Glass surface

Set surface="glass" on Dialog.Content for a translucent background with blur. Omit surface to inherit --mielui-surface from your theme, or set surface="solid" to override it. The glass surface keeps an opaque fallback when backdrop filtering is unavailable and respects reduced-transparency preferences.

Dismissal

A dialog closes four ways: the corner button, Escape, a press outside the panel, and its own Close or Confirm buttons. Content has a switch for each of the first three. showClose={false} removes the corner button, allowEscape={false} ignores Escape, and allowClickOutside={false} ignores presses on the backdrop.

Turn all three off when the person has to make a choice, and leave at least one button in the footer so they are never stuck.

Action buttons

Close and Confirm close the dialog after their onclick runs. Call event.preventDefault() inside onclick to keep it open, then set open to false yourself when the work is done. Pair that with loading and labels so the button shows progress and ignores a second click.

Trigger, Close and Confirm are built on Button, so they accept its props. variant and size change the look, disabled blocks activation, and href renders a link. loading shows a spinner and ignores clicks, and labels sets the text for each state. unstyled removes the Button classes so class alone styles the part. onclick and onkeydown run before the part does its own work. Confirm defaults to the primary variant and Close to ghost.

Destructive dialogs

Set error on Root when confirming is destructive. Confirm switches to the destructive variant unless you pass your own variant, and browsers that tint their toolbar from the page's theme color turn it red while the dialog is open.

Styling the layers

Content renders three nested layers and takes a class for each. overlayClass styles the backdrop, class the dialog panel, and surfaceClass the inner surface that holds your parts. The style attribute lands on the panel too.

Header, Title, Description, Body and Footer render their children and accept class and style like any element.

Labels

Built-in text is English. Pass a labels object to Root to translate the accessible name of the corner close button. DialogLabels types it; every key is optional and omitted keys keep their English default.

<Dialog.Root labels={{ close: 'Fermer' }}>
    <Dialog.Trigger>Ouvrir</Dialog.Trigger>
    <Dialog.Content>
        <Dialog.Header>
            <Dialog.Title>Renommer le projet</Dialog.Title>
        </Dialog.Header>
    </Dialog.Content>
</Dialog.Root>

API reference

Dialog.Root

Controls dialog state.

  • labels
    DialogLabels

    Overrides the built-in text and accessible names. Every key is optional and English is the fallback.

  • open Bindable
    boolean

    Whether it is open. Bind it to control the state from outside.

    Default false

  • onOpenChange
    ((open: boolean) => void)

    Called with the new state whenever it opens or closes.

  • error
    boolean

    Marks the dialog as destructive. Confirm defaults to the destructive variant, and browsers that tint their chrome from the theme color turn it red while the dialog is open.

    Default false

  • children
    Snippet<[]>

    Content rendered inside.

Dialog.Body

Renders the dialog body.

  • class
    string

    Classes added to the element.

  • children
    Snippet<[]>

    Content rendered inside.

Dialog.Close

Closes the dialog.

  • disabled
    boolean

    Prevents activation. A disabled link loses its destination and is skipped by Tab.

  • variant
    ButtonVariant

    Visual style. Choose it for what the action means, not for decoration.

  • size
    "sm" | "md" | "lg" | "icon"

    Control height. icon is a square button for a single icon.

  • children
    Snippet<[]>

    Content rendered inside.

  • element Bindable
    HTMLButtonElement | HTMLAnchorElement

    Bindable reference to the rendered DOM element. Type is the union of the two possible element types -- narrow at the use site:

  • unstyled
    boolean

    Skip the variant/size base classes and render with class alone.

  • status
    ButtonStatus

    Controlled visual state. Loading remains focusable and refuses activation.

  • loading
    boolean

    Convenience alias for status="loading".

  • labels
    ButtonLabels

    Overrides the built-in text and accessible names. Every key is optional and English is the fallback.

  • onclick
    ((event: MouseEvent) => void)

    Called when the button is activated. Loading and disabled buttons do not call it.

  • onkeydown
    ((event: KeyboardEvent) => void)

    Called on key down, before the button handles the key itself.

  • href
    string

    Renders a link to this address instead of a button.

Dialog.Confirm

Confirms and closes the dialog.

  • disabled
    boolean

    Prevents activation. A disabled link loses its destination and is skipped by Tab.

  • variant
    ButtonVariant

    Visual style. Choose it for what the action means, not for decoration.

  • size
    "sm" | "md" | "lg" | "icon"

    Control height. icon is a square button for a single icon.

  • children
    Snippet<[]>

    Content rendered inside.

  • element Bindable
    HTMLButtonElement | HTMLAnchorElement

    Bindable reference to the rendered DOM element. Type is the union of the two possible element types -- narrow at the use site:

  • unstyled
    boolean

    Skip the variant/size base classes and render with class alone.

  • status
    ButtonStatus

    Controlled visual state. Loading remains focusable and refuses activation.

  • loading
    boolean

    Convenience alias for status="loading".

  • labels
    ButtonLabels

    Overrides the built-in text and accessible names. Every key is optional and English is the fallback.

  • onclick
    ((event: MouseEvent) => void)

    Called when the button is activated. Loading and disabled buttons do not call it.

  • onkeydown
    ((event: KeyboardEvent) => void)

    Called on key down, before the button handles the key itself.

  • href
    string

    Renders a link to this address instead of a button.

Dialog.Content

Renders the dialog surface.

  • surface
    "solid" | "glass"

    Surface treatment. Omit it to inherit --mielui-surface from the theme.

  • allowClickOutside
    boolean

    Closes when the pointer is pressed outside it. Set false to keep it open until it is dismissed another way.

    Default true

  • allowEscape
    boolean

    Lets Escape dismiss the dialog.

    Default true

  • overlayClass
    string

    Classes for the backdrop.

    Default ''

  • surfaceClass
    string

    Classes for the inner surface.

    Default ''

  • showClose
    boolean

    Shows the close button in the corner.

    Default true

  • size
    DialogSize

    Maximum width of the panel. Defaults to lg.

    Default 'lg'

  • class
    string

    Classes added to the element.

  • children
    Snippet<[]>

    Content rendered inside.

Dialog.Description

Renders the dialog description.

  • class
    string

    Classes added to the element.

  • children
    Snippet<[]>

    Content rendered inside.

Dialog.Footer

Groups dialog actions.

  • class
    string

    Classes added to the element.

  • children
    Snippet<[]>

    Content rendered inside.

Dialog.Header

Groups dialog heading content.

  • class
    string

    Classes added to the element.

  • children
    Snippet<[]>

    Content rendered inside.

Dialog.Title

Renders the dialog title.

  • class
    string

    Classes added to the element.

  • children
    Snippet<[]>

    Content rendered inside.

Dialog.Trigger

Opens the dialog.

  • disabled
    boolean

    Prevents activation. A disabled link loses its destination and is skipped by Tab.

  • variant
    ButtonVariant

    Visual style. Choose it for what the action means, not for decoration.

  • size
    "sm" | "md" | "lg" | "icon"

    Control height. icon is a square button for a single icon.

  • children
    Snippet<[]>

    Content rendered inside.

  • element Bindable
    HTMLButtonElement | HTMLAnchorElement

    Bindable reference to the rendered DOM element. Type is the union of the two possible element types -- narrow at the use site:

  • unstyled
    boolean

    Skip the variant/size base classes and render with class alone.

  • status
    ButtonStatus

    Controlled visual state. Loading remains focusable and refuses activation.

  • loading
    boolean

    Convenience alias for status="loading".

  • labels
    ButtonLabels

    Overrides the built-in text and accessible names. Every key is optional and English is the fallback.

  • onclick
    ((event: MouseEvent) => void)

    Called when the button is activated. Loading and disabled buttons do not call it.

  • onkeydown
    ((event: KeyboardEvent) => void)

    Called on key down, before the button handles the key itself.

  • href
    string

    Renders a link to this address instead of a button.