Sheet
An edge-docked panel that renders through Dialog.
View code
<Button commandfor="preview-cart" command="show-modal">
Open cart
<Badge variant="secondary">{lines.length}</Badge>
<Keyboard>⌘B</Keyboard>
</Button>
<Sheet
id="preview-cart"
side="right"
aria-labelledby="preview-cart-title"
// A docked column fills the edge it is docked to; a dialog's own block size
// hugs its content, so the panel states the full height it wants.
mix={[hotkey("mod+b"), bs("100%")]}
>
<Sheet.Header>
<Sheet.Title id="preview-cart-title">Your cart</Sheet.Title>
<Sheet.Description>
{lines.length} {lines.length === 1 ? "item" : "items"} · billed yearly
</Sheet.Description>
</Sheet.Header>
<div mix={[vstack({ gap: 3, align: "stretch" }), is("100%")]}>
{lines.length === 0 ? (
<Empty>
<Empty.Title>Your cart is empty</Empty.Title>
<Empty.Description>Put the sample lines back to look again.</Empty.Description>
<Empty.Action>
<Button
size="sm"
variant="outline"
mix={[on<HTMLButtonElement, "click">("click", restore)]}
>
Restore the cart
</Button>
</Empty.Action>
</Empty>
) : (
lines.map((line) => (
<div key={line.id} mix={[vstack({ gap: 3, align: "stretch" })]}>
<div mix={[hstack({ gap: 3, align: "center", justify: "between" })]}>
<span mix={[vstack({ gap: 0, align: "start" }), css({ flexGrow: "1" })]}>
<span mix={[text("sm"), weight("medium")]}>{line.name}</span>
<Text>{line.detail}</Text>
</span>
<span mix={[text("sm"), weight("medium")]}>${line.price}</span>
<Button
variant="ghost"
size="sm"
aria-label={`Remove ${line.name}`}
mix={[on<HTMLButtonElement, "click">("click", () => remove(line.id))]}
>
<Trash2Icon />
</Button>
</div>
<Separator />
</div>
))
)}
<div mix={[hstack({ gap: 2, align: "center", justify: "between" })]}>
<Text>Total</Text>
<span mix={[text("lg"), weight("semibold")]}>${total}</span>
</div>
</div>
<Sheet.Footer>
<Button commandfor="preview-cart" command="close" variant="outline">
Keep shopping
</Button>
<Button commandfor="preview-cart" command="close" disabled={lines.length === 0}>
Check out
</Button>
</Sheet.Footer>
<Sheet.Close commandfor="preview-cart" aria-label="Close the cart" />
</Sheet>Installation
An ordinary dependency: install it, import the theme once, and import the component where it is used.
npm add @sdxc/uiimport "@sdxc/ui/theme.css";Usage
import { Sheet } from "@sdxc/ui";Renders the panel through Dialog: fixed, pinned to whichever
inline edge side names with the far edge released to auto, and slid
via @starting-style/allow-discrete so the close animates too.
Composition
The parts the component publishes, each a static property of the host.
SheetSheet.HeaderRenders Sheet.HeaderProps.children as the panel's header slot: a tightly-stacked column of Sheet.Title and Sheet.Description, start-aligned at every width rather than centering like Dialog.Header.Sheet.DescriptionRenders Sheet.DescriptionProps.children as the panel's supporting copy, in a native<p>set to the panel's own foreground color rather than Dialog.Description's more muted one.Sheet.FooterRenders Sheet.FooterProps.children as the panel's action row: a single end-aligned row pinned to the block-end edge at every width, unlike Dialog.Footer's column that only becomes a row as its container grows.
Examples
<Sheet id="filters" side="left" aria-labelledby="filters-title">
<Sheet.Header>
<Sheet.Title id="filters-title">{t("filters.title")}</Sheet.Title>
</Sheet.Header>
</Sheet>Props
Read from the component's own types, so every prop, every default and every allowed value is listed.
| Prop | Type | Description |
|---|---|---|
side? | "left" | "right" | Physical side of the viewport to dock to. Defaults to DEFAULT_SIDE. |
Also accepts everything in Dialog.Props.
Sheet.Header
Also accepts everything in Dialog.HeaderProps.
Sheet.Description
Also accepts everything in Dialog.DescriptionProps.
Sheet.Footer
Also accepts everything in Dialog.FooterProps.
Types
| Name | Values | What it decides |
|---|---|---|
Side | "left" | "right" | Physical side of the viewport the panel docks to and slides in from, fixed rather than reading-direction-relative so it keeps docking to the same edge under any dir value, matching the safe-area padding's edge. |