Files
livewire-material/resources/css/components/navigation-bar.css
T
Andreas Reinhold / reiniandClaude Opus 5 fb7007c976
tests / feature (8.4) (push) Successful in 2m0s
tests / feature (8.5) (push) Successful in 2m0s
tests / browser (chrome, chromium) (push) Failing after 8m3s
tests / browser (firefox, firefox) (push) Failing after 12m58s
tests / browser (safari, webkit) (push) Failing after 13m8s
Take Tailwind out of the package, and its detection out of the guard
Tailwind left the stack in 2.0.0, but the package still carried about 330
mentions of it. What the guard's Tailwind detection protected — a class
that compiles to nothing — is now protected by a check that does not care
where a dead class came from.

DesignGuard: about 500 lines of Tailwind tables, scales, palettes and
"2.0.0 replacement" hints give way to one check — a class a view or PHP
file writes that neither the application's stylesheets nor the package's
own declare. It catches a utility of any framework, a typo and a class
whose rules were deleted alike, so it also found two classes ReStride
draws nothing with. A stylesheet has to be in reach for it: the `.css`
files among the scanned paths, or what the `missingStylesheets()` entry
imports. The class reader no longer mistakes an array index for a class
list (`$block['base']`), and it reads the array a class helper is given,
where it read nothing before.

The package's own three Tailwind self-guards go with it. Only their one
unique check stays, as a test of its own: every `matchMedia` width in
resources/js is an M3 breakpoint.

The pagination views are `material.blade.php` and
`simple-material.blade.php`; only Laravel's and Livewire's default theme
names ever made them `tailwind`. The provider sets `Paginator`'s default
views and switches `livewire.pagination_theme` to `material` when it is
still Livewire's own default, so no application can forget the config; a
theme an application chose, and a component's own `$paginationTheme` or
`paginationView()`, still win.

The rest is prose: the layer-order guidance for an application that still
builds Tailwind, the Tailwind wording in the README, the Boost guidelines
and the development skill, and about 25 "this used to be a Tailwind
utility" comments, along with every "plan step NN" pointer into a
gitignored folder. The reset keeps its credit, and NOTICE now carries it
too.

Feature suite 1159 passed, Chrome browser suite 299 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 21:07:39 +02:00

91 lines
5.3 KiB
CSS

/*
* `<x-navigation-bar>`: M3 Expressive's flexible navigation bar, three to five destinations
* pinned to the bottom of a compact or medium window (NavigationBarTokens.kt, androidx Compose
* Material 3, Apache-2.0, and the layout of ShortNavigationBar.kt;
* docs/reference/m3/components-navigation-selection-inputs.md § Navigation Bar).
*
* [data-md-navigation-bar] 64px in surface-container; data-md-tall (80px),
* data-md-hide-on-scroll and data-md-hidden
* [data-md-navigation-bar-items] equal widths below 600px; centred with Compose's
* calculateCenteredContentHorizontalPadding from it
* [data-md-navigation-bar-item] navigation-bar-item.css
*
* `data-md-tall` is `NavigationBarTokens.TallContainerHeight`, 80px against the short bar's 64
* (`ContainerHeight`) — Missing until this rewrite. It keeps the vertical, icon-over-label item
* layout at every width, so every rule below that only the short bar's horizontal layout needs
* says `:not([data-md-tall])` first.
*
* `data-md-hide-on-scroll` is M3's scrolling behaviour, also Missing until now: "hides on
* scroll-down, reappears on scroll-up… never hide it while a screen reader is active"
* (§ Navigation Bar/Behaviour). resources/js/navigation.js drives `data-md-hidden` and never hides
* the bar while a snackbar, a bottom sheet or a drawer rests on its edge — those are anchored to
* its bottom edge and would slide with it — and focus reaching the bar brings it back, the web's
* nearest equivalent of "a screen reader is active". It slides on the default spatial spring,
* which reduced motion zeroes along with every other duration token.
*
* The item's own indicator, pill and state layer (shared with the rail item) are
* navigation-bar-item.css and navigation-item.css.
*/
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
@layer material.components {
[data-md-navigation-bar] {
container-type: inline-size;
padding-inline: var(--material-safe-left, env(safe-area-inset-left)) var(--material-safe-right, env(safe-area-inset-right));
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-navigation-bar-items] {
display: flex;
min-height: 64px;
margin-inline: auto;
}
[data-md-navigation-bar][data-md-tall] [data-md-navigation-bar-items] {
min-height: 80px;
}
[data-md-navigation-bar][data-md-hide-on-scroll] {
transition: translate var(--md-sys-motion-spatial-default-duration) var(--md-sys-motion-spatial-default);
}
[data-md-navigation-bar][data-md-hide-on-scroll][data-md-hidden] {
translate: 0 100%;
}
/* From 600px (M3's medium window, the bar's own width): icon and label side by side in a
40px pill, the items gathered in the middle with Centered arrangement's own padding. The
short bar only — a tall bar is the vertical layout everywhere. */
@container (width >= 600px) {
[data-md-navigation-bar]:not([data-md-tall]) [data-md-navigation-bar-items] {
width: calc(10% * (var(--navigation-bar-count, 7) + 3));
min-width: fit-content;
max-width: 100%;
}
[data-md-navigation-bar]:not([data-md-tall]) [data-md-navigation-bar-items]:has(> :last-child:nth-child(1)) { --navigation-bar-count: 1; }
[data-md-navigation-bar]:not([data-md-tall]) [data-md-navigation-bar-items]:has(> :last-child:nth-child(2)) { --navigation-bar-count: 2; }
[data-md-navigation-bar]:not([data-md-tall]) [data-md-navigation-bar-items]:has(> :last-child:nth-child(3)) { --navigation-bar-count: 3; }
[data-md-navigation-bar]:not([data-md-tall]) [data-md-navigation-bar-items]:has(> :last-child:nth-child(4)) { --navigation-bar-count: 4; }
[data-md-navigation-bar]:not([data-md-tall]) [data-md-navigation-bar-items]:has(> :last-child:nth-child(5)) { --navigation-bar-count: 5; }
[data-md-navigation-bar]:not([data-md-tall]) [data-md-navigation-bar-items]:has(> :last-child:nth-child(6)) { --navigation-bar-count: 6; }
}
/*
* A bar that has slid off the bottom of the window is no longer there to clear, so the offset
* everything pinned to the bottom reads drops to the bottom safe area and whatever the
* application has docked on the bar — a `fab` button, the snackbar and the page's own bottom
* padding all follow it down and come back up with it. `<x-scaffold>` publishes
* `--material-bottom-bar` itself, in `material.layout` (layout/scaffold.css), a layer this
* file's own `material.components` always outranks by declaration order alone — the same
* reason toolbar.css's and fab.css's overrides only have to beat another `material.components`
* rule. Keyed on `data-md-scaffold`, the hook the scaffold renders on its root.
*/
[data-md-scaffold]:has([data-md-navigation-bar][data-md-hide-on-scroll][data-md-hidden]) {
--material-bottom-bar: calc(var(--material-safe-bottom, env(safe-area-inset-bottom)) + var(--material-bottom-extra, 0px));
}
}