Overlays

Alert Dialog

A modal for critical confirmations.

Rust
1use herogpui::components::alert_dialog::{AlertDialog, AlertDialogCloseTrigger};

Usage

Rust
1use herogpui::prelude::{AlertDialog, AlertDialogCloseTrigger, Button, Variant};
2use gpui::prelude::*;
3
4stretch_col(vec![{
5    overlay_min_h(
6        gpui::div()
7            .relative()
8            .flex()
9            .flex_col()
10            .items_start()
11            .w_full(),
12        is_open,
13        240.,
14    )
15    .child(
16        Button::new("alert-dialog-open")
17            .label("Delete project")
18            .variant(Variant::Danger)
19            .on_press(cx.listener(|this, _, _, cx| {
20                this.alert_dialog_open = true;
21                cx.notify();
22            })),
23    )
24    .child(
25        AlertDialog::new("Delete this project?").id("alert-dialog")
26            .description(
27                "This removes the project and every deployment. \
28                                     This action cannot be undone.",
29            )
30            .is_open(is_open)
31            .child(AlertDialogCloseTrigger::new())
32            .footer_child(
33                Button::new("alert-dialog")
34                    .label("Cancel")
35                    .variant(Variant::Tertiary)
36                    .on_press(cx.listener(|this, _, _, cx| {
37                        this.alert_dialog_open = false;
38                        cx.notify();
39                    })),
40            )
41            .footer_child(
42                Button::new("alert-dialog")
43                    .label("Delete")
44                    .variant(Variant::Danger)
45                    .on_press(cx.listener(|this, _, _, cx| {
46                        this.alert_dialog_open = false;
47                        cx.notify();
48                    })),
49            )
50            .on_open_change(bool_cb(cx.listener(|this, v: &bool, _, cx| {
51                this.alert_dialog_open = *v;
52                cx.notify();
53            }))),
54    )
55    .into_any_element()
56}])

Live HeroGPUI compiled to WebAssembly. Select any example without loading another WASM instance.

Anatomy

Rust types

AlertDialogAlertDialogCloseTrigger

Alert Dialog is assembled from these builders. The API reference lists each one.

Customization

Appearance builders and theme tokens Alert Dialog uses.

Styling

