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.

The components in this family, with their descriptions and import paths
ComponentDescriptionImport from
SlideoutMenuThe 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.TriggerReact 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.HeaderThe 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.ContentThe 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.FooterThe 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
CloseButtonThe 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.
FeaturedIconThe header glyph on most compositions, at size md theme modern: a 40px box with an 8px radius and a 20px icon.
ButtonThe footer actions, at size sm — a secondary and a primary at the end edge, or a link-colour action leading a two-group row.
SelectThe 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.

Desktop, 400px: the overlay’s md:pl-10 leaves 400 of a 440 sheet, and the panel draws the border-secondary_alt hairline down its leading edge

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.

Mobile, 351px: the overlay’s pl-6 leaves 351 of a 375 sheet, and the sheet draws no hairline at this breakpoint

Why these are boxes and not overlays

Both cells above compose the real 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

The 351px cell is the Mobile panel’s width, not a Mobile viewport. Every other switch this family has — Header and Content 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

Nine rows in a 420px panel: the title stays at the top, the two buttons stay at the bottom, and only the list moves

New in this batch

Until the 2026-08-24 edit the panel scrolled as a whole and the chrome scrolled away with it: 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

The base defaults 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 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.

bg-overlay/70 with backdrop-blur-[8px], over content. Open any Type on the contextual demo to see the same treatment over a real page

An arbitrary value, by ruling

The sheet’s 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

The panel enters with 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 introai-assistant-intro-menu.tsxThe 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 messageai-assistant-message-menu.tsxA 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 eventcalendar-event-menu.tsxA read-only event: a date chip, details rows, an attendee list, and a Going? button group in the footer.
Create eventcreate-event-menu.tsxThe write half of the same screen: name, date range picker, location, description, and a Cancel/Create footer.
File uploadfile-upload-menu.tsxThe file-upload family’s drop zone and list inside a panel — the shortest composition in the family.
Filters (advanced)filters-advanced-menu.tsxThe 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.
Filtersfilters-menu.tsxA 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.
Integrationintegration-menu.tsxA connection flow: two app icons either side of a swap glyph, a permissions list, and a copyable link card. Draws the 40px close button.
Labelslabels-menu.tsxA combobox over a checkbox group of label badges, with a link-colour action at the start of the footer.
Message chatmessage-chat-menu.tsxThe 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.
Messagesmessages-menu.tsxAn 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 groupsnotification-settings-button-menu.tsxPer-channel settings where each row’s choice is a ButtonGroup, plus a frequency select.
Notification settings, checkboxesnotification-settings-checkbox-menu.tsxThe same screen with checkboxes and a radio group instead, wrapped in a form.
Notificationsnotifications-menu.tsxThe activity-feeds family’s FeedItem list in a panel. No footer: there is nothing to commit.
Order summaryorder-summary-menu.tsxA total, line items, a discount field, and dividers, ending in a full-width Continue to checkout inside Content rather than a Footer.
Payment detailspayment-details-menu.tsxA card form: the card graphic, number and expiry inputs, billing fields, and a Cancel/Confirm footer.
Payment methodpayment-method-menu.tsxA radio group of payment brands over an email field — the method picker that precedes the details form.
Placeholderplaceholder-menu.tsxThe 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.
Planplan-menu.tsxA radio group of icon cards, one per tier, with a Cancel/Confirm footer.
Project detailsproject-details-menu.tsxA form panel: name, description, a status select, a share row and a team list, separated by rules.
Share projectshare-project-menu.tsxAn 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 membersteam-members-menu.tsxThe 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 profileuser-profile-menu.tsxA profile card — photo, name, badges, location — over an About block and a work-experience timeline. No footer.
User settingsuser-settings-menu.tsxThe 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

The sheet’s Type axis has 23 entries and the pairing to files is 1:1. The twenty-fourth, 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.