Add the Material 3 Expressive foundation
tests / lint (push) Successful in 1m0s
tests / feature (8.4) (push) Successful in 1m0s
tests / feature (8.5) (push) Successful in 1m1s
tests / browser (safari, webkit) (push) Successful in 1m59s
tests / browser (chrome, chromium) (push) Successful in 1m49s
tests / browser (firefox, firefox) (push) Successful in 1m54s
tests / lint (push) Successful in 1m0s
tests / feature (8.4) (push) Successful in 1m0s
tests / feature (8.5) (push) Successful in 1m1s
tests / browser (safari, webkit) (push) Successful in 1m59s
tests / browser (chrome, chromium) (push) Successful in 1m49s
tests / browser (firefox, firefox) (push) Successful in 1m54s
Colour, shape, type, elevation and motion as tokens and Tailwind utilities; `php artisan material:scheme`, which generates an app's colour roles with Google's material-color-utilities (spec 2025); the theme head script with light, dark and system and its Alpine store; Google Sans Flex; every Material Symbol (4,135, outlined and filled) drawn by <x-icon> without blade-icons; all 35 M3 Expressive shapes, ported from androidx, as <x-shape>; the x-figure directive; the Toasts concern; DesignGuard for applications' tests; and a showcase with every token, both themes side by side and an icon search. Colour utilities are `@theme inline`, so a section with its own data-theme repaints; without it they resolve once on :root. 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
9b53891a8e
commit
b48e879254
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
function packageCss(string $path = ''): string
|
||||
{
|
||||
return __DIR__.'/../../resources/css'.($path === '' ? '' : '/'.$path);
|
||||
}
|
||||
|
||||
/**
|
||||
* The custom properties one selector's block declares.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
function declarations(string $css, string $selector): array
|
||||
{
|
||||
preg_match_all('/(--[\w-]+):\s*([^;]+);/', Str::of($css)->after($selector)->before('}')->toString(), $matches);
|
||||
|
||||
return array_combine($matches[1], $matches[2]);
|
||||
}
|
||||
|
||||
it('ships a default scheme that declares every role in both themes', function () {
|
||||
$scheme = json_decode(File::get(packageCss('tokens/scheme.json')), true);
|
||||
$css = File::get(packageCss('tokens/scheme.css'));
|
||||
|
||||
$light = declarations($css, "[data-theme='light']");
|
||||
$dark = declarations($css, "[data-theme='dark']");
|
||||
|
||||
expect(array_keys($light))->toEqual(array_keys($dark))
|
||||
->and(count($light))->toBe(count($scheme['light']))
|
||||
->and(Str::of($css)->after("[data-theme='light']")->before('}')->toString())->toContain('color-scheme: light;')
|
||||
->and(Str::of($css)->after("[data-theme='dark']")->before('}')->toString())->toContain('color-scheme: dark;');
|
||||
|
||||
foreach ($scheme['light'] as $role => $hex) {
|
||||
expect($light["--md-sys-color-{$role}"])->toBe($hex)
|
||||
->and($dark["--md-sys-color-{$role}"])->toBe($scheme['dark'][$role]);
|
||||
}
|
||||
});
|
||||
|
||||
it('turns every role into a Tailwind colour', function () {
|
||||
$roles = array_keys(json_decode(File::get(packageCss('tokens/scheme.json')), true)['light']);
|
||||
$theme = File::get(packageCss('tokens/theme.css'));
|
||||
|
||||
foreach ($roles as $role) {
|
||||
expect($theme)->toContain("--color-{$role}: var(--md-sys-color-{$role});");
|
||||
}
|
||||
});
|
||||
|
||||
it('resolves colour utilities on the element, so a nested data-theme repaints them', function () {
|
||||
$theme = File::get(packageCss('tokens/theme.css'));
|
||||
|
||||
preg_match_all('/@theme inline \{(.*?)\}/s', $theme, $inline);
|
||||
|
||||
expect(implode("\n", $inline[1]))
|
||||
->toContain('--color-primary: var(--md-sys-color-primary);')
|
||||
->toContain('--color-body: var(--md-sys-color-on-surface-variant);')
|
||||
->not->toMatch('/--[\w-]+:\s*#/');
|
||||
});
|
||||
|
||||
it('leaves the choice of theme to the head script, never to a media query', function () {
|
||||
foreach (File::allFiles(packageCss()) as $file) {
|
||||
expect($file->getContents())->not->toContain('prefers-color-scheme');
|
||||
}
|
||||
});
|
||||
|
||||
it('makes every motion token instant under reduced motion', function () {
|
||||
$motion = File::get(packageCss('tokens/motion.css'));
|
||||
$reduced = Str::of($motion)->after('prefers-reduced-motion: reduce')->toString();
|
||||
|
||||
preg_match_all('/(--md-sys-motion-[\w-]*duration[\w-]*):/', Str::before($motion, '@media'), $durations);
|
||||
|
||||
expect($durations[1])->not->toBeEmpty();
|
||||
|
||||
foreach ($durations[1] as $duration) {
|
||||
expect($reduced)->toContain("{$duration}: 0ms;");
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user