SidePanel
A panel beside the page, such as help, that narrows the page instead of covering it.
import { SidePanel } from "@butteryui/react";Demo
When to use it
Use it for
- Help, notes or an assistant that someone keeps open while they work on the page beside it
- Details that shouldn't cover the page they're about, in an AppShell's panel slot
Not for
- Something to finish before going back to the page: use Dialog or FormDialog
- A record's details that belong to the page itself: use AppPage's aside or DetailPage
- A short note beside a control: use Popover or InfoButton
Examples
Help panel
A help panel the top bar opens, docked beside the page from 1280px of shell and a sheet under that.
Orders
Today
Twelve orders to roast and pack.
Order notes
An order's notes, whether they're open kept in state: closed, a button brings them back, and their menu clears them.
Roast log
A roast log docked where it's placed, open from the start, its body scrolling above a pinned footer.
Variants and states
Its parts
Outside a shell it docks where it's placed: its title, a menu, expand and close, a body that scrolls and a pinned footer.
As a sheet
In a shell under 1280px wide, Help opens it as a sheet at the window's end over a dim scrim, and the page keeps its width.
Orders
Props
SidePanel
| Prop | Type | Default | Description |
|---|---|---|---|
titlerequired | string | Its title, an h2 in title-sm, which names it, such as "Help". | |
open | boolean | Whether it's open. Pair with onOpenChange to control it. | |
defaultOpen | boolean | false | Whether it starts open when uncontrolled. Defaults to false. |
onOpenChange | ((open: boolean) => void) | Called when it opens or closes, from its close button, Escape or its sheet's scrim. | |
menu | ReactNode | Its overflow menu's items, Menu.Items and Menu.Links, an item that deletes or resets something with
tone="danger". Leave it out for no menu. | |
menuLabel | string | "More actions" | Names the overflow menu's button. Defaults to "More actions". |
expanded | boolean | Whether a docked panel is widened to 560px. Pair with onExpandedChange to control it. | |
defaultExpanded | boolean | false | Whether a docked panel starts widened when uncontrolled. Defaults to false. |
onExpandedChange | ((expanded: boolean) => void) | Called when its expand button widens or narrows it. | |
expandLabel | string | "Expand" | Names the expand button, a toggle that's pressed while the panel is widened. Defaults to "Expand". |
footer | ReactNode | What stays pinned under the body while it scrolls, such as a field to ask a question in. | |
closeLabel | string | "Close" | Names the close button. Defaults to "Close". |
children | ReactNode | What the panel holds. It scrolls on its own. |
It also takes Omit<MotionElementProps<"aside">, "title" | "children">.
Accessibility
Docked, it's a complementary landmark named by its title: opening it moves focus to its first field to type in, else to the panel itself, and Escape or its close button closes it and hands focus back to what opened it. As a sheet it's a modal dialog named by its title that keeps focus inside. The expand button is a toggle (aria-pressed); a menu item that deletes or resets something is drawn in danger and says so in its words.
| Keys | What they do |
|---|---|
| Escape | Closes the panel and hands focus back to what opened it |
| Enter or Space | Presses the focused header button: expand, which a docked panel has, or close |
| Enter, Space or Down | Opens the More actions menu from its button, where Up and Down move, Enter runs an item and Escape closes just the menu |
| Tab | Stays inside the panel while it's a sheet |
Motion
It appears with a `Presence` preset and leaves faster than it came, in about 60% of the time.
With reduced motion: Docked, its content fades in over 100ms instead of 120; as a sheet it fades in and out over 100ms without rising, its scrim fading with it.
Layout
Docked, it's 360px wide (560 expanded) and its parent's height, with a 1px edge on its start side and a --bt-h-header (56px) header whose title truncates and shows in full on hover; its body scrolls on its own above the pinned footer. As a sheet it sits 8px in from the window's end, up to 360px wide; either way its header buttons are 28px.
Related
See it in the roastery demo.