Sheet

Opens the details of one thing at the side.

Side

  • enddefault

  • start

  • bottom

  • top

Mode

  • modaldefault

    • run_8f2a91
    • run_8f2a90
    • run_8f2a8c

    docked

States

  • open

  • pending

  • failed

Usage

When to use it
Use
The details of one item from a list the person keeps in mind: a run, a key, an integration.
Use
The details of the selected row beside its list, while the list stays usable (docked).
Use
A short choice on a phone, from the bottom edge.
Not for
A yes or no question, or a form of up to three fields. Use Dialog.
Not for
A few lines next to the control that opened them. Use Popover.
Not for
The panel is the page's main subject. Give it a route.

Rules

9 rules
Must
Build the inside from SheetHeader, SheetBody and SheetFooter; never plain padded divs. Because the header and body together are the paper panel, the body is the only part that scrolls, and the footer is the shell's row.
Must
End the footer with SheetClose (paper) and at most one SheetAction. Because the footer is one group of actions, and two ink buttons in it split the eye.
Never
Put a shell card inside the body; group rows with SheetSection. Because the sheet is already the card, and a second shell inside its panel is a box in a box.
Must
Title the sheet with the item's name or ID and put context in SheetDescription. Because the title is the sheet's accessible name, so "Details" tells a screen reader nothing about which run is open.
Should
Use modal={false} for the details of a selected row; a modal sheet hides the list the person is working through.
Must
Open a docked panel with SheetTrigger and close it with SheetClose. Because the docked panel returns focus on Escape to the element SheetTrigger registered, so a plain button leaves focus lost.
Must
Give actions in a side="bottom" sheet the default size: render={<Button variant="secondary"/>} on SheetClose and size="default" on SheetAction. Because a bottom sheet is the phone layout, and a 26px small button is too short a target for a thumb.
Should
Pass mono to SheetField for an ID, a hash or a key.
Never
Add a shadow or a border to SheetContent; the ring and the scrim are its lift. Because Glaze has no shadows, and a border on top of the ring doubles the edge.

State order

3 states, highest first
1
pending
2
failed
3
open

Accessibility

5 checks
A modal sheet is a dialog named by SheetTitle; the page behind is inert and Tab cycles inside.
A docked sheet is an aside labelled by its title; Escape inside it closes it and returns focus to its trigger.
The resize handle is a focusable separator with its width as the value, moved by the arrow keys.
The body takes a tab stop only while it has more to scroll.
A failed action shows a role="alert" message above the footer.

Keyboard

4 keys
Escape
Closes the sheet and returns focus to its trigger; nothing while an action runs.
Enter
Presses the footer's default action when focus is not on a control.
Left and Right arrows
On the docked handle: narrow or widen by 32px, 8px with Shift.
Home, End, Enter
On the docked handle: the narrowest, the widest, the starting width.

Edge cases

6 edge cases
Sixty fields
Only the body scrolls; a hairline shows under the header once it has.
Phone, 390 wide
A side sheet leaves 40px of page beside it; a docked sheet takes the full width under its list.
Right to left
end is the left edge, the swipe to dismiss runs left and the resize handle moves to the panel's right.
Swipe on touch
The panel follows the finger toward its edge and the scrim fades with it.
A slow action
The arc turns; Close, the X, Escape, a swipe and the scrim wait for it.
Reduced motion
The panel fades without travelling.

API

10 props
Sheet modal
boolean default true
Sheet open, defaultOpen, onOpenChange
boolean, (open) => void default false
Sheet contained
boolean default false
SheetContent side
'start' | 'end' | 'top' | 'right' | 'bottom' | 'left' default 'end'
SheetContent showCloseButton
boolean default not on bottom
SheetContent defaultWidth, minWidth, maxWidth
number default 360, 280, 640
SheetContent onWidthChange
(width) => void
SheetFooter defaultAction, meta
'primary' | 'dismiss' | 'none', ReactNode default auto
SheetAction onAction, close
() => void | Promise, boolean
SheetField mono
boolean default false

Tokens

25 tokens
--shell
#f1f1f0
--paper
#ffffff
--line
rgb(5 5 5 / 0.10)
--line-faint
rgb(5 5 5 / 0.06)
--line-control
rgb(5 5 5 / 0.15)
--scrim
rgb(5 5 5 / 0.24)
--radius-shell
14px
--radius-panel
11px
--shell-inset
3px
--overlay
480px
--overlay-lg
640px
--space-2
8px
--space-4
16px
--space-5
20px
--space-10
40px
--row
36px
--type-heading-sm
500 16px/1.35 var(--font-sans)
--type-body-sm
400 13px/1.5 var(--font-sans)
--type-code-sm
400 calc(13px * var(--mono-scale))/1.5 var(--font-mono)
--fail-tint
#fbe9e5
--fail-text
#b23c29
--z-overlay
50
--dur-overlay
200ms
--dur-state
160ms
--ease-out
cubic-bezier(0.2, 0.8, 0.2, 1)

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.