Klarhimmel UI

Sheet

Stable

A modal side panel for a task that keeps the page in view. Built like Dialog.

npx shadcn add @klarhimmel/sheet @klarhimmel/button

When 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

PropTypeDefaultDescription
titlestring-Required. Names the panel.
descriptionstring-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 / onOpenChangeReactNode / boolean-The element that opens it; or control it.
footerReactNode-Buttons pinned under the scrolling body. Wrap closing buttons in SheetClose.
classNamestring-Extra classes, merged after the component's own.

Accessibility

KeyDoes
Tab / Shift+TabCycles inside the sheet; the page behind is unreachable
EscapeCloses and returns focus to the trigger
Enter / SpaceActivates the focused button

Same behaviour as Dialog: modal, labelled, focus returns. Left and right sheets take the full width on a narrow screen.

Accessibility