Alert Dialog styling
StyleDescription
scrim absolute inset_0 + bg + outside press on panelThe scrim fills the window but never grabs presses, because the panel's on_mouse_down_out owns backdrop dismissal.
Backdrop::Opaque -> colors.backdropDefault scrim colour.
Backdrop::Blur -> colors.backdrop alpha 0.6Gpui has no backdrop filter, so Blur renders a lighter scrim instead.
Backdrop::Transparent -> transparent_blackNo scrim at all.
util::window_overlay + p(px(40.)) + placement flex matchCovers the window even inside clipped or positioned containers, paints above later page content, and blocks pointer input to the page beneath. The desktop container keeps 40px padding and applies placement alignment inside.
anim::Motion::PANEL_IN entering_zoomThe panel settles down onto the page from 105%; it shrinks to fit, it does not grow from 90%.
anim::Motion::PANEL_OUT exitingThe exit is 100ms at the same curve.
panel relative flex w_full + max_h(viewport-80) + overflow_hidden + p(px(24.)) + overlay bg + overlay_shadow + container_radiusPanel surface: overlay background and shadow with the floating-panel radius, a uniform 24px inset, and the clip that keeps the footer pinned while a long body scrolls; the max-height resolves against the container's content box, which names as viewport height less the 80px inset.
Auto/Center -> container items_center justify_centerAuto reads centered from the sm step up, which is what draws for both values; the mobile mt-auto sheet branch is unreachable on a desktop app.
Top -> items_start / Bottom -> items_end container alignmentTop pins the panel to the container's start and bottom to its end.
AlertDialogSize::max_width match px(320./384./448./512.)Tailwind's width scale — 20rem, 24rem, 28rem, 32rem — transcribed as fixed pixels.
AlertDialogSize::Cover -> w_full + h/min_h(viewport-80)Fills the container's content box on both axes while retaining the normal floating-panel radius and shadow.
header flex flex_col + gap(px(12.))Title column with a 12px gap; the icon is a child of it, above the heading.
text_size(px(16.)) + FontWeight::MEDIUM16px medium foreground title; align-middle has no gpui text equivalent.
icon size(px(40.)) + control_radius + svg size(px(20.))40px glyph box with the control radius holding a 20px glyph; tints it from a role colour rather than composed children, so select-none has no surface.
Color::Default -> colors.default.color + colors.foreground + INFO_CIRCLEThe plain default surface with the info glyph — not the role colour and not a soft mix.
role.soft() + role.color + INFO_CIRCLE / CHECK_CIRCLE / WARNING_TRIANGLE / CIRCLE_EXCLAMATIONThe soft role surfaces with the role colour as the glyph colour and v3's per-status glyph match: info for accent, then the success, warning and danger icons, embedded with upstream geometry.
body text_size(px(14.)) + line_height(px(20.)) + muted + mx(-3px) + p(3px) + max_h(body_max) + overflow_y_scroll14px muted content on a 20px line box; the 3px margin/padding pair survives as margins and padding, but gpui reserves gutter space only through scrollbar_width and paints none on a plain div, has no overscroll containment, and flex-1 applies only when Cover fixes the panel height (an auto-height scroller measures as zero).
actions flex items_center justify_end + gap(px(8.))End-aligned action row with an 8px gap; the margin is supplied by the sibling rule.
CloseButton absolute top(px(16.)) right(px(16.))Neutral close button pinned 16px from the top end, outside the header.
body mt(px(8.)) + actions mt(px(20.))Sibling spacing: 8px below the header and 20px above the footer; carries the margins on the body and the action row because a panel with no gap produces the same rhythm.
anim::Motion::BACKDROP_IN / BACKDROP_OUTThe scrim fades alone at 150ms in and 100ms out on ease-out.

API reference

Builders

