Plan step 25 (navigation Missing): M3's toolbar configuration table lists "With FAB" for both toolbars, but the `fab` slot was gated to the floating one. A docked toolbar now takes it at its end, inside the toolbar where the arrow keys reach it; its controls gather at the start, and the FAB rests flat on the bar — elevation 0 for a FAB nested in another component, as Compose's bottomAppBarFabElevation is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
142 lines
5.2 KiB
CSS
142 lines
5.2 KiB
CSS
/*
|
|
* M3 Expressive's toolbars (DockedToolbarTokens, FloatingToolbarTokens, androidx Compose Material 3,
|
|
* Apache-2.0).
|
|
*
|
|
* 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.
|
|
*
|
|
* Unlayered on purpose: a toolbar recolours the icon buttons inside it — primary in a standard
|
|
* one, on-primary-container in a vibrant one — and they draw their ink as utilities, which a rule
|
|
* in any layer loses to.
|
|
*/
|
|
|
|
[data-toolbar] {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
[data-toolbar][data-variant="docked"] {
|
|
width: 100%;
|
|
min-height: calc(4rem + var(--material-safe-bottom, env(safe-area-inset-bottom)));
|
|
justify-content: center;
|
|
column-gap: clamp(0.25rem, 4vw, 2rem);
|
|
padding-inline: 1rem;
|
|
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-toolbar][data-variant="floating"] {
|
|
display: inline-flex;
|
|
height: 4rem;
|
|
padding-inline: 0.5rem;
|
|
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-toolbar][data-variant="floating"][data-vertical] {
|
|
width: 4rem;
|
|
height: auto;
|
|
flex-direction: column;
|
|
padding-block: 0.5rem;
|
|
padding-inline: 0;
|
|
}
|
|
|
|
[data-toolbar][data-vibrant] {
|
|
background-color: var(--md-sys-color-primary-container);
|
|
color: var(--md-sys-color-on-primary-container);
|
|
}
|
|
|
|
[data-toolbar][data-vibrant] [data-icon-button]:not([aria-pressed="true"]) {
|
|
color: var(--md-sys-color-on-primary-container);
|
|
}
|
|
|
|
[data-toolbar][data-vibrant] [data-icon-button][aria-pressed="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. An icon button carries its own
|
|
ink as a utility, so the container's `color` never reaches it and this rule has to. */
|
|
[data-toolbar]:not([data-vibrant]) [data-icon-button]:not([aria-pressed="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. `<x-fab>` draws its
|
|
shadow with utilities, which is one more reason this file is unlayered. */
|
|
[data-toolbar][data-variant="docked"]:has(> [data-toolbar-fab]) {
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
[data-toolbar-fab] {
|
|
display: flex;
|
|
margin-inline-start: auto;
|
|
}
|
|
|
|
[data-toolbar-fab] [data-fab],
|
|
[data-toolbar-fab] [data-fab]:hover {
|
|
box-shadow: none;
|
|
}
|
|
|
|
/* A floating toolbar and its FAB, side by side (or stacked, vertical). */
|
|
[data-toolbar-group] {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
[data-toolbar-group][data-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
|
|
* <x-app-shell> 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. A *docked* toolbar and a navigation bar must never be on screen
|
|
* together at all (docs/reference/m3/components-navigation-selection-inputs.md § Toolbars); the
|
|
* offset is the backstop, not a licence. */
|
|
[data-toolbar-place="bottom"] {
|
|
position: fixed;
|
|
bottom: calc(max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom))) + 1rem);
|
|
left: 50%;
|
|
z-index: 30;
|
|
translate: -50% 0;
|
|
}
|
|
|
|
[data-toolbar-place="end"] {
|
|
position: fixed;
|
|
top: 50%;
|
|
inset-inline-end: calc(1rem + 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"
|
|
(docs/reference/m3/components-navigation-selection-inputs.md § Toolbars). */
|
|
[data-toolbar][data-vertical][data-toolbar-place="end"],
|
|
[data-toolbar-group][data-vertical][data-toolbar-place="end"] {
|
|
inset-inline-end: calc(1.5rem + var(--material-safe-right, env(safe-area-inset-right)));
|
|
}
|
|
|
|
[data-toolbar][data-variant="docked"][data-toolbar-place="bottom"] {
|
|
inset-inline: 0;
|
|
bottom: var(--material-bottom-bar, 0px);
|
|
left: 0;
|
|
translate: none;
|
|
}
|