Pular para o conteúdo

FAB menu

components specs/components/fab-menu.kmd

Material 3 Expressive FAB menu — successor of legacy "speed-dial". ToggleFloatingActionButton + N FloatingActionButtonMenuItems with staggered spring expansion + shape morph (FAB → "X"). Max 6 items (above → migrate to Navigation drawer).

Quando esta spec se aplica

Triggers primários

Todos os triggers

Corpo da especificação

Spec — FAB menu

Companion: buttons.kmd FAB section. Animation via motion.kmd R9.1 spring + shape-library.kmd R2 morph.

Princípios

  1. 2-6 items — < 2 → use single FAB. > 6 → migrate to Navigation drawer.
  2. Staggered expansion — items appear in sequence (50-100ms offset) via spring.
  3. Toggle morph — main FAB morphs FAB → "X" shape on open; reverse on close.
  4. Anchored above by default — fan upward; auto-flip if window orientation requires.

R1 — Anatomia

              Item 3 ⬡  ← label
              Item 2 ⬡
              Item 1 ⬡
   ┌──────────────────┐
   │      [×]         │  ← toggle FAB (morphed)
   └──────────────────┘

Slots:

SlotFunction
Toggle FABPrimary. Tap = open/close menu; shape morph between FAB (default) and "X" (open).
Menu items1-6 secondary actions; each a small FAB + label chip.

R2 — Sizes

Toggle FAB sizes inherit buttons.kmd FAB:

  • Small (40dp) / Default (56dp) / Large (96dp).

Menu items: small FAB only (40dp) — visually subordinate.

R3 — Anchor + direction

Default: above (fan upward from toggle).

Auto-flip rules:

Window orientation/classDirection
Compact portrait (mobile)Above (default)
Compact landscape (mobile)Above (still — UX consistency)
Medium/Expanded (tablet/desktop)Beside (left-of-toggle if RTL; right-of-toggle if LTR)
Toggle at top of viewportForce below (auto-flip)
Toggle at sideForce opposite direction

R4 — Expansion animation

On tap toggle (open):

  1. Toggle FAB morph: FAB shape → "X" (Cookie-4 → diagonal cross via shape-library.kmd Cookie-4 → Diamond morph).
  2. Items appear: stagger 60ms offset per item; each via motion-spatial-fast spring (slight overshoot).
  3. Item labels: fade-in via motion-effect-fast.
  4. Optional scrim: backdrop tint surface-dim 0.4 alpha (configurable).

On tap toggle (close) OR outside tap OR item selection OR ESC:

  1. Reverse — items fade-out in reverse order (stagger 30ms; faster than open).
  2. Toggle morph back: "X" → FAB shape.
  3. Scrim fades out.

R5 — Threshold + alternatives

Item countRecommended pattern
1Single FAB (no menu)
2-6FAB menu (this spec)
7+Migrate to Navigation drawer ou Bottom sheet

Anti-pattern: hiding important destructive actions inside FAB menu (use Confirm dialog instead).

R6 — Surface bindings

SurfaceAPI
FlutterKoderFabMenu({toggleIcon, items, onItemTap, direction}) em koder_kit/ (futuro)
Web<koder-fab-menu> em koder_web_kit
Compose AndroidKoderFabMenu (futuro)
SwiftUI iOSidem (futuro)
CLI / TUIn/a

R7 — Acessibilidade

  • Toggle: role="button" aria-haspopup="menu" aria-expanded="<state>" aria-label="More actions".
  • Items: role="menuitem" aria-label="<action>".
  • Keyboard: Tab to toggle + Enter opens; Arrow Up/Down navigates items; Enter selects; ESC closes.
  • Focus trap: while open, Tab cycles within items + toggle only.
  • Screen reader announces toggle state change.

R8 — i18n

Keyen-USpt-BR
fab_menu.toggle.label_closed"More actions""Mais ações"
fab_menu.toggle.label_open"Close menu""Fechar menu"

R9 — Reduced-motion

Open/close: instant. Toggle morph: skip; show static "X" icon swap. Staggered appearance: replaced by simultaneous fade-in (no stagger).

R10 — Per-preset variation

PresetFAB menu style
material3 / material_expressiveDefault morph + stagger spring
material2Simple icon swap + linear stagger (no spring)
terminal_classicList below toggle (no animation)
brutalistSharp items; no morph
cyberpunk_neonDefault + glow on items + connection lines toggle ↔ items
minimalist_monoNo labels (icon-only); ultra-spartan

T-suite

  • T1 Mount closed: toggle FAB visible; items hidden.
  • T2 Open: tap toggle → items appear staggered; toggle morphs to X.
  • T3 Item count 3: render 3 items vertically above toggle.
  • T4 Direction auto-flip: position toggle at top edge → menu opens BELOW.
  • T5 Tablet landscape: render direction = beside.
  • T6 Item select: tap item → menu closes; onItemTap fires.
  • T7 Outside tap closes: tap scrim → menu closes.
  • T8 ESC closes: menu open + ESC → closes.
  • T9 Keyboard nav: arrows + Enter functional.
  • T10 Reduced-motion: instant open/close.
  • T11 A11y: focus trap; aria-expanded transitions.
  • N1 > 6 items: spec validation OR runtime warning suggesting Navigation drawer.

