Jump to a component
Core · Containers
Sheet
Sheet is the bottom panel on phones for choices that belong to the current screen: audio and subtitles, reading direction.
Native on iOS and Android
Sakuga for iOS and Android is coming
Sheet is native on iOS and Android: each platform draws it in its own language, so the web package has none to show here.
iOS
Coming soon
Sheet is native on iOS: the system draws it, and Sakuga for iOS will give it Sakuga’s tokens.
Android
Coming soon
Sheet is native on Android: the system draws it, and Sakuga for Android will give it Sakuga’s tokens.
- Use it for
- Choices that belong to the current phone screen: audio and subtitles, reading direction, a row's menu.
Guidelines
- It slides up from the bottom edge on
motion-slowwithease-enter, over a dimsheet-scrim, with a grab handle and a Done button. Tapping the scrim closes it; it slides back down onmotion-basewithease-exit. - The selected option's fill slides within its group, and its check settles in.
- Group options with
Sheet.Group(a short heading) andSheet.Option(one choice, check on the selected one). mediaover video or manga pages, so it stays dark in either theme.- Desktop uses a popover menu for the same choices, not a sheet.
- It stays silent, on the web and in the apps (README · Sound).
- An option can hold a lead (a
CastAvatar, aThumb) and two lines ofText; the row grows past its 48 px when it needs to.doneLabel={null}drops Done when an option or a button below is the action.
Props
SheetProps
openrequiredbooleantitlerequiredstringonClose() => voidmediabooleandoneLabelstring | nullchildrenrequiredReactNodeclassNamestringSheetGroupProps
titlerequiredstringchildrenrequiredReactNodeSheetOptionProps · Holds a lead (CastAvatar, Thumb) and one or two lines; grows past 48 px when it needs to.
selectedbooleanonSelectMouseEventHandlerchildrenrequiredReactNodeTypeScript
export interface SheetProps { open: boolean; title: string; onClose?: () => void; media?: boolean; doneLabel?: string | null; children: ReactNode; className?: string; } export interface SheetGroupProps { title: string; children: ReactNode; } /** Holds a lead (CastAvatar, Thumb) and one or two lines; grows past 48 px when it needs to. */ export interface SheetOptionProps { selected?: boolean; onSelect?: MouseEventHandler; children: ReactNode; }
Each token reads a role, and the role lands on a value in each theme. Change the role and every component that reads it follows.
Colour
sheet-bgSheet: the sliding panel over the page.bg-glass-strongglass-light-a90rgba(250,250,252,0.9)bg-glass-strongglass-dark-a82rgba(28,28,34,0.82)sheet-media-bgSheet media: the panel over video.bg-glass-media-strongglass-dark-a82rgba(28,28,34,0.82)bg-glass-media-strongglass-dark-a82rgba(28,28,34,0.82)sheet-handleSheet: the grab handle.border-controlshade-a52rgba(11,10,16,0.52)border-controlwhite-a45rgba(255,255,255,0.45)sheet-scrimSheet: the dim layer behind it.bg-overlayshade-a40rgba(11,10,16,0.4)bg-overlayblack-a50rgba(0,0,0,0.5)sheet-row-selected-bgSheet: the chosen row.bg-fill-selectedshade-a08rgba(11,10,16,0.08)bg-fill-selectedwhite-a12rgba(255,255,255,0.12)sheet-checkSheet: the check beside the chosen row.accentcamellia-600#c2335faccentcamellia-400#f4779fsheet-media-done-bgSheet media: the Done button over video.action-medianeutral-0#ffffffaction-medianeutral-0#ffffffsheet-media-done-fgSheet media: Done label.text-on-action-medianeutral-925#0b0a10text-on-action-medianeutral-925#0b0a10sheet-fgSheet: row names.text-primaryneutral-925#0b0a10text-primaryneutral-0#ffffffsheet-titleSheet: group headings.text-mutedneutral-600#5c5b67text-mutedneutral-400#a3a3a7sheet-borderSheet: panel edge.border-glasswhite-a70rgba(255,255,255,0.7)border-glasswhite-a16rgba(255,255,255,0.16)sheet-media-fgSheet media: row names over video.text-on-medianeutral-0#fffffftext-on-medianeutral-0#ffffffsheet-media-titleSheet media: group headings over video.text-on-media-mutedneutral-400#a3a3a7text-on-media-mutedneutral-400#a3a3a7sheet-media-borderSheet media: panel edge.border-glass-mediawhite-a16rgba(255,255,255,0.16)border-glass-mediawhite-a16rgba(255,255,255,0.16)Space and shape
sheet-radiusradius-2xlsheet-insetinset-smsheet-padding-topinset-smsheet-padding-xinset-lgsheet-padding-bottominset-xlsheet-blurblur-glasssheet-inset-ring-widthline-hairlinesheet-media-inset-ring-widthline-hairlinesheet-handle-widthsheet-handle-heightsheet-handle-radiusradius-xssheet-handle-margin-bottomgap-lgsheet-title-margin-bottomgap-mdsheet-row-min-heightsize-targetsheet-row-radiusradius-smsheet-row-gapgap-lgsheet-row-padding-yinset-smsheet-row-padding-xinset-mdsheet-row-active-scalescale-press-lgsheet-row-check-icon-sizeicon-size-smsheet-done-margin-topgap-lgsheet-group-margin-bottomgap-lgsheet-group-glide-radiusradius-smType
sheet-font-familyfont-sanssheet-title-fonttext-microsheet-row-fonttext-subtitle-smMotion
sheet-scrim-fade-in-animationmotion-scrim-insheet-in-animationsheet-row-durationmotion-fastsheet-row-easingease-standardsheet-row-active-durationmotion-instantsheet-row-check-settle-animationmotion-settlesheet-scrim-leaving-fade-out-animationmotion-scrim-outsheet-leaving-out-animationsheet-scrim-fade-in-animationmotion-scrim-insheet-scrim-leaving-fade-out-animationmotion-scrim-outChecked automatically with axe-core 4.11.4 against WCAG 2.2 A and AA, on the live preview above, in light and dark, on 26 October 2026.
- Result
- No issues found, in light or in dark.
- By design
- A modal dialog named by
title: focus moves to the chosen option (else the first control), Tab and Shift+Tab stay inside it, Escape, Done or a tap on the scrim runonClose, and focus goes back to what opened it. Sheet.Group is a group named by itstitle, and each Sheet.Option is a toggle button, "pressed" when selected, with its check hidden. - Not tested
- The automated check doesn't test keyboard use, focus order or what a screen reader says. The lines above are the design system's own account of them.