[RFC] Sheet component — API review (evaluate DJ's Drawer draft: layer integration + optional mobile swipe)
- Dominant language
- TypeScript
- Stars
- 13k
- Forks
- 1.1k
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 690
Description
## Summary
Open an API review for a first-class **Sheet** component. DJ (@thedeej) already has a substantial `Drawer` implementation in progress (branch `drawer-component`); this issue is to evaluate that API before it lands and to align on a few open design questions — especially the sheet-specific behaviors that the current draft doesn't yet cover.
Related prior gap: #2575 (no Drawer/Sheet component for side-anchored slide-in panels).
## What already exists (DJ's `drawer-component` branch)
`packages/core/src/Drawer/Drawer.tsx` — a native ``-based, edge-anchored overlay:
- `side: 'start' | 'end' | 'top' | 'bottom'` (logical, RTL-aware). `top`/`bottom` render as full-width sheets on the block axis.
- `size: number | string` — budget along the slide axis; fills the axis on smaller viewports.
- `hasScrim` — `showModal()` (top layer, focus trap, scroll lock, click-outside) vs `show()` (non-modal, page stays interactive).
- `isCollapsed` / `onCollapsedChange` — collapse non-modal side drawers to a click-to-expand rail.
- `hasCloseButton`, `label` (required a11y name), fully controlled `isOpen` / `onClose`.
- LIFO stacking — Escape closes the top-most open drawer; relies on the browser top layer's chronological stacking.
This is already close to what we want: **its own component**, four sides, stacking, modal/non-modal.
## Open questions for review
1. **Own component vs. a mode of Dialog** — the draft is already a standalone `Drawer`. Confirm that's the direction (vs. WWW, where the sheet is folded into the dialog). A bottom sheet should be *the same pattern* as a floating/modal panel, differing mainly by placement + optional mobile swipe — not a separate mental model.
2. **Layer system integration** — the draft leans on the native `` top layer for stacking/dismissal. We likely want explicit layer-system integration for top-most dismissal ordering, coordination with other overlays (popovers, toasts, tooltips), and predictable Escape/scrim-click routing across mixed overlay types. Worth deciding whether native top-layer ordering is sufficient or we need to route through XDS's layer machinery.
3. **Mobile swipe + gestures** — the draft has no swipe-to-dismiss (only `touch-action: pan-y` for scroll). Sheets should support **optional** swipe-to-dismiss on mobile (drag handle, velocity/threshold dismissal, snap points?). This should be opt-in, not implied by placement.
4. **Naming: Sheet vs. Drawer** — reconcile the component name. Side-anchored inspectors read as "drawer"; block-axis bottom/top read as "sheet." One component with a `side` prop, or distinct exports?
## Non-goal / scope clarification: modal → bottom-sheet auto-transition
We do **not** want the mobile modal to auto-transition into a bottom sheet.
- Only one team was experimenting with modals auto-transiting to a bottom sheet at mobile breakpoints. An internal update deprecates the `'full-page'` and `'bottom-sheet'` `mobileVariant`s in favor of a **responsive modal** that caps to the viewport via max-height.
- There's still a strong case for mobile-specific dialogs (e.g. simple validation flows), so we keep the responsive modal — but the modal → bottom-sheet transition for a confirmation flow is uncommon and arguably unnecessary.
- A bottom sheet is more useful for **denser / more complex** content. For those cases we already have a separate Sheet pattern, and for multi-step / immersive flows we have full-page modals. So whether there's a strong need for a modal → bottom-sheet *switch* remains to be seen.
**Bottom line:** Sheet is its own component + pattern (same as floating/modal panels) with *optional* mobile swipe. It is not something a Dialog silently becomes at a breakpoint.
## Ask
Review the `drawer-component` API against the above before it merges — particularly (a) layer-system integration for top-most dismissal, and (b) the opt-in mobile swipe surface. Land the API shape here first.
cc @thedeej @athz
Contributor guide
Assessment
This issue has not been assessed yet.