/* * : M3 Expressive's floating action button and extended FAB. * * FabBaseline/Medium/LargeTokens and ExtendedFab*Tokens (androidx Compose Material 3, Apache-2.0), * per `data-md-size`: * * size FAB corner glyph extended: min width gap padding type * sm 56px lg (16px) 24px 80px 8px 16px title-md * md 80px lg-increased 28px 80px 16px 26px title-lg * lg 96px xl (28px) 32px 96px 20px 28px headline-sm * * A FAB and its extended form share the height and the corner. `data-md-color` is `primary`, * `secondary` or `tertiary`, drawn in its container (`data-md-variant="container"`) or in the * colour itself (`filled`), at elevation 3, 4 under a hovering pointer, the shadow and colours on * the fast effects spring. It renders the foundation's `md-state-layer` and `md-focus-ring` * (foundation/interaction.css); every size draws at 56px or more, so it needs no `md-touch-target`. * `data-md-fab` on the root is also the hook a place uses to draw a nested FAB its own way: a * navigation rail or a docked toolbar flattens it to elevation 0. * * `data-md-collapse-on-scroll` is M3's extended FAB that "can collapse to a FAB on scroll-down and * re-expand to extended on scroll-up — when switching between FAB↔extended FAB, shape changes, the * icon moves left, and the text label fades in/out" (resources/js/fab.js sets * `data-md-collapsed`). `width: auto` cannot transition, so the label sits in a one-column grid * whose track closes to zero; that track is what animates, and the button lays itself out around * it every frame. The gap and the minimum width go with it, all on the default spatial spring, * while the label fades on the effects spring. The collapsed width is the FAB's own size. The * extended padding already centres the glyph at `sm` (16 + 24 + 16) and `md` (26 + 28 + 26); at * `lg` its 28px is 4px short of the 32px that centres a 32px glyph in 96px, so that one moves. The * label stays in the accessibility tree — clipped and transparent, never `display: none` — so the * collapsed FAB keeps the name the extended one had. Under reduced motion tokens/motion.css takes * every duration to zero, so it swaps. */ @layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility; @import './icon.css'; @import './tooltip.css'; @layer material.components { [data-md-fab] { --md-fab-color: var(--md-sys-color-primary); --md-fab-on-color: var(--md-sys-color-on-primary); --md-fab-container: var(--md-sys-color-primary-container); --md-fab-on-container: var(--md-sys-color-on-primary-container); display: inline-flex; flex-shrink: 0; align-items: center; justify-content: center; block-size: var(--md-fab-size); border-radius: var(--md-fab-corner); background-color: var(--md-fab-container); color: var(--md-fab-on-container); box-shadow: var(--md-sys-elevation-3); white-space: nowrap; cursor: pointer; -webkit-user-select: none; user-select: none; transition-property: box-shadow, background-color, color; transition-duration: var(--md-sys-motion-effects-fast-duration); transition-timing-function: var(--md-sys-motion-effects-fast); @media (hover: hover) { &:hover { box-shadow: var(--md-sys-elevation-4); } } } [data-md-fab][data-md-size='sm'] { --md-fab-size: 56px; --md-fab-corner: var(--md-sys-shape-corner-lg); --md-fab-extended-min: 80px; } [data-md-fab][data-md-size='md'] { --md-fab-size: 80px; --md-fab-corner: var(--md-sys-shape-corner-lg-increased); --md-fab-extended-min: 80px; } [data-md-fab][data-md-size='lg'] { --md-fab-size: 96px; --md-fab-corner: var(--md-sys-shape-corner-xl); --md-fab-extended-min: 96px; } [data-md-fab][data-md-color='secondary'] { --md-fab-color: var(--md-sys-color-secondary); --md-fab-on-color: var(--md-sys-color-on-secondary); --md-fab-container: var(--md-sys-color-secondary-container); --md-fab-on-container: var(--md-sys-color-on-secondary-container); } [data-md-fab][data-md-color='tertiary'] { --md-fab-color: var(--md-sys-color-tertiary); --md-fab-on-color: var(--md-sys-color-on-tertiary); --md-fab-container: var(--md-sys-color-tertiary-container); --md-fab-on-container: var(--md-sys-color-on-tertiary-container); } [data-md-fab][data-md-variant='filled'] { background-color: var(--md-fab-color); color: var(--md-fab-on-color); } [data-md-fab]:not([data-md-extended]) { inline-size: var(--md-fab-size); } [data-md-fab][data-md-extended] { min-inline-size: var(--md-fab-extended-min); } [data-md-fab][data-md-extended][data-md-size='sm'] { gap: var(--md-sys-measurement-space100); padding-inline: var(--md-sys-measurement-space200); font: var(--md-sys-typescale-title-md); letter-spacing: var(--md-sys-typescale-title-md-tracking); font-variation-settings: normal; } [data-md-fab][data-md-extended][data-md-size='md'] { gap: var(--md-sys-measurement-space200); padding-inline: 26px; font: var(--md-sys-typescale-title-lg); letter-spacing: var(--md-sys-typescale-title-lg-tracking); font-variation-settings: normal; } [data-md-fab][data-md-extended][data-md-size='lg'] { gap: 20px; padding-inline: 28px; font: var(--md-sys-typescale-headline-sm); letter-spacing: var(--md-sys-typescale-headline-sm-tracking); font-variation-settings: normal; } /* The extended FAB that collapses to a FAB while the page scrolls down. */ [data-md-fab][data-md-collapse-on-scroll] { transition-property: min-inline-size, padding-inline, gap, box-shadow, background-color, color; transition-duration: var(--md-sys-motion-spatial-default-duration), var(--md-sys-motion-spatial-default-duration), var(--md-sys-motion-spatial-default-duration), var(--md-sys-motion-effects-fast-duration), var(--md-sys-motion-effects-fast-duration), var(--md-sys-motion-effects-fast-duration); transition-timing-function: var(--md-sys-motion-spatial-default), var(--md-sys-motion-spatial-default), var(--md-sys-motion-spatial-default), var(--md-sys-motion-effects-fast), var(--md-sys-motion-effects-fast), var(--md-sys-motion-effects-fast); } [data-md-fab-label] { display: grid; grid-template-columns: 1fr; transition-property: grid-template-columns, opacity; transition-duration: var(--md-sys-motion-spatial-default-duration), var(--md-sys-motion-effects-default-duration); transition-timing-function: var(--md-sys-motion-spatial-default), var(--md-sys-motion-effects-default); & > span { overflow: hidden; white-space: nowrap; } } [data-md-fab][data-md-collapse-on-scroll][data-md-collapsed] { min-inline-size: var(--md-fab-size); gap: 0; & [data-md-fab-label] { grid-template-columns: 0fr; opacity: 0; } } [data-md-fab][data-md-collapse-on-scroll][data-md-size='lg'][data-md-collapsed] { padding-inline: var(--md-sys-measurement-space400); } }