/* * : an M3 Expressive FAB that opens into a short list of related actions. * * FabMenuBaselineTokens (androidx Compose Material 3, Apache-2.0): the trigger is a 56px FAB, * `data-md-color`'s container, large corner, elevation 3, rising to elevation 4 under a hovering * pointer; while its list is open (`aria-expanded`) it turns fully round and paints the colour * itself, on the default spatial and effects springs (ACT-19 splits the two). It renders the * foundation's `md-state-layer` and `md-focus-ring` (foundation/interaction.css); its 56px already * meets M3's target, so it needs no `md-touch-target`. The list sits * `data-md-position`'s corner of the trigger, 8px padding — the room the close button keeps below * the first item — and scrolls past `calc(100dvh - 128px)` on a short window, behind the close * button, which stays fixed (ACT-32). * * The items (fab-menu-item.css) rise into place as the list opens and sink back as it closes: the * entry is `@starting-style` on the item itself; the exit is the popover's exit copy * (`data-md-popover-exit`, resources/js/popover-exit.js), which stays on screen after the popover * has closed, so the items have somewhere to animate to (ACT-33) in every engine, Firefox included. * `[data-md-fab-menu-popover]:not(:popover-open) > *` and the copy's `[data-md-popover-closing] > *` * are the closed values; inside the layer they beat fab-menu-item.css's resting `translate: 0 0` * and `opacity: 1` on specificity — two attributes, or an attribute and a pseudo-class, against the * item's one attribute — whichever order the two files are bundled in. */ @layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility; @import './icon.css'; @layer material.components { [data-md-fab-menu] { position: relative; display: inline-flex; } [data-md-fab-menu-anchor] { display: inline-flex; } [data-md-fab-menu-trigger] { --md-fab-menu-container: var(--md-sys-color-primary-container); --md-fab-menu-on-container: var(--md-sys-color-on-primary-container); --md-fab-menu-color: var(--md-sys-color-primary); --md-fab-menu-on-color: var(--md-sys-color-on-primary); display: inline-flex; flex-shrink: 0; align-items: center; justify-content: center; inline-size: var(--md-sys-measurement-space700); block-size: var(--md-sys-measurement-space700); border-radius: var(--md-sys-shape-corner-lg); background-color: var(--md-fab-menu-container); color: var(--md-fab-menu-on-container); box-shadow: var(--md-sys-elevation-3); cursor: pointer; -webkit-user-select: none; user-select: none; transition-property: border-radius, background-color, color, box-shadow; transition-duration: var(--md-sys-motion-spatial-default-duration), var(--md-sys-motion-effects-default-duration), var(--md-sys-motion-effects-default-duration), var(--md-sys-motion-effects-default-duration); transition-timing-function: var(--md-sys-motion-spatial-default), var(--md-sys-motion-effects-default), var(--md-sys-motion-effects-default), var(--md-sys-motion-effects-default); @media (hover: hover) { &:hover { box-shadow: var(--md-sys-elevation-4); } } &[aria-expanded='true'] { border-radius: var(--md-sys-shape-corner-full); background-color: var(--md-fab-menu-color); color: var(--md-fab-menu-on-color); } } [data-md-fab-menu-trigger][data-md-color='secondary'] { --md-fab-menu-container: var(--md-sys-color-secondary-container); --md-fab-menu-on-container: var(--md-sys-color-on-secondary-container); --md-fab-menu-color: var(--md-sys-color-secondary); --md-fab-menu-on-color: var(--md-sys-color-on-secondary); } [data-md-fab-menu-trigger][data-md-color='tertiary'] { --md-fab-menu-container: var(--md-sys-color-tertiary-container); --md-fab-menu-on-container: var(--md-sys-color-on-tertiary-container); --md-fab-menu-color: var(--md-sys-color-tertiary); --md-fab-menu-on-color: var(--md-sys-color-on-tertiary); } [data-md-fab-menu-close-icon] { display: none; } [data-md-fab-menu-trigger][aria-expanded='true'] [data-md-fab-menu-icon] { display: none; } [data-md-fab-menu-trigger][aria-expanded='true'] [data-md-fab-menu-close-icon] { display: inline-flex; } [data-md-fab-menu-popover] { inset: auto; margin: 0; display: none; flex-direction: column; gap: var(--md-sys-measurement-space50); max-block-size: calc(100dvh - 128px); overflow-y: auto; border-width: 0; background-color: transparent; padding: var(--md-sys-measurement-space100); &:popover-open { display: flex; } } [data-md-fab-menu-popover][data-md-position='top-end'] { margin-inline: calc(-1 * var(--md-sys-measurement-space100)); margin-block-start: calc(-1 * var(--md-sys-measurement-space100)); align-items: flex-end; position-area: top span-left; } [data-md-fab-menu-popover][data-md-position='top-start'] { margin-inline: calc(-1 * var(--md-sys-measurement-space100)); margin-block-start: calc(-1 * var(--md-sys-measurement-space100)); align-items: flex-start; position-area: top span-right; } [data-md-fab-menu-popover][data-md-position='bottom-end'] { margin-inline: calc(-1 * var(--md-sys-measurement-space100)); margin-block-end: calc(-1 * var(--md-sys-measurement-space100)); align-items: flex-end; position-area: bottom span-left; } [data-md-fab-menu-popover][data-md-position='bottom-start'] { margin-inline: calc(-1 * var(--md-sys-measurement-space100)); margin-block-end: calc(-1 * var(--md-sys-measurement-space100)); align-items: flex-start; position-area: bottom span-right; } [data-md-fab-menu-popover]:not(:popover-open) > *, [data-md-fab-menu-popover][data-md-popover-closing] > * { translate: 0 var(--md-sys-measurement-space100); opacity: 0; } }