Side sheets
Side sheets show secondary content anchored to the side of the screen.
Side sheets display supplementary surfaces mostly in medium and expanded window sizes (like tablet and desktop). They provide a consistent and predictable surface for contextual actions, navigation, or filters.
Introduction
Material Design 3 Expressive Side Sheets are panels that slide in from the screen edges (usually on the right side). They have a fixed width and span the full height of the viewport.
We support two variants:
- Standard side sheets: Co-exist alongside primary content. They can be coplanar (pushing main content to adapt layout) or overlay.
- Modal side sheets: Block background interaction on smaller viewports, using a Scrim to lock attention.
Anatomy
- Container: The full-height container.
- Headline: The header area describing the sheet's purpose.
- Close / Back Button (Optional): Provides a quick way to dismiss the sheet.
- Divider (Optional): Groups content and separates actions from core details.
- Scrim (Modal only): A semi-transparent overlay blocking underneath interaction.
Variants
Standard Side Sheet
Standard side sheets co-exist with the page. You can supply a coplanarSiblingRef to automatically resize the page content alongside the sheet.
The demo below illustrates a coplanar side sheet displaying file metadata information that pushes page content to adapt dynamically.
Modal Side Sheet
Modal side sheets block background interaction using a Scrim, making them great for filters or detailed forms on mobile and tablet.
The demo below presents a modal side sheet dimmed with a background scrim, overlaying page content.
Responsive Adaptive Sheet
By combining bottom and side sheets with responsive hooks, you can automatically present a Bottom Sheet Modal on mobile and transition to a Side Sheet Modal on desktop.
Features
Sibling Resizing (Coplanar Layout)
The standard side sheet supports Option D (Coplanar sibling) from the MD3 spec. Pass coplanarSiblingRef with a ref pointing to your main container, and its margin will automatically transition to make space for the side sheet.
Edge Support
Sheets can slide in from either the start (left in LTR) or end (right in LTR) edge. For right-to-left (RTL) locales, they automatically adapt.
Usage
import { SideSheet, SideSheetModal } from "@bug-on/m3-expressive";
import { useRef, useState } from "react";
export function Example() {
const [standardOpen, setStandardOpen] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const mainRef = useRef<HTMLDivElement>(null);
return (
<div className="flex">
<main ref={mainRef} className="flex-1">
Main page content
</main>
{/* Standard Side Sheet */}
<SideSheet
isOpen={standardOpen}
onClose={() => setStandardOpen(false)}
coplanarSiblingRef={mainRef}
edge="end"
>
<div>Standard content</div>
</SideSheet>
{/* Modal Side Sheet */}
<SideSheetModal
isOpen={modalOpen}
onClose={() => setModalOpen(false)}
edge="end"
>
<div>Modal content</div>
</SideSheetModal>
</div>
);
}
Best Practices
Do
- Place side sheets along the right edge to avoid interference with left navigation.
- Use Standard side sheets for medium/expanded screens (tablet, desktop).
- Include a close icon button for easy dismissal.
- Reverse side sheet directions to the left side in RTL locales.
Don't
- Don't enable horizontal scrolling inside a side sheet; keep layout simple.
- Don't overlay navigation bars with standard side sheets.
- Avoid using modal side sheets on small viewports unless necessary (use bottom sheets instead).
Design Tokens
Geometry
Side sheets do not have rounded corners by default when docked to the screen edges, except when slightly offset.
Motion
| Animation | Spring Token | Tier |
|---|---|---|
| Slide In / Out | DEFAULT_SPATIAL_SPRING | Default |
Accessibility
- Keyboard Support: Full keyboard trap on modal variant.
Escapedismisses the modal side sheet automatically. - Roles: Standard variant uses
role="complementary"while modal usesrole="dialog". - Focus Restoration: Focus returns to the trigger button when the sheet is closed.
API Reference
SideSheet / SideSheetModal
| Prop | Type | Default | Description |
|---|---|---|---|
isOpen | boolean | — | Required. Controlled open state. |
onClose | () => void | — | Required. Callback triggered on dismissal. |
edge | "start" | "end" | "end" | Screen edge to slide in from (start = left, end = right in LTR). |
width | string | "360px" | Custom width of the sheet container. |
coplanarSiblingRef | React.RefObject<HTMLElement> | — | Standard only: Ref of sibling container to apply margin compression. |
header | ReactNode | — | Static header content rendered at the top of the sheet. |
footer | ReactNode | — | Static footer content rendered at the bottom of the sheet. |
divider | boolean | { header?: boolean; footer?: boolean } | true | Divider line display configuration. |
| scrollAreaProps | Omit<ScrollAreaProps, "children"> | — | Custom settings for the internal Radix ScrollArea (e.g. type="hover" for modern scrollbars). |
| className | string | — | Custom Tailwind CSS styling classes. |
| aria-labelledby | string | — | Target title ID for screen reader dialogue header. |
| aria-label | string | — | Screen reader announcement text. |