Sheet
Anatomy
Slide-out panel modal extending the Dialog primitive. Slides from a configurable edge (top, right, bottom, left) and uses the native <dialog> element for accessibility. Brand-new v0.2.1 primitive distinct from Dialog.
maud_ui::primitives::sheet
Props
Generated from public Rust fields. Defaults are evaluated from the implementation.
| Prop | Type | Default | Description |
|---|---|---|---|
id | String | "sheet" | Unique identifier for the sheet (used by trigger to open it) |
title | String | "Sheet" | Sheet title |
description | Option<String> | None | Optional description text displayed below title |
children | Markup | PreEscaped("") | Markup content displayed in sheet body |
footer | Option<Markup> | None | Optional footer markup pinned at the bottom |
side | Side | Right | Which side the sheet slides from (default Right) |
show_close_button | bool | true | Whether to render the close button (default true) |
open | bool | false | Initial open state (default false; if true renders with open attribute for SSR) |
Examples
right edge
left edge
top edge
bottom edge
Usage and composition
Import
use maud_ui::primitives::sheet::{self, Side, Props};
Example
use maud::html;
use maud_ui::primitives::sheet;
html! {
(sheet::trigger("edit-profile", "Open sheet"))
(sheet::render(sheet::Props {
id: "edit-profile".to_string(),
title: "Edit Profile".to_string(),
description: Some("Update your personal information.".to_string()),
children: html! {
div class="mui-field" {
label class="mui-label" { "Name" }
input class="mui-input" type="text" {}
}
},
footer: Some(html! {
button class="mui-btn mui-btn--primary" { "Save" }
}),
side: sheet::Side::Right,
show_close_button: true,
open: false,
}))
}
Side Enum
| Variant | Data Attr | Behavior |
|---|---|---|
| Top | top | Slides down from the top. |
| Right | right | Slides in from the right (default). |
| Bottom | bottom | Slides up from the bottom. |
| Left | left | Slides in from the left. |
Helpers
trigger
sheet::trigger(target_id: &str, label: &str) -> Markup
Renders a button that opens the sheet with the given target_id. Emits data-mui="dialog-trigger" (reuses dialog behaviour).
close_button
sheet::close_button(label: &str) -> Markup
Renders a close button for use inside the sheet body. Typically rendered by render() automatically if show_close_button: true, but available for custom layouts.
render
sheet::render(props: Props) -> Markup
Renders the sheet dialog element with all accessibility attributes wired up.
CSS Classes
| Class | Purpose |
|---|---|
mui-sheet | Root sheet container. |
mui-sheet--top | Side variant class (also --right, --bottom, --left). |
mui-sheet__header | Header region holding title and optional description. |
mui-sheet__title | Sheet title (<h2>). |
mui-sheet__description | Optional description paragraph. |
mui-sheet__body | Main content area (scrollable). |
mui-sheet__footer | Footer region (sticky to bottom). |
mui-sheet__close | Close button styling. |
Related
Dialog, Drawer, AlertDialog.
Shadcn reference
https://ui.shadcn.com/docs/components/base/sheet
Small screens and long content
In Standard, headers and footer actions remain visible while the body scrolls within the dynamic viewport. Close has a 44px target, and long titles wrap beside it. Standard footers include bottom safe-area padding. Nova uses the pinned 28px dismiss control and borderless footer recipe. Motion respects prefers-reduced-motion. The showcase includes long content and all four sheet edges.
Accessibility
- Sheet uses native
<dialog>element withrole="dialog". aria-labelledbypoints to the sheet title;aria-describedbypoints to the description (if present).- Close button labeled "Close" by default.
- Focus is managed by the browser's native
<dialog>behaviour; CSS animates the slide transition. The default close control follows body and footer content in DOM order, so a content action receives initial focus before that control. Its visual position remains at the top of the sheet. Nova touch activation then focuses the popup itself, unless an authoredautofocustarget takes precedence. - Tab order is trapped within the open sheet.