- Components
Sheet
A modal side panel for a task that keeps the page in view. Built like Dialog.
npx shadcn add @klarhimmel/sheet @klarhimmel/buttonWhen to use
Use for
Filters, a cart or record details: a side task next to the page.
Not for
Navigation menus. Short decisions. Use Dialog or AlertDialog.
Pairs with
Button, Field, Checkbox.
Preview
No filters applied.
sheet.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { Button } from '@/components/klarhimmel/ui/button';
import { Sheet, SheetClose } from '@/components/klarhimmel/ui/sheet';
export default function Example() {
return (
<Theme theme="brand">
<Sheet
title="Filter rooms"
trigger={<Button variant="secondary">Filters</Button>}
footer={<SheetClose><Button>Show rooms</Button></SheetClose>}
>
…
</Sheet>
</Theme>
);
}Install
Import
import { Sheet, SheetClose } from '@/components/klarhimmel/ui/sheet';- Lands in
- components/klarhimmel/ui/sheet.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
- Render in
- Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.
- Limit
- Overlays render into the Theme root with position: fixed. A transform on an ancestor re-parents fixed (a dialog centres in the wrapper) and overflow: hidden clips menus. Render <Theme> at the app root, outside any transformed wrapper.
Examples
Bottom
import { Button } from '@/components/klarhimmel/ui/button'; import { Sheet, SheetClose } from '@/components/klarhimmel/ui/sheet'; export default function SheetBottom() { return ( <div className="flex min-h-28 items-start justify-center"> <Sheet side="bottom" title="Your booking" description="Garden room, 3 nights." trigger={<Button>Review booking</Button>} footer={<SheetClose><Button>Done</Button></SheetClose>} > <p className="text-kh-muted">Arrival Friday 14 March, departure Monday 17 March. Breakfast included.</p> <p className="text-kh-muted">Total 4 350 SEK, paid at the desk.</p> </Sheet> </div> ); }Left
import { Button } from '@/components/klarhimmel/ui/button'; import { Sheet, SheetClose } from '@/components/klarhimmel/ui/sheet'; export default function SheetLeft() { return ( <div className="flex min-h-28 items-start justify-center"> <Sheet side="left" title="Menu" trigger={<Button variant="secondary">Open menu</Button>} footer={<SheetClose><Button variant="secondary">Close</Button></SheetClose>}> <nav aria-label="Menu" className="flex flex-col gap-1 text-kh-body"> <a className="rounded-kh px-2 py-2.5 hover:bg-kh-surface-2" href="#rooms">Rooms</a> <a className="rounded-kh px-2 py-2.5 hover:bg-kh-surface-2" href="#spa">Spa</a> <a className="rounded-kh px-2 py-2.5 hover:bg-kh-surface-2" href="#contact">Contact</a> </nav> </Sheet> </div> ); }
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | - | Required. Names the panel. |
description | string | - | One sentence under the title, read out with it. |
side | 'right' | 'left' | 'bottom' | 'right' | Which edge it comes from. Left and right take the full width on a narrow screen. |
trigger / open / onOpenChange | ReactNode / boolean | - | The element that opens it; or control it. |
footer | ReactNode | - | Buttons pinned under the scrolling body. Wrap closing buttons in SheetClose. |
className | string | - | Extra classes, merged after the component's own. |
Accessibility
| Key | Does |
|---|---|
Tab / Shift+Tab | Cycles inside the sheet; the page behind is unreachable |
Escape | Closes and returns focus to the trigger |
Enter / Space | Activates the focused button |
Same behaviour as Dialog: modal, labelled, focus returns. Left and right sheets take the full width on a narrow screen.
Accessibility