Draw the scheme picker without Tailwind

Plan step 36 (navigation group, third batch, last component):
<x-scheme-picker>'s class lists move into resources/css/components/
scheme-picker.css, keyed on data-md-scheme-picker (its legend and
options grid, 2 columns below medium and 4 from it) and
data-md-scheme-picker-option, whose radio inside is read with :has()
for the chosen and the focus states — the input, not the label, is
the real control and carries the ring group.css's own segments keep
the same refinement for. `dark:bg-(--swatch-dark)` becomes
[data-theme='dark'] on the swatch (theme.css's own dark custom
variant reduces to a plain descendant selector here, since the
swatch never carries data-theme itself); the profile's name keeps its
label-large type as a text class on the view, since it is the
caller's own prose.

Hooks renamed: data-scheme-picker to data-md-scheme-picker,
data-scheme-option to data-md-scheme-picker-option, updated in
SchemePickerTest.php and tests/Browser/{ThemeTest,ColourProfilesTest}.php.

Browser tests added (docs/plans/material-3-browser-tests.md): the
contrast switch repaints at once and survives a wire:navigate, and
prefers-contrast: more picks high (Playwright's contrast context
option, the same shape as the existing reducedMotion tests).

This is the last navigation-group component off Tailwind. Every
package component is now drawn without Tailwind; tailwind.css keeps
only tokens/theme.css and tokens/utilities.css, which the showcase
still needs until step 38.

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-15 00:52:12 +02:00
co-authored by Claude Sonnet 5
parent 74cbcd2f44
commit bc284b2b33
7 changed files with 253 additions and 23 deletions
+48 -1
View File
@@ -103,7 +103,7 @@ it('adds a theme-color meta in the painted surface, and follows the theme and th
->assertScript(themeColorIs($profiles['baseline']['dark']['surface']))
->assertScript(pageSurfaceIs($profiles['baseline']['dark']['surface']));
$page->click('#colour [data-scheme-option="rose"]')
$page->click('#colour [data-md-scheme-picker-option="rose"]')
->assertScript(theme('data-scheme', 'rose'))
->assertScript(themeColorIs($profiles['rose']['dark']['surface']))
->assertScript(pageSurfaceIs($profiles['rose']['dark']['surface']));
@@ -114,6 +114,53 @@ it('adds a theme-color meta in the painted surface, and follows the theme and th
->assertNoJavaScriptErrors();
});
it('picks the high contrast level when the operating system prefers it, until the visitor chooses', function () {
visit('/material', ['contrast' => 'more'])
->assertScript(theme('data-contrast', 'high'))
->assertNoJavaScriptErrors();
// Standard is the absence of the attribute (theme-script.blade.php): the stylesheet's plain
// blocks already are that level.
visit('/material', ['contrast' => 'no-preference'])
->assertScript("document.documentElement.hasAttribute('data-contrast') === false");
});
it('repaints the contrast level at once, and it survives a wire:navigate', function () {
Route::middleware('web')->get('/contrast-probe/{page}', fn (string $page) => Blade::render(<<<'BLADE'
<!DOCTYPE html>
<html>
<head>
<x-theme-script />
@vite(config('livewire-material.showcase.vite'))
@livewireStyles
</head>
<body>
<x-theme-toggle mode="contrast" />
<p id="page">This is page {{ $page }}.</p>
<a id="next" href="/contrast-probe/two" wire:navigate>Next</a>
@livewireScripts
</body>
</html>
BLADE, ['page' => $page]));
// on-surface-variant is one of the roles a contrast level moves most (showcase/sections/colour.blade.php).
$ink = "getComputedStyle(document.documentElement).getPropertyValue('--md-sys-color-on-surface-variant').trim()";
$page = themeReady(visit('/contrast-probe/one'));
$standard = $page->script($ink);
$page->click('label:has(input[name="material-contrast"][value="high"])')
->assertScript(theme('data-contrast', 'high'))
->assertScript("{$ink} !== '{$standard}'")
->assertScript("localStorage.getItem('material-contrast') === 'high'");
$page->click('#next')
->assertSeeIn('#page', 'This is page two.')
->assertScript(theme('data-contrast', 'high'))
->assertScript("{$ink} !== '{$standard}'")
->assertNoJavaScriptErrors();
});
it('repaints the page\'s own theme-color meta, and the next page\'s after wire:navigate', function () {
config(['livewire-material.theme.meta' => true]);