Open a menu in a bottom sheet on a compact window
Plan step 22, item 8 (actions.md § Missing, "Adaptive menu → bottom sheet
at compact"): M3 says "at compact breakpoints, consider swapping a menu for
a bottom sheet", and nothing connected <x-menu> to <x-bottom-sheet>.
<x-menu sheet-at-compact> draws its slot twice, in the popover and in a
modal <x-bottom-sheet> teleported to <body>, and below `medium`
(upTo('medium')) the trigger opens the sheet. The trigger says
aria-haspopup="dialog" there and "menu" from medium, aria-expanded in
both. Items keep their roles and the APG keyboard in the sheet; choosing
one, Escape or Tab close it and return focus to the trigger; a submenu
opens in place under its item; `filter` works in both; a resize across
600px closes whichever is open. The sheet has a fixed id that menu.js
makes unique and keeps as its wire:key, and the lists are keyed, so a
Livewire render patches an open sheet instead of swapping it.
bottom-sheet.blade.php is used as is. The filter now finds its field and
empty row per list instead of through x-refs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Opus 5
parent
12cdeaaf67
commit
deb1442dfb
@@ -79,17 +79,76 @@
|
||||
|
||||
/* The filter hides what the query leaves out with the `hidden` attribute; a row's own
|
||||
`display: flex` utility would otherwise beat the user agent's `[hidden] { display: none }`. */
|
||||
[data-menu] [hidden] {
|
||||
:is([data-menu], [data-menu-sheet]) [hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* `data-active` is the row the arrow keys are on while the focus stays in the field — the state
|
||||
layer the roving focus would have drawn, without taking the focus off the field. A chosen or
|
||||
current row keeps its own colour. */
|
||||
[data-menu] [role^="menuitem"][data-active]:not([aria-checked="true"], [aria-current="page"]) {
|
||||
:is([data-menu], [data-menu-sheet]) [role^="menuitem"][data-active]:not([aria-checked="true"], [aria-current="page"]) {
|
||||
background: color-mix(in srgb, var(--material-menu-ink, var(--md-sys-color-on-surface)) 8%, transparent);
|
||||
}
|
||||
|
||||
/*
|
||||
* `<x-menu sheet-at-compact>`: the copy of the list in the bottom sheet a compact window opens
|
||||
* instead of the popover. The sheet is surface-container-low, as the standard menu is, so the
|
||||
* fallbacks above already paint its field; the field reaches both edges of the sheet and holds
|
||||
* its icon where the rows hold theirs, 24px in (the list is 8px in, a row's own padding 16px).
|
||||
*/
|
||||
[data-menu-sheet] [data-menu-filter] {
|
||||
padding-inline: 1.5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* A submenu there opens in place: its list stands under the item, inset by 16px, instead of in a
|
||||
* popover beside it, which a sheet has no room for (M3 calls submenus "best suited to large
|
||||
* screens", docs/reference/m3/components-actions-communication-containment.md § Menus). The
|
||||
* markup is the popover's, never shown as one: the user agent's `[popover]` box is undone, the
|
||||
* list is shown while its item says `aria-expanded="true"`, and it fades in on the effects track.
|
||||
*/
|
||||
[data-menu-sheet] [data-submenu] {
|
||||
position: static;
|
||||
inset: auto;
|
||||
inline-size: auto;
|
||||
max-inline-size: none;
|
||||
block-size: auto;
|
||||
max-block-size: none;
|
||||
margin: 0;
|
||||
padding-block: 0.125rem;
|
||||
padding-inline: 1rem 0;
|
||||
overflow: visible;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
box-shadow: none;
|
||||
opacity: 1;
|
||||
scale: none;
|
||||
transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast);
|
||||
}
|
||||
|
||||
[data-menu-sheet] [aria-expanded="true"] + [data-submenu] {
|
||||
display: block;
|
||||
|
||||
@starting-style {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* The chevron turns from the way a popover would open to the way this list did: down, which is a
|
||||
quarter turn the other way on the chevron `rtl:-scale-x-100` mirrors — so the same RTL test. */
|
||||
[data-menu-sheet] [data-submenu-chevron] {
|
||||
transition: rotate var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast);
|
||||
}
|
||||
|
||||
[data-menu-sheet] [aria-expanded="true"] > [data-submenu-chevron] {
|
||||
rotate: 90deg;
|
||||
|
||||
&:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) {
|
||||
rotate: -90deg;
|
||||
}
|
||||
}
|
||||
|
||||
.field-menu {
|
||||
max-block-size: 18rem;
|
||||
overflow-y: auto;
|
||||
|
||||
Reference in New Issue
Block a user