Files
livewire-material/resources/css/components/toolbar.css
T
Andreas Reinhold / reiniandClaude Opus 5 4ad257034e Draw the button without Tailwind
Plan step 36. <x-button> renders data-md-button with its variant, colour,
size, shape, icon-button width, selected state and compact FAB as
data-md-* attributes, and passes the glyph's size to <x-icon>.
button.css draws Button*Tokens' geometry per size, the colour roles
through custom properties that the disabled treatment replaces, the
state layer, focus ring and 48px target, and the compact FAB below
600px. Its corner rules carry no specificity (:where), so a group's
stylesheet reshapes the buttons inside it with any selector; the
split button no longer passes an empty corners prop, which is gone.

data-icon-button becomes data-md-icon-button, also in groups.css,
toolbar.css and theme-toggle's own button (navigation group), and the
AppBarTest assertions follow the new hooks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-14 14:50:58 +02:00

183 lines
7.5 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.
*
* On web and large screens a docked toolbar "can be rounded", and dividers can organise its
* controls into groups (docs/reference/m3/components-navigation-selection-inputs.md § Toolbars):
* `rounded` does the first from `expanded`, and a divider in the slot the second at any width.
*
* 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-md-icon-button]:not([aria-pressed="true"]) {
color: var(--md-sys-color-on-primary-container);
}
[data-toolbar][data-vibrant] [data-md-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-md-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;
}
/* Groups of controls, a divider between each two (`<x-divider vertical />`, or `<x-divider />` 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). The divider is `self-stretch`, a utility,
which this unlayered rule beats. */
[data-toolbar]:not([data-vertical]) > [role="separator"][aria-orientation="vertical"] {
align-self: center;
height: 2.5rem;
}
[data-toolbar][data-vertical] > [role="separator"][aria-orientation="horizontal"] {
align-self: center;
width: 2.5rem;
}
/* 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;
}
/* 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 >= 52.5rem) {
[data-toolbar][data-variant="docked"][data-rounded] {
min-height: 4rem;
padding-bottom: 0;
border-radius: var(--md-sys-shape-corner-full);
}
[data-toolbar][data-variant="docked"][data-rounded][data-toolbar-place="bottom"] {
right: calc(1rem + 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))) + 1rem);
left: calc(1rem + var(--material-safe-left, env(safe-area-inset-left)));
width: auto;
}
}