From eac7f6a1deb517dcf2fcd44cff2d34bdf19c391e Mon Sep 17 00:00:00 2001 From: Andreas Reinhold Date: Mon, 14 Sep 2026 06:07:22 +0200 Subject: [PATCH] Name the FAB menu, scroll it, and let its items leave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Its trigger holds two aria-hidden icons and wrote aria-label only when a caller passed `label`, so an unnamed button was the default; `label` now falls back to "Toggle menu" and is always written. The list had no ceiling, so six items ran off a landscape phone — it now scrolls under the FAB, which stays put — and its items rose into place but vanished on close, because the popover took its display away before they could move. Plan step 18, actions.md ACT-21, ACT-32, ACT-33. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9 --- .../livewire-material-development/SKILL.md | 2 +- resources/css/components/actions.css | 13 +++++++ resources/views/components/fab-menu.blade.php | 36 +++++++++++++------ tests/Feature/Components/FabTest.php | 10 +++++- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/resources/boost/skills/livewire-material-development/SKILL.md b/resources/boost/skills/livewire-material-development/SKILL.md index 1b58f02b..7d2f7475 100644 --- a/resources/boost/skills/livewire-material-development/SKILL.md +++ b/resources/boost/skills/livewire-material-development/SKILL.md @@ -304,7 +304,7 @@ Attributes go to the leading button; the slot is the menu. `variant` (`filled` d ``` -Two to six items open above the FAB, which turns into a close button. ``: `icon` (`add`), `label`, `color`, `position` (`top-end` default). Give items the same `color`. Keyboard, and staying open through a Livewire render, as ``. +Two to six items open above the FAB, which turns into a close button, rising into place as it opens and sinking back as it closes; a window too short for them scrolls the list while the FAB stays put. ``: `icon` (`add`), `label` (defaults to "Toggle menu" — the trigger has no other accessible name), `color`, `position` (`top-end` default). Give items the same `color`. Keyboard, and staying open through a Livewire render, as ``. The wrapper keeps M3's margin from the window edge: 16dp, 24dp from `large`. ### `` diff --git a/resources/css/components/actions.css b/resources/css/components/actions.css index 3eb2b95a..f0abf45c 100644 --- a/resources/css/components/actions.css +++ b/resources/css/components/actions.css @@ -68,3 +68,16 @@ } } } + +/* + * A FAB menu's items rise into place as the list opens and sink back as it closes. The entry is + * `@starting-style` on the item itself; the exit needs the state to be readable while the popover + * is on its way out, which is what `:not(:popover-open)` is — the popover keeps `display: flex` + * for the length of its own discrete transition, so the items have somewhere to animate to. + * + * Unlayered (a plain rule, not an `@utility`), so it beats the item's own utilities. + */ +[data-fab-menu]:not(:popover-open) > * { + translate: 0 0.5rem; + opacity: 0; +} diff --git a/resources/views/components/fab-menu.blade.php b/resources/views/components/fab-menu.blade.php index e0116c5f..69798c96 100644 --- a/resources/views/components/fab-menu.blade.php +++ b/resources/views/components/fab-menu.blade.php @@ -1,6 +1,6 @@ {{-- An M3 Expressive FAB menu: a FAB that opens into a short list of related actions. -
+
@@ -10,8 +10,14 @@ Two to six items. The FAB (`icon`, `add` by default, in `color`'s container) turns into a round close button in the colour itself while the list is open above it, end-aligned; the list is a `popover="auto"` menu with the menu keyboard of ``. `label` names the FAB - for screen readers. Give the items the same `color`. Like ``'s, the list is keyed for - Livewire, so it stays open through a render of the component around it. + and the menu for screen readers, and defaults to "Toggle menu" — the icons inside the button + are aria-hidden, so without it the trigger has no name at all. Give the items the same + `color`. Like ``'s, the list is keyed for Livewire, so it stays open through a render + of the component around it. + + It does not place itself. M3's margin from the edge of the window is 16dp, and 24dp from the + `large` class up: `fixed end-4 bottom-4 large:end-6 large:bottom-6`. A window too short for + the list scrolls it; the items rise into place as it opens and sink back as it closes. FabMenuBaselineTokens (androidx Compose Material 3, Apache-2.0): 56px items, 4px apart, 8px above the close button. The FAB's corner and colours morph on the two default springs at once @@ -25,6 +31,10 @@ ]) @php + // The trigger's only content is two icons, which are aria-hidden, so without a name it is an + // unnamed button. M3 asks the FAB's label to say what menu it opens; "Toggle menu" is its own + // fallback wording for the close button. + $label = filled($label) ? $label : __('Toggle menu'); $color = in_array($color, ['primary', 'secondary', 'tertiary'], true) ? $color : 'primary'; $key = \Illuminate\Support\Str::lower(\Illuminate\Support\Str::random(10)); $anchor = "--material-fab-menu-{$key}"; @@ -44,7 +54,7 @@ >