# Bottom sheet

> Secondary content anchored to the bottom of the screen.

## Anatomy

A `dialog.bottom-sheet` (or `.bottom`) is the surface. Use it for additional or secondary content — not the app’s main view — on compact and medium windows. People dismiss it to get back to the page.

Tokens follow the [M3 bottom sheet spec](https://m3.material.io/components/bottom-sheets). The container is `surface-container-low`, 28dp top corners, elevation 1. Max width is 640dp. From the Medium breakpoint the sheet is inset 56dp from the sides. It never covers the top 72dp of the viewport. The drag handle is a 32×4dp bar in a 48dp hit target.

Two variants, same sheet:

-   **Modal** — `showModal()`. A scrim sits behind the sheet and the page is inert. Tap the scrim, drag the handle down, or submit a `<form method="dialog">` to dismiss.
-   **Standard** — `show()`. No scrim; the page stays interactive. Drag the handle down or use a close action to dismiss.

Show modal Show standard

## Open file

-   Draft.md
-   Cover.png
-   Archive

Cancel

## Now playing

Secondary controls for the current track. The page behind this sheet stays usable.

Close

```
<button type="button" onclick="document.getElementById('sheet').showModal()">
  Show modal
</button>

<dialog id="sheet" class="bottom-sheet" aria-labelledby="open-file-title">
  <h2 id="open-file-title">Open file</h2>
  <div>…</div>
  <form method="dialog">
    <button type="submit" class="text" value="cancel">Cancel</button>
  </form>
</dialog>
```

There is no extra class for the modal variant — `showModal()` vs `show()` is the split. The handle is painted with `::before`. Put a [drag handle](/drag-handle.html.md) first if you want a real element in that slot.

## Handle

Drag the top 48dp (or an explicit [drag handle](/drag-handle.html.md)) down to dismiss. A flick counts. The rest of the sheet scrolls if the body overflows.

```
<dialog class="bottom-sheet" aria-labelledby="title-title">
  <span class="drag-handle" aria-hidden="true"></span>
  <h2 id="title-title">Title</h2>
  …
</dialog>
```

A `<span>` there is decoration and does nothing on its own — dragging is a pointer gesture, and Esc already dismisses the sheet from the keyboard, so the bar sits on top of a path that exists without it.

Make it a `<button>` when you want a visible dismiss control, and give it a name. Activating it closes the sheet, by pointer or by Enter — dragging is not the only way in. A drag that snaps back does not also dismiss: the click that ends a drag is told apart from a tap, so the sheet the drag declined to close stays open.

```
<dialog class="bottom-sheet" aria-labelledby="title-title">
  <button type="button" class="drag-handle" aria-label="Dismiss"></button>
  <h2 id="title-title">Title</h2>
  …
</dialog>
```

`.handle` is the pre-1.0 spelling of the same slot and still works, decorative or wired.

## Layout

Compact (below 600px) is full-bleed, flush to the bottom. From the Medium breakpoint the sheet is at most 640dp wide with 56dp side inset. It always stops 72dp short of the top. On large screens a [side sheet](/side-sheet.html.md) or a basic dialog is usually a better fit; the bottom sheet still lays out if you open one.
