Right

Default side. The sheet slides in from the right edge — commonly used for edit-profile or settings panels.

<!-- Trigger -->
<button class="btn" data-variant="outline"
        data-sheet-trigger="sheet-right"
        aria-haspopup="dialog">Open Right Sheet</button>

<!-- Sheet (place as direct child of body) -->
<dialog id="sheet-right" class="sheet" data-side="right"
        role="dialog" aria-modal="true"
        aria-labelledby="sheet-right-title">
  <div class="sheet-content">
    <div class="sheet-header">
      <h2 class="sheet-title" id="sheet-right-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-body">
      <div style="display:flex;flex-direction:column;gap:1rem;">
        <div>
          <label class="label" for="sheet-name">Name</label>
          <input id="sheet-name" class="input" type="text" value="Cody Lindley">
        </div>
        <div>
          <label class="label" for="sheet-username">Username</label>
          <input id="sheet-username" class="input" type="text" value="@codylindley">
        </div>
      </div>
    </div>
    <div class="sheet-footer">
      <button class="btn" data-variant="outline" data-sheet-close>Cancel</button>
      <button class="btn" data-variant="default" data-sheet-close>Save changes</button>
    </div>
  </div>
  <button class="sheet-close-x" data-sheet-close aria-label="Close">
    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor"
         stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
      <path d="M18 6 6 18M6 6l12 12"/>
    </svg>
  </button>
</dialog>

Left

Slides in from the left edge — ideal for navigation menus or sidebar-style panels.

<!-- Trigger -->
<button class="btn" data-variant="outline"
        data-sheet-trigger="sheet-left"
        aria-haspopup="dialog">Open Left Sheet</button>

<!-- Sheet -->
<dialog id="sheet-left" class="sheet" data-side="left"
        role="dialog" aria-modal="true"
        aria-labelledby="sheet-left-title">
  <div class="sheet-content">
    <div class="sheet-header">
      <h2 class="sheet-title" id="sheet-left-title">Navigation</h2>
      <p class="sheet-description">Browse sections of the application.</p>
    </div>
    <div class="sheet-body">
      <nav style="display:flex;flex-direction:column;gap:0.25rem;">
        <a href="#" style="display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0.75rem;border-radius:var(--radius-lg);font-size:0.875rem;text-decoration:none;color:var(--foreground);transition:background 150ms;" onmouseover="this.style.background='var(--accent)'" onmouseout="this.style.background=''">Home</a>
        <a href="#" style="display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0.75rem;border-radius:var(--radius-lg);font-size:0.875rem;text-decoration:none;color:var(--foreground);transition:background 150ms;" onmouseover="this.style.background='var(--accent)'" onmouseout="this.style.background=''">Dashboard</a>
        <a href="#" style="display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0.75rem;border-radius:var(--radius-lg);font-size:0.875rem;text-decoration:none;color:var(--foreground);transition:background 150ms;" onmouseover="this.style.background='var(--accent)'" onmouseout="this.style.background=''">Settings</a>
        <a href="#" style="display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0.75rem;border-radius:var(--radius-lg);font-size:0.875rem;text-decoration:none;color:var(--foreground);transition:background 150ms;" onmouseover="this.style.background='var(--accent)'" onmouseout="this.style.background=''">Help</a>
      </nav>
    </div>
  </div>
  <button class="sheet-close-x" data-sheet-close aria-label="Close">
    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor"
         stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
      <path d="M18 6 6 18M6 6l12 12"/>
    </svg>
  </button>
</dialog>

Top

Slides down from the top edge — useful for notifications or announcement banners.

<!-- Trigger -->
<button class="btn" data-variant="outline"
        data-sheet-trigger="sheet-top"
        aria-haspopup="dialog">Open Top Sheet</button>

