Sheet

Opens a focused task from a screen edge. Native dialog with platform focus handling and backdrop.

Filters

Everything narrows from here. Press Escape to close.

Side

Notes

A panel from the edge. Escape closes it.

Use it for

  • Filters, settings, or a detail view that slides in beside the page.
  • side="left" for navigation on phones.

Not for

  • Short decisions; use Dialog.
  • Content that should stay open while the page is used; the sheet is modal.

Three ways in. They share one source, so the pixels match whichever you pick.

1. Import the package

npm install @noorddev/vlak-react

// once, next to your app's root
import "@noorddev/vlak-react/css";

import { Button, Sheet, SheetBody, SheetTitle } from "@noorddev/vlak-react";

Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/sheet.

2. Vendor the source

npx @noorddev/vlak-cli add sheet

The StyleX leaf lands in components/vlak/ with its dependencies, for your own compiler to own. See StyleX.

3. Through shadcn

npx shadcn add https://vlak.dev/r/sheet.json

The same registry item, installed by shadcn's CLI.

CSS only

<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />

No React. Link vlak.css and use the markup and classes below.

import { useState } from "react";
import { Button, Sheet, SheetBody, SheetTitle } from "@noorddev/vlak-react";

const [open, setOpen] = useState(false);

<Button variant="ghost" onClick={() => setOpen(true)}>Filters</Button>
<Sheet open={open} onClose={() => setOpen(false)} side="right" closeLabel="Close">
  <SheetTitle>Filters</SheetTitle>
  <SheetBody>Everything narrows from here.</SheetBody>
</Sheet>

Sheet

A native <dialog> at the screen edge. The platform provides the focus trap, Escape, and the backdrop; the title names it.

PropTypeDefaultDescription
openrequiredboolean
onClose() => void
dismissablebooleanEscape (and any other platform close request) asks the parent to close. Off, the dialog must be answered; maps to closedby="none".
lightDismissbooleanA click on the backdrop closes too; maps to closedby="any".
closeLabelstringRenders a labelled close button in the top corner.
side"left" | "right""right"

Also accepts Omit<DialogHTMLAttributes<HTMLDialogElement>, "open" | "onClose">.

SheetBody

Also accepts HTMLAttributes<HTMLParagraphElement>.

SheetTitle

PropTypeDefaultDescription
as"h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div""h2"

Also accepts HTMLAttributes<HTMLHeadingElement>.

KeysDoes
EscapeCalls onClose (when dismissable, the default)
Tab, Shift + TabCycles focus inside the sheet
<dialog class="rs-sheet" aria-labelledby="filters-title" open><button class="rs-sheet-close" type="button" aria-label="Close">&times;</button><h2 class="rs-sheet-title" id="filters-title">Filters</h2><p class="rs-sheet-body">Everything narrows from here.</p></dialog>
.rs-sheet.rs-sheet-left.rs-sheet-title.rs-sheet-body.rs-sheet-close