Files
skills/docs/ui/react/react-ui/src/components/sheet/sheet.md
2026-05-29 08:20:56 +01:00

6.5 KiB

Sheet

(no submodules)

- sheet.variants — `sheetOverlayVariants`, `sheetCont…
  • sheet.variants — sheetOverlayVariants, sheetContentVariants, sheetHeaderVariants, sheetFooterVariants, sheetTitleVariants, `sh…

  • sheet.variants — sheetOverlayVariants, `sheetCont…

  • sheet.variants — sheetOverlayVariants, sheetContentVariants, sheetHeaderVariants, sheetFooterVariants, sheetTitleVariants, `sh…

  • sheet.variants — sheetOverlayVariants, `sheetCont…

  • sheet.variants — sheetOverlayVariants, sheetContentVariants, sheetHeaderVariants, sheetFooterVariants, sheetTitleVariants, `sh…

  • sheet.variants — sheetOverlayVariants, `sheetCont…

  • sheet.variants — sheetOverlayVariants, sheetContentVariants, sheetHeaderVariants, sheetFooterVariants, sheetTitleVariants, `sh…

  • sheet.variants — sheetOverlayVariants, sheetContentVariants, sheetHeaderVariants, sheetFooterVariants, sheetTitleVariants, `sh…

Purpose

<Sheet> and its sub-components (<SheetTrigger>, <SheetPortal>, <SheetOverlay>, <SheetContent>, <SheetHeader>, <SheetFooter>, <SheetTitle>, <SheetDescription>, <SheetClose>) are the chassis side-anchored panel primitives built on @radix-ui/react-dialog. Sheet is structurally a Dialog with side-anchored positioning, so Radix supplies the same WAI-ARIA dialog semantics (focus-trap, restore-focus, Escape closes, click-outside closes) and the chassis adds a side axis on <SheetContent> (top / right / bottom / left).

Per the WAI-ARIA dialog pattern, every sheet MUST contain a <SheetTitle> so the accessible name is announced. Use className="sr-only" if the visible UI does not need a heading. Logical-property edges (start-0 / end-0) ensure left and right automatically swap under dir="rtl".

Features

  • Four side anchors (top, right, bottom, left) with matching slide-in/slide-out animations.
  • Focus-trap on open; restore-focus on close (Radix-managed).
  • Escape closes; click on the overlay backdrop closes the sheet.
  • Translated close-button aria-label via react-ui.sheet.close (Close / 关闭 / إغلاق / Cau / Закрыть).
  • Theme-driven cva overrides for every slot via useThemedVariants("Sheet<Slot>").
  • All sub-components forward refs to their underlying Radix elements.
  • motion-reduce:animate-none collapses the slide animation under prefers-reduced-motion: reduce.
  • Logical-property anchoring (start-0 / end-0) so left and right flip in RTL automatically.

Usage: Basic Example

import {
    Sheet,
    SheetClose,
    SheetContent,
    SheetDescription,
    SheetFooter,
    SheetHeader,
    SheetTitle,
    SheetTrigger,
} from "@suaveplan/react-ui";

export function FiltersSheet() {
    return (
        <Sheet>
            <SheetTrigger>Filters</SheetTrigger>
            <SheetContent side="right">
                <SheetHeader>
                    <SheetTitle>Filters</SheetTitle>
                    <SheetDescription>Refine the results.</SheetDescription>
                </SheetHeader>
                <SheetFooter>
                    <SheetClose>Apply</SheetClose>
                </SheetFooter>
            </SheetContent>
        </Sheet>
    );
}

Usage: Advanced Example

import {
    Button,
    Sheet,
    SheetContent,
    SheetDescription,
    SheetFooter,
    SheetHeader,
    SheetTitle,
    SheetTrigger,
} from "@suaveplan/react-ui";
import { useState } from "react";

interface NavItem {
    readonly id: string;
    readonly label: string;
}

export function MobileNavSheet({ items }: { items: readonly NavItem[] }) {
    const [open, setOpen] = useState(false);
    return (
        <Sheet open={open} onOpenChange={setOpen}>
            <SheetTrigger asChild>
                <Button variant="outline">Open menu</Button>
            </SheetTrigger>
            <SheetContent side="left">
                <SheetHeader>
                    <SheetTitle>Navigation</SheetTitle>
                    <SheetDescription>Browse the app.</SheetDescription>
                </SheetHeader>
                <nav className="mt-4 flex flex-col gap-1">
                    {items.map((item) => (
                        <a
                            key={item.id}
                            href={`#${item.id}`}
                            className="rounded px-3 py-2 hover:bg-accent"
                            onClick={() => setOpen(false)}
                        >
                            {item.label}
                        </a>
                    ))}
                </nav>
            </SheetContent>
        </Sheet>
    );
}

API Reference

<Sheet>

Renders the Radix Dialog Root. Same lifecycle props as <Dialog>.

<SheetContent>

Prop Type Default Description
side "top" | "right" | "bottom" | "left" "right" Anchor edge (RTL flips left/right).
closeAriaLabelKey TranslationKey react-ui.sheet.close Override the close-button accessible name.

<SheetTitle>

REQUIRED — Radix surfaces a dev warning when omitted. Render className="sr-only" if no visible heading is needed.

<SheetTrigger>, <SheetPortal>, <SheetOverlay>, <SheetHeader>, <SheetFooter>, <SheetDescription>, <SheetClose>

Same as Dialog's analogous sub-components — direct re-exports of the Radix sub-components with the chassis cva applied.

Implementation Notes

  • The Radix Content props type is a discriminated union once cva-extra variant props (side) are layered on; TS cannot narrow the spread back through the chassis surface, so <SheetContent> casts the Radix Content to a relaxed shape via unknown. The runtime accepts both shapes equivalently. This mirrors the Wave 3b ToggleGroup reconciliation finding and is documented in the implementation comment.
  • The chassis NEVER assigns a numeric z-index to the overlay or content — Radix portals handle stacking.
  • start-0 / end-0 are logical properties — left / right automatically swap in RTL without a chevron-flip step.
  • motion-reduce:animate-none motion-reduce:transition-none collapses both the slide and the duration transitions under prefers-reduced-motion: reduce.
  • <SheetContent> self-portals via <SheetPortal> so consumers do not need to remember to portal manually — matches the shadcn upstream convention exactly.