<!-- Sheet -->
<dialog id="sheet-top" class="sheet" data-side="top"
        role="dialog" aria-modal="true"
        aria-labelledby="sheet-top-title">
  <div class="sheet-content">
    <div class="sheet-header">
      <h2 class="sheet-title" id="sheet-top-title">Notifications</h2>
      <p class="sheet-description">You have 3 unread messages.</p>
    </div>
    <div class="sheet-body">
      <div style="display:flex;flex-direction:column;gap:0.75rem;">
        <div style="display:flex;align-items:flex-start;gap:0.75rem;padding:0.75rem;border-radius:var(--radius-lg);background:color-mix(in oklch, var(--muted), transparent 50%);">
          <div style="width:0.5rem;height:0.5rem;border-radius:9999px;background:var(--primary);margin-top:0.375rem;flex-shrink:0;"></div>
          <div><p style="font-size:0.875rem;font-weight:500;">New comment on your post</p><p style="font-size:0.75rem;color:var(--muted-foreground);margin-top:0.125rem;">2 minutes ago</p></div>
        </div>
        <div style="display:flex;align-items:flex-start;gap:0.75rem;padding:0.75rem;border-radius:var(--radius-lg);background:color-mix(in oklch, var(--muted), transparent 50%);">
          <div style="width:0.5rem;height:0.5rem;border-radius:9999px;background:var(--primary);margin-top:0.375rem;flex-shrink:0;"></div>
          <div><p style="font-size:0.875rem;font-weight:500;">Deployment successful</p><p style="font-size:0.75rem;color:var(--muted-foreground);margin-top:0.125rem;">15 minutes ago</p></div>
        </div>
        <div style="display:flex;align-items:flex-start;gap:0.75rem;padding:0.75rem;border-radius:var(--radius-lg);background:color-mix(in oklch, var(--muted), transparent 50%);">
          <div style="width:0.5rem;height:0.5rem;border-radius:9999px;background:var(--muted-foreground);margin-top:0.375rem;flex-shrink:0;"></div>
          <div><p style="font-size:0.875rem;font-weight:500;">Team invitation accepted</p><p style="font-size:0.75rem;color:var(--muted-foreground);margin-top:0.125rem;">1 hour ago</p></div>
        </div>
      </div>
    </div>
    <div class="sheet-footer">
      <button class="btn" data-variant="outline" data-size="sm" data-sheet-close>Dismiss all</button>
    </div>
  </div>
  <button class="sheet-close-x" data-sheet-close aria-label="Close">
    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor"
         stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
      <path d="M18 6 6 18M6 6l12 12"/>
    </svg>
  </button>
</dialog>

Bottom

Slides up from the bottom edge — great for cookie consent, action sheets, or preference panels.

<!-- Trigger -->
<button class="btn" data-variant="outline"
        data-sheet-trigger="sheet-bottom"
        aria-haspopup="dialog">Open Bottom Sheet</button>

<!-- Sheet -->
<dialog id="sheet-bottom" class="sheet" data-side="bottom"
        role="dialog" aria-modal="true"
        aria-labelledby="sheet-bottom-title">
  <div class="sheet-content">
    <div class="sheet-header">
      <h2 class="sheet-title" id="sheet-bottom-title">Cookie Preferences</h2>
      <p class="sheet-description">Manage your cookie settings. You can enable or disable different types of cookies below.</p>
    </div>
    <div class="sheet-body">
      <div style="display:flex;flex-direction:column;gap:1rem;">
        <div style="display:flex;align-items:center;justify-content:space-between;">
          <div><p style="font-size:0.875rem;font-weight:500;">Essential Cookies</p><p style="font-size:0.75rem;color:var(--muted-foreground);margin-top:0.125rem;">Required for the website to function.</p></div>
          <span style="font-size:0.75rem;color:var(--muted-foreground);">Always on</span>
        </div>
        <div style="display:flex;align-items:center;justify-content:space-between;">
          <div><p style="font-size:0.875rem;font-weight:500;">Analytics Cookies</p><p style="font-size:0.75rem;color:var(--muted-foreground);margin-top:0.125rem;">Help us improve our website.</p></div>
          <input type="checkbox" checked>
        </div>
        <div style="display:flex;align-items:center;justify-content:space-between;">
          <div><p style="font-size:0.875rem;font-weight:500;">Marketing Cookies</p><p style="font-size:0.75rem;color:var(--muted-foreground);margin-top:0.125rem;">Used for targeted advertising.</p></div>
          <input type="checkbox">
        </div>
      </div>
    </div>
    <div class="sheet-footer">
      <button class="btn" data-variant="outline" data-sheet-close>Cancel</button>
      <button class="btn" data-variant="default" data-sheet-close>Save preferences</button>
    </div>
  </div>
  <button class="sheet-close-x" data-sheet-close aria-label="Close">
    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor"
         stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
      <path d="M18 6 6 18M6 6l12 12"/>
    </svg>
  </button>
</dialog>

CSS view file

/* -- Sheet component ------------------------------------------- */

