Sheet
The sheet slides a modal panel in from a screen edge. Use it for edit forms, filters, carts, and mobile navigation; for a centered window, reach for the dialog.
<button id="sheet-1-trigger" class="btn btn-outline">Open</button>
<dialog class="sheet" data-sp-toggle="#sheet-1-trigger">
<div class="sheet-panel">
<button
class="btn btn-ghost btn-xs btn-icon absolute top-3 right-3"
aria-label="Close"
data-sp-dismiss
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
</button>
<div class="sheet-header">
<h2 class="sheet-title">Edit profile</h2>
<p class="sheet-description">
Make changes to your profile here. Click save when you're done.
</p>
</div>
<div class="sheet-content">
<div class="field-group">
<div class="field">
<label class="label" for="sheet-profile-name">Name</label>
<input class="input" id="sheet-profile-name" value="Pedro Duarte" />
</div>
<div class="field">
<label class="label" for="sheet-profile-username">Username</label>
<input class="input" id="sheet-profile-username" value="@peduarte" />
</div>
</div>
</div>
<div class="sheet-footer">
<button class="btn">Save changes</button>
<button class="btn btn-outline" data-sp-dismiss>Close</button>
</div>
</div>
</dialog>Usage
Add the sheet class to a <dialog> element. Give your trigger an id, then point the sheet at it with data-sp-toggle. The trigger can be any element. Close the sheet with data-sp-dismiss on a button, a backdrop click, or the Escape key.
<!-- This is a button toggling the sheet -->
<button id="my-trigger" class="btn btn-outline">Open Sheet</button>
<!-- This is the sheet -->
<dialog class="sheet" data-sp-toggle="#my-trigger">
<div class="sheet-panel">
<div class="sheet-header">
<h2 class="sheet-title">Title</h2>
<p class="sheet-description">Description</p>
</div>
<div class="sheet-footer">
<button class="btn btn-outline" data-sp-dismiss>Close</button>
</div>
</div>
</dialog>Examples
Side
Pick the edge the sheet slides from with sheet-start, sheet-end, sheet-top, or sheet-bottom; start and end follow the text direction, and sheet-left or sheet-right pin a physical edge. Without one, it defaults to the end.
<!-- Start -->
<button id="side-left" class="btn btn-outline">Start</button>
<dialog class="sheet sheet-start" data-sp-toggle="#side-left">
<div class="sheet-panel">
<div class="sheet-header">
<h2 class="sheet-title">Start</h2>
<p class="sheet-description">Great for navigation menus.</p>
</div>
<div class="sheet-footer">
<button class="btn btn-outline" data-sp-dismiss>Close</button>
</div>
</div>
</dialog>
<!-- End -->
<button id="side-right" class="btn btn-outline">End</button>
<dialog class="sheet sheet-end" data-sp-toggle="#side-right">
<div class="sheet-panel">
<div class="sheet-header">
<h2 class="sheet-title">End</h2>
<p class="sheet-description">Great for details and settings.</p>
</div>
<div class="sheet-footer">
<button class="btn btn-outline" data-sp-dismiss>Close</button>
</div>
</div>
</dialog>
<!-- Top -->
<button id="side-top" class="btn btn-outline">Top</button>
<dialog class="sheet sheet-top" data-sp-toggle="#side-top">
<div class="sheet-panel">
<div class="sheet-header">
<h2 class="sheet-title">Top</h2>
<p class="sheet-description">Great for notifications.</p>
</div>
<div class="sheet-footer">
<button class="btn btn-outline self-start" data-sp-dismiss>Close</button>
</div>
</div>
</dialog>
<!-- Bottom -->
<button id="side-bottom" class="btn btn-outline">Bottom</button>
<dialog class="sheet sheet-bottom" data-sp-toggle="#side-bottom">
<div class="sheet-panel">
<div class="sheet-header">
<h2 class="sheet-title">Bottom</h2>
<p class="sheet-description">Great for actions on mobile.</p>
</div>
<div class="sheet-footer">
<button class="btn btn-outline self-start" data-sp-dismiss>Close</button>
</div>
</div>
</dialog>Mobile navigation
Pair a start-side sheet with the sidebar menu classes for a simple mobile navigation drawer. For a full navigation layout with a desktop column, reach for the sidebar component instead.
<button id="sheet-nav-trigger" class="btn btn-ghost btn-icon" aria-label="Open menu">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16"/><path d="M4 18h16"/><path d="M4 6h16"/></svg>
</button>
<dialog class="sheet sheet-start" aria-label="Main menu" data-sp-toggle="#sheet-nav-trigger">
<div class="sheet-panel">
<button
class="btn btn-ghost btn-xs btn-icon absolute top-3 right-3"
aria-label="Close"
data-sp-dismiss
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
</button>
<div class="sheet-header">
<a href="#" class="flex h-10 min-w-0 items-center gap-2 text-foreground">
<span class="flex size-6 shrink-0 items-center justify-center overflow-hidden rounded-sm bg-primary text-base leading-none text-primary-foreground">
<i class="font-serif font-bold not-italic">A</i>
</span>
<span class="truncate text-sm font-medium">Acme Inc.</span>
</a>
</div>
<div class="sheet-content px-2">
<nav class="sidebar-menu">
<div class="sidebar-menu-item">
<a href="#" class="sidebar-menu-button active">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"/><path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>
<span>Home</span>
</a>
</div>
<div class="sidebar-menu-item">
<a href="#" class="sidebar-menu-button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-6l-2 3h-4l-2-3H2"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/></svg>
<span>Inbox</span>
</a>
</div>
<div class="sidebar-menu-item">
<a href="#" class="sidebar-menu-button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2v4"/><path d="M16 2v4"/><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M3 10h18"/></svg>
<span>Calendar</span>
</a>
</div>
<div class="sidebar-menu-item">
<a href="#" class="sidebar-menu-button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
<span>Settings</span>
</a>
</div>
</nav>
</div>
<div class="sheet-footer">
<button class="btn">Upgrade to Pro</button>
</div>
</div>
</dialog>RTL
Add dir="rtl" to any parent element, typically <html>, to render right-to-left. See the RTL guide for details.
<div id="sheet-rtl-demo" dir="rtl">
<button id="sheet-rtl-trigger" class="btn btn-outline" data-i18n="open">فتح</button>
<dialog class="sheet" data-sp-toggle="#sheet-rtl-trigger">
<div class="sheet-panel">
<div class="sheet-header">
<h2 class="sheet-title" data-i18n="editProfile">تعديل الملف الشخصي</h2>
<p class="sheet-description" data-i18n="description">قم بإجراء تغييرات على ملفك الشخصي هنا. انقر حفظ عند الانتهاء.</p>
</div>
<div class="sheet-content">
<div class="field-group">
<div class="field">
<label class="label" for="sheet-rtl-name" data-i18n="name">الاسم</label>
<input class="input" id="sheet-rtl-name" value="Pedro Duarte" />
</div>
<div class="field">
<label class="label" for="sheet-rtl-username" data-i18n="username">اسم المستخدم</label>
<input class="input" id="sheet-rtl-username" value="@peduarte" />
</div>
</div>
</div>
<div class="sheet-footer">
<button class="btn" data-i18n="save">حفظ التغييرات</button>
<button class="btn btn-outline" data-sp-dismiss data-i18n="close">إغلاق</button>
</div>
</div>
</dialog>
</div>
<script>
const translations = {
ar: { dir: "rtl", open: "فتح", editProfile: "تعديل الملف الشخصي", description: "قم بإجراء تغييرات على ملفك الشخصي هنا. انقر حفظ عند الانتهاء.", name: "الاسم", username: "اسم المستخدم", save: "حفظ التغييرات", close: "إغلاق" },
he: { dir: "rtl", open: "פתח", editProfile: "עריכת פרופיל", description: "בצע שינויים בפרופיל שלך כאן. לחץ שמור כשתסיים.", name: "שם", username: "שם משתמש", save: "שמור שינויים", close: "סגור" },
en: { dir: "ltr", open: "Open", editProfile: "Edit profile", description: "Make changes to your profile here. Click save when you're done.", name: "Name", username: "Username", save: "Save changes", close: "Close" },
};
window.addEventListener("message", (e) => {
const t = translations[e.data?.lang];
if (e.data?.type !== "sp-language" || !t) return;
document.getElementById("sheet-rtl-demo").dir = t.dir;
document.querySelectorAll("[data-i18n]").forEach((el) => {
el.textContent = t[el.dataset.i18n];
});
});
</script>Options
Sheet
Set each option with its own data-sp-* attribute on the sheet element.
<dialog class="sheet" data-sp-toggle="#my-trigger" data-sp-static>JavaScript
Events
Lifecycle events fire on the sheet element; before* events are cancelable.
el.addEventListener("sp-beforeshow", (e) => {
if (!ready) e.preventDefault(); // veto the open
});Methods
Get the instance with sp.sheet(el), then call its methods:
const sheet = sp.sheet(document.querySelector("#my-sheet"));
sheet.show();