# Sheet A panel docked to an edge of the viewport. - Group: Overlays - Import: `import { Sheet } from '@misoto22/folio'` - Page: https://ui.misoto22.com/components/sheet/ - Related: dialog, popover ## When to reach for it A modal that needs room — a filter panel, a detail view. It IS a Dialog, docked; the sides are named in reading order, so `end` is the right in English and the left in Arabic. ## Anatomy - **Scrim** (required) — The same --scrim at --z-overlay that Dialog uses — the same component, in fact, so the page behind is inert and scroll-locked exactly as it is under a dialog. - **Panel** (required) — The docked box. start and end are a min(24rem, 92vw) column at full height; top and bottom are a full-width band capped at 85vh. A flex column that scrolls itself. - **Title** (required) — title, typed as required rather than optional — there is no unnamed-sheet path to fall into, only a hidden-title one via hideTitle. - **Description** — description, under the title, sharing the wrapper that hideTitle hides. - **Close** (required) — The X in the top-end corner. Unlike Dialog there is no showClose to turn it off, so every sheet has one whichever edge it is docked to. ## Best practices ### Do - Reach for a Sheet over a Dialog when the content is a list or a form long enough to scroll: it gets the full height of the viewport rather than Dialog’s 32rem by 85vh box, and the reader keeps the page edge as an anchor. - Use top or bottom when the content is wide and short — a filter bar, a date range. start and end are a 24rem column, and a table pushed into one wraps into a ribbon. - Name the sides start and end rather than reaching for left and right: each edge has its own literal class string carrying its own rtl: variant, so end arrives from the right in English and the left in Arabic with no second code path. - Wrap the cancelling control in SheetClose so the close runs through Radix — a sheet closed by your own state setter leaves focus inside a panel that is no longer on the page. ### Don’t - Do not re-declare the travel in className. The panel carries data-folio-animated, which removes its transform outright under reduced motion; a second transform of your own only gets the universal floor, so it still arrives — a hundredth of a millisecond later, from wherever you put it. - An OverlayContainer whose element is not positioned docks the sheet to the wrong box: naming a container switches the panel from fixed to absolute, so an unpositioned container sends it to the nearest positioned ancestor rather than to the frame. - It is a modal dialog, so the page behind is scroll-locked and pointer-inert: this is not the home for a filter panel the reader is meant to work alongside. That is a Popover, or a column in the layout. ## Accessibility - Shares Dialog’s focus trap, Escape handling and scroll lock rather than reproducing them — a second focus trap is a second one to get wrong. - The title is required, visible or not. ## Keyboard - Escape — Closes it, and focus returns to the trigger. - Tab — Cycles inside the sheet. ## Sheet Re-export of `DialogPrimitive.Root`. Radix Dialog root, trigger and close — a sheet IS a dialog, docked. ## SheetTrigger Re-export of `DialogPrimitive.Trigger`. ## SheetClose Re-export of `DialogPrimitive.Close`. ## SheetContent A panel docked to an edge of the viewport. It is a modal dialog — Radix's, so the focus trap, the escape key, the scroll lock and the `aria-modal` wiring are the same ones `Dialog` gets. The only differences are where it sits and which way it arrives, which is why this shares that implementation rather than reproducing it: a second focus trap is a second focus trap to get wrong. The title is required, visible or not. A modal with no accessible name drops a screen reader into an unnamed region with no way back out. Portals into the element an enclosing `OverlayContainer` names, docking to that element's edge rather than the viewport's when there is one. ### Props - `side` — `SheetSide` default `'end'`. Which edge it is docked to. `end` by default. - `title` (required) — `ReactNode`. - `description` — `ReactNode`. - `hideTitle` — `boolean` default `false`. Hide the title visually while keeping it for assistive tech. - `closeLabel` — `string` default `'Close'`. Accessible close action, supplied by the host locale. Also accepts: `Omit, 'title'>`. ## Example — sides ```tsx import { Button, Field, Input, Sheet, SheetContent, SheetTrigger } from '@misoto22/folio'
{(['start', 'end', 'top', 'bottom'] as const).map((side) => ( ))}
``` ## Example — room to scroll ```tsx
Branch {BRANCHES.map((branch) => ( ))}
``` ## Example — a wide band ```tsx
```