SchemeCommand::levels()/selectors()/blocks() move to a new SchemeStylesheet, and Scheme gains forStylesheet(), which resolves a scheme (and every profile) at all three contrast levels together. Plan step 40's fallback needs to draw the same colours material:scheme writes, without duplicating its selector logic. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
88 lines
3.7 KiB
PHP
88 lines
3.7 KiB
PHP
<?php
|
|
|
|
namespace NoNameWeb\LivewireMaterial\Support;
|
|
|
|
/**
|
|
* The selector shape a Material 3 colour scheme is written in: the standard level on `:root` and
|
|
* `[data-theme]` (or, under a prefix, repeated so it still reaches a theme set on `<html>` or on a
|
|
* nested panel), the medium and high contrast levels under `[data-contrast]` beside it, and — when
|
|
* a caller gives one — every colour profile again under its own prefix (`[data-scheme='name']`).
|
|
*
|
|
* `SchemeCommand::handle()` writes this to the application's `material-scheme.css`; plan step 40
|
|
* moved the shape itself here so `ErrorPage::fallbackStyles()` can draw the same scheme, from
|
|
* `Scheme::forStylesheet()` rather than a generated file, without a second copy of the selectors to
|
|
* keep in step with the command's.
|
|
*/
|
|
final class SchemeStylesheet
|
|
{
|
|
/**
|
|
* M3's two contrast levels beyond standard, keyed on `<html data-contrast>`; standard is the
|
|
* scheme's own level and has no attribute of its own.
|
|
*
|
|
* @var list<string>
|
|
*/
|
|
public const array LEVELS = ['medium', 'high'];
|
|
|
|
/**
|
|
* One scheme, whole: the standard level first, under `$prefix` (or `:root`/`[data-theme]`
|
|
* without one), then M3's medium and high levels under `[data-contrast]` beside it. A level's
|
|
* blocks are the standard ones with one more attribute, so they outrank them wherever both
|
|
* match, and the light blocks of a level come before its dark ones as in every other pair here.
|
|
*
|
|
* @param array{contrast: array<string, array{light: array<string, string>, dark: array<string, string>}>, light: array<string, string>, dark: array<string, string>} $scheme
|
|
*/
|
|
public static function levels(array $scheme, string $prefix = ''): string
|
|
{
|
|
[$light, $dark] = self::selectors($prefix);
|
|
|
|
$css = self::blocks($light, $dark, $scheme);
|
|
|
|
foreach (self::LEVELS as $level) {
|
|
[$light, $dark] = self::selectors($prefix."[data-contrast='{$level}']");
|
|
|
|
/** @var array{light: array<string, string>, dark: array<string, string>} $roles */
|
|
$roles = $scheme['contrast'][$level];
|
|
|
|
$css .= "\n".self::blocks($light, $dark, $roles);
|
|
}
|
|
|
|
return $css;
|
|
}
|
|
|
|
/**
|
|
* The light and the dark selectors of one block, under the given prefix. Without a prefix the
|
|
* plain blocks stand on `:root` and on the attribute alone; with one, the prefix is on `<html>`
|
|
* and the theme may be on `<html>` or on a panel inside it, so both forms are written.
|
|
*
|
|
* @return array{list<string>, list<string>}
|
|
*/
|
|
public static function selectors(string $prefix): array
|
|
{
|
|
if ($prefix === '') {
|
|
return [[':root', "[data-theme='light']"], ["[data-theme='dark']"]];
|
|
}
|
|
|
|
return [
|
|
[$prefix, "{$prefix}[data-theme='light']", "{$prefix} [data-theme='light']"],
|
|
["{$prefix}[data-theme='dark']", "{$prefix} [data-theme='dark']"],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* A light and a dark block of roles under the given selectors.
|
|
*
|
|
* @param list<string> $light
|
|
* @param list<string> $dark
|
|
* @param array{light: array<string, string>, dark: array<string, string>} $scheme
|
|
*/
|
|
public static function blocks(array $light, array $dark, array $scheme): string
|
|
{
|
|
$roles = fn (array $roles): string => collect($roles)
|
|
->map(fn (string $hex, string $role): string => " --md-sys-color-{$role}: {$hex};")
|
|
->implode("\n");
|
|
|
|
return implode(",\n", $light)." {\n color-scheme: light;\n\n".$roles($scheme['light'])."\n}\n\n"
|
|
.implode(",\n", $dark)." {\n color-scheme: dark;\n\n".$roles($scheme['dark'])."\n}\n";
|
|
}
|
|
}
|