ResponsiveSheet

A side sheet on desktop, a bottom drawer on small screens.

Usage

import { ResponsiveSheet } from "@hoplallc/ui/responsive-sheet";
<ResponsiveSheet.Root>
	<ResponsiveSheet.Trigger render={<Button variant="outline" />}>Open</ResponsiveSheet.Trigger>
	<ResponsiveSheet.Content size="xl">
		<ResponsiveSheet.Header>
			<ResponsiveSheet.Title>Email details</ResponsiveSheet.Title>
		</ResponsiveSheet.Header>
		<ResponsiveSheet.Body>…</ResponsiveSheet.Body>
	</ResponsiveSheet.Content>
</ResponsiveSheet.Root>

A Sheet on desktop and a bottom Drawer on small screens. Reach for this over plain Sheet for detail panels, which are the ones that hurt most on a phone: a side panel on a narrow viewport is either a sliver or a modal pretending not to be one.

Recipes

size sets the desktop panel width; the mobile drawer is always full-width, because a partial bottom sheet on a phone wastes the only dimension it has.

As with ResponsiveDialog, the root holds the open state so crossing the breakpoint does not close the panel, and autoFocus={false} keeps the keyboard down when the first field is a text input.

API

ResponsiveSheet.Root

PropTypeDefault
childrenReactNode—The trigger and the content.
defaultOpenbooleanfalseWhether it starts open, when uncontrolled.
onOpenChange((open: boolean) => void)—Called when the overlay opens or closes, on either surface.
openboolean—Controlled open state. The root owns it so it survives crossing the breakpoint.

Plus the DOM props of the element it renders. Source: responsive-sheet/components/responsive-sheet-root.tsx.

ResponsiveSheet.Body

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

Plus every <div> prop. Source: responsive-sheet/components/responsive-sheet-body.tsx.

ResponsiveSheet.Close

PropTypeDefault
renderReactElement<unknown, string | JSXElementConstructor<any>>—Element form only — the drawer half clones it, so render functions are unsupported.
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

Plus the DOM props of the element it renders. Source: responsive-sheet/components/responsive-sheet-close.tsx.

ResponsiveSheet.Content

PropTypeDefault
autoFocusbooleantrueWhether opening moves focus into the content (the default). Pass false when the first field is a text input you don't want to focus on open — on a phone that focus opens the keyboard over the content you just asked to see. The content stays reachable by tab either way.
childrenReactNode—Header, body and footer parts.
classNamestring—Classes for the panel on both surfaces.
closeButtonLabelstring"Close"Accessible name for that X. This package ships no translations.
overlayClassNamestring—Classes for the backdrop behind the panel. className styles the panel itself.
showCloseButtonbooleantrueDraws the X in the corner.
sizeSheetContentSize"default"Desktop panel width — the mobile drawer is always full-width.

Plus the DOM props of the element it renders. Source: responsive-sheet/components/responsive-sheet-content.tsx.

ResponsiveSheet.Description

PropTypeDefault
asChildboolean—
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

Plus the DOM props of the element it renders. Source: responsive-sheet/components/responsive-sheet-description.tsx.

ResponsiveSheet.Footer

PropTypeDefault
closeLabelstring—Renders an outline close button after children.
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

Plus every <div> prop. Source: responsive-sheet/components/responsive-sheet-footer.tsx.

ResponsiveSheet.Header

PropTypeDefault
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

Plus every <div> prop. Source: responsive-sheet/components/responsive-sheet-header.tsx.

ResponsiveSheet.Title

PropTypeDefault
asChildboolean—
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

Plus the DOM props of the element it renders. Source: responsive-sheet/components/responsive-sheet-title.tsx.

ResponsiveSheet.Trigger

PropTypeDefault
renderReactElement<unknown, string | JSXElementConstructor<any>>—Element form only — the drawer half clones it, so render functions are unsupported.
exitExit—Inside a <Presence>: plays this child's way out before it unmounts.

Plus the DOM props of the element it renders. Source: responsive-sheet/components/responsive-sheet-trigger.tsx.

Sheet · ResponsiveDialog · Drawer