Dialog
Anatomy
Modal dialog using native <dialog> element with focus trap, ESC/backdrop close, and size variants.
Nova's default follows the pinned 24rem dialog recipe. Size::Sm is a Rust
extension using the 20rem compact surface width; Standard retains its 28rem
compact width.
maud_ui::primitives::dialog
Props
Generated from public Rust fields. Defaults are evaluated from the implementation.
| Prop | Type | Default | Description |
|---|---|---|---|
id | String | "dialog" | Unique identifier for the dialog (used by trigger to open it) |
title | String | "Dialog" | Dialog title |
description | Option<String> | None | Optional description text displayed below title |
children | Markup | PreEscaped("") | Markup content displayed in dialog body |
footer | Option<Markup> | None | Optional footer markup (buttons/actions, rendered right-aligned) |
open | bool | false | Initial open state (default false; if true renders with open attribute for SSR) |
show_close_button | bool | true | Whether to render the close button in the top-right (default true) |
size | Size | Default | Dialog size variant (default Default) |
Examples
Usage and composition
Import
use maud_ui::primitives::dialog::{self, Props, Size};
Example
use maud::html;
use maud_ui::primitives::dialog;
html! {
(dialog::trigger("demo-dlg", "Open Dialog"))
(dialog::render(dialog::Props {
id: "demo-dlg".to_string(),
title: "Confirm".to_string(),
description: Some("Are you sure?".to_string()),
children: html! { p { "This action cannot be undone." } },
footer: Some(html! {
button class="mui-btn mui-btn--secondary" data-mui-close { "Cancel" }
button class="mui-btn mui-btn--primary" { "Delete" }
}),
show_close_button: true,
size: dialog::Size::Default,
..Default::default()
}))
}
Size Enum
| Variant | Description |
|---|---|
Default | Default size (max-width 32rem, padded) |
Sm | Compact size (max-width 28rem, reduced padding) |
Helper Functions
trigger(target_id, label) → Markup
Renders a button with data-mui="dialog-trigger" and data-target attributes. JS wires opening.
close_button(label) → Markup
Renders a close button with data-mui-close attribute. CSS positions it at the upper corner.
Features
- Native
<dialog>element: Uses built-in browser modal behavior; ESC and backdrop click close - ARIA attributes:
aria-labelledby(title),aria-describedby(description),aria-modal="true" - ID linking: Title ID auto-derived as
{id}-title; description ID as{id}-desc - Automatic footer layout: Footer content rendered right-aligned in footer slot
Related
- Drawer
- AlertDialog
- Command
Shadcn reference
https://ui.shadcn.com/docs/components/dialog
Small screens and long content
Standard keeps the header and footer around its scrolling body. Enhanced Nova uses the pinned unclipped surface when its content fits; when header, body or footer content exceeds the viewport, the whole dialog scrolls so every section remains reachable. This also responds to content and viewport changes. The native scrolling default remains available without JavaScript.
Standard has a 44px close target; Nova follows the pinned 28px ghost control. Long titles wrap without overlapping the Standard close target. On browsers supporting discrete display and overlay transitions, Nova uses the pinned 100 ms ease fade and scale from 95%; Standard retains its existing motion. Reduced motion suppresses popup animation; the existing backdrop fade remains an explicit difference. The showcase includes long content and a long-title review dialog.
Accessibility
- Dialog has
aria-modal="true" - Title and description linked via
aria-labelledbyandaria-describedby - Close button has
aria-label="Close" - Semantic
<h2>for title,<p>for description - The dismiss control follows body and footer content in DOM order. Native opening
focuses the first interactive body or footer control when present, and otherwise
the dismiss control; Tab follows that same order. Nova touch activation then focuses
the popup itself, unless an authored
autofocustarget takes precedence. - An initially open enhanced dialog returns focus to its sole associated, enabled trigger when dismissed. It does not guess among multiple triggers or override focus deliberately moved by the caller.