Side Panel
SidePanel is a portal-based drawer that slides in from the left or right edge of the screen. Use it for detail views, advanced filters, forms, or any secondary content that shouldn't fully interrupt page flow the way a Modal would.
- Renders into a portal with
role="dialog"/aria-modal="true", witharia-labelledbylinked to the title. - Focus is trapped inside the panel while open and released on close.
- Body scroll is locked while the panel is open.
- Closes on the close button,
Escape, or backdrop click — all callonClose. - Optional
title,description, andfooterslots, plus per-section className overrides for theming.
Import
import { SidePanel } from "h2o-library";Usage
Basic
const [isOpen, setIsOpen] = useState(false);
<Button label="Open Panel" onClick={() => setIsOpen(true)} />
<SidePanel
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Patient Details"
description="View and edit patient information"
>
<div style={{ padding: "0 1.5rem" }}>
<p>Panel body content goes here.</p>
</div>
</SidePanel>
With Footer
The footer slot pins action buttons at the bottom regardless of scroll position.
<SidePanel
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Edit Record"
footer={
<>
<Button variant="ghost" label="Cancel" onClick={() => setIsOpen(false)} />
<Button label="Save changes" onClick={handleSave} />
</>
}
>
{/* form fields */}
</SidePanel>;Left Position
Use position="left" for navigation-style or filter panels that pair with a right-aligned content area.
<SidePanel
position="left"
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Filters"
>
{/* filter controls */}
</SidePanel>;Sizes
<SidePanel size="sm" ...> {/* 20rem — narrow auxiliary panels */}
<SidePanel size="md" ...> {/* 24rem — default */}
<SidePanel size="lg" ...> {/* 36rem — wide detail / form panels */}
Playground
Loading playground…
Keyboard
| Key | Action |
|---|---|
Escape | Calls onClose. |
Tab / Shift+Tab | Focus is trapped inside the panel while it is open. |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
isOpen* | boolean | — | Controls whether the panel is visible. |
onClose* | () => void | — | Called when the user dismisses the panel (close button, Escape, or backdrop click). |
position | "left" | "right" | "right" | Which edge of the screen the panel slides in from. |
size | "sm" | "md" | "lg" | "md" | Maximum width: sm = 20rem, md = 24rem, lg = 36rem. |
title | string | undefined | Title shown in the header. Also used as the accessible label via aria-labelledby. |
description | string | undefined | Subtitle shown below the title. |
children* | React.ReactNode | — | Scrollable panel body content. |
footer | React.ReactNode | undefined | Content pinned to the bottom of the panel (ideal for action buttons). |
className | string | undefined | Additional class on the panel element. |
headerClassName | string | undefined | Additional class on the header section. |
titleContainerClassName | string | undefined | Additional class on the title + description container. |
titleClassName | string | undefined | Additional class on the title element. |
descriptionClassName | string | undefined | Additional class on the description element. |
contentClassName | string | undefined | Additional class on the body section. |
closeButtonClassName | string | undefined | Additional class on the close button. |
* Required
Design Guidelines
- Use
SidePanelinstead ofModalwhen the content is long (needs scrolling) or when the user should still be able to reference the main page. - Use
size="lg"for complex forms or detail views;size="sm"for narrow filters or inspector panels. - Always place action buttons in the
footerprop — they stay visible regardless of body scroll position. - Pair
position="left"with navigation or filter UIs; default torightfor record details and forms. - Best fits: record details, advanced filter forms, multi-step wizards, side-by-side editing.
Accessibility
- Renders with
role="dialog"andaria-modal="true", plusaria-labelledbylinked to the title. - Focus is trapped inside the panel while open and returned to the originating element on close.
- Body scroll is locked while the panel is open.
Escape, the close button, and backdrop click all callonClose.