Sheet

Native drawer component using <dialog>

GitHub

Sheet drawer

Applies minimal styling and gesture-controls for making a <dialog> element look and behave like an iOS Sheet component

Close the drawer by dragging down the handle.


Inputs

Automatically resizes itself to fit the screen, even when a mobile keyboard is visible. Focus the input field below to see it in action.

The automatic scrolling and scaling on iOS is also disabled, removing the possibility to scroll the sheet out of view when an input is in focus.


Scroll

The sheet doesn't effect the native scrolling of the page. There's no need for any position: fixed;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15


Installation

import { sheet } from 'https://esm.sh/gh/erikthalen/sheet/src/lib'

const sheets = document.querySelectorAll('dialog').map(sheet)

// init function returns a teardown function
sheets.forEach(destroy => destroy())
body {
  /* can be any color */
  background-color: white;

  /* disable scroll */
  &:has(dialog[open]) {
    overflow: hidden;
    touch-action: none;
  }

  /* can be any transition */
  dialog {
    transform: translateY(100vh);
    transition: all 500ms var(--sheet-easing);
    transition-behavior: allow-discrete;
  }

  dialog::backdrop {
    background-color: rgb(0 0 0 / 25%);
    opacity: 0;
    transition: all 500ms var(--sheet-easing) allow-discrete;

    touch-action: none;
  }

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

  dialog[open]::backdrop {
    opacity: calc(1 - var(--amount, 0));
  }

  @starting-style {
    dialog[open] {
      transform: translateY(100vh);
    }

    dialog[open]::backdrop {
      opacity: 0;
    }
  }
}

Minimal example

<button onclick="document.getElementById('dialog')?.showModal()">
  Open sheet
</button>

<dialog id="dialog" closedby="any">
  <button data-sheet-handle aria-title="Drag to close">
    —
  </button>
  
  <p>Content</p>
</dialog>

Configuration

body {
  --sheet-top-margin: 3rem;
  --sheet-background-scale-amount: 10px;
  --sheet-background-border-radius: 10px;
  --sheet-easing: cubic-bezier(0.32, 0.72, 0, 1);
  --sheet-duration: 500ms;
}