Actions
FAB menu
A FAB that opens two to six related actions, springing out above it.
- COMPONENT_SPEC §6
- CSS · custom elements · React
- Open in the playground
Examples
FAB menu
The FAB turns into a close button and two to six related actions spring out above it. Escape or a press outside closes it.
The live demo runs with JavaScript. Its code is below.
import { FabMenu, Icon } from '@kyralabs/m3exp-web/react';
export default function Menu() {
return (
<FabMenu
aria-label="Create"
items={[
{ key: 'doc', icon: <Icon name="description" />, label: 'Document' },
{ key: 'sheet', icon: <Icon name="table_chart" />, label: 'Spreadsheet' },
{ key: 'slides', icon: <Icon name="slideshow" />, label: 'Presentation' },
]}
/>
);
}
<m3e-fab-menu label="Create" icon="add">
<m3e-fab-menu-item icon="description" value="doc">Document</m3e-fab-menu-item>
<m3e-fab-menu-item icon="table_chart" value="sheet">Spreadsheet</m3e-fab-menu-item>
<m3e-fab-menu-item icon="slideshow" value="slides">Presentation</m3e-fab-menu-item>
</m3e-fab-menu>
Run the HTML version (custom elements, no framework).
Colors and sizes
Secondary and tertiary color sets, from a medium FAB.
The live demo runs with JavaScript. Its code is below.
import { FabMenu, Icon } from '@kyralabs/m3exp-web/react';
const items = [
{ key: 'mail', icon: <Icon name="mail" />, label: 'Email' },
{ key: 'link', icon: <Icon name="link" />, label: 'Copy link' },
];
export default function Colors() {
return (
<>
<FabMenu aria-label="Share" icon={<Icon name="share" />} color="secondary" items={items} />
<FabMenu
aria-label="Share"
icon={<Icon name="share" />}
color="tertiary"
size="medium"
items={items}
/>
</>
);
}
<m3e-fab-menu label="Share" icon="share" color="secondary">
<m3e-fab-menu-item icon="mail" value="mail">Email</m3e-fab-menu-item>
<m3e-fab-menu-item icon="link" value="link">Copy link</m3e-fab-menu-item>
</m3e-fab-menu>
<m3e-fab-menu label="Share" icon="share" color="tertiary" size="medium">
<m3e-fab-menu-item icon="mail" value="mail">Email</m3e-fab-menu-item>
<m3e-fab-menu-item icon="link" value="link">Copy link</m3e-fab-menu-item>
</m3e-fab-menu>
Run the HTML version (custom elements, no framework).
Usage
A FAB that opens 2–6 closely related actions (verified, m3.material.io FAB menu › Guidelines, content JSON 2026-10-13_06-10-19/f00af5db-…json: "FAB menus can contain 2–6 items"; don't use one with a single item, with an extended FAB, or next to a floating toolbar / navigation rail). React warns in development outside 2–6.
API
React
<FabMenu>
FAB menu (COMPONENT_SPEC §6; Compose FloatingActionButtonMenu with a ToggleFloatingActionButton and FloatingActionButtonMenuItems): a toggle FAB that turns into the close button and opens a role="menu" of items, on the m3e-css .m3e-fab-menu classes.
Opening leaves focus on the toggle, now the close button (Compose FloatingActionButtonMenu.kt L138–152, m3.material.io FAB menu › Initial focus): ArrowDown or Tab moves to the top item, ArrowUp or Shift+Tab to the bottom one; among the items ArrowUp/Down (wrapping) and Home/End move, and Tab / Shift+Tab return to the toggle, so focus stays in the menu while it is open. Escape, a press outside or activating an item closes the menu and returns focus to the toggle. Motion follows Compose's springs and keeps velocity when interrupted.
| Prop | Type | Default | Description |
|---|---|---|---|
items
required
|
readonly FabMenuItem[]
|
2–6 related actions (m3.material.io FAB menu › Guidelines). | |
isOpen
|
boolean
|
Controlled open state (Compose expanded).
|
|
icon
|
ReactNode
|
The toggle's icon while closed; default add. A name or an element.
|
|
closeIcon
|
ReactNode
|
The close icon it swaps to at half progress; default close.
|
|
size
|
"medium" | "large" | "baseline"
|
The FAB size it opens from; default baseline. The close button is always 56dp.
|
|
color
|
"primary" | "secondary" | "tertiary"
|
Color set; default primary.
|
|
align
|
"start" | "end" | "center"
|
Item alignment; default end.
|
|
aria-label
|
string
|
The toggle's accessible name; it should describe the menu (m3.material.io). Required. | |
defaultOpen
|
boolean
|
||
className
|
string
|
||
onOpenChange
|
(open: boolean) => void
|
||
open
|
boolean
|
Deprecated:
Since 1.8: use isOpen. Removed in 2.0.
Controlled open state.
|
Also accepts HTML attributes not listed here.
fabMenuClassName()
Classes for a FAB menu root (the m3e-css contract).
Custom elements
<m3e-fab-menu>
import '@kyralabs/m3e-elements/fab-menu.js';
<m3e-fab-menu label="Share" icon="share" open> (COMPONENT_SPEC §6; Compose FloatingActionButtonMenu + ToggleFloatingActionButton). Renders the m3e-css .m3e-fab-menu contract inside itself (the host is display: contents) from its <m3e-fab-menu-item> children: the toggle FAB (.m3e-fab.m3e-fab-menu__toggle, aria-expanded, aria-controls) and a role="menu" of menuitems.
- Attributes:
open(reflected),label(the toggle's accessible name; m3.material.io: it describes the menu),icon(defaultadd) andclose-icon(defaultclose),size(baseline|medium|large: the FAB it opens from),color(primary|secondary|tertiarycolor set),align(end|start|center). - Motion (FloatingActionButtonMenu.kt): the toggle's checked progress on the fast spatial spring; the items stagger in from the bottom on a slow-effects Int spring, each springing its width (fast spatial) and alpha (fast effects). Every spring is interruptible and keeps its velocity, like Compose's Animatable; spatial springs jump under reduced motion.
- Focus and keys (Compose FloatingActionButtonMenu.kt L138–152, m3.material.io FAB menu › Initial focus): opening leaves focus on the toggle, now the close button. From it, ArrowDown or Tab moves to the top item and ArrowUp or Shift+Tab to the bottom one; among the items ArrowUp/Down (wrapping) and Home/End move; Tab and Shift+Tab from an item go back to the toggle, so focus stays in the menu while it is open. Escape, an outside press or activating an item closes it and returns focus to the toggle.
- Events:
open-change(detail: { open }) when the user opens or closes it;actionon the activated<m3e-fab-menu-item>.
| Attribute | Property | Type | Notes |
|---|---|---|---|
open
|
open
|
boolean
|
Reflected. |
label
|
label
|
string
|
|
icon
|
icon
|
string
|
|
close-icon
|
closeIcon
|
string
|
|
size
|
size
|
string
|
|
color
|
color
|
string
|
|
align
|
align
|
string
|
| Event | Detail | Description |
|---|---|---|
open-change
|
OpenChangeDetail
|
The user opened or closed it (after the change). |
action
|
FabMenuActionDetail
|
An item was activated (from the <m3e-fab-menu-item>, bubbling).
|
<m3e-fab-menu-item>
import '@kyralabs/m3e-elements/fab-menu.js';
<m3e-fab-menu-item icon="mail" value="email">Email</m3e-fab-menu-item> — data for one item of an <m3e-fab-menu> (it renders nothing itself; the menu renders .m3e-fab-menu__item). The label is the text content. Activating it fires action on this element (bubbles) with detail: { value } and closes the menu.
| Attribute | Property | Type | Notes |
|---|---|---|---|
icon
|
icon
|
string
|
|
value
|
value
|
string
|
| Event | Detail | Description |
|---|---|---|
action
|
FabMenuActionDetail
|
Activated (it bubbles to the FAB menu). |
CSS
fab-menu.css
@import '@kyralabs/m3e-css/fab-menu.css';
FAB menu (COMPONENT_SPEC §6): a toggle FAB that turns into the menu's close button, and the menu items above it. Static layout and the open/closed states; without JavaScript the toggle morphs on the fast spatial curve and the items fade in together. <m3e-fab-menu> and m3e-react <FabMenu> add Compose's interruptible springs (velocity kept), the item stagger, focus handling and light dismiss on the same classes.
<div class="m3e-fab-menu m3e-fab-menu--open">
<button class="m3e-fab m3e-fab-menu__toggle" aria-label="Toggle menu" aria-expanded="true"
aria-haspopup="menu" aria-controls="share-menu">
<span class="m3e-icon" aria-hidden="true">add</span>
<span class="m3e-icon" aria-hidden="true">close</span>
</button>
<div class="m3e-fab-menu__items" id="share-menu" role="menu" aria-label="Share">
<button class="m3e-fab-menu__item" role="menuitem" tabindex="-1">
<span class="m3e-icon" aria-hidden="true">mail</span>
<span class="m3e-fab-menu__label">Email</span>
</button>
</div>
</div>
The toggle comes first in the DOM (focus order close button → top item → … as m3.material.io FAB menu › Accessibility › Initial focus and the Compose sample's traversalIndex), and is drawn at the bottom (column-reverse). Its first child is the closed icon, its last the close icon. Modifiers: --open; toggle (FAB) size --size-medium --size-large (baseline default); color set --secondary --tertiary (primary default); item alignment --align-start --align-center (end default, Compose Alignment.End).
32 custom properties it reads
| Custom property | Default |
|---|---|
--m3e-comp-fab-baseline-container-height
|
56px |
--m3e-comp-fab-baseline-icon-size
|
24px |
--m3e-comp-fab-large-container-height
|
96px |
--m3e-comp-fab-medium-container-height
|
80px |
--m3e-comp-fab-medium-icon-size
|
28px |
--m3e-comp-fab-menu-baseline-close-button-between-space
|
8px |
--m3e-comp-fab-menu-baseline-close-button-container-height
|
56px |
--m3e-comp-fab-menu-baseline-close-button-container-width
|
56px |
--m3e-comp-fab-menu-baseline-close-button-icon-size
|
20px |
--m3e-comp-fab-menu-baseline-list-item-between-space
|
4px |
--m3e-comp-fab-menu-baseline-list-item-container-height
|
56px |
--m3e-comp-fab-menu-baseline-list-item-container-shape
|
var(--m3e-sys-shape-corner-full) |
--m3e-comp-fab-menu-baseline-list-item-icon-label-space
|
8px |
--m3e-comp-fab-menu-baseline-list-item-icon-size
|
24px |
--m3e-comp-fab-menu-baseline-list-item-leading-space
|
24px |
--m3e-comp-fab-menu-baseline-list-item-trailing-space
|
24px |
--m3e-comp-fab-menu-defaults-button-padding-bottom
|
16px |
--m3e-comp-fab-menu-defaults-item-container-color
|
var(--m3e-sys-color-primary-container) |
--m3e-comp-fab-menu-defaults-item-label-font
|
var(--m3e-sys-typescale-title-medium-font) |
--m3e-comp-fab-menu-defaults-item-label-line-height
|
var(--m3e-sys-typescale-title-medium-line-height) |
--m3e-comp-fab-menu-defaults-item-label-size
|
var(--m3e-sys-typescale-title-medium-size) |
--m3e-comp-fab-menu-defaults-item-label-tracking
|
var(--m3e-sys-typescale-title-medium-tracking) |
--m3e-comp-fab-menu-defaults-item-label-weight
|
var(--m3e-sys-typescale-title-medium-weight) |
--m3e-comp-fab-menu-defaults-item-opacity-duration
|
var(--m3e-sys-motion-spring-fast-effects-duration) |
--m3e-comp-fab-menu-defaults-item-opacity-easing
|
var(--m3e-sys-motion-spring-fast-effects-easing) |
--m3e-comp-fab-menu-defaults-padding-horizontal
|
16px |
--m3e-comp-fab-menu-defaults-toggle-container-color
|
var(--m3e-sys-color-primary-container) |
--m3e-comp-fab-menu-defaults-toggle-final-corner-radius
|
28px |
--m3e-comp-fab-menu-defaults-toggle-icon-color
|
var(--m3e-sys-color-on-primary-container) |
--m3e-comp-fab-menu-defaults-toggle-initial-corner-radius
|
16px |
--m3e-comp-fab-menu-defaults-toggle-progress-duration
|
var(--m3e-sys-motion-spring-fast-spatial-duration) |
--m3e-comp-fab-menu-defaults-toggle-progress-easing
|
var(--m3e-sys-motion-spring-fast-spatial-easing) |
Accessibility
toggle aria-expanded aria-controls aria-haspopup="menu" and an aria-label describing the menu; the item column is role="menu" labelled by the toggle with menuitems (tabindex −1, focus managed); among the items ArrowUp/Down (wrapping, APG menu) and Home/End; from the toggle ArrowDown/Tab → top item (Compose L138–152: Tab, DirectionDown, NumPadDirectionDown request focus on the item column, whose first child is the top item) and ArrowUp/Shift+Tab → bottom item (the toggle's key handler ignores Up, so Compose's default 2D focus search applies; the nearest focusable above the close button is the bottom item). The Compose sample also sends ArrowUp/Shift+Tab from the top item back to the close button (FloatingActionButtonMenuSamples.kt L207–221); here Shift+Tab does, and ArrowUp wraps within the menu as APG role="menu" expects. DOM order is toggle then items (m3.material.io › Initial focus: close button, then top item down).
Every interactive part has a target of at least 48 × 48 px, a visible focus ring for
keyboard focus only, and works under forced-colors and
prefers-reduced-motion.
Sources
Values come from androidx Compose Material3 1.5.0-beta01 at 19991f2c and from
m3.material.io, as cited below from COMPONENT_SPEC §6 and the m3e-css sources.
FloatingActionButtonMenu.kt (vendored), FabMenuBaselineTokens.kt, FloatingActionButtonMenuSamples.kt, ui-graphics Color.kt (lerp); m3.material.io FAB menu (overview, specs, guidelines, accessibility). The 1.5.0 release notes add nothing beyond the API.
FabMenuBaselineTokens.kt@19991f2c plus the FloatingActionButtonMenu.kt literals Compose renders (components/fab-menu-defaults.css; SOURCES register #24).
Placement (m3.material.io › Specs): the close button keeps the FAB's top end corner, so opened from a medium / large FAB it sits 40 / 56dp above the bottom; Compose aligns the shrinking container Alignment.TopEnd in a FAB-sized box (L517–518).
fab-menu.css sources
(compose/material3 FloatingActionButtonMenu.kt @ 19991f2c, generated values in fab-menu-defaults.css, lines in PARITY.md):
- Toggle (ToggleFloatingActionButton, L445-575): a box of the FAB's size holds the container, aligned to its top end (contentAlignment = Alignment.TopEnd). With checked progress p (fast spatial spring, L460): size = lerp(FAB size, CloseButtonContainerHeight 56dp, p), corner radius = lerp(16/20/28dp, 28dp, p), container color = lerp(primaryContainer, primary, p), icon color = lerp(onPrimaryContainer, onPrimary, p), icon size = lerp(FAB icon size, CloseButtonIconSize, p) (ToggleFloatingActionButtonDefaults, L578-656). Dp lerps extrapolate with the spring's overshoot; Color lerp mixes in Oklab and clamps p (ui-graphics Color.kt@19991f2c L546-569). The sample (FloatingActionButtonMenuSamples.kt L171-180) swaps Add for Close at p > 0.5. Shadow: FabPrimaryContainerTokens.ContainerElevation (Level3, L735), constant (no hover change).
- Layout (FloatingActionButtonMenu, L116-192; item column L195-308): 16dp horizontal padding (L738), 16dp under the toggle (L742), items CloseButtonBetweenSpace above it and ListItemBetweenSpace apart, aligned by horizontalAlignment. Closed (no item visible), the item column takes no space (L292).
- Items (FloatingActionButtonMenuItem, L331-414): a pill Surface (ListItemContainerShape) in primaryContainer / onPrimaryContainer, ListItemContainerHeight tall and at least as wide, leading/trailing ListItemLeadingSpace/TrailingSpace, ListItemIconLabelSpace between a ListItemIconSize icon and a titleMedium label. No shadow: Compose's Surface has shadowElevation 0 (the token file's ListItemContainerElevation Level3 is not applied).
- Color sets (m3.material.io FAB menu › Specs › Color; content JSON f00af5db-…): primary, secondary, tertiary; the close button takes the role, the items its container role.
- Touch targets: the close button is ≥ 56dp (overshoot ≥ 52dp) and items 56dp tall, above the 48dp minimum, so no ::after target is needed.