Add colour profiles
An application lists named schemes in livewire-material.profiles, and material:scheme without a seed generates them all into one stylesheet: the default profile as the plain blocks, every profile under <html data-scheme>, with descendant selectors so a nested data-theme panel keeps the page's profile. The JSON keeps the 1.0 top level. Scheme::resolveProfileUsing() lets the application name the active profile; it is asked on every use and falls back to the default for an unknown name or a resolver that throws. The head script writes it before the first paint and keeps it through wire:navigate, and the mail theme and the error pages' fallback draw it. <x-scheme-picker> chooses one and previews it through $store.theme.previewScheme(); the showcase previews the Workbench's profiles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
This commit is contained in:
co-authored by
Claude Opus 5
parent
7798352cfc
commit
fb42f004b1
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
use NoNameWeb\LivewireMaterial\Support\Scheme;
|
||||
|
||||
/**
|
||||
* A scheme file with colour profiles, as `material:scheme` writes one from config.
|
||||
*
|
||||
* @param array<string, string> $primaries profile => light primary
|
||||
*/
|
||||
function writeProfiles(string $path, array $primaries, string $default): void
|
||||
{
|
||||
$profiles = collect($primaries)->map(fn (string $primary, string $name): array => [
|
||||
'label' => Str::headline($name),
|
||||
'seed' => $primary,
|
||||
'variant' => 'vibrant',
|
||||
'spec' => '2025',
|
||||
'contrast' => 0,
|
||||
'light' => ['primary' => $primary, 'surface' => '#fafafa'],
|
||||
'dark' => ['primary' => '#eeeeee', 'surface' => '#111111'],
|
||||
])->all();
|
||||
|
||||
File::put($path, json_encode([...collect($profiles[$default])->except('label')->all(), 'default' => $default, 'profiles' => $profiles]));
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
$this->path = sys_get_temp_dir().'/material-profiles-'.Str::random(8).'.json';
|
||||
writeProfiles($this->path, ['indigo' => '#4f46e5', 'teal' => '#00897b', 'rose' => '#c2185b'], 'indigo');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Scheme::resolveProfileUsing(null);
|
||||
File::delete($this->path);
|
||||
});
|
||||
|
||||
it('lists the generated profiles with their labels and roles, filling missing roles from the default', function () {
|
||||
$profiles = Scheme::profiles($this->path);
|
||||
|
||||
expect(array_keys($profiles))->toBe(['indigo', 'teal', 'rose'])
|
||||
->and($profiles['teal']['label'])->toBe('Teal')
|
||||
->and($profiles['teal']['light']['primary'])->toBe('#00897b')
|
||||
->and($profiles['teal']['light'])->toHaveKey('on-surface-variant')
|
||||
->and(Scheme::profiles(sys_get_temp_dir().'/missing-scheme.json'))->toBe([]);
|
||||
});
|
||||
|
||||
it('follows the resolver to the active profile, and falls back to the default', function () {
|
||||
expect(Scheme::profile($this->path))->toBe('indigo');
|
||||
|
||||
Scheme::resolveProfileUsing(fn (): string => 'teal');
|
||||
expect(Scheme::profile($this->path))->toBe('teal')
|
||||
->and(Scheme::light($this->path)['primary'])->toBe('#00897b')
|
||||
->and(Scheme::load($this->path)['dark']['surface'])->toBe('#111111');
|
||||
|
||||
Scheme::resolveProfileUsing(fn (): string => 'ocean');
|
||||
expect(Scheme::profile($this->path))->toBe('indigo');
|
||||
|
||||
Scheme::resolveProfileUsing(fn (): ?string => null);
|
||||
expect(Scheme::profile($this->path))->toBe('indigo');
|
||||
|
||||
Scheme::resolveProfileUsing(fn () => throw new RuntimeException('The database is down.'));
|
||||
expect(Scheme::profile($this->path))->toBe('indigo');
|
||||
});
|
||||
|
||||
it('loads a profile by name, whatever is active', function () {
|
||||
Scheme::resolveProfileUsing(fn (): string => 'teal');
|
||||
|
||||
expect(Scheme::light($this->path, 'rose')['primary'])->toBe('#c2185b')
|
||||
->and(Scheme::light($this->path, 'ocean')['primary'])->toBe('#00897b');
|
||||
});
|
||||
|
||||
it('has no profile for a single scheme, and loads it as before', function () {
|
||||
File::put($this->path, json_encode(['light' => ['primary' => '#123456'], 'dark' => ['primary' => '#abcdef']]));
|
||||
Scheme::resolveProfileUsing(fn (): string => 'teal');
|
||||
|
||||
expect(Scheme::profile($this->path))->toBeNull()
|
||||
->and(Scheme::profiles($this->path))->toBe([])
|
||||
->and(Scheme::light($this->path)['primary'])->toBe('#123456')
|
||||
->and(Scheme::light($this->path))->toHaveKey('on-surface');
|
||||
});
|
||||
Reference in New Issue
Block a user