/* * ``: M3 Expressive's toolbars (DockedToolbarTokens.kt, FloatingToolbarTokens.kt, * androidx Compose Material 3, Apache-2.0; docs/reference/m3/components-navigation-selection-inputs.md * § Toolbars). * * [data-md-toolbar] role="toolbar"; data-md-variant, data-md-vertical, data-md-vibrant, * data-md-rounded, data-md-toolbar-place * [data-md-toolbar-fab] a FAB docked at the end, flat on the bar (elevation 0) * [data-md-toolbar-group] a floating toolbar and its own FAB, side by side (or stacked) * * docked a 64px bar across the bottom of the screen in surface-container, square, its * controls spread out 4 to 32px apart between 16px ends; with a FAB, the controls * at the start and the FAB flat at the end * floating a 64px pill in surface-container (or primary-container, `vibrant`) at elevation 3, * 8px at its ends and 4px between controls; `vertical` stands it on end * * Placed over the page, a horizontal toolbar keeps 16dp from the window's edge and a vertical one * 24dp, which is M3's minimum for each (N-12). * * On web and large screens a docked toolbar "can be rounded", and dividers can organise its * controls into groups: `rounded` does the first from `expanded` (840px), and a divider in the * slot the second at any width. * * `` and `` draw their own ink from `data-md-color`/`data-md-variant`, not a * utility, so a toolbar's recolouring rules — vibrant's icon buttons, a standard toolbar's primary * ones (N-13), a docked FAB's flattened shadow — sit in this file's own `material.components` * layer like everything else, each more specific than the button.css or fab.css rule it overrides, * whichever file an application imports first. The recolouring leaves a disabled button alone, so * button.css's disabled colours still apply inside a toolbar. */ @layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility; @layer material.components { [data-md-toolbar] { display: flex; align-items: center; gap: var(--md-sys-measurement-space50); } [data-md-toolbar][data-md-variant='docked'] { width: 100%; min-height: calc(64px + var(--material-safe-bottom, env(safe-area-inset-bottom))); justify-content: center; column-gap: clamp(var(--md-sys-measurement-space50), 4vw, var(--md-sys-measurement-space400)); padding-inline: var(--md-sys-measurement-space200); padding-bottom: var(--material-safe-bottom, env(safe-area-inset-bottom)); background-color: var(--md-sys-color-surface-container); color: var(--md-sys-color-on-surface-variant); } [data-md-toolbar][data-md-variant='floating'] { display: inline-flex; height: 64px; padding-inline: var(--md-sys-measurement-space100); border-radius: var(--md-sys-shape-corner-full); background-color: var(--md-sys-color-surface-container); color: var(--md-sys-color-on-surface-variant); box-shadow: var(--md-sys-elevation-3); } [data-md-toolbar][data-md-variant='floating'][data-md-vertical] { width: 64px; height: auto; flex-direction: column; padding-block: var(--md-sys-measurement-space100); padding-inline: 0; } [data-md-toolbar][data-md-vibrant] { background-color: var(--md-sys-color-primary-container); color: var(--md-sys-color-on-primary-container); } [data-md-toolbar][data-md-vibrant] [data-md-icon-button]:not([data-md-variant='filled'], [data-md-variant='tonal'], [data-md-selected='true'], [aria-pressed='true'], :disabled, [aria-disabled='true']) { color: var(--md-sys-color-on-primary-container); } [data-md-toolbar][data-md-vibrant] [data-md-icon-button][aria-pressed='true']:not(:disabled, [aria-disabled='true']) { background-color: var(--md-sys-color-surface-container); color: var(--md-sys-color-on-surface); } /* M3's colour list for a standard toolbar ends "Standard button (Primary)", as the vibrant list ends "Standard button (On primary container)" — the row above (N-13). Both rows name the *standard* button, so a button that fills its own container is left alone: it carries the matching label colour (`filled` on-primary, `tonal` on-tone, either selected), and painting that label primary put a primary icon on a primary fill — the current page's button in a navigation toolbar came out a blank circle. A disabled button keeps button.css's disabled colours in either. */ [data-md-toolbar]:not([data-md-vibrant]) [data-md-icon-button]:not([data-md-variant='filled'], [data-md-variant='tonal'], [data-md-selected='true'], [aria-pressed='true'], :disabled, [aria-disabled='true']) { color: var(--md-sys-color-primary); } /* A docked toolbar with a FAB: the controls at the start, the FAB at the end, resting on the bar at elevation 0 — M3 puts a FAB nested in another component there, and Compose's FloatingActionButtonDefaults.bottomAppBarFabElevation() is 0 in every state. */ [data-md-toolbar][data-md-variant='docked']:has(> [data-md-toolbar-fab]) { justify-content: flex-start; } [data-md-toolbar-fab] { display: flex; margin-inline-start: auto; } [data-md-toolbar-fab] [data-md-fab], [data-md-toolbar-fab] [data-md-fab]:hover { box-shadow: none; } /* Groups of controls, a divider between each two (``, or `` in a vertical toolbar). The divider draws itself stretched across its row, which in a toolbar would run it into the container's edges; here it stands as long as the 40px icon buttons it separates, centred on them, as the large-screen docked toolbar in M3's guidelines draws it (toolbars/guidelines § Adaptive design → Resizing). */ [data-md-toolbar]:not([data-md-vertical]) > [role='separator'][aria-orientation='vertical'] { align-self: center; height: 40px; } [data-md-toolbar][data-md-vertical] > [role='separator'][aria-orientation='horizontal'] { align-self: center; width: 40px; } /* A floating toolbar and its FAB, side by side (or stacked, vertical). */ [data-md-toolbar-group] { display: inline-flex; align-items: center; gap: var(--md-sys-measurement-space100); } [data-md-toolbar-group][data-md-vertical] { flex-direction: column; } /* Placed over the page: centred above the bottom edge, or centred against the end edge. * * A toolbar at the bottom shares the screen region with a navigation bar, so it clears the one * publishes as --material-bottom-bar, exactly as the FAB and the snackbar do — * `max()`, not a sum, because that height already swallows the bottom safe area; without a bar * the safe area alone applies (N-02). A *docked* toolbar and a navigation bar must never be on * screen together at all; the offset is the backstop, not a licence. */ [data-md-toolbar-place='bottom'] { position: fixed; bottom: calc(max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom))) + var(--md-sys-measurement-space200)); left: 50%; z-index: 30; translate: -50% 0; } [data-md-toolbar-place='end'] { position: fixed; top: 50%; inset-inline-end: calc(var(--md-sys-measurement-space200) + var(--material-safe-right, env(safe-area-inset-right))); z-index: 30; translate: 0 -50%; } /* "Horizontal toolbars should have a minimum 16dp margin from the edge of the window. Vertical toolbars should have a minimum 24dp margin" (N-12). */ [data-md-toolbar][data-md-vertical][data-md-toolbar-place='end'], [data-md-toolbar-group][data-md-vertical][data-md-toolbar-place='end'] { inset-inline-end: calc(var(--md-sys-measurement-space300) + var(--material-safe-right, env(safe-area-inset-right))); } [data-md-toolbar][data-md-variant='docked'][data-md-toolbar-place='bottom'] { inset-inline: 0; bottom: var(--material-bottom-bar, 0px); left: 0; translate: none; } /* The large-screen docked toolbar: from `expanded` (840px) fully rounded, like the one M3 shows on the web, and spanning what it is written in. At the bottom of the window it lifts off the edges — 16dp from each side, M3's minimum outside padding for a toolbar, and 16px above the bottom bar or the safe area, as a floating toolbar sits — so the curve never meets the window's frame, and the bottom safe area, which it no longer touches, stops padding it. Below `expanded` it is the square, full-width docked toolbar M3 requires ("Avoid applying rounded corners to the container"). */ @media (width >= 840px) { [data-md-toolbar][data-md-variant='docked'][data-md-rounded] { min-height: 64px; padding-bottom: 0; border-radius: var(--md-sys-shape-corner-full); } [data-md-toolbar][data-md-variant='docked'][data-md-rounded][data-md-toolbar-place='bottom'] { right: calc(var(--md-sys-measurement-space200) + var(--material-safe-right, env(safe-area-inset-right))); bottom: calc(max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom))) + var(--md-sys-measurement-space200)); left: calc(var(--md-sys-measurement-space200) + var(--material-safe-left, env(safe-area-inset-left))); width: auto; } } /* What a toolbar at the bottom covers, published as --material-bottom-toolbar: the distance from * the window's bottom edge to the toolbar's top, each formula the placement above plus the * height. The snackbar clears it (toast.css) — M3 nudges a snackbar "upward to avoid overlapping * FABs/docked toolbars" and never puts one "in front of … navigation" * (docs/reference/m3/components-actions-communication-containment.md § Snackbar) — and a page * pads its end with it, so its last line never hides behind the toolbar. * * Declared on the root and again on 's root: a custom property resolves its var()s * where it is declared, and --material-bottom-bar is only set on the scaffold's root, so only * there does the formula see the bar. A floating toolbar is 64px, beside an 80px (`md`) or * 96px (`lg`) FAB as tall as the FAB; a docked one is 64px plus the bottom safe area it pads, * until `rounded` lifts it off the edge from `expanded`. A vertical toolbar, which belongs at * the end edge, publishes nothing. */ :root:has([data-md-toolbar-place='bottom']:is([data-md-toolbar], [data-md-toolbar-group]):not([data-md-vertical])), [data-md-scaffold]:has([data-md-toolbar-place='bottom']:is([data-md-toolbar], [data-md-toolbar-group]):not([data-md-vertical])) { --material-bottom-toolbar: calc(max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom))) + var(--md-sys-measurement-space200) + 64px); } :root:has([data-md-toolbar-group][data-md-toolbar-place='bottom']:not([data-md-vertical]) [data-md-fab][data-md-size='md']), [data-md-scaffold]:has([data-md-toolbar-group][data-md-toolbar-place='bottom']:not([data-md-vertical]) [data-md-fab][data-md-size='md']) { --material-bottom-toolbar: calc(max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom))) + var(--md-sys-measurement-space200) + 80px); } :root:has([data-md-toolbar-group][data-md-toolbar-place='bottom']:not([data-md-vertical]) [data-md-fab][data-md-size='lg']), [data-md-scaffold]:has([data-md-toolbar-group][data-md-toolbar-place='bottom']:not([data-md-vertical]) [data-md-fab][data-md-size='lg']) { --material-bottom-toolbar: calc(max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom))) + var(--md-sys-measurement-space200) + 96px); } :root:has([data-md-toolbar][data-md-variant='docked'][data-md-toolbar-place='bottom']), [data-md-scaffold]:has([data-md-toolbar][data-md-variant='docked'][data-md-toolbar-place='bottom']) { --material-bottom-toolbar: calc(var(--material-bottom-bar, 0px) + 64px + var(--material-safe-bottom, env(safe-area-inset-bottom))); } @media (width >= 840px) { :root:has([data-md-toolbar][data-md-variant='docked'][data-md-rounded][data-md-toolbar-place='bottom']), [data-md-scaffold]:has([data-md-toolbar][data-md-variant='docked'][data-md-rounded][data-md-toolbar-place='bottom']) { --material-bottom-toolbar: calc(max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom))) + var(--md-sys-measurement-space200) + 64px); } } }