Primitives
Drawer
Edge-anchored drawer (vaul) with HUD styling.
● LIVE
[ interactive — use the trigger to open ]
"use client";import { Button } from "@/registry/okibi/ui/button";import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger,} from "@/registry/okibi/ui/drawer";export default function DrawerDemo() { return ( <Drawer> <DrawerTrigger asChild> <Button variant="outline-tech">Open Telemetry</Button> </DrawerTrigger> <DrawerContent> <DrawerHeader> <DrawerTitle>Telemetry Feed</DrawerTitle> <DrawerDescription> Live readout streaming from the SEC // 0x4A2 sensor grid. </DrawerDescription> </DrawerHeader> <div className="px-4 font-mono text-sm text-muted-foreground"> SIGNAL 98.2% · LATENCY 12ms · NODES 0x0C </div> <DrawerFooter> <DrawerClose asChild> <Button variant="signal">Acknowledge</Button> </DrawerClose> </DrawerFooter> </DrawerContent> </Drawer> );}npx shadcn@latest add https://okibi.cndr.dev/r/drawer.jsonInstallation
Install the theme first, then add the component:
npx shadcn@latest add https://okibi.cndr.dev/r/drawer.jsonInstall the required dependencies:
npm install lucide-react vaulCopy the source from the Code tab above into components/ui/drawer.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.
Usage
import {
Drawer,
DrawerContent,
DrawerDescription,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
<Drawer>
<DrawerTrigger>Open</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Telemetry</DrawerTitle>
<DrawerDescription>Live channel readout.</DrawerDescription>
</DrawerHeader>
</DrawerContent>
</Drawer>Anatomy
<Drawer direction="right">
<DrawerTrigger>Open</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Telemetry</DrawerTitle>
<DrawerDescription>Live channel readout.</DrawerDescription>
</DrawerHeader>
<DrawerFooter>
<DrawerClose>Close</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>Examples
Right
Set direction="right" to anchor the drawer to the right edge; the close control sits top-right.
● LIVE
[ interactive — use the trigger to open ]
"use client";import { Button } from "@/registry/okibi/ui/button";import { Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle, DrawerTrigger,} from "@/registry/okibi/ui/drawer";export default function DrawerRightDemo() { return ( <Drawer direction="right"> <DrawerTrigger asChild> <Button variant="outline-tech">Open Right Panel</Button> </DrawerTrigger> <DrawerContent> <DrawerHeader> <DrawerTitle>Starboard Bay</DrawerTitle> <DrawerDescription> Slides in from the right edge of the HUD. </DrawerDescription> </DrawerHeader> <div className="px-4 font-mono text-sm text-muted-foreground"> DOCK R · THRUST 64% · DRIFT 0x1F </div> </DrawerContent> </Drawer> );}Left
direction="left" anchors the drawer to the left edge.
● LIVE
[ interactive — use the trigger to open ]
"use client";import { Button } from "@/registry/okibi/ui/button";import { Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle, DrawerTrigger,} from "@/registry/okibi/ui/drawer";export default function DrawerLeftDemo() { return ( <Drawer direction="left"> <DrawerTrigger asChild> <Button variant="outline-tech">Open Left Panel</Button> </DrawerTrigger> <DrawerContent> <DrawerHeader> <DrawerTitle>Port Bay</DrawerTitle> <DrawerDescription> Slides in from the left edge of the HUD. </DrawerDescription> </DrawerHeader> <div className="px-4 font-mono text-sm text-muted-foreground"> DOCK L · THRUST 71% · DRIFT 0x0A </div> </DrawerContent> </Drawer> );}Top
direction="top" drops the drawer down from the top edge.
● LIVE
[ interactive — use the trigger to open ]
"use client";import { Button } from "@/registry/okibi/ui/button";import { Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle, DrawerTrigger,} from "@/registry/okibi/ui/drawer";export default function DrawerTopDemo() { return ( <Drawer direction="top"> <DrawerTrigger asChild> <Button variant="outline-tech">Open Top Panel</Button> </DrawerTrigger> <DrawerContent> <DrawerHeader> <DrawerTitle>Overhead Console</DrawerTitle> <DrawerDescription> Drops down from the top edge of the HUD. </DrawerDescription> </DrawerHeader> <div className="px-4 pb-4 font-mono text-sm text-muted-foreground"> ALT 0x3C2 · PITCH +4.0 · YAW 0x07 </div> </DrawerContent> </Drawer> );}API Reference
Drawer
| Prop | Type | Default | Description |
|---|---|---|---|
direction | "top" | "bottom" | "left" | "right" | "bottom" | Edge the drawer anchors to and slides from. |
DrawerContent
| Prop | Type | Default | Description |
|---|---|---|---|
showCloseButton | boolean | true | Render the top-right close (X) control — the visible dismiss for edge-anchored drawers. |
Accessibility
- Built on
vaul, whose Radix-wiredDrawerTitleandDrawerDescriptionmake the panel labelled and described by construction. - Set the anchor edge with
direction(top/bottom/left/right) on theDrawerroot; the content's edge, border, and slide-in follow it. DrawerContentrenders a close (X) control by default (showCloseButton) with ansr-only'Close' label and a 44px pointer hit area — the visible dismiss for edge-anchored drawers that can't be swiped.- Native drag / swipe-to-dismiss, overlay fade, and
Escare owned byvaul; the global reduced-motion guard handles motion.
Guidelines
Do
- Pick
directionto match the content:bottomfor mobile sheets,left/rightfor side panels. - Keep
showCloseButtonon forleft/rightdrawers — they have no swipe handle, so the X is the only visible dismiss. - Always provide a
DrawerTitle(and ideally aDrawerDescription) so the panel is named and described.
Don't
- Don't hide the close button on a side drawer without supplying another labeled dismiss in the footer.
- Don't pour long content into a
top/bottomdrawer without internal scrolling — they cap atmax-h-[80vh]and can clip. - Don't reintroduce rounded corners or drop the strong border; the edge-snapped HUD panel is the look.