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.
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.
<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 first if you
want a real element in that slot.
Handle
Drag the top 48dp (or an explicit drag handle) 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 or a basic dialog is usually a better fit; the bottom sheet still lays out if you open one.