Requirements (testable)

Requirement: Toggle opens and closes the menu with shape morph {#req-fab-menu-toggle-morph}

The toggle FAB SHALL open the menu on tap when closed and close it on tap when open, and while opening it SHALL morph its shape from the FAB shape to the "X" shape (reversing on close).

Scenario: Tapping the closed toggle opens the menu

  • GIVEN a FAB menu mounted in the closed state with the toggle FAB visible and its menu items hidden
  • WHEN the user taps the toggle FAB
  • THEN the menu items become visible
  • AND the toggle FAB shape morphs from the FAB shape to the "X" shape
  • AND aria-expanded on the toggle reads true

Scenario: Tapping the open toggle closes the menu

  • GIVEN a FAB menu in the open state showing its menu items
  • WHEN the user taps the toggle FAB again
  • THEN the menu items become hidden
  • AND the toggle FAB shape morphs from the "X" shape back to the FAB shape

Requirement: Item count stays within the 2-6 range {#req-fab-menu-item-count}

The FAB menu SHALL be used only for 2 to 6 secondary actions; below 2 items a single FAB SHALL be used instead, and above 6 items the component SHALL emit a validation error or runtime warning recommending migration to a Navigation drawer.

Scenario: Rendering three items produces three menu entries

  • GIVEN a FAB menu configured with exactly 3 items
  • WHEN the menu is opened
  • THEN 3 menu items are rendered vertically anchored to the toggle FAB

Scenario: Exceeding six items raises a warning

  • GIVEN a FAB menu configured with 7 items
  • WHEN the component is validated or mounted
  • THEN a validation error or runtime warning is emitted
  • AND the warning message recommends migrating to a Navigation drawer

Requirement: Menu direction defaults above and auto-flips by position and window class {#req-fab-menu-direction}

The menu SHALL fan upward above the toggle by default, SHALL force the opposite direction when the toggle is at a viewport edge, and SHALL open beside the toggle on Medium or Expanded window size classes.

Scenario: Toggle at the top edge opens the menu below

  • GIVEN a FAB menu whose toggle FAB is positioned at the top edge of the viewport
  • WHEN the menu is opened
  • THEN the menu items fan downward below the toggle instead of above it

Scenario: Expanded window class opens the menu beside the toggle

  • GIVEN a FAB menu rendered in a Medium or Expanded window size class (tablet/desktop) in an LTR layout
  • WHEN the menu is opened
  • THEN the menu items are anchored beside the toggle to the right of it

Requirement: Menu closes on item select, outside tap, or ESC {#req-fab-menu-close-triggers}

While open, the menu SHALL close on selection of a menu item, on a tap outside the menu (scrim), or on the ESC key, and item selection SHALL invoke the item's tap callback.

Scenario: Selecting an item closes the menu and fires the callback

  • GIVEN a FAB menu in the open state
  • WHEN the user taps one of the menu items
  • THEN the menu closes and its items become hidden
  • AND the onItemTap callback fires for the selected item

Scenario: ESC closes the open menu

  • GIVEN a FAB menu in the open state with keyboard focus inside it
  • WHEN the user presses the ESC key
  • THEN the menu closes and its items become hidden

Requirement: Accessibility roles, keyboard navigation, and focus trap {#req-fab-menu-a11y}

The toggle SHALL expose role="button", aria-haspopup="menu", and an aria-expanded state that tracks open/closed; each item SHALL expose role="menuitem"; and while the menu is open keyboard focus SHALL be trapped so that Tab cycles only within the items and toggle, with Arrow Up/Down navigating items and Enter selecting.

Scenario: Arrow keys navigate items and aria-expanded tracks state

  • GIVEN a FAB menu that has just been opened via the keyboard so aria-expanded is true
  • WHEN the user presses Arrow Down
  • THEN keyboard focus moves to the next menu item in sequence
  • AND each item carries role="menuitem" with its action aria-label

Scenario: Focus is trapped within the open menu

  • GIVEN a FAB menu in the open state with focus on the last menu item
  • WHEN the user presses Tab
  • THEN focus cycles back to the toggle or first item and does not leave the menu

Requirement: Reduced-motion disables morph and stagger {#req-fab-menu-reduced-motion}

When reduced-motion is active, the menu SHALL open and close instantly, SHALL skip the toggle shape morph in favor of a static "X" icon swap, and SHALL replace the staggered item appearance with a simultaneous fade-in.

Scenario: Opening under reduced-motion is instant without stagger

  • GIVEN a FAB menu rendered with reduced-motion active
  • WHEN the user taps the toggle to open the menu
  • THEN all menu items appear simultaneously without staggered timing
  • AND the toggle presents a static "X" icon swap rather than a shape morph animation

Referências