Give success, warning and info the 2025 spec and three contrast levels
The three state colours are DynamicColors on their own tonal palette now, built exactly as Google builds error/on-error/error-container/on-error-container in both specs, so the scheme's contrast level, dark tones, spec version and platform reach them as they reach every other role; --harmonize (and a profile's harmonize) pulls each source towards the seed, off by default. material:scheme also writes M3's medium (0.5) and high (1.0) levels for both themes and every profile, keyed on data-contrast and never on a media query, and --contrast now moves the standard block alone. Scheme::load() takes the level; the mail theme stays on standard. Plan: docs/plans/material-3-alignment.md, steps 6 and 7 (core C1, C2, C15). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
b1fc0c9cfa
commit
4263982369
+50
-21
@@ -18,6 +18,11 @@ use Throwable;
|
||||
* the role existed) is taken from the default, and a value that is not a #rrggbb hex is
|
||||
* refused, so nothing but a colour ever reaches a stylesheet.
|
||||
*
|
||||
* The roles at the top level are the standard contrast level; M3's medium and high levels sit
|
||||
* under `contrast` and are asked for by name (`load($path, $profile, 'high')`). A file written
|
||||
* before the levels existed — or one whose level is missing a role — falls back to the standard
|
||||
* roles, so a mail or an error page never draws a hole.
|
||||
*
|
||||
* A file generated from `livewire-material.profiles` holds every profile under `profiles`, its
|
||||
* default under `default`, and the default's roles at the top level as a single scheme does. Which
|
||||
* profile is active is the application's to say, through resolveProfileUsing(); the resolver is
|
||||
@@ -30,6 +35,14 @@ use Throwable;
|
||||
*/
|
||||
class Scheme
|
||||
{
|
||||
/**
|
||||
* M3's contrast levels, as `material:scheme` writes them and as <html data-contrast> names
|
||||
* them. `standard` is the scheme's own level and has no attribute.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
public const LEVELS = ['standard', 'medium', 'high'];
|
||||
|
||||
/**
|
||||
* @var (Closure(): ?string)|null
|
||||
*/
|
||||
@@ -46,14 +59,15 @@ class Scheme
|
||||
}
|
||||
|
||||
/**
|
||||
* The roles to draw: the given profile's, the active profile's, or the single scheme's.
|
||||
* The roles to draw: the given profile's, the active profile's, or the single scheme's, at
|
||||
* the given contrast level (`standard`, `medium` or `high`; anything else is standard).
|
||||
*
|
||||
* @return array{light: array<string, string>, dark: array<string, string>}
|
||||
*/
|
||||
public static function load(?string $path = null, ?string $profile = null): array
|
||||
public static function load(?string $path = null, ?string $profile = null, string $contrast = 'standard'): array
|
||||
{
|
||||
$data = static::data($path);
|
||||
$profiles = static::profilesFrom($data);
|
||||
$profiles = static::profilesFrom($data, $contrast);
|
||||
|
||||
if ($profiles !== []) {
|
||||
$name = $profile !== null && isset($profiles[$profile]) ? $profile : static::activeFrom($data, $profiles);
|
||||
@@ -61,13 +75,7 @@ class Scheme
|
||||
return ['light' => $profiles[$name]['light'], 'dark' => $profiles[$name]['dark']];
|
||||
}
|
||||
|
||||
$default = static::defaultScheme();
|
||||
$scheme = ['light' => static::roles($data['light'] ?? null), 'dark' => static::roles($data['dark'] ?? null)];
|
||||
|
||||
return [
|
||||
'light' => [...$default['light'], ...$scheme['light']],
|
||||
'dark' => [...$default['dark'], ...$scheme['dark']],
|
||||
];
|
||||
return static::scheme($data, $contrast);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,9 +83,9 @@ class Scheme
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function light(?string $path = null, ?string $profile = null): array
|
||||
public static function light(?string $path = null, ?string $profile = null, string $contrast = 'standard'): array
|
||||
{
|
||||
return static::load($path, $profile)['light'];
|
||||
return static::load($path, $profile, $contrast)['light'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,9 +93,9 @@ class Scheme
|
||||
*
|
||||
* @return array<string, array{label: string, light: array<string, string>, dark: array<string, string>}>
|
||||
*/
|
||||
public static function profiles(?string $path = null): array
|
||||
public static function profiles(?string $path = null, string $contrast = 'standard'): array
|
||||
{
|
||||
return static::profilesFrom(static::data($path));
|
||||
return static::profilesFrom(static::data($path), $contrast);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,13 +124,12 @@ class Scheme
|
||||
* @param array<mixed> $data
|
||||
* @return array<string, array{label: string, light: array<string, string>, dark: array<string, string>}>
|
||||
*/
|
||||
protected static function profilesFrom(array $data): array
|
||||
protected static function profilesFrom(array $data, string $contrast = 'standard'): array
|
||||
{
|
||||
if (! is_array($data['profiles'] ?? null)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$default = static::defaultScheme();
|
||||
$profiles = [];
|
||||
|
||||
foreach ($data['profiles'] as $name => $profile) {
|
||||
@@ -132,14 +139,32 @@ class Scheme
|
||||
|
||||
$profiles[$name] = [
|
||||
'label' => is_string($profile['label'] ?? null) ? $profile['label'] : Str::headline($name),
|
||||
'light' => [...$default['light'], ...static::roles($profile['light'] ?? null)],
|
||||
'dark' => [...$default['dark'], ...static::roles($profile['dark'] ?? null)],
|
||||
...static::scheme($profile, $contrast),
|
||||
];
|
||||
}
|
||||
|
||||
return $profiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* One scheme's roles at one contrast level: the level's own, over the standard ones it does
|
||||
* not name, over the package's default. A scheme file written before the levels existed has
|
||||
* only the standard roles, and draws them at every level rather than nothing.
|
||||
*
|
||||
* @param array<mixed> $data
|
||||
* @return array{light: array<string, string>, dark: array<string, string>}
|
||||
*/
|
||||
protected static function scheme(array $data, string $contrast): array
|
||||
{
|
||||
$default = static::defaultScheme($contrast);
|
||||
$level = is_array($data['contrast'][$contrast] ?? null) ? $data['contrast'][$contrast] : [];
|
||||
|
||||
return [
|
||||
'light' => [...$default['light'], ...static::roles($data['light'] ?? null), ...static::roles($level['light'] ?? null)],
|
||||
'dark' => [...$default['dark'], ...static::roles($data['dark'] ?? null), ...static::roles($level['dark'] ?? null)],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $data
|
||||
* @param non-empty-array<string, mixed> $profiles
|
||||
@@ -162,15 +187,19 @@ class Scheme
|
||||
}
|
||||
|
||||
/**
|
||||
* The package's own scheme, which fills any role a file lacks.
|
||||
* The package's own scheme at one contrast level, which fills any role a file lacks.
|
||||
*
|
||||
* @return array{light: array<string, string>, dark: array<string, string>}
|
||||
*/
|
||||
protected static function defaultScheme(): array
|
||||
protected static function defaultScheme(string $contrast = 'standard'): array
|
||||
{
|
||||
$data = json_decode((string) file_get_contents(dirname(__DIR__, 2).'/resources/css/tokens/scheme.json'), true);
|
||||
$level = is_array($data['contrast'][$contrast] ?? null) ? $data['contrast'][$contrast] : [];
|
||||
|
||||
return ['light' => static::roles($data['light'] ?? null), 'dark' => static::roles($data['dark'] ?? null)];
|
||||
return [
|
||||
'light' => [...static::roles($data['light'] ?? null), ...static::roles($level['light'] ?? null)],
|
||||
'dark' => [...static::roles($data['dark'] ?? null), ...static::roles($level['dark'] ?? null)],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user