From 700869ded4b732dcebdc806e02ecb6cd39c3acee Mon Sep 17 00:00:00 2001 From: Andreas Reinhold / reini Date: Mon, 14 Sep 2026 05:53:37 +0200 Subject: [PATCH] Lift a bottom-placed toolbar above the navigation bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `place="bottom"` ignored --material-bottom-bar, so a floating toolbar overlapped the shell's navigation bar and a docked one landed squarely on it. Both now clear it — max(), not a sum, since that height already swallows the bottom safe area — and the docs say what M3 says: never show a docked toolbar and a navigation bar together. Plan: docs/plans/material-3-alignment.md, step 14 (navigation N-02). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9 --- .../skills/livewire-material-development/SKILL.md | 2 ++ resources/css/components/toolbar.css | 13 ++++++++++--- resources/views/components/toolbar.blade.php | 5 +++++ tests/Feature/Components/AppBarTest.php | 9 +++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/resources/boost/skills/livewire-material-development/SKILL.md b/resources/boost/skills/livewire-material-development/SKILL.md index 38ecc56c..2f2afe86 100644 --- a/resources/boost/skills/livewire-material-development/SKILL.md +++ b/resources/boost/skills/livewire-material-development/SKILL.md @@ -789,6 +789,8 @@ M3 Expressive toolbar, `role="toolbar"` (arrow keys move between controls). `var ``` +A docked toolbar and a navigation bar occupy the same screen region and must never be on screen together: show the bar on a primary page and the toolbar on a secondary or contextual one. A `place="bottom"` toolbar clears `--material-bottom-bar` if a bar is there anyway, so nothing is buried. + ### ``, `` M3 tabs with a server-rendered tablist (arrow keys, Home/End, disabled tabs skipped, the indicator moves in a view transition). `tabs`: `['name', 'label', 'icon', 'badge', 'disabled']`; panels are `` in the slot. Bind with `wire:model` (entangled), or `selected` / `x-model` without Livewire. `variant` `primary` (default) or `secondary`; `stacked` (icon over label), `scrollable`. Give two identical tab sets on one page distinct `id`s. diff --git a/resources/css/components/toolbar.css b/resources/css/components/toolbar.css index 512e5c42..0372a20e 100644 --- a/resources/css/components/toolbar.css +++ b/resources/css/components/toolbar.css @@ -71,10 +71,17 @@ flex-direction: column; } -/* Placed over the page: centred above the bottom edge, or centred against the end edge. */ +/* 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. 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(1rem + var(--material-safe-bottom, env(safe-area-inset-bottom))); + 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; @@ -90,7 +97,7 @@ [data-toolbar][data-variant="docked"][data-toolbar-place="bottom"] { inset-inline: 0; - bottom: 0; + bottom: var(--material-bottom-bar, 0px); left: 0; translate: none; } diff --git a/resources/views/components/toolbar.blade.php b/resources/views/components/toolbar.blade.php index c0451eb3..61b6ecd0 100644 --- a/resources/views/components/toolbar.blade.php +++ b/resources/views/components/toolbar.blade.php @@ -7,6 +7,11 @@ toolbar sits where it is written. A `fab` slot sets an `` beside a floating toolbar. `label` names it for screen readers. + A docked toolbar and a navigation bar occupy the same region of the screen and M3 says never to + show both at once: the bar belongs on a primary page, the toolbar on a secondary or contextual + one. Either way a toolbar placed at `bottom` clears `--material-bottom-bar`, so it is never + buried under ``'s bar. + Put `` controls in the slot (`:selected` for toggles). It is a `role="toolbar"`: the arrow keys move between its controls (resources/js/toolbar.js, resources/css/components/toolbar.css). --}} diff --git a/tests/Feature/Components/AppBarTest.php b/tests/Feature/Components/AppBarTest.php index 95760299..f084b7fa 100644 --- a/tests/Feature/Components/AppBarTest.php +++ b/tests/Feature/Components/AppBarTest.php @@ -69,6 +69,15 @@ it('draws floating and docked toolbars', function () { ->not->toContain('data-toolbar-group'); }); +it('keeps a toolbar at the bottom clear of the navigation bar', function () { + $css = file_get_contents(__DIR__.'/../../../resources/css/components/toolbar.css'); + + expect($css) + // Never a sum: --material-bottom-bar already swallows the bottom safe area (N-02). + ->toContain('bottom: calc(max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom))) + 1rem);') + ->toContain('bottom: var(--material-bottom-bar, 0px);'); +}); + it('offers M3\'s three contrast levels, marking the one in force', function () { expect((string) $this->blade('')) ->toContain('data-theme-toggle="contrast"')