Slideout menus
A modal panel sliding in from the right: one small base — SlideoutMenu plus Trigger, Header, Content, Footer — and 24 compositions in the same directory.
Slideout menus
A modal panel sliding in from the right: one small base — SlideoutMenu plus Trigger, Header, Content, Footer — and 24 compositions in the same directory.
Contextual demo
Every Type in the sheet, each over the screen that would own it — 23 scenarios, no docs shell in the way.
A small base, and 24 compositions on top of it
SlideoutMenu ships the overlay, the panel and three slots; everything a Type looks like — the icon, the tabs, the rows, the footer’s buttons — is children you write. There is no type prop, which is why a snippet carries a trigger, a render function handing you close, and the slots you opened.The preview draws all 23 of the sheet’s Types, each over a page that would plausibly own it — Order summary over a cart, Calendar event over a week, Integration over an integrations list — and every panel opens from a real trigger. The panel bodies there are the shipped compositions’ own markup, slot for slot; they are rebuilt page-side only because each shipped file owns its overlay and carries a demo hook that reopens it 700ms after close.
The family
Five entries on one export: the root, React Aria’s Trigger unchanged, three plain elements — hence in-flow specimens.
| Component | Description | Import from |
|---|---|---|
| SlideoutMenu | The whole overlay: the backdrop, the right-anchored panel, and the dialog inside it. Children can be a function, which receives React Aria’s modal render props plus close. | @/components/application/slideout-menus/slideout-menu |
| SlideoutMenu.Trigger | React Aria’s DialogTrigger, re-exported unchanged. Give it two children — the button and the SlideoutMenu — and it owns the open state, aria-expanded and aria-haspopup, and focus return. | @/components/application/slideout-menus/slideout-menu |
| SlideoutMenu.Header | The pinned top block: px-4 pt-6 pb-6 md:px-6, plus an absolutely positioned close button at top-3 right-3. closeButtonSize picks 36 or 40. Everything else is your children. | @/components/application/slideout-menus/slideout-menu |
| SlideoutMenu.Content | The only scroller in the panel: flex-1 min-h-0 with gap-6 px-4 md:px-6. The header and footer pin because this element, not the panel, takes the overflow. | @/components/application/slideout-menus/slideout-menu |
| SlideoutMenu.Footer | The pinned bottom block: a real border-t border-secondary over p-4 md:px-6. The action row inside it is yours — the sheet draws sm buttons at the end edge, or two groups spread apart. | @/components/application/slideout-menus/slideout-menu |
Built from
The pieces with their own docs page.
Component | Where it’s used here |
|---|---|
| CloseButton | The header’s close affordance at size sm (36) or md (40). It fills React Aria’s close slot, so it dismisses the dialog with or without an onClose. |
| FeaturedIcon | The header glyph on most compositions, at size md theme modern: a 40px box with an 8px radius and a 20px icon. |
| Button | The footer actions, at size sm — a secondary and a primary at the end edge, or a link-colour action leading a two-group row. |
| Select | The search and picker fields inside the panels: Select.ComboBox on Team members, Filters and Labels, plain Selects elsewhere. |
Anatomy
The panel at both real widths, 400px and 351px — neither set on the panel: the overlay’s padding is what changes.
Project settings
Configure your project preferences.
Project name
Sample body copy, long enough to show the content column’s measure at this width.
Team permissions
Sample body copy, long enough to show the content column’s measure at this width.
Integrations
Sample body copy, long enough to show the content column’s measure at this width.
Project settings
Configure your project preferences.
Project name
Sample body copy, long enough to show the content column’s measure at this width.
Team permissions
Sample body copy, long enough to show the content column’s measure at this width.
Integrations
Sample body copy, long enough to show the content column’s measure at this width.
Why these are boxes and not overlays
Header, Content and Footer — the same elements a shipped composition uses, with the same paddings — inside a bounded relative box carrying the panel’s own bg-primary, hairline and shadow-xl. What they do not mount is SlideoutMenu itself, because it renders a fixed inset-0 overlay: two of them side by side is one panel on top of another, over the whole page. The live section below shows the overlay doing its real job, one at a time, behind a trigger.The widths are honest; the paddings inside them are the window’s
px-4 md:px-6, Footer p-4 md:px-6, the heading’s text-md md:text-lg, the hairline’s md:border-l — is a media query on the viewport, so at a desktop window that cell draws the desktop paddings at the mobile width. Narrow the browser, or open the contextual demo, to see the pair resolve. The hairline is the one place these specimens state the intent rather than inherit it: the Desktop cell writes border-l outright and the Mobile cell writes none, because that is what the sheet draws at each end.The chrome pins, the body scrolls
Content is the panel’s only scroller, so a long body runs under a header and above a footer that never move. Scroll it.
Team members
Olivia Rhye
Product Designer
Natali Craig
UX Designer
Drew Cano
Frontend Developer
Orlando Diggs
Backend Developer
Phoenix Baker
Product Manager
Lana Steiner
QA Engineer
Demi Wilkinson
UX Copywriter
Candice Wu
Fullstack Developer
Andi Lane
Product Designer
New in this batch
Content was size-full with overflow-y-auto on both Content and Dialog, two nested scrollers, and h-full overflowed the flex column. The sheet requires the other reading — its Panel is overflow-clip and its Content flex-[1_0_0] min-h-px — so Content is now the sole scroller and the Dialog’s scroller is gone. The same edit removed the Dialog’s gap-6, which had been inserting a 24px band between the body and the footer’s rule; the header now owns its own pb-6, and the footer sits flush. The Code tab carries the before and after.Content’s role in these specimens
Content to role="main". In flow on a docs page that would be a second main landmark per specimen, so the cells here pass presentation, and the scrolling cell passes group with a name so a keyboard user can reach and scroll it.The header
A padded box with an absolute close button; the rest is children. The sheet’s four header symbols compose from it.
Project settings
Configure your project preferences.
Team members
Messages
Everything addressed to you.
Integration
closeButtonSize is new, and the padding is already yours
size="sm", so the three Types the sheet draws at 40 were 4px short and no caller could correct it; the 2026-08-24 edit added closeButtonSize, which defaults to sm. The other thing to know about this element: the base already sets relative w-full px-4 pt-6 pb-6 md:px-6, and the shipped compositions re-declare most of that in their own className. The cells above pass only what is genuinely new — the flex axis and the gap — which is the shape to copy.The backdrop
An interface-950 fill at 70 percent with an 8px blur, drawn over sample content: seeing it means seeing through it.
The page behind the panel
Body copy, a rule, and a block of colour — enough texture that the blur has something to work on.
An arbitrary value, by ruling
backdrop-blur-md is a Figma radius of 16, which halves to 8px in CSS by the rule theme.css records. The theme’s blur scale has no 8 — sm 2, md 4, lg 6, xl 10 — and Figma’s backdrop-blur scale and the theme’s blur scale do not share a naming, so the value is written as backdrop-blur-[8px] rather than tokenised. Whether it earns a token is a proposal, not a read result. The fill itself is bg-overlay, which is interface-950 in light and switches to interface-800 in dark; the sheet is drawn in light only, so the dark value is unread.What a real overlay adds beyond the fill
slide-in-from-right over 300ms and leaves over 500ms. React Aria supplies the rest: focus moves into the dialog and is trapped there, Escape and a backdrop click close it under isDismissable, the page behind stops scrolling, and focus returns to the trigger that opened it. None of that is visible in the swatch above — it only shows up on a real overlay, which is what the contextual demo opens.The catalogue
Twenty-four compositions ship beside the base, listed rather than rendered. Open the file to read one.
Type | File | What it shows |
|---|---|---|
| A.I. assistant intro | ai-assistant-intro-menu.tsx | The empty state of an assistant panel: a logo, “Hi Olivia,” over a greeting, and suggestion badges. No footer — the composer belongs to the message Type. |
| A.I. assistant message | ai-assistant-message-menu.tsx | A live thread. The header draws only the logo, so its title is an sr-only Heading; the composer is a sticky block inside Content rather than a Footer, since a Footer would force the border-t. Draws the 40px close button. |
| Calendar event | calendar-event-menu.tsx | A read-only event: a date chip, details rows, an attendee list, and a Going? button group in the footer. |
| Create event | create-event-menu.tsx | The write half of the same screen: name, date range picker, location, description, and a Cancel/Create footer. |
| File upload | file-upload-menu.tsx | The file-upload family’s drop zone and list inside a panel — the shortest composition in the family. |
| Filters (advanced) | filters-advanced-menu.tsx | The only composition with props, and the only one not on this Figma page: it takes the filter-bar family’s FilterRow rows and is the slideout the Filters page falls back to below 768px. |
| Filters | filters-menu.tsx | A saved-filter select, a search field, and two checkbox lists. Its footer is the family’s two-group form: a Save filter link at the start, Cancel and Apply at the end. |
| Integration | integration-menu.tsx | A connection flow: two app icons either side of a swap glyph, a permissions list, and a copyable link card. Draws the 40px close button. |
| Labels | labels-menu.tsx | A combobox over a checkbox group of label badges, with a link-colour action at the start of the footer. |
| Message chat | message-chat-menu.tsx | The one composition with no Content slot: a tabbed header, then the thread as a bare ol written straight into the panel, with only the composer inside Footer. |
| Messages | messages-menu.tsx | An inbox feed under the tabbed header variant — Tabs type="button-minimal" size="sm" fullWidth, an exact match to the sheet with no kit edit. |
| Notification settings, button groups | notification-settings-button-menu.tsx | Per-channel settings where each row’s choice is a ButtonGroup, plus a frequency select. |
| Notification settings, checkboxes | notification-settings-checkbox-menu.tsx | The same screen with checkboxes and a radio group instead, wrapped in a form. |
| Notifications | notifications-menu.tsx | The activity-feeds family’s FeedItem list in a panel. No footer: there is nothing to commit. |
| Order summary | order-summary-menu.tsx | A total, line items, a discount field, and dividers, ending in a full-width Continue to checkout inside Content rather than a Footer. |
| Payment details | payment-details-menu.tsx | A card form: the card graphic, number and expiry inputs, billing fields, and a Cancel/Confirm footer. |
| Payment method | payment-method-menu.tsx | A radio group of payment brands over an email field — the method picker that precedes the details form. |
| Placeholder | placeholder-menu.tsx | The base with nothing in it: a featured icon, a title and supporting text, three grey blocks, and Cancel/Save. This is the Type the sheet’s Desktop and Mobile panel symbols are drawn from. |
| Plan | plan-menu.tsx | A radio group of icon cards, one per tier, with a Cancel/Confirm footer. |
| Project details | project-details-menu.tsx | A form panel: name, description, a status select, a share row and a team list, separated by rules. |
| Share project | share-project-menu.tsx | An invite field over a “Who has access” list, each row carrying a permission dropdown, and a copy-link action at the start of the footer. |
| Team members | team-members-menu.tsx | The longest list in the family: a search field over eleven people grouped by team, each with an icon-only mail button, and the two-group footer. |
| User profile | user-profile-menu.tsx | A profile card — photo, name, badges, location — over an About block and a work-experience timeline. No footer. |
| User settings | user-settings-menu.tsx | The one composition with no Header slot: a cover photo bleeds to the panel edges, so it hand-rolls its own 40px CloseButton over the image and starts at Content. |
Twenty-three Types on the sheet, twenty-four files
filters-advanced-menu.tsx, is not on this sheet at all — it belongs to the Filters page and was read in batch C, where it is a live consumer of the filter dropdown’s FilterRow type. No composition is unmapped and no Type is Figma-only.