Merge branch 'worktree-agent-a9537eabbf38e0608'

# Conflicts:
#	resources/views/components/scheme-picker.blade.php
#	resources/views/components/theme-script.blade.php
#	resources/views/showcase/sections/colour.blade.php
#	tests/Feature/Components/ThemeScriptTest.php
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 05:43:37 +02:00
23 changed files with 4086 additions and 408 deletions
@@ -8,7 +8,10 @@
(`$store.theme.previewScheme`); storing it and telling `Scheme::resolveProfileUsing()` is
the application's. The dots are the only colours not drawn from tokens: they show other
profiles than the page's, so they are custom properties set inline from the scheme file's
checked hexes, light or dark with the page. Without profiles it renders nothing.
checked hexes, light or dark with the page. The inline pair is the standard contrast level,
which is what the server can know; Alpine swaps in the level on screen
(`$store.theme.resolvedContrast`) so a dot never promises a colour the page would not paint.
Without profiles it renders nothing.
Props: `label`, `hint`, `name` (needed with `x-model`), `profiles` (default: every generated
profile). A validation message for the bound property replaces the hint. --}}
@@ -26,10 +29,32 @@
$name ??= $model ?: 'scheme';
$errorKey = $model ?: (filled($attributes->get('name')) ? (string) $attributes->get('name') : null);
$messages = $errorKey !== null && isset($errors) ? \Illuminate\Support\Arr::flatten($errors->get($errorKey)) : [];
$roles = ['primary', 'secondary', 'tertiary'];
// profile => level => theme => role => hex, for the dots. Every level is filled — a profile
// the caller passed in by hand, or a scheme file without the levels, repeats its standard
// colours — so the binding below never asks whether a level is there.
$swatches = [];
foreach ($profiles === [] ? [] : \NoNameWeb\LivewireMaterial\Support\Scheme::LEVELS as $level) {
$generated = $level === 'standard' ? [] : \NoNameWeb\LivewireMaterial\Support\Scheme::profiles(contrast: $level);
foreach ($profiles as $profile => $scheme) {
foreach (['light', 'dark'] as $theme) {
foreach ($roles as $role) {
$swatches[$profile][$level][$theme][$role] = ($generated[$profile] ?? $scheme)[$theme][$role];
}
}
}
}
@endphp
@if ($profiles !== [])
<fieldset x-data data-scheme-picker {{ $attributes->whereDoesntStartWith(['wire:model', 'x-model'])->except('name')->class('min-w-0') }}>
<fieldset
x-data="{ swatches: @js($swatches) }"
data-scheme-picker
{{ $attributes->whereDoesntStartWith(['wire:model', 'x-model'])->except('name')->class('min-w-0') }}
>
@if (filled($label))
<legend class="mb-2 type-label-lg text-on-surface-variant">{{ $label }}</legend>
@endif
@@ -50,9 +75,13 @@
/>
<span class="flex shrink-0 -space-x-1.5" aria-hidden="true">
@foreach (['primary', 'secondary', 'tertiary'] as $role)
@foreach ($roles as $role)
<span
style="--swatch-light: {{ $scheme['light'][$role] }}; --swatch-dark: {{ $scheme['dark'][$role] }}"
x-bind:style="{
'--swatch-light': swatches['{{ $profile }}'][$store.theme.resolvedContrast].light['{{ $role }}'],
'--swatch-dark': swatches['{{ $profile }}'][$store.theme.resolvedContrast].dark['{{ $role }}'],
}"
class="size-5 rounded-corner-full bg-(--swatch-light) ring-2 ring-surface-container-low in-has-checked:ring-secondary-container dark:bg-(--swatch-dark)"
></span>
@endforeach