@layer components {
  /* -- Base -------------------------------------------------- */
  dialog.sheet {
    border: none;
    border-radius: 0;
    background-color: var(--background);
    color: var(--foreground);
    padding: 0;
    margin: 0;
    max-width: none;
    max-height: none;
    opacity: 0;
    transition: opacity 300ms ease, transform 300ms ease, display 300ms allow-discrete;

    &[open] { opacity: 1; }

    /* -- Side: right (default) --------------------------------- */
    &, &[data-side="right"] {
      position: fixed;
      top: 0; right: 0; bottom: 0;
      left: auto;
      width: 24rem;
      max-width: 100vw;
      max-height: 100vh;
      height: 100%;
      border-left: 1px solid var(--border);
      transform: translateX(100%);
    }

    &[open], &[data-side="right"][open] {
      transform: translateX(0);
    }

    /* -- Side: left -------------------------------------------- */
    &[data-side="left"] {
      position: fixed;
      top: 0; left: 0; bottom: 0;
      right: auto;
      width: 24rem;
      max-width: 100vw;
      max-height: 100vh;
      height: 100%;
      border-left: none;
      border-right: 1px solid var(--border);
      transform: translateX(-100%);

      &[open] { transform: translateX(0); }
    }

    /* -- Side: top --------------------------------------------- */
    &[data-side="top"] {
      position: fixed;
      top: 0; left: 0; right: 0;
      bottom: auto;
      width: 100%;
      max-width: 100vw;
      max-height: 100vh;
      height: auto;
      border-left: none;
      border-bottom: 1px solid var(--border);
      transform: translateY(-100%);

      &[open] { transform: translateY(0); }
    }

    /* -- Side: bottom ------------------------------------------ */
    &[data-side="bottom"] {
      position: fixed;
      bottom: 0; left: 0; right: 0;
      top: auto;
      width: 100%;
      max-width: 100vw;
      max-height: 100vh;
      height: auto;
      border-left: none;
      border-top: 1px solid var(--border);
      transform: translateY(100%);

      &[open] { transform: translateY(0); }
    }

    /* -- Backdrop ---------------------------------------------- */
    &::backdrop {
      background: oklch(0 0 0 / 0);
      backdrop-filter: blur(0px);
      transition: all 300ms ease, display 300ms allow-discrete;
    }

    &[open]::backdrop {
      background: oklch(0 0 0 / 0.45);
      backdrop-filter: blur(3px);
    }
  }

  @starting-style {
    dialog.sheet[open],
    dialog.sheet[data-side="right"][open] {
      opacity: 0;
      transform: translateX(100%);
    }

    dialog.sheet[data-side="left"][open] {
      opacity: 0;
      transform: translateX(-100%);
    }

    dialog.sheet[data-side="top"][open] {
      opacity: 0;
      transform: translateY(-100%);
    }

    dialog.sheet[data-side="bottom"][open] {
      opacity: 0;
      transform: translateY(100%);
    }

    dialog.sheet[open]::backdrop {
      background: oklch(0 0 0 / 0);
      backdrop-filter: blur(0px);
    }
  }

  /* -- Content sections -------------------------------------- */
  .sheet-content     { padding: 1.5rem; position: relative; }
  .sheet-header      { margin-bottom: 1rem; padding-right: 2rem; }
  .sheet-title       { font-size: 1.0625rem; font-weight: 600; margin: 0 0 0.375rem; letter-spacing: -0.01em; }
  .sheet-description { font-size: 0.875rem; color: var(--muted-foreground); margin: 0; line-height: 1.6; }
  .sheet-body        { margin-top: 1rem; }
  .sheet-footer      { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1.5rem; }

  /* -- Close button ------------------------------------------ */
  .sheet-close-x {
    position: absolute;
    top: 1rem; right: 1rem;
    width: 1.75rem; height: 1.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    color: var(--muted-foreground);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: color 150ms, background-color 150ms;

    &:hover {
      color: var(--foreground);
      background-color: var(--accent);
    }
  }
}

JavaScript view file

Identical pattern to Dialog. Wire triggers via data-sheet-trigger, close buttons via data-sheet-close, and backdrop click. Targets dialog.sheet elements.

// -- Sheet ----------------------------------------------------
// Wires [data-sheet-trigger] buttons to <dialog class="sheet"> elements.

function init() {
document.querySelectorAll('[data-sheet-trigger]:not([data-init])').forEach((trigger) => {
  trigger.dataset.init = '';
  const sheet = document.getElementById(trigger.dataset.sheetTrigger);
  if (!sheet) return;
  trigger.addEventListener('click', () => {
    sheet._trigger = trigger;
    sheet.showModal();
  });
});
document.querySelectorAll('dialog.sheet:not([data-init])').forEach((sheet) => {
  sheet.dataset.init = '';
  sheet.addEventListener('click', (e) => {
    if (e.target === sheet) sheet.close();
  });
  sheet.querySelectorAll('[data-sheet-close]').forEach((btn) => {
    btn.addEventListener('click', () => { sheet.close(); });
  });
  sheet.addEventListener('close', () => {
    if (sheet._trigger) sheet._trigger.focus();
  });
});
}

init();
new MutationObserver(init).observe(document, { childList: true, subtree: true });

Edit Profile

Make changes to your profile here. Click save when you're done.

Navigation

Browse sections of the application.

Notifications

You have 3 unread messages.

New comment on your post

2 minutes ago

Deployment successful

15 minutes ago

Team invitation accepted

1 hour ago

Cookie Preferences

Manage your cookie settings. You can enable or disable different types of cookies below.

Essential Cookies

Required for the website to function.

Always on

Analytics Cookies

Help us improve our website.

Marketing Cookies

Used for targeted advertising.