--- name: drawer-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Drawer for transient edge panels, modal or non-modal behavior, snap points, swipe gestures, multiple triggers, custom portals, controlled state, and stacks. metadata: component_version: "1.0.0" type: "core" library: "@techsio/ui-kit" library_version: "0.3.2" requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" sources: "libs/ui/src/molecules/drawer.tsx libs/ui/src/internal/molecules/drawer.context.tsx libs/ui/src/internal/molecules/drawer.styles.ts libs/ui/src/tokens/components/molecules/_drawer.css libs/ui/stories/molecules/drawer.stories.tsx https://zagjs.com/components/react/drawer" --- # @techsio/ui-kit Drawer Usage Use Drawer for transient panels that enter from a viewport or container edge. Use Sidebar for persistent application navigation, Dialog for centered focused flows, and Popover for anchored contextual content. ## UX/UI guidelines House rules come from the `ux-guidelines` skill (writing, formatting, states, where actions and feedback live). This section applies them to `Drawer`. **Use it when** - Transient panels that slide from an edge: cart, mobile filters, mobile navigation, quick views. - Content that relates to the page behind it and should keep that context visible. **Use something else when** | Need | Use instead | | --- | --- | | Persistent desktop navigation | Sidebar | | Record create/edit/read in admin | Dialog placement="right" (the CRUD reference pattern) | | Focused blocking decision | Dialog | | Anchored small content | Popover | **Do** - Open from the edge that matches the content's origin: navigation start, cart/filters/details end. - Keep a visible title and close button; footer actions bottom-right like dialogs. - Return focus to the trigger on close; preserve page scroll. **Don't** - Stack drawers. - Use a drawer for long multi-step flows — use a page with Steps. **Copy and states** - Title names the content (`Cart (3)`, `Filters`); filter drawers end with `[Reset] [Show 24 results]`. ## Setup ```tsx import { Drawer } from "@techsio/ui-kit/molecules/drawer" Open cart Cart Review items before checkout. {/* cart content */} Close ``` Supported root props: ```text placement: start | end | top | bottom size: xs | sm | md | lg | xl | full open/defaultOpen, onOpenChange triggerValue/defaultTriggerValue, onTriggerValueChange snapPoints, snapPoint/defaultSnapPoint, onSnapPointChange snapToSequentialPoints, swipeVelocityThreshold, closeThreshold modal, trapFocus, preventScroll, restoreFocus closeOnEscape, closeOnInteractOutside, role ids, dir, getRootNode, initialFocusEl, finalFocusEl onEscapeKeyDown, onFocusOutside, onInteractOutside, onPointerDownOutside preventDragOnScroll, swipeVelocityThreshold, closeThreshold lazyMount, unmountOnExit, immediate, skipAnimationOnMount onEnterComplete, onExitComplete ``` ## Core Patterns ### Preserve the compound anatomy Keep Backdrop and Positioner inside Portal, then place Content inside Positioner. Title and Description supply the accessible dialog name and description. Do not conditionally remove the portal based on `open`; Drawer coordinates exit presence itself. ### Use placement instead of swipeDirection `placement` owns both the rendered edge and the closing gesture. `start` and `end` follow text direction. Root intentionally does not expose `swipeDirection`; `Drawer.SwipeArea` accepts an optional opening direction. For a machine-level integration, call the exported `useDrawer` hook with raw Zag props and provide its result to `Drawer.RootProvider`. This is also the escape hatch for a custom `swipeDirection` or externally shared machine API. This advanced surface tracks the exact pinned Zag 1.43.3 machine contract; prefer the compound root API when raw machine access is unnecessary. ```tsx const drawer = useDrawer({ swipeDirection: "end", open, onOpenChange }) {/* standard anatomy */} ``` ### Add snap points for draggable sheets Use numeric viewport ratios or CSS lengths such as `"320px"` and `"24rem"`. Pair snap points with Grabber and GrabberIndicator. Set `draggable={false}` on Content when dragging should start only from the grabber. ```tsx {/* standard anatomy */} ``` ### Select content with multiple triggers Give each `Drawer.Trigger` a stable `value`. Read `api.triggerValue` through `Drawer.Context` or control it with `triggerValue` and `onTriggerValueChange`. ### Coordinate nested drawers with Stack Wrap related roots in `Drawer.Stack`. Nested roots inherit the stack store; `Drawer.Indent` and `Drawer.IndentBackground` expose optional app-shell depth effects. ### Scope rendering with Portal Pass a mounted element ref to `Drawer.Portal container={ref}`. A custom mount target does not change fixed positioning by itself. Container-scoped panels must provide an appropriate positioned containing block and override Backdrop and Positioner positioning together. ### Configure non-modal panels completely For a panel that permits background interaction, use `modal={false}` together with `trapFocus={false}` and `preventScroll={false}`. Omit Backdrop when the background should remain directly interactive. ## Common Mistakes ### HIGH Custom edge panel state Wrong: ```tsx {open &&