Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* BEM-Typen für CSS-Klassen
*/
enum BemType: string
{
case BLOCK = 'block';
case ELEMENT = 'element';
case MODIFIER = 'modifier';
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Enum für Color Formate
*/
enum ColorFormat: string
{
case HEX = 'hex';
case RGB = 'rgb';
case RGBA = 'rgba';
case HSL = 'hsl';
case HSLA = 'hsla';
case OKLCH = 'oklch';
case NAMED = 'named';
case CUSTOM_PROPERTY = 'custom_property';
}

View File

@@ -0,0 +1,185 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Value Object für erkannte Component-Patterns
*/
final readonly class ComponentPattern
{
public function __construct(
public ComponentPatternType $type,
public string $name,
public array $classes,
public array $metadata = []
) {
}
/**
* Factory für BEM Component
*/
public static function createBem(
string $blockName,
array $elements = [],
array $modifiers = [],
array $classes = []
): self {
return new self(
type: ComponentPatternType::BEM,
name: $blockName,
classes: $classes,
metadata: [
'block' => $blockName,
'elements' => $elements,
'modifiers' => $modifiers,
'element_count' => count($elements),
'modifier_count' => count($modifiers),
]
);
}
/**
* Factory für Utility Component
*/
public static function createUtility(string $category, array $classes = []): self
{
return new self(
type: ComponentPatternType::UTILITY,
name: $category,
classes: $classes,
metadata: [
'category' => $category,
'class_count' => count($classes),
]
);
}
/**
* Factory für Traditional Component
*/
public static function createComponent(string $name, array $classes = []): self
{
return new self(
type: ComponentPatternType::COMPONENT,
name: $name,
classes: $classes,
metadata: [
'component_type' => $name,
'class_count' => count($classes),
]
);
}
/**
* Gibt alle Klassennamen als Strings zurück
*/
public function getClassNames(): array
{
return array_map(
fn (CssClassName $class) => $class->name,
$this->classes
);
}
/**
* Prüft ob Pattern bestimmte Klasse enthält
*/
public function hasClass(string $className): bool
{
foreach ($this->classes as $class) {
if ($class instanceof CssClassName && $class->name === $className) {
return true;
}
}
return false;
}
/**
* Analysiert Pattern-Komplexität
*/
public function getComplexity(): string
{
$classCount = count($this->classes);
return match(true) {
$classCount <= 3 => 'simple',
$classCount <= 10 => 'moderate',
default => 'complex'
};
}
/**
* Gibt Pattern-spezifische Informationen zurück
*/
public function getPatternInfo(): array
{
return match($this->type) {
ComponentPatternType::BEM => [
'type' => 'BEM',
'block' => $this->metadata['block'] ?? '',
'elements' => $this->metadata['elements'] ?? [],
'modifiers' => $this->metadata['modifiers'] ?? [],
'structure' => $this->getBemStructure(),
],
ComponentPatternType::UTILITY => [
'type' => 'Utility Classes',
'category' => $this->metadata['category'] ?? '',
'utilities' => $this->getClassNames(),
],
ComponentPatternType::COMPONENT => [
'type' => 'Traditional Component',
'component_type' => $this->metadata['component_type'] ?? '',
'classes' => $this->getClassNames(),
]
};
}
/**
* Analysiert BEM-Struktur
*/
private function getBemStructure(): array
{
$elements = $this->metadata['elements'] ?? [];
$modifiers = $this->metadata['modifiers'] ?? [];
return [
'has_elements' => ! empty($elements),
'has_modifiers' => ! empty($modifiers),
'element_count' => count($elements),
'modifier_count' => count($modifiers),
'completeness' => $this->getBemCompleteness($elements, $modifiers),
];
}
private function getBemCompleteness(array $elements, array $modifiers): string
{
if (empty($elements) && empty($modifiers)) {
return 'block_only';
}
if (! empty($elements) && empty($modifiers)) {
return 'block_with_elements';
}
if (empty($elements) && ! empty($modifiers)) {
return 'block_with_modifiers';
}
return 'full_bem';
}
public function toArray(): array
{
return [
'type' => $this->type->value,
'name' => $this->name,
'classes' => $this->getClassNames(),
'complexity' => $this->getComplexity(),
'metadata' => $this->metadata,
'pattern_info' => $this->getPatternInfo(),
];
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Enum für Component Pattern Typen
*/
enum ComponentPatternType: string
{
case BEM = 'bem';
case UTILITY = 'utility';
case COMPONENT = 'component';
}

View File

@@ -0,0 +1,192 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Repräsentiert eine CSS-Klasse
*/
final readonly class CssClass
{
public function __construct(
public string $name
) {
}
public static function fromSelector(string $selector): self
{
// Remove the leading dot from class selector
$className = ltrim($selector, '.');
return new self($className);
}
public function isBemBlock(): bool
{
// BEM Block: doesn't contain __ or --
return ! str_contains($this->name, '__') && ! str_contains($this->name, '--');
}
public function isBemElement(): bool
{
// BEM Element: contains __ but not --
return str_contains($this->name, '__') && ! str_contains($this->name, '--');
}
public function isBemModifier(): bool
{
// BEM Modifier: contains -- (and possibly __)
return str_contains($this->name, '--');
}
public function isUtilityClass(): bool
{
// Common utility class patterns
$utilityPatterns = [
'/^(text|bg|p|m|pt|pb|pl|pr|mt|mb|ml|mr|w|h|flex|grid|hidden|block|inline)-/',
'/^(sm|md|lg|xl):|:/',
'/^(hover|focus|active|disabled):/',
];
foreach ($utilityPatterns as $pattern) {
if (preg_match($pattern, $this->name)) {
return true;
}
}
return false;
}
public function getBemBlock(): ?string
{
if ($this->isBemBlock()) {
return $this->name;
}
// Extract block from element or modifier
if (str_contains($this->name, '__')) {
return explode('__', $this->name)[0];
}
if (str_contains($this->name, '--')) {
return explode('--', $this->name)[0];
}
return null;
}
public function getBemElement(): ?string
{
if (! $this->isBemElement()) {
return null;
}
$parts = explode('__', $this->name);
if (count($parts) >= 2) {
$element = $parts[1];
// Remove modifier if present
if (str_contains($element, '--')) {
$element = explode('--', $element)[0];
}
return $element;
}
return null;
}
public function getBemModifier(): ?string
{
if (! $this->isBemModifier()) {
return null;
}
$parts = explode('--', $this->name);
if (count($parts) >= 2) {
return $parts[1];
}
return null;
}
public function getUtilityCategory(): ?string
{
$categoryPatterns = [
'spacing' => '/^[mp][trblxy]?-\d+$/',
'sizing' => '/^[wh]-\d+$/',
'typography' => '/^text-(xs|sm|base|lg|xl|\d+xl|center|left|right)$/',
'color' => '/^(text|bg|border)-(red|blue|green|gray|yellow|purple|pink|indigo)-\d+$/',
'display' => '/^(block|inline|flex|grid|hidden)$/',
'flexbox' => '/^(justify|items|self)-(start|end|center|between|around)$/',
];
foreach ($categoryPatterns as $category => $pattern) {
if (preg_match($pattern, $this->name)) {
return $category;
}
}
return null;
}
public function getComponentType(): ?string
{
$componentPatterns = [
'button' => ['btn', 'button'],
'card' => ['card'],
'modal' => ['modal', 'dialog'],
'form' => ['form', 'input', 'select', 'textarea'],
'navigation' => ['nav', 'navbar', 'menu'],
'table' => ['table', 'thead', 'tbody', 'tr', 'td', 'th'],
'alert' => ['alert', 'message', 'notification'],
'badge' => ['badge', 'tag', 'chip'],
'dropdown' => ['dropdown', 'select'],
'tabs' => ['tab', 'tabs'],
'accordion' => ['accordion', 'collapse'],
'breadcrumb' => ['breadcrumb'],
'pagination' => ['pagination', 'pager'],
];
foreach ($componentPatterns as $componentType => $patterns) {
foreach ($patterns as $pattern) {
if (str_starts_with($this->name, $pattern) || str_contains($this->name, $pattern)) {
return $componentType;
}
}
}
return null;
}
public function getNamingConvention(): string
{
if ($this->isBemBlock() || $this->isBemElement() || $this->isBemModifier()) {
return 'bem';
}
if (preg_match('/^[a-z][a-zA-Z0-9]*$/', $this->name)) {
return 'camelCase';
}
if (preg_match('/^[a-z][a-z0-9-]*$/', $this->name)) {
return 'kebab-case';
}
if (preg_match('/^[a-z][a-z0-9_]*$/', $this->name)) {
return 'snake_case';
}
return 'other';
}
public function toString(): string
{
return $this->name;
}
public static function fromString(string $className): self
{
return new self(trim($className, '. '));
}
}

View File

@@ -0,0 +1,190 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Value Object für CSS-Klassennamen
*/
final readonly class CssClassName
{
public function __construct(
public string $name
) {
}
public static function fromString(string $className): self
{
return new self(trim($className, '. '));
}
/**
* Prüft ob die Klasse BEM-Konvention folgt
*/
public function isBemBlock(): bool
{
return preg_match('/^[a-z][a-z0-9-]*$/', $this->name) &&
! str_contains($this->name, '__') &&
! str_contains($this->name, '--');
}
/**
* Prüft ob die Klasse ein BEM-Element ist
*/
public function isBemElement(): bool
{
return preg_match('/^[a-z][a-z0-9-]*__[a-z][a-z0-9-]*$/', $this->name);
}
/**
* Prüft ob die Klasse ein BEM-Modifier ist
*/
public function isBemModifier(): bool
{
return str_contains($this->name, '--');
}
/**
* Extrahiert BEM-Block-Namen
*/
public function getBemBlock(): ?string
{
if (preg_match('/^([a-z][a-z0-9-]*)/', $this->name, $matches)) {
return $matches[1];
}
return null;
}
/**
* Extrahiert BEM-Element-Namen
*/
public function getBemElement(): ?string
{
if (preg_match('/__([a-z][a-z0-9-]*)/', $this->name, $matches)) {
return $matches[1];
}
return null;
}
/**
* Extrahiert BEM-Modifier-Namen
*/
public function getBemModifier(): ?string
{
if (preg_match('/--([a-z][a-z0-9-]*)/', $this->name, $matches)) {
return $matches[1];
}
return null;
}
/**
* Prüft ob es eine Utility-Klasse ist (Tailwind-Style)
*/
public function isUtilityClass(): bool
{
$utilityPatterns = [
'/^[mp][trblxy]?-\d+$/', // Margin/Padding
'/^[wh]-\d+$/', // Width/Height
'/^text-(xs|sm|base|lg|xl|\d+xl)$/', // Text sizes
'/^(text|bg|border)-(red|blue|green|gray|yellow|purple|pink|indigo)-\d+$/', // Colors
'/^(block|inline|flex|grid|hidden)$/', // Display
'/^(justify|items|self)-(start|end|center|between|around)$/', // Flexbox
];
foreach ($utilityPatterns as $pattern) {
if (preg_match($pattern, $this->name)) {
return true;
}
}
return false;
}
/**
* Erkennt Utility-Kategorie
*/
public function getUtilityCategory(): ?string
{
$categoryPatterns = [
'spacing' => '/^[mp][trblxy]?-\d+$/',
'sizing' => '/^[wh]-\d+$/',
'typography' => '/^text-(xs|sm|base|lg|xl|\d+xl|center|left|right)$/',
'color' => '/^(text|bg|border)-(red|blue|green|gray|yellow|purple|pink|indigo)-\d+$/',
'display' => '/^(block|inline|flex|grid|hidden)$/',
'flexbox' => '/^(justify|items|self)-(start|end|center|between|around)$/',
];
foreach ($categoryPatterns as $category => $pattern) {
if (preg_match($pattern, $this->name)) {
return $category;
}
}
return null;
}
/**
* Erkennt Component-Typ basierend auf Namen
*/
public function getComponentType(): ?string
{
$componentPatterns = [
'button' => ['btn', 'button'],
'card' => ['card'],
'modal' => ['modal', 'dialog'],
'form' => ['form', 'input', 'select', 'textarea'],
'navigation' => ['nav', 'navbar', 'menu'],
'table' => ['table', 'thead', 'tbody', 'tr', 'td', 'th'],
'alert' => ['alert', 'message', 'notification'],
'badge' => ['badge', 'tag', 'chip'],
'dropdown' => ['dropdown', 'select'],
'tabs' => ['tab', 'tabs'],
'accordion' => ['accordion', 'collapse'],
'breadcrumb' => ['breadcrumb'],
'pagination' => ['pagination', 'pager'],
];
foreach ($componentPatterns as $componentType => $patterns) {
foreach ($patterns as $pattern) {
if (str_starts_with($this->name, $pattern) || str_contains($this->name, $pattern)) {
return $componentType;
}
}
}
return null;
}
/**
* Analysiert Naming-Convention
*/
public function getNamingConvention(): string
{
if ($this->isBemBlock() || $this->isBemElement() || $this->isBemModifier()) {
return 'bem';
}
if (preg_match('/^[a-z][a-zA-Z0-9]*$/', $this->name)) {
return 'camelCase';
}
if (preg_match('/^[a-z][a-z0-9-]*$/', $this->name)) {
return 'kebab-case';
}
if (preg_match('/^[a-z][a-z0-9_]*$/', $this->name)) {
return 'snake_case';
}
return 'other';
}
public function toString(): string
{
return $this->name;
}
}

View File

@@ -0,0 +1,285 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
use App\Framework\Core\ValueObjects\RGBColor;
/**
* Value Object für CSS-Farben mit verschiedenen Formaten
*/
final readonly class CssColor
{
public function __construct(
public string $originalValue,
public ColorFormat $format,
public ?RGBColor $rgbColor = null,
public ?array $hslValues = null,
public ?array $oklchValues = null,
public ?string $namedColor = null,
public ?string $customPropertyName = null
) {
}
public static function fromString(string $value): self
{
$value = trim($value);
// Hex Color
if (preg_match('/^#[0-9A-Fa-f]{3,6}$/', $value)) {
try {
$rgbColor = RGBColor::fromHex($value);
return new self($value, ColorFormat::HEX, $rgbColor);
} catch (\Exception) {
return new self($value, ColorFormat::HEX);
}
}
// RGB Color
if (preg_match('/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/', $value, $matches)) {
$rgbColor = new RGBColor(
(int) $matches[1],
(int) $matches[2],
(int) $matches[3]
);
return new self($value, ColorFormat::RGB, $rgbColor);
}
// RGBA Color
if (preg_match('/^rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)$/', $value, $matches)) {
$rgbColor = new RGBColor(
(int) $matches[1],
(int) $matches[2],
(int) $matches[3]
);
return new self($value, ColorFormat::RGBA, $rgbColor);
}
// HSL Color
if (preg_match('/^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/', $value, $matches)) {
$hslValues = [
'h' => (int) $matches[1],
's' => (int) $matches[2],
'l' => (int) $matches[3],
];
return new self($value, ColorFormat::HSL, null, $hslValues);
}
// HSLA Color
if (preg_match('/^hsla\((\d+),\s*(\d+)%,\s*(\d+)%,\s*([\d.]+)\)$/', $value, $matches)) {
$hslValues = [
'h' => (int) $matches[1],
's' => (int) $matches[2],
'l' => (int) $matches[3],
'a' => (float) $matches[4],
];
return new self($value, ColorFormat::HSLA, null, $hslValues);
}
// OKLCH Color
if (preg_match('/^oklch\(([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\)$/', $value, $matches)) {
$oklchValues = [
'l' => (float) $matches[1], // Lightness (0-1)
'c' => (float) $matches[2], // Chroma (0-0.4+)
'h' => (float) $matches[3], // Hue (0-360)
];
if (isset($matches[4])) {
$oklchValues['a'] = (float) $matches[4]; // Alpha
}
return new self($value, ColorFormat::OKLCH, null, null, $oklchValues);
}
// CSS Custom Property
if (preg_match('/^var\(--([^)]+)\)$/', $value, $matches)) {
return new self($value, ColorFormat::CUSTOM_PROPERTY, null, null, null, null, $matches[1]);
}
// Named Color
$namedColors = [
'red', 'blue', 'green', 'white', 'black', 'transparent', 'currentColor',
'gray', 'grey', 'yellow', 'orange', 'purple', 'pink', 'brown',
'cyan', 'magenta', 'lime', 'navy', 'silver', 'gold',
];
if (in_array(strtolower($value), $namedColors)) {
return new self($value, ColorFormat::NAMED, null, null, null, strtolower($value));
}
// Fallback - behandle als named color
return new self($value, ColorFormat::NAMED, null, null, null, $value);
}
/**
* Konvertiert die Farbe zu RGB wenn möglich
*/
public function toRGB(): ?RGBColor
{
if ($this->rgbColor) {
return $this->rgbColor;
}
// HSL zu RGB konvertieren wenn verfügbar
if ($this->hslValues && isset($this->hslValues['h'], $this->hslValues['s'], $this->hslValues['l'])) {
return $this->hslToRgb(
$this->hslValues['h'],
$this->hslValues['s'] / 100,
$this->hslValues['l'] / 100
);
}
// OKLCH zu RGB konvertieren wenn verfügbar
if ($this->oklchValues && isset($this->oklchValues['l'], $this->oklchValues['c'], $this->oklchValues['h'])) {
return $this->oklchToRgb(
$this->oklchValues['l'],
$this->oklchValues['c'],
$this->oklchValues['h']
);
}
return null;
}
/**
* Gibt die Hex-Repräsentation zurück wenn möglich
*/
public function toHex(): ?string
{
$rgb = $this->toRGB();
if ($rgb) {
return $rgb->toHex();
}
return null;
}
/**
* Prüft ob die Farbe transparent ist
*/
public function isTransparent(): bool
{
return $this->namedColor === 'transparent' ||
$this->originalValue === 'transparent' ||
($this->format === ColorFormat::RGBA && str_contains($this->originalValue, ', 0)')) ||
($this->format === ColorFormat::HSLA && str_contains($this->originalValue, ', 0)'));
}
/**
* Prüft ob die Farbe eine Custom Property ist
*/
public function isCustomProperty(): bool
{
return $this->format === ColorFormat::CUSTOM_PROPERTY;
}
/**
* Gibt den Custom Property Namen zurück
*/
public function getCustomPropertyName(): ?string
{
return $this->customPropertyName;
}
private function hslToRgb(int $h, float $s, float $l): RGBColor
{
$h = $h / 360;
if ($s === 0.0) {
$r = $g = $b = $l; // Grayscale
} else {
$hue2rgb = function ($p, $q, $t) {
if ($t < 0) {
$t += 1;
}
if ($t > 1) {
$t -= 1;
}
if ($t < 1 / 6) {
return $p + ($q - $p) * 6 * $t;
}
if ($t < 1 / 2) {
return $q;
}
if ($t < 2 / 3) {
return $p + ($q - $p) * (2 / 3 - $t) * 6;
}
return $p;
};
$q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
$p = 2 * $l - $q;
$r = $hue2rgb($p, $q, $h + 1 / 3);
$g = $hue2rgb($p, $q, $h);
$b = $hue2rgb($p, $q, $h - 1 / 3);
}
return new RGBColor(
(int) round($r * 255),
(int) round($g * 255),
(int) round($b * 255)
);
}
private function oklchToRgb(float $l, float $c, float $h): RGBColor
{
// OKLCH zu OKLab
$hRad = deg2rad($h);
$a = $c * cos($hRad);
$b = $c * sin($hRad);
// OKLab zu Linear RGB (vereinfachte Approximation)
// Für eine exakte Konvertierung wären komplexere Matrizen-Operationen nötig
$lRgb = $l + 0.3963377774 * $a + 0.2158037573 * $b;
$mRgb = $l - 0.1055613458 * $a - 0.0638541728 * $b;
$sRgb = $l - 0.0894841775 * $a - 1.2914855480 * $b;
// Cube root für Linear RGB
$lRgb = $this->cubeRoot($lRgb);
$mRgb = $this->cubeRoot($mRgb);
$sRgb = $this->cubeRoot($sRgb);
// Linear RGB zu sRGB (vereinfacht)
$r = +4.0767416621 * $lRgb - 3.3077115913 * $mRgb + 0.2309699292 * $sRgb;
$g = -1.2684380046 * $lRgb + 2.6097574011 * $mRgb - 0.3413193965 * $sRgb;
$b = -0.0041960863 * $lRgb - 0.7034186147 * $mRgb + 1.7076147010 * $sRgb;
// Gamma correction und Clamping
$r = $this->gammaCorrect($r);
$g = $this->gammaCorrect($g);
$b = $this->gammaCorrect($b);
return new RGBColor(
max(0, min(255, (int) round($r * 255))),
max(0, min(255, (int) round($g * 255))),
max(0, min(255, (int) round($b * 255)))
);
}
private function cubeRoot(float $value): float
{
return $value >= 0 ? pow($value, 1 / 3) : -pow(-$value, 1 / 3);
}
private function gammaCorrect(float $value): float
{
if ($value >= 0.0031308) {
return 1.055 * pow($value, 1 / 2.4) - 0.055;
} else {
return 12.92 * $value;
}
}
public function toString(): string
{
return $this->originalValue;
}
}

View File

@@ -0,0 +1,263 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
use App\Framework\Filesystem\FilePath;
/**
* Ergebnis des CSS-Parsing-Prozesses
*/
final readonly class CssParseResult
{
/**
* @param CssRule[] $rules
* @param DesignToken[] $customProperties
* @param CssClassName[] $classNames
*/
public function __construct(
public ?FilePath $sourceFile,
public array $rules,
public array $customProperties,
public array $classNames,
public string $rawContent
) {
}
/**
* Gibt alle gefundenen Selektoren zurück
* @return CssSelector[]
*/
public function getAllSelectors(): array
{
$selectors = [];
foreach ($this->rules as $rule) {
foreach ($rule->selectors as $selector) {
$selectors[] = $selector;
}
}
return $selectors;
}
/**
* Gibt alle gefundenen Properties zurück
* @return CssProperty[]
*/
public function getAllProperties(): array
{
$properties = [];
foreach ($this->rules as $rule) {
foreach ($rule->properties as $property) {
$properties[] = $property;
}
}
return $properties;
}
/**
* Filtert Regeln nach Selektor-Typ
*/
public function getRulesBySelectorType(CssSelectorType $type): array
{
$matchingRules = [];
foreach ($this->rules as $rule) {
foreach ($rule->selectors as $selector) {
if ($selector->getType() === $type) {
$matchingRules[] = $rule;
break;
}
}
}
return $matchingRules;
}
/**
* Filtert Properties nach Kategorie
*/
public function getPropertiesByCategory(CssPropertyCategory $category): array
{
return array_filter(
$this->getAllProperties(),
fn (CssProperty $property) => $property->getCategory() === $category
);
}
/**
* Filtert Custom Properties nach Typ
*/
public function getDesignTokensByType(DesignTokenType $type): array
{
return array_filter(
$this->customProperties,
fn (DesignToken $token) => $token->type === $type
);
}
/**
* Filtert CSS-Klassen nach Pattern
*/
public function getClassNamesByPattern(string $pattern): array
{
return array_filter(
$this->classNames,
fn (CssClassName $className) => str_contains($className->name, $pattern)
);
}
/**
* Gibt alle BEM-Klassen zurück
*/
public function getBemClasses(): array
{
return array_filter(
$this->classNames,
fn (CssClassName $className) =>
$className->isBemBlock() ||
$className->isBemElement() ||
$className->isBemModifier()
);
}
/**
* Gibt alle Utility-Klassen zurück
*/
public function getUtilityClasses(): array
{
return array_filter(
$this->classNames,
fn (CssClassName $className) => $className->isUtilityClass()
);
}
/**
* Analysiert verwendete Farben
*/
public function getColorAnalysis(): array
{
$colors = [];
$colorProperties = $this->getPropertiesByCategory(CssPropertyCategory::COLOR);
foreach ($colorProperties as $property) {
$color = $property->toColor();
if ($color) {
$colors[] = [
'property' => $property->name,
'color' => $color,
'format' => $color->format->value,
'hex' => $color->toHex(),
];
}
}
return $colors;
}
/**
* Analysiert Naming-Konventionen
*/
public function getNamingConventionAnalysis(): array
{
$conventions = [
'bem' => 0,
'camelCase' => 0,
'kebab-case' => 0,
'snake_case' => 0,
'other' => 0,
'violations' => [],
];
foreach ($this->classNames as $className) {
$convention = $className->getNamingConvention();
if (isset($conventions[$convention])) {
$conventions[$convention]++;
} else {
$conventions['other']++;
$conventions['violations'][] = $className->name;
}
}
$conventions['total_classes'] = count($this->classNames);
$conventions['dominant_convention'] = $this->getDominantConvention($conventions);
return $conventions;
}
/**
* Statistiken über die geparsten Daten
*/
public function getStatistics(): array
{
return [
'source_file' => $this->sourceFile?->toString(),
'total_rules' => count($this->rules),
'total_selectors' => count($this->getAllSelectors()),
'total_properties' => count($this->getAllProperties()),
'design_tokens' => count($this->customProperties),
'class_names' => count($this->classNames),
'content_size_bytes' => strlen($this->rawContent),
'selector_types' => $this->getSelectorTypeStats(),
'property_categories' => $this->getPropertyCategoryStats(),
'token_types' => $this->getTokenTypeStats(),
];
}
private function getDominantConvention(array $conventions): string
{
$max = 0;
$dominant = 'mixed';
foreach (['bem', 'camelCase', 'kebab-case', 'snake_case'] as $convention) {
if ($conventions[$convention] > $max) {
$max = $conventions[$convention];
$dominant = $convention;
}
}
return $dominant;
}
private function getSelectorTypeStats(): array
{
$stats = [];
foreach ($this->getAllSelectors() as $selector) {
$type = $selector->getType()->value;
$stats[$type] = ($stats[$type] ?? 0) + 1;
}
return $stats;
}
private function getPropertyCategoryStats(): array
{
$stats = [];
foreach ($this->getAllProperties() as $property) {
$category = $property->getCategory()->value;
$stats[$category] = ($stats[$category] ?? 0) + 1;
}
return $stats;
}
private function getTokenTypeStats(): array
{
$stats = [];
foreach ($this->customProperties as $token) {
$type = $token->type->value;
$stats[$type] = ($stats[$type] ?? 0) + 1;
}
return $stats;
}
}

View File

@@ -0,0 +1,149 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Repräsentiert eine CSS-Property mit Namen und Wert
*/
final readonly class CssProperty
{
public function __construct(
public string $name,
public string $value
) {
}
/**
* Kategorisiert die Property
*/
public function getCategory(): CssPropertyCategory
{
$colorProperties = [
'color', 'background-color', 'border-color', 'outline-color',
'text-decoration-color', 'caret-color', 'column-rule-color',
];
$spacingProperties = [
'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
'gap', 'row-gap', 'column-gap',
];
$typographyProperties = [
'font-family', 'font-size', 'font-weight', 'font-style',
'line-height', 'letter-spacing', 'text-align', 'text-decoration',
'text-transform', 'font-variant',
];
$layoutProperties = [
'display', 'position', 'top', 'right', 'bottom', 'left',
'width', 'height', 'max-width', 'max-height', 'min-width', 'min-height',
'flex', 'flex-direction', 'flex-wrap', 'justify-content', 'align-items',
'grid', 'grid-template-columns', 'grid-template-rows',
];
$borderProperties = [
'border', 'border-top', 'border-right', 'border-bottom', 'border-left',
'border-width', 'border-style', 'border-radius', 'outline',
];
$animationProperties = [
'transition', 'animation', 'transform', 'transition-duration',
'transition-property', 'transition-timing-function', 'transition-delay',
];
if (in_array($this->name, $colorProperties)) {
return CssPropertyCategory::COLOR;
}
if (in_array($this->name, $spacingProperties)) {
return CssPropertyCategory::SPACING;
}
if (in_array($this->name, $typographyProperties)) {
return CssPropertyCategory::TYPOGRAPHY;
}
if (in_array($this->name, $layoutProperties)) {
return CssPropertyCategory::LAYOUT;
}
if (in_array($this->name, $borderProperties)) {
return CssPropertyCategory::BORDER;
}
if (in_array($this->name, $animationProperties)) {
return CssPropertyCategory::ANIMATION;
}
return CssPropertyCategory::OTHER;
}
/**
* Prüft ob die Property einen CSS Custom Property Wert verwendet
*/
public function usesCustomProperty(): bool
{
return str_contains($this->value, 'var(--');
}
/**
* Extrahiert Custom Property Namen aus dem Wert
*/
public function getCustomPropertyReferences(): array
{
preg_match_all('/var\(--([^)]+)\)/', $this->value, $matches);
return $matches[1] ?? [];
}
/**
* Konvertiert zu CssColor wenn es eine Farbproperty ist
*/
public function toColor(): ?CssColor
{
if ($this->getCategory() !== CssPropertyCategory::COLOR) {
return null;
}
return CssColor::fromString($this->value);
}
/**
* Extrahiert numerischen Wert und Einheit
*/
public function parseNumericValue(): ?array
{
if (preg_match('/^(-?\d*\.?\d+)([a-zA-Z%]*)$/', $this->value, $matches)) {
return [
'value' => (float) $matches[1],
'unit' => $matches[2] ?: null,
];
}
return null;
}
/**
* Prüft ob die Property wichtig ist (!important)
*/
public function isImportant(): bool
{
return str_contains($this->value, '!important');
}
/**
* Gibt den Wert ohne !important zurück
*/
public function getValueWithoutImportant(): string
{
return trim(str_replace('!important', '', $this->value));
}
public function toString(): string
{
return "{$this->name}: {$this->value}";
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Enum für CSS Property Kategorien
*/
enum CssPropertyCategory: string
{
case COLOR = 'color';
case SPACING = 'spacing';
case TYPOGRAPHY = 'typography';
case LAYOUT = 'layout';
case BORDER = 'border';
case ANIMATION = 'animation';
case TRANSFORM = 'transform';
case OTHER = 'other';
}

View File

@@ -0,0 +1,190 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Repräsentiert eine CSS-Regel mit Selektoren und Properties
*/
final readonly class CssRule
{
/**
* @param CssSelector[] $selectors
* @param CssProperty[] $properties
*/
public function __construct(
public array $selectors,
public array $properties
) {
}
/**
* Gibt alle Selektor-Strings zurück
*/
public function getSelectorStrings(): array
{
return array_map(fn (CssSelector $selector) => $selector->value, $this->selectors);
}
/**
* Gibt alle Property-Namen zurück
*/
public function getPropertyNames(): array
{
return array_map(fn (CssProperty $property) => $property->name, $this->properties);
}
/**
* Sucht eine Property nach Namen
*/
public function getProperty(string $name): ?CssProperty
{
foreach ($this->properties as $property) {
if ($property->name === $name) {
return $property;
}
}
return null;
}
/**
* Gibt alle Properties einer bestimmten Kategorie zurück
*/
public function getPropertiesByCategory(CssPropertyCategory $category): array
{
return array_filter(
$this->properties,
fn (CssProperty $property) => $property->getCategory() === $category
);
}
/**
* Prüft ob die Regel einen bestimmten Selektor enthält
*/
public function hasSelector(string $selectorValue): bool
{
foreach ($this->selectors as $selector) {
if ($selector->value === $selectorValue) {
return true;
}
}
return false;
}
/**
* Prüft ob die Regel eine bestimmte Property enthält
*/
public function hasProperty(string $propertyName): bool
{
return $this->getProperty($propertyName) !== null;
}
/**
* Prüft ob die Regel Custom Properties verwendet
*/
public function usesCustomProperties(): bool
{
foreach ($this->properties as $property) {
if ($property->usesCustomProperty()) {
return true;
}
}
return false;
}
/**
* Extrahiert alle Custom Property Referenzen
*/
public function getCustomPropertyReferences(): array
{
$references = [];
foreach ($this->properties as $property) {
$propertyRefs = $property->getCustomPropertyReferences();
$references = array_merge($references, $propertyRefs);
}
return array_unique($references);
}
/**
* Gibt die Regel als CSS-String zurück
*/
public function toCssString(): string
{
$selectors = implode(', ', $this->getSelectorStrings());
$properties = [];
foreach ($this->properties as $property) {
$properties[] = " {$property->name}: {$property->value};";
}
$propertiesString = implode("\n", $properties);
return "$selectors {\n$propertiesString\n}";
}
/**
* Analysiert die Spezifität der Selektoren
*/
public function getSpecificityAnalysis(): array
{
$analysis = [];
foreach ($this->selectors as $selector) {
$analysis[] = [
'selector' => $selector->value,
'specificity' => $selector->calculateSpecificity(),
'type' => $selector->getType()->value,
];
}
return $analysis;
}
/**
* Kategorisiert die Regel basierend auf Selektoren und Properties
*/
public function categorize(): array
{
$selectorTypes = [];
$propertyCategories = [];
foreach ($this->selectors as $selector) {
$selectorTypes[] = $selector->getType()->value;
}
foreach ($this->properties as $property) {
$propertyCategories[] = $property->getCategory()->value;
}
return [
'selector_types' => array_unique($selectorTypes),
'property_categories' => array_unique($propertyCategories),
'uses_custom_properties' => $this->usesCustomProperties(),
'complexity' => $this->calculateComplexity(),
];
}
private function calculateComplexity(): string
{
$selectorCount = count($this->selectors);
$propertyCount = count($this->properties);
$totalSpecificity = array_sum(array_map(
fn (CssSelector $selector) => $selector->calculateSpecificity(),
$this->selectors
));
$complexityScore = $selectorCount + $propertyCount + ($totalSpecificity / 10);
return match(true) {
$complexityScore <= 5 => 'simple',
$complexityScore <= 15 => 'moderate',
default => 'complex'
};
}
}

View File

@@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Repräsentiert einen CSS-Selektor
*/
final readonly class CssSelector
{
public function __construct(
public string $value
) {
}
public static function fromString(string $selector): self
{
return new self(trim($selector));
}
/**
* Berechnet die CSS-Spezifität des Selektors
*/
public function calculateSpecificity(): int
{
$specificity = 0;
// IDs (#id) = 100
preg_match_all('/#[a-zA-Z][\w-]*/', $this->value, $ids);
$specificity += count($ids[0]) * 100;
// Classes (.class), Attributes ([attr]) und Pseudo-Classes (:hover) = 10
preg_match_all('/\.[a-zA-Z][\w-]*/', $this->value, $classes);
$specificity += count($classes[0]) * 10;
preg_match_all('/\[[^\]]*\]/', $this->value, $attributes);
$specificity += count($attributes[0]) * 10;
preg_match_all('/:(?!not\(|where\(|is\()[a-zA-Z][\w-]*/', $this->value, $pseudoClasses);
$specificity += count($pseudoClasses[0]) * 10;
// Elements (div, span) und Pseudo-Elements (::before) = 1
$elements = preg_replace('/[#.\[\]:][^#.\[\]\s]*/', '', $this->value);
preg_match_all('/[a-zA-Z][\w-]*/', $elements, $elementMatches);
$specificity += count($elementMatches[0]);
return $specificity;
}
/**
* Analysiert den Selektor-Typ
*/
public function getType(): CssSelectorType
{
if (str_starts_with($this->value, '.')) {
return CssSelectorType::CLASS_SELECTOR;
}
if (str_starts_with($this->value, '#')) {
return CssSelectorType::ID;
}
if (str_contains($this->value, '[')) {
return CssSelectorType::ATTRIBUTE;
}
if (str_contains($this->value, ':')) {
return CssSelectorType::PSEUDO;
}
if (preg_match('/^[a-zA-Z][\w-]*$/', $this->value)) {
return CssSelectorType::ELEMENT;
}
return CssSelectorType::COMPLEX;
}
/**
* Extrahiert alle CSS-Klassennamen aus dem Selektor
*/
public function extractClasses(): array
{
preg_match_all('/\.([a-zA-Z][\w-]*)/', $this->value, $matches);
return $matches[1] ?? [];
}
/**
* Extrahiert alle IDs aus dem Selektor
*/
public function extractIds(): array
{
preg_match_all('/#([a-zA-Z][\w-]*)/', $this->value, $matches);
return $matches[1] ?? [];
}
/**
* Extrahiert alle Element-Namen aus dem Selektor
*/
public function extractElements(): array
{
$cleaned = preg_replace('/[#.\[\]:][^#.\[\]\s]*/', '', $this->value);
preg_match_all('/[a-zA-Z][\w-]*/', $cleaned, $matches);
return array_filter($matches[0], fn ($element) => ! empty($element));
}
public function toString(): string
{
return $this->value;
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Enum für CSS Selektor Typen
*/
enum CssSelectorType: string
{
case CLASS_SELECTOR = 'class';
case ID = 'id';
case ELEMENT = 'element';
case ATTRIBUTE = 'attribute';
case PSEUDO = 'pseudo';
case COMPLEX = 'complex';
}

View File

@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Repräsentiert eine CSS Custom Property (CSS Variable)
*/
final readonly class CustomProperty
{
public function __construct(
public string $name,
public string $value
) {
}
public static function fromDeclaration(string $declaration): self
{
// Parse "--property-name: value" format
if (preg_match('/--([^:]+):\s*([^;]+)/', $declaration, $matches)) {
return new self(trim($matches[1]), trim($matches[2]));
}
throw new \InvalidArgumentException('Invalid CSS custom property declaration');
}
public function hasValueType(string $type): bool
{
return match($type) {
'color' => $this->isColorValue(),
'size' => $this->isSizeValue(),
'number' => $this->isNumberValue(),
default => false
};
}
public function getValueAs(string $type): mixed
{
return match($type) {
'color' => $this->getColorValue(),
'size' => $this->getSizeValue(),
'number' => $this->getNumberValue(),
default => $this->value
};
}
private function isColorValue(): bool
{
return preg_match('/^(#[0-9a-fA-F]{3,8}|rgb|hsl|oklch|color)/', $this->value) === 1;
}
private function isSizeValue(): bool
{
return preg_match('/^\d+(\.\d+)?(px|em|rem|%|vh|vw)$/', $this->value) === 1;
}
private function isNumberValue(): bool
{
return is_numeric($this->value);
}
private function getColorValue(): CssColor
{
if ($this->isColorValue()) {
$format = match(true) {
str_starts_with($this->value, '#') => ColorFormat::HEX,
str_starts_with($this->value, 'rgb') => ColorFormat::RGB,
str_starts_with($this->value, 'hsl') => ColorFormat::HSL,
str_starts_with($this->value, 'oklch') => ColorFormat::OKLCH,
default => ColorFormat::HEX
};
return new CssColor($this->value, $format);
}
throw new \InvalidArgumentException('Value is not a color');
}
private function getSizeValue(): string
{
return $this->value;
}
private function getNumberValue(): float
{
return (float) $this->value;
}
}

View File

@@ -0,0 +1,220 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
use App\Framework\Design\Analyzer\ColorAnalysisResult;
use App\Framework\Design\Analyzer\ComponentDetectionResult;
use App\Framework\Design\Analyzer\ConventionCheckResult;
use App\Framework\Design\Analyzer\TokenAnalysisResult;
/**
* Complete Design System Analysis Result
*/
final readonly class DesignSystemAnalysis
{
public function __construct(
public TokenAnalysisResult $tokenAnalysis,
public ColorAnalysisResult $colorAnalysis,
public ComponentDetectionResult $componentAnalysis,
public ConventionCheckResult $conventionAnalysis,
public array $metadata = []
) {
}
public static function empty(): self
{
return new self(
tokenAnalysis: new TokenAnalysisResult(0, [], [], [], [], [], []),
colorAnalysis: new ColorAnalysisResult(0, [], [], [], [], []),
componentAnalysis: new ComponentDetectionResult(0, [], [], [], [], []),
conventionAnalysis: new ConventionCheckResult(100, [], [], [], 'none'),
metadata: []
);
}
/**
* Gets the overall design system maturity level
*/
public function getMaturityLevel(): string
{
$score = $this->getOverallDesignSystemScore();
return match(true) {
$score >= 90 => 'Mature',
$score >= 70 => 'Established',
$score >= 50 => 'Developing',
$score >= 30 => 'Emerging',
default => 'Basic'
};
}
/**
* Calculates overall design system score (0-100)
*/
public function getOverallDesignSystemScore(): float
{
$tokenCoverage = $this->tokenAnalysis->getTokenCoverage();
$tokenScore = $tokenCoverage['usage_percentage'] ?? 0;
$colorScore = $this->colorAnalysis->getConsistencyScore();
$componentScore = $this->componentAnalysis->getConsistencyScore();
$conventionScore = $this->conventionAnalysis->overallScore;
return ($tokenScore + $colorScore + $componentScore + $conventionScore) / 4;
}
/**
* Gets critical issues that need immediate attention
*/
public function getCriticalIssues(): array
{
$issues = [];
// Token issues
$tokenCoverage = $this->tokenAnalysis->getTokenCoverage();
if (($tokenCoverage['usage_percentage'] ?? 0) < 30) {
$issues[] = [
'type' => 'tokens',
'severity' => 'critical',
'message' => 'Very low design token usage',
'impact' => 'Inconsistent styling and maintenance difficulties',
];
}
// Color issues - check contrast compliance
if ($this->colorAnalysis->getContrastComplianceScore() < 80) {
$issues[] = [
'type' => 'colors',
'severity' => 'critical',
'message' => 'WCAG accessibility violations found',
'impact' => 'Poor accessibility for users',
];
}
// Convention issues
if ($this->conventionAnalysis->overallScore < 40) {
$issues[] = [
'type' => 'conventions',
'severity' => 'critical',
'message' => 'Poor naming convention consistency',
'impact' => 'Decreased developer productivity and maintenance',
];
}
return $issues;
}
/**
* Gets quick wins for easy improvements
*/
public function getQuickWins(): array
{
$wins = [];
// Duplicate color removal
if (count($this->colorAnalysis->duplicateColors) > 0) {
$wins[] = [
'type' => 'colors',
'effort' => 'low',
'impact' => 'medium',
'message' => 'Remove duplicate color values',
'action' => 'Consolidate ' . count($this->colorAnalysis->duplicateColors) . ' duplicate colors',
];
}
// Utility class organization based on pattern diversity
if ($this->componentAnalysis->getPatternDiversity() > 80) {
$wins[] = [
'type' => 'components',
'effort' => 'low',
'impact' => 'high',
'message' => 'Standardize component patterns',
'action' => 'Choose one primary CSS methodology for better consistency',
];
}
return $wins;
}
/**
* Gets development roadmap based on analysis
*/
public function getDevelopmentRoadmap(): array
{
$roadmap = [];
$score = $this->getOverallDesignSystemScore();
if ($score < 30) {
$roadmap[] = [
'phase' => 1,
'title' => 'Foundation Setup',
'priority' => 'critical',
'tasks' => [
'Establish core design tokens',
'Define color palette',
'Set naming conventions',
'Create basic component library',
],
'timeline' => '2-4 weeks',
];
}
if ($score >= 30 && $score < 60) {
$roadmap[] = [
'phase' => 2,
'title' => 'System Expansion',
'priority' => 'high',
'tasks' => [
'Expand token coverage',
'Improve component organization',
'Add responsive design tokens',
'Implement consistent spacing scale',
],
'timeline' => '4-6 weeks',
];
}
if ($score >= 60) {
$roadmap[] = [
'phase' => 3,
'title' => 'Optimization & Polish',
'priority' => 'medium',
'tasks' => [
'Optimize token usage',
'Refine component APIs',
'Add advanced theming',
'Implement design system documentation',
],
'timeline' => '6-8 weeks',
];
}
return $roadmap;
}
/**
* Exports analysis as array for JSON serialization
*/
public function exportReport(): array
{
return [
'overall_score' => $this->getOverallDesignSystemScore(),
'maturity_level' => $this->getMaturityLevel(),
'critical_issues' => $this->getCriticalIssues(),
'quick_wins' => $this->getQuickWins(),
'roadmap' => $this->getDevelopmentRoadmap(),
'detailed_analysis' => [
'tokens' => $this->tokenAnalysis->toArray(),
'colors' => $this->colorAnalysis->toArray(),
'components' => $this->componentAnalysis->toArray(),
'conventions' => $this->conventionAnalysis->toArray(),
],
'metadata' => array_merge($this->metadata, [
'generated_at' => date('c'),
'version' => '1.0',
]),
];
}
}

View File

@@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Design Token Value Object
*/
final readonly class DesignToken
{
public function __construct(
public string $name,
public DesignTokenType $type,
public mixed $value,
public string $description,
public array $metadata = []
) {
}
/**
* Factory für Color Token
*/
public static function color(string $name, CssColor $color, string $description = ''): self
{
return new self(
name: $name,
type: DesignTokenType::COLOR,
value: $color,
description: $description ?: "Color: " . ucwords(str_replace(['-', '_'], ' ', $name)),
metadata: ['source' => 'css_custom_property']
);
}
/**
* Factory für Spacing Token
*/
public static function spacing(string $name, string|int $value, string $description = ''): self
{
return new self(
name: $name,
type: DesignTokenType::SPACING,
value: $value,
description: $description ?: "Spacing: " . ucwords(str_replace(['-', '_'], ' ', $name)),
metadata: ['source' => 'css_custom_property']
);
}
/**
* Factory für Typography Token
*/
public static function typography(string $name, mixed $value, string $description = ''): self
{
return new self(
name: $name,
type: DesignTokenType::TYPOGRAPHY,
value: $value,
description: $description ?: "Typography: " . ucwords(str_replace(['-', '_'], ' ', $name)),
metadata: ['source' => 'css_custom_property']
);
}
/**
* Konvertiert zu Array für Export/Serialisierung
*/
public function toArray(): array
{
return [
'name' => $this->name,
'type' => $this->type->value,
'value' => $this->serializeValue(),
'description' => $this->description,
'metadata' => $this->metadata,
];
}
/**
* Erstellt CSS Custom Property String
*/
public function toCssCustomProperty(): string
{
$value = match($this->type) {
DesignTokenType::COLOR => $this->value instanceof CssColor ? $this->value->toString() : (string) $this->value,
default => (string) $this->value
};
return "--{$this->name}: {$value};";
}
/**
* Gibt CSS var() Referenz zurück
*/
public function toCssVar(): string
{
return "var(--{$this->name})";
}
/**
* Prüft ob Token einen bestimmten Wert-Typ hat
*/
public function hasValueType(string $type): bool
{
return match($type) {
'color' => $this->value instanceof CssColor,
'string' => is_string($this->value),
'int' => is_int($this->value),
'float' => is_float($this->value),
'array' => is_array($this->value),
default => false
};
}
/**
* Gibt Wert als bestimmten Typ zurück
*/
public function getValueAs(string $type): mixed
{
return match($type) {
'string' => (string) $this->value,
'int' => (int) $this->value,
'float' => (float) $this->value,
'array' => is_array($this->value) ? $this->value : [$this->value],
'color' => $this->value instanceof CssColor ? $this->value : null,
default => $this->value
};
}
private function serializeValue(): mixed
{
if ($this->value instanceof CssColor) {
return [
'original' => $this->value->originalValue,
'format' => $this->value->format->value,
'hex' => $this->value->toHex(),
'rgb' => $this->value->toRGB()?->toArray(),
];
}
return $this->value;
}
public function toString(): string
{
return $this->toCssVar();
}
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Enum für Design Token Typen
*/
enum DesignTokenType: string
{
case COLOR = 'color';
case SPACING = 'spacing';
case TYPOGRAPHY = 'typography';
case SHADOW = 'shadow';
case RADIUS = 'radius';
case OPACITY = 'opacity';
case BORDER = 'border';
case ANIMATION = 'animation';
case BREAKPOINT = 'breakpoint';
}

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\ValueObjects;
/**
* Kategorien für Design Tokens
*/
enum TokenCategory: string
{
case COLOR = 'color';
case TYPOGRAPHY = 'typography';
case SPACING = 'spacing';
case BORDER = 'border';
case SHADOW = 'shadow';
case ANIMATION = 'animation';
case OTHER = 'other';
}