Skip to the page
Tactile UI

Kit parts

Bottom sheet

A panel that rises from the bottom edge of the window, on the native dialog, for a short task that should not leave the page: add an expense, set some filters, pick an action. It stops at two heights, half and full. A grip in its top edge drags it, or steps it between the heights from the keyboard; dragged down from half it goes away. The key that saves stays in sight at half, and the content scrolls only when the sheet is full. On a phone it is the whole width; on a wide screen it is a card, centred, standing on the bottom edge.

On this page
  1. Anatomy
  2. Variants
  3. States
  4. Props and options
  5. Usage notes
  6. Accessibility
  7. Related parts

Seats open in 11 days. Want one email that morning?

Anatomy

The drawer’s plate and scrim, turned to the bottom edge: one native dialog, always as tall as its full height, so nothing about its layout changes as it moves. Half is the dialog slid down by half its height with a transform, and the foot is pulled back up by the same distance so it stays on screen; both move together, on the same curve. A drag sets one custom property and the snap is a short pure function of where the sheet is, how fast it was going and which heights exist. The scrim is the dialog’s own backdrop, with no blur.

Variants

  • Half height · a short form
  • Full height
  • Actions · no foot
  • A decision · data-lock, no scrim click
  • Taller

States

  • Half
  • Full
  • Grip focused
  • Dragging

Props and options

NameKindDescription
data-open-atattributeThe height it opens at. Half is the default.
data-detentattributeThe height it is at now, set by the script. Read it, or set it before opening.
data-sheet-open · data-sheet-closeattributeOn any element: opens that sheet; closes the one it is in, with an optional returnValue.
data-lockattributeA click on the scrim no longer closes it. Escape still does; intercept cancel to stop that too.
openSheet(d, at) · closeSheet(d, value) · setSheetDetent(d, detent)functionshowModal() at a height, close(), and move to a height, said politely. Safe to call when already there.
sheetStep() · sheetToggle() · sheetSays()functionWhere a drag ends (half, full or closed), the height after an arrow key, the other height, and the words for a height.
sheetDetentY() · sheetClampOffset() · sheetVelocity() · bottomSheetHTML()function
Enter · SpacekeyboardSwap half and full.
Up · Page Up · HomekeyboardGo to full height. Pressed at full height they stay there; they never close the sheet.
Down · Page Down · EndkeyboardGo to half height. They never close the sheet.
EscapekeyboardCloses it; focus returns to the key that opened it.
Tab · Shift TabkeyboardCycle inside it: the page behind is inert while it is open.

Usage notes

Press Add expense: the sheet rises to half height and the focus lands on its title. Drag the grip up to full, or press the grip, or focus it and use Up and Down. Drag it down from half, press Escape, press the scrim or the close key to put it away. Filters opens full height, with content that scrolls; on a phone, drag from anywhere in the body.

Accessibility

  • It is a native dialog opened with showModal(), named by its title: focus is held inside, the page behind is inert and does not scroll, Escape closes it, and focus returns to the key that opened it. On opening, focus lands on the title, so the sheet is announced by name and no key is pressed by accident.
  • Dragging is never the only way. The grip is a real button, “Resize sheet”, with aria-expanded true at full: Enter and Space swap the heights, Up and Down step them, and the new height is said politely (“Full height”). Closing is a key, Escape and the scrim as well as a drag.
  • The key that saves is in the foot, and at half it is lifted to stay in sight, so the person never has to expand the sheet to find it. If focus moves to a field below the visible part, the sheet goes to full so the field is in view.
  • The content scrolls only at full height. At half a drag on the body moves the sheet; at full, a drag moves it only when the content is already at its top, so scrolling a long list is never taken for a drag. The page behind does not scroll at all.
  • Dragging follows the finger or pointer one to one. On release it goes to the nearest of the heights, counting how fast it was going, and dragged down from half it closes once it is past the middle of what shows, or with a flick. There is no bounce and no stretch.
  • Under reduced motion the sheet appears and goes without sliding, and every height and every word is still there.

Drawer

A panel that slides in from any edge of the window, on the native dialog. A face plate bolted to the frame, with a machined grip let into its inner edge to pull it shut by.

Dialog

A decision that must be made now: a native dialog with a question, one sentence of consequence, a way back and the act.

Alert dialog

The dialog plate for a question that must be answered before an act that matters. A caution strip is set across its head with one lamp in it, red when the act cannot be undone and amber when it can. The question is the title, one sentence says what will happen, and a grave act can ask for its name to be typed first.

Key

Every button, and every link that acts like one: a cap on a skirt that drops below the plate when pressed.