Alert Dialog builders
BuilderTypeDefaultDescription
new(title) + description + extend bodyAnyElementTrigger and container elements; composes the panel itself and the ParentElement::extend children land in the body slot, with the trigger left to the caller.
backdrop(Backdrop)BackdropBackdrop::OpaqueBackdrop overlay style; all three variants render, but gpui has no backdrop-filter, so Blur draws a lighter scrim rather than a blur.
is_dismissible(bool)boolfalseClose on backdrop click; gates the outside-press dismissal on it. Like v3, the gate covers only the scrim — the close trigger renders in every documented example, including the isDismissable={false} one.
is_keyboard_dismiss_disabled(bool)booltrueDisable ESC key to close; Escape is already off by default here, so allowing it is the opt-in.
is_open(bool)boolControlled open state; the owner, not the dialog, decides the next render.
on_open_change(callback)Fn(isOpen: bool) -> ()Open state change handler; fires with false on every close path — the composed X, the backdrop, Escape and the built-in confirm/cancel stand-in — and never through onCancel. A composed footer's Buttons own their own close wiring.
placement(ModalPlacement)ModalPlacementModalPlacement::AutoDialog position on screen; all four anchor the panel, but v3's ±4px enter slide for top/bottom/auto is not ported — the fade and the zoom are.
size(AlertDialogSize)AlertDialogSizeAlertDialogSize::MdAlert Dialog size variant; the four max-width steps match and Cover fills the container's content box. There is no `full`: a critical confirmation never fills the viewport edge to edge.
new(title) + status(Color)title) + status(ColorHeader content (typically Icon and Heading); renders the header column from the title and the optional status icon, without a composable children slot.
new(title)titleHeading text; accepts a string via the title argument instead of composed children.
description + ParentElement::extendAnyElementBody content; one scrolling slot holds the description and the ParentElement::extend children, but v3's per-part prop surface is not exposed.
footer_child(Button) or the confirm_label/cancel_label pairAnyElementFooter content (typically action buttons); the port's built-in confirm/cancel pair stands in for v3's slot="close" compositions and retires whole the moment the caller composes footer children — a composed footer Button owns its own danger and pending spellings and its own close wiring.
status(Color)ColorCustom icon element; draws the status glyph itself, so composed children are not supported.
status(Color) -> icon_presentation glyph map"default" | "accent" | "success" | "warning" | "danger""danger"Status colour variant; the port's status defaults to None, which renders no icon at all — the analogue of not composing AlertDialog.Icon in v3. The role colour, soft surface and glyph all match v3's icon map: info for default and accent, then success, warning and danger, each embedded with upstream geometry.
new() + ParentElement childrenAnyElementThe dialog composes this part as a child: without children it draws the built-in neutral CloseButton wired to onOpenChange(false) alone — never on_cancel — and custom children replace the CloseButton's glyph while the press stays automatically wired to close the same way.

Parts

Alert Dialog parts
PartDescription
AlertDialogComposition root that owns the open state and the focus trap; configured callbacks receive every enabled dismissal path.
AlertDialogScrim behind the panel, dimmed but press-less; outside presses dismiss through the panel's own bounds.
AlertDialogWindow-pinning wrapper that applies the placement alignment and the 40px desktop inset.
AlertDialogThe panel itself: overlay surface, 24px inset, max-height cap and the overflow clip that keeps the footer pinned.
AlertDialogTitle column with a 12px gap; the optional icon is a child of it, above the heading, not a floating corner disc.
AlertDialog16px medium foreground title; accepts a string via the title argument instead of composed children.
AlertDialogOne scrolling slot holding the wrapping description and composed children, capped under the panel and given flex-1 only when Cover fixes the panel height.
AlertDialogEnd-aligned action row: the built-in cancel/confirm pair stands in for v3's slot="close" buttons and retires whole the moment the caller composes footer children, which own their own danger and pending spellings.
AlertDialog40px glyph box above the heading; tints it from a status role rather than composed children.
AlertDialogCloseTriggerComposed part pinned 16px from the top end: the default content is the built-in neutral CloseButton wired to onOpenChange(false) alone regardless of is_dismissible, and custom children replace the glyph while staying wired to close the same way. With no on_open_change to wire the part draws nothing.

States

Alert Dialog states
BuilderStateDescription
focus_handle + trap_tab + CloseButton tab_stop_handle ringFocusApplied to the trigger, the dialog and the close button; claims the panel's focus handle and traps Tab, and the close button rings from its own tab stop, while the trigger is not a part here.
CloseButton hover surfaceHoverApplied to the close button on hover.
CloseButton .active centered root-bounds shrinkActiveApplied to the trigger and the close button when pressed; the close button uses a centered root-bounds shrink while fixed child content remains unscaled, and the absent trigger has no surface.
OverlayPhase::Open + Motion::BACKDROP_IN + entering_zoom Motion::PANEL_INEnteringApplied during the opening animation: the scrim fades at 150ms and the panel fades while zooming in from 105% at 250ms — but v3's ±4px placement slide is not ported, because the existing listener-free motion primitives own the whole fade/zoom transition and a second fading layer would double-fade the panel.
overlay_phase::Exiting + Motion::PANEL_OUT + Motion::BACKDROP_OUTExitingApplied during the closing animation; the panel and the scrim stay mounted for the 100ms fade/zoom out, which carries no slide in either.
ModalPlacement anchored matchPlacementAuto, top, center and bottom anchor the dialog and choose the container alignment; only top and bottom carry the ±4px enter slide (slide-in-from-top/bottom-1) and that slide is not ported. Auto's slide-in-from-bottom-1 is canceled at sm, so a desktop Auto has no slide — that is upstream's value, not a missing one.