Skip to content
Glint UI

Dialog

A modal overlay that dims the window behind it and holds the reader on one focused task until they close it.

Usage

import { Dialog } from "@glint/components/dialog.slint";
export component AppWindow inherits Window {
in-out property <bool> editing: false;
callback reload-profile();
VerticalLayout {
// your screen
}
// Last child of the window, sized to it: Slint has no portals, so what
// draws on top is what comes last.
Dialog {
width: parent.width;
height: parent.height;
open <=> root.editing;
title: "Edit profile";
description: "Change your details and save.";
dismissed => { root.reload-profile(); }
}
}

A Dialog is the whole overlay: the dimmed backdrop and the card on top of it. It covers the window it is mounted in, so it takes the window’s full size and goes last among the window’s children — a Dialog declared before your screen’s layout would be painted underneath it.

Opening it is setting open to true; it sets open back to false itself when the reader closes it, and calls dismissed on the way out. The button that opens it should set haspopup, which is how a trigger says a surface is about to appear over the page.

If you already have your own overlay root and want only the card, mount DialogPanel instead — it is the same surface without the backdrop and without the full-window mount.

Examples

Body content

Whatever you put inside the Dialog becomes the body of the card, between the title block and the footer. The panel is 420px wide and as tall as its content.

Holding the reader until they choose

By default a click on the backdrop closes the modal. Set dismiss-on-backdrop: false when the dialog is asking for a decision that a stray click should not answer — the close button and Escape still work, so the reader is never trapped.

Naming the close button

close-label sets the accessible name of the corner close button and the text of the built-in button in the panel’s footer. It ships as @tr("Close") — translated with the rest of Glint — and you override it when the action deserves a name of its own.

Custom actions

actions lets you supply custom buttons for the panel footer as an array of DialogAction structs. action(int) fires with the index of the clicked action. Set show-footer-close: false to hide the default Close button when your custom actions are the only exits.

Restoring keyboard focus

restore-focus fires on every close — backdrop click, Escape, close button, or setting open = false programmatically. Slint has no automatic focus restoration, so wire restore-focus to return keyboard focus to the trigger that opened the dialog.

Driving it from your own state

open is two-way, so it binds to a property of yours: your screen decides when the dialog appears. dismissed reports closes the dialog initiates — the backdrop, the close button and Escape alike. Setting the bound property to false closes it without firing dismissed; restore-focus still fires for both paths.

API Reference

Properties

PropertyTypeDefaultDescription
openin-out boolfalseTwo-way; consumer sets true to show, Glint sets false on close.
titlein stringno defaultHeading at the top of the modal.
descriptionin stringno defaultBody text under the title.
dismiss-on-backdropin booltrueWhen true, clicking outside the panel closes the modal.
close-labelin string@tr("Close")Accessible label for the X-close button, and the text of the built-in footer Close button.
show-close-buttonin booltrueWhether the top-right X is rendered.
show-footer-closein booltrueWhether the footer carries the built-in Close button. Turn both off for a dialog whose own actions are the only exits.
actionsin [DialogAction][]The footer's own actions — Cancel + Save and the like — left to right, ahead of the built-in Close button.
panel-widthin length420pxWidth of the panel.
panel-max-heightin length0pxCeiling on the panel's height; 0 (the default) lets it grow with its content. Past the ceiling the body scrolls under a pinned footer.
content-takes-focusin boolfalseWhether the body slotted into @children answers the keyboard itself — a form does, a paragraph of text does not. See DialogPanel, which the modal hands this straight to.

Callbacks

CallbackDescription
dismissed()Fired when the modal closes itself — backdrop, Escape, the X or the footer Close button.
action(int)Fired when one of actions is pressed, with its index. The dialog does not close itself: whether Save closes and Cancel does not is the consumer's decision.
restore-focus()Fired on EVERY close, including one the consumer drives by setting open = false, so keyboard focus always gets a new home: restore-focus => { my-button.focus(); }. Slint exposes no "previously focused element", so only the trigger's owner can name it. Same contract Drawer, Sheet and AlertDialog publish.

DialogPanel

The card without the backdrop and without the full-window mount, for a screen that already has an overlay root of its own. Its host is what decides where it sits and when it is shown.

Properties

PropertyTypeDefaultDescription
titlein stringno defaultHeading at the top of the panel.
descriptionin stringno defaultBody text under the title.
close-labelin string@tr("Close")Accessible label for the X-close button, and the text of the built-in footer Close button.
show-close-buttonin booltrueWhether the top-right X is rendered. Turn it off — together with show-footer-close — for a dialog whose own actions are the only exits.
show-footer-closein booltrueWhether the footer carries the built-in Close button.
actionsin [DialogAction][]The footer's own actions, left to right, ahead of the built-in Close.
panel-widthin length420pxWidth of the card.
panel-max-heightin length0pxCeiling on the card's height; 0 (the default) lets it grow with its content. Past the ceiling the body scrolls and the footer stays put.
trap-focusin booltrueWhether Tab cycles inside the panel instead of leaving it. Escape closes either way — it is the modal's exit, not part of the trap.
content-takes-focusin boolfalseWhether the body slotted into @children answers the keyboard itself — a form does, a paragraph of text does not. The panel cannot answer this for itself: the body is the consumer's, and Slint's tab walk is pre-order, so the body's scrolling surface would take a stop *in front of* the first field. Left false the surface takes the stop and the arrows scroll it, which is right for a long inert body and costs one extra Tab in front of a form.

Callbacks

CallbackDescription
closed()Fired when the X or the footer Close button is pressed.
action(int)Fired when one of actions is pressed, with its index.

Functions

FunctionDescription
take-focus()Parks the keyboard just ahead of the body, so Escape has somewhere to land and the first Tab steps onto the first body control.
release-focus()Gives the keyboard up again. Taking the focus before dropping it is what makes this work from anywhere: clear-focus() only speaks for the scope that holds the focus, and on close that is usually one of the consumer's body controls, which this file cannot name.

Accessibility

  • Role and name. Slint has no dialog role, so the open panel claims the closest landmark it has — a region — named by title and described by description. That is the surface assistive technology lands on, and giving both properties a value is what makes it findable by name.
  • Focus. Opening the dialog moves focus into the panel; while it is open, Tab is trapped there, so the keyboard cannot wander into the screen behind the backdrop.
  • Keyboard. Escape closes the dialog and fires dismissed, including when dismiss-on-backdrop is false — that property governs the backdrop click and nothing else, so a modal a pointer cannot dismiss is still one a keyboard can leave.
  • Closing controls. close-label provides the accessible name for both the top-right close button (X) and the built-in footer button. The X is a fully accessible button in the tab order and accessibility tree, so screen readers and keyboard users have clear exits alongside Escape.
  • A closed dialog is not there. The panel leaves the accessibility tree once it has faded out — a screen reader may only find what a sighted reader can see — and the backdrop stops intercepting clicks, so a closed Dialog mounted over your screen does not block it.
  • The trigger. The control that opens a dialog should set haspopup, so that what it does is announced before it is pressed.