Sheet primitives
Gallery recipes
The gallery demonstrates production-shaped layouts such as bottom sheets, drawers, modals, detents, lightboxes, and persistent surfaces.
Every homepage card is built from the public package. The concise recipes explain the reusable structure; the source links below are generated from the exact live JSX and CSS, so the documentation cannot silently drift away from the gallery.
Exact live source
- Bottom Sheet exact JSX and CSS
- Modal Dialog exact JSX and CSS
- Navigation Drawer exact JSX and CSS
- Detent Sheet exact JSX and CSS
- Top Sheet exact JSX and CSS
- Scrollable Sheet exact JSX and CSS
- Stacked Sheets exact JSX and CSS
- Detached Sheet exact JSX and CSS
- Full-Screen Sheet exact JSX and CSS
- Depth Sheet exact JSX and CSS
- Lightbox exact JSX and CSS
- Parallax Sheet exact JSX and CSS
- Side Sheet exact JSX and CSS
- Keyboard-Aware Sheet exact JSX and CSS
- Card Expansion exact JSX and CSS
- Persistent Sheet exact JSX and CSS
The raw files include playground icons, image helpers, and local import paths because they are the real implementation. Replace those helpers with product assets and use the package imports from the concise recipe.
1. Bottom Sheet
tsx
<BottomSheet.Root>
<BottomSheet.Trigger>View stay</BottomSheet.Trigger>
<BottomSheet.Content>
<BottomSheet.Title>Five nights in Tulum</BottomSheet.Title>
<StayDetails />
<BottomSheet.Close>Done</BottomSheet.Close>
</BottomSheet.Content>
</BottomSheet.Root>2. Modal Dialog
tsx
<Sheet.Root>
<Sheet.Trigger>Invite people</Sheet.Trigger>
<Sheet.Panel
side="center"
viewProps={{ tracks: ["top", "bottom"], swipeOvershoot: false }}
>
<Sheet.Title>Join Atlas Redesign?</Sheet.Title>
<InviteForm />
</Sheet.Panel>
</Sheet.Root>3. Navigation Drawer
tsx
<Sheet.Root>
<Sheet.Trigger>Menu</Sheet.Trigger>
<Sheet.Panel side="left" className="navigation-drawer">
<Sheet.Title>Workspace navigation</Sheet.Title>
<Navigation />
</Sheet.Panel>
</Sheet.Root>4. Detent Sheet
tsx
<Sheet.Root defaultSnap={1}>
<Sheet.Trigger>Open queue</Sheet.Trigger>
<Sheet.Panel side="bottom" snapPoints={["92px", "72lvh"]}>
<Sheet.Step direction="up">Expand</Sheet.Step>
<Queue />
</Sheet.Panel>
</Sheet.Root>5. Top Sheet
tsx
<Sheet.Root>
<Sheet.Trigger>Notifications</Sheet.Trigger>
<Sheet.Panel side="top">
<Sheet.Title>Team inbox</Sheet.Title>
<Notifications />
</Sheet.Panel>
</Sheet.Root>6. Scrollable Sheet
tsx
<Sheet.Root>
<Sheet.Trigger>Open recipe</Sheet.Trigger>
<Sheet.Portal>
<Sheet.View side="bottom" tracks="auto">
<Sheet.Backdrop travelAnimation={{ opacity: [0, 0.4] }} />
<Sheet.Content asChild>
<Scroll.Root asChild>
<Scroll.View><Scroll.Content><Recipe /></Scroll.Content></Scroll.View>
</Scroll.Root>
</Sheet.Content>
</Sheet.View>
</Sheet.Portal>
</Sheet.Root>7. Stacked Sheets
tsx
<SheetStack.Root>
<Sheet.Root>
<Sheet.Trigger>Product</Sheet.Trigger>
<Sheet.Panel side="bottom">
<Product />
<Sheet.Root>
<Sheet.Trigger>Select size</Sheet.Trigger>
<Sheet.Panel side="bottom"><SizePicker /></Sheet.Panel>
</Sheet.Root>
</Sheet.Panel>
</Sheet.Root>
</SheetStack.Root>Repeat the nested Root pattern for any number of layers. The frontmost modal owns Escape, focus, and outside interaction.
8. Detached Sheet
tsx
<Sheet.Panel
side="center"
className="detached-sheet"
viewProps={{ tracks: ["top", "bottom"] }}
>
<WatchDetails />
</Sheet.Panel>css
.detached-sheet {
width: min(92vw, 38rem);
max-height: calc(100dvh - 32px);
border-radius: 28px;
}9. Full-Screen Sheet
tsx
<Sheet.Panel side="bottom" className="full-screen-sheet">
<Sheet.Title>New note</Sheet.Title>
<Editor />
</Sheet.Panel>css
.full-screen-sheet {
width: 100%;
height: var(--velvet-100lvh);
max-height: none;
}10. Depth Sheet
tsx
<DepthSheet.Root>
<DepthSheet.Page asChild>
<main>
<Home />
<DepthSheet.Trigger>Open profile</DepthSheet.Trigger>
</main>
</DepthSheet.Page>
<DepthSheet.Content>
<DepthSheet.Title>Mara Vale</DepthSheet.Title>
<Profile />
</DepthSheet.Content>
</DepthSheet.Root>11. Lightbox
tsx
<Lightbox.Root>
<Lightbox.Trigger asChild><button><Thumbnail /></button></Lightbox.Trigger>
<Lightbox.Content>
<Lightbox.Title>Golden hour</Lightbox.Title>
<Gallery />
<Lightbox.Close aria-label="Close">×</Lightbox.Close>
</Lightbox.Content>
</Lightbox.Root>12. Parallax Sheet
tsx
<Sheet.View side="bottom" tracks="auto">
<Sheet.Backdrop travelAnimation={{ opacity: [0, 0.3] }} />
<Sheet.Outlet travelAnimation={{
transform: ({ tween }) => "translateY(" + tween(0, -48) + "px)",
}}>
<Cover />
</Sheet.Outlet>
<Sheet.Content><Article /></Sheet.Content>
</Sheet.View>13. Side Sheet
tsx
<Sheet.Root>
<Sheet.Trigger>Account</Sheet.Trigger>
<Sheet.Panel side="right">
<Sheet.Title>Account settings</Sheet.Title>
<Settings />
</Sheet.Panel>
</Sheet.Root>14. Keyboard-Aware Sheet
tsx
<Sheet.View side="bottom" tracks="auto">
<Sheet.Content asChild>
<Scroll.Root asChild>
<Scroll.View safeArea="visual-viewport">
<Scroll.Content><Conversation /><Composer /></Scroll.Content>
</Scroll.View>
</Scroll.Root>
</Sheet.Content>
</Sheet.View>15. Card Expansion
tsx
<CardExpansion.Root>
<CardExpansion.Trigger asChild><article><StoryCard /></article></CardExpansion.Trigger>
<CardExpansion.Content>
<CardExpansion.Title>Designing motion that feels real</CardExpansion.Title>
<Story />
<CardExpansion.Close>Done</CardExpansion.Close>
</CardExpansion.Content>
</CardExpansion.Root>16. Persistent Sheet
tsx
<PersistentSheet.Root defaultOpen defaultSnap={1}>
<PersistentSheet.Content>
<PersistentSheet.Title>Midnight Drive</PersistentSheet.Title>
<MiniPlayer />
<PersistentSheet.Step direction="up">Open queue</PersistentSheet.Step>
</PersistentSheet.Content>
</PersistentSheet.Root>Required imports
tsx
import { Sheet, SheetStack, Scroll } from "velvet-ui";
import { BottomSheet } from "velvet-ui/bottom-sheet";
import { CardExpansion } from "velvet-ui/card-expansion";
import { DepthSheet } from "velvet-ui/depth-sheet";
import { Lightbox } from "velvet-ui/lightbox";
import { PersistentSheet } from "velvet-ui/persistent-sheet";