Let the navigation bar hide on a scroll down

Plan step 25 (navigation Missing): M3's "hides on scroll-down, reappears on
scroll-up" had no implementation. `<x-navigation-bar hide-on-scroll>` slides the
bar out on the default spatial spring — an instant swap under reduced motion,
which zeroes the duration token — and never while a snackbar, a bottom sheet or
a drawer is anchored to its edge; focus reaching the bar brings it back.
`<x-app-shell hide-bar-on-scroll>` picks it, and --material-bottom-bar follows
the bar down and up so a `fab` button and the snackbar keep their offsets.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 06:36:57 +02:00
co-authored by Claude Fable 5.1
parent 4d00d87cd2
commit ce1cd6eec0
8 changed files with 167 additions and 11 deletions
@@ -20,11 +20,20 @@
vertical item layout at every width — icon over label, never side by side — for a bar whose
labels need the room. The short bar stays the default.
`hide-on-scroll` is M3's scrolling behaviour: the bar slides out of the window on a scroll
down and springs back on a scroll up, never before the first screenful has gone by, and never
while a snackbar, a bottom sheet or a drawer is on screen those are anchored to the bar's
edge and would slide with it. Focus reaching the bar brings it back, which is as close as the
web gets to M3's "never hide it while a screen reader is active". Inside `<x-app-shell>`
`--material-bottom-bar` follows the bar down and up, so a `fab` button, the snackbar and the
page's bottom padding keep their distance from it rather than from where it was.
It does not position itself: wrap it in the element that pins it (`fixed inset-x-0 bottom-0`)
and hides it where a rail takes over. `<x-app-shell>` does both, and lifts the snackbar and a
`fab` button above it through `--material-bottom-bar`.
`label` names the landmark ("Main" by default); `tall` picks the 80px container.
`label` names the landmark ("Main" by default); `tall` picks the 80px container;
`hide-on-scroll` lets it leave the window while the page scrolls down.
Values from androidx Compose Material 3 (Apache-2.0), androidx-main
27cf9a7d5788aa0f5f2d8b6699ce279560daf326: NavigationBarTokens.kt,
@@ -36,9 +45,21 @@
@props([
'label' => null,
'tall' => false,
'hideOnScroll' => false,
])
<nav aria-label="{{ $label ?? __('Main') }}" data-navigation-bar @if ($tall) data-tall @endif {{ $attributes }}>
<nav
aria-label="{{ $label ?? __('Main') }}"
data-navigation-bar
@if ($tall) data-tall @endif
@if ($hideOnScroll)
data-hide-on-scroll
x-data="materialNavigationBar"
x-bind:data-hidden="away ? '' : null"
x-on:focusin="show()"
@endif
{{ $attributes }}
>
<div data-navigation-bar-items>
{{ $slot }}
</div>