Draw the toolbar without Tailwind

Plan step 36 (navigation group, second stream): <x-toolbar>'s class
lists move into resources/css/components/toolbar.css, keyed on
data-md-toolbar (data-md-variant/-vertical/-vibrant/-rounded/
-toolbar-place) and data-md-toolbar-group/-toolbar-fab. The docked
form with a FAB (N-02's --material-bottom-bar clearance), the vertical
24dp margin (N-12), the standard toolbar's primary icon buttons
(N-13), and the rounded large-screen form with dividers from 840px
were all already correct; only their hooks, units and layer needed to
change. The file no longer sits outside every layer: button.css and
fab.css now draw their own ink through data-md-* attributes rather
than a Tailwind utility, so a toolbar's more specific selector always
beats their single base color/background-color/box-shadow declaration
without needing to escape the cascade layers.

Sizes stay px (64px row, 40px divider); spacing goes through the
measurement tokens where one matches the value (4px leading/trailing
gap, 16/32px docked spread, 8px floating ends, 16/24px placed
margins).

Hooks renamed: every unprefixed data-toolbar* attribute to data-md-*,
updated in tests/Feature/Components/{AppBarTest,ToolbarTest}.php and
tests/Browser/BarsTest.php. toolbar.js needed no change: it reads the
bar through $root and role="toolbar", not by hook name.

Carry-over from the app-bar step: layout/pane.css now imports
app-bar.css and button.css for the pane's own top app bar and back
button, now that both are rewritten. That import crosses into
material.components for the first time from a layout file, so
tests/Feature/StylesheetsTest.php's two layout-shape checks now skip
non-`layout/` files reached through it — each already covered by its
own group's stylesheet test.

Added the owed browser tests (docs/plans/material-3-browser-tests.md):
a docked toolbar clear of the navigation bar below medium, and the
rounded form with its divider height from 840px, clear of the
window's edges.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 23:27:26 +02:00
co-authored by Claude Sonnet 5
parent b7641a35c2
commit 9c134b2dc7
10 changed files with 322 additions and 217 deletions
+56
View File
@@ -246,3 +246,59 @@ it('runs a wire:click action from either the icon button or its overflow menu it
->click('[role="menuitem"]:has-text("Star")')
->assertSeeIn('#stars', '2');
});
function toolbarScaffoldProbe()
{
Route::middleware('web')->get('/toolbar-scaffold-probe', fn () => Blade::render(<<<'BLADE'
<!DOCTYPE html>
<html>
<head>
<x-theme-script />
@vite(config('livewire-material.showcase.vite'))
@livewireStyles
</head>
<body class="bg-surface">
<x-scaffold :destinations="[
['title' => 'Inbox', 'icon' => 'inbox', 'url' => '/toolbar-scaffold-probe', 'active' => true],
['title' => 'Sent', 'icon' => 'send', 'url' => '/toolbar-scaffold-probe'],
]">
<x-toolbar variant="docked" rounded place="bottom" label="Formatting" id="toolbar">
<x-button icon="format_bold" tooltip="Bold" />
<x-divider vertical />
<x-button icon="format_italic" tooltip="Italic" />
</x-toolbar>
<div style="height: 200vh"></div>
</x-scaffold>
@livewireScripts
</body>
</html>
BLADE));
return visit('/toolbar-scaffold-probe')->waitForEvent('networkidle')
->assertScript("document.readyState === 'complete' && typeof window.Alpine !== 'undefined' && typeof window.Livewire !== 'undefined'");
}
it('keeps a docked toolbar clear of the navigation bar below medium', function () {
toolbarScaffoldProbe()->resize(599, 800)
->assertScript("getComputedStyle(document.querySelector('[data-app-shell-bar]')).display !== 'none'")
// A docked toolbar at place="bottom" sits above --material-bottom-bar, never on top of it.
->assertScript("document.querySelector('#toolbar').getBoundingClientRect().bottom <= document.querySelector('[data-app-shell-bar]').getBoundingClientRect().top")
->assertNoJavaScriptErrors();
});
it('rounds a docked toolbar and its divider from 840px, clear of the window\'s edges', function () {
$page = toolbarScaffoldProbe()->resize(800, 800);
$page->assertScript("getComputedStyle(document.querySelector('#toolbar')).borderRadius === '0px'")
->assertScript("Math.round(document.querySelector('#toolbar').getBoundingClientRect().left) === 0")
->assertScript("Math.round(document.querySelector('#toolbar').getBoundingClientRect().right) === Math.round(window.innerWidth)");
$page->resize(1024, 800)
->assertScript("getComputedStyle(document.querySelector('#toolbar')).borderRadius !== '0px'")
->assertScript("document.querySelector('#toolbar').getBoundingClientRect().left >= 16")
->assertScript("window.innerWidth - document.querySelector('#toolbar').getBoundingClientRect().right >= 16")
->assertScript("window.innerHeight - document.querySelector('#toolbar').getBoundingClientRect().bottom >= 16")
->assertScript("getComputedStyle(document.querySelector('#toolbar [role=\"separator\"]')).height === '40px'")
->assertNoJavaScriptErrors();
});