From 30387920f3c489f44e85bdf6b9dc48ad11340e73 Mon Sep 17 00:00:00 2001 From: Alex Ulko Date: Sat, 18 Oct 2025 23:22:49 +0200 Subject: [PATCH 1/5] fix hsl and rgba color args with spaces instead of commas --- src/Compiler.php | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index d22d0a35..a159247c 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4888,6 +4888,46 @@ protected function hueToRGB($m1, $m2, $h) return $m1; } + /** + * Normalize color function arguments (handle space-separated syntax) + * + * @param array $args + * @param int $expectedCount + * + * @return array + */ + protected function normalizeColorArgs($args, $expectedCount) + { + // Check if first arg is a list (space-separated syntax) + if (isset($args[0]) && is_array($args[0]) && $args[0][0] === Type::T_LIST) { + $listItems = $args[0][2]; // Get the list items + + // Flatten the list items, handling slash-separated values + $normalizedItems = []; + foreach ($listItems as $item) { + if (is_array($item) && $item[0] === Type::T_STRING && is_array($item[2] ?? [])) { + // This is a string like "48% / 50%" - extract just the numbers + foreach ($item[2] as $part) { + if ($part instanceof Node\Number) { + $normalizedItems[] = $part; + } + } + } else { + $normalizedItems[] = $item; + } + } + + // Pad with nulls if needed + while (count($normalizedItems) < $expectedCount) { + $normalizedItems[] = 0; + } + + return array_slice($normalizedItems, 0, $expectedCount); + } + + return $args; + } + /** * Convert HSL to RGB * @@ -5009,6 +5049,8 @@ protected function libIndex($args) protected static $libRgb = ['red', 'green', 'blue']; protected function libRgb($args) { + // Handle space-separated syntax: rgb(255 255 255) + $args = $this->normalizeColorArgs($args, 3); list($r, $g, $b) = $args; return [Type::T_COLOR, $r[1], $g[1], $b[1]]; @@ -5019,15 +5061,23 @@ protected function libRgb($args) 'green', 'blue', 'alpha']; protected function libRgba($args) { + // Handle space-separated syntax: rgba(255 255 255 50%) + $args = $this->normalizeColorArgs($args, 4); if ($color = $this->coerceColor($args[0])) { $num = isset($args[3]) ? $args[3] : $args[1]; $alpha = $this->assertNumber($num); + if ($alpha > 1) { + $alpha = $alpha / 100; + } $color[4] = $alpha; return $color; } list($r, $g, $b, $a) = $args; + if (($a[1] ?? 0) > 1) { + $a[1] = $a[1] / 100; + } return [Type::T_COLOR, $r[1], $g[1], $b[1], $a[1]]; } @@ -5224,6 +5274,8 @@ protected function libMix($args) protected static $libHsl = ['hue', 'saturation', 'lightness']; protected function libHsl($args) { + // Handle space-separated syntax: hsl(180deg 50% 50%) + $args = $this->normalizeColorArgs($args, 3); list($h, $s, $l) = $args; return $this->toRGB($h[1], $s[1], $l[1]); @@ -5232,9 +5284,14 @@ protected function libHsl($args) protected static $libHsla = ['hue', 'saturation', 'lightness', 'alpha']; protected function libHsla($args) { + // Handle space-separated syntax: hsla(180deg 50% 58% / 50%) + $args = $this->normalizeColorArgs($args, 4); list($h, $s, $l, $a) = $args; $color = $this->toRGB($h[1], $s[1], $l[1]); + if (($a[1] ?? 0) > 1) { + $a[1] = $a[1] / 100; + } $color[4] = $a[1]; return $color; From dcde7213dfd6b16211b6006b469b5d0c56570a11 Mon Sep 17 00:00:00 2001 From: Alex Ulko Date: Sat, 18 Oct 2025 23:54:45 +0200 Subject: [PATCH 2/5] fix rgba alpha arg --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index a159247c..59266485 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -5064,7 +5064,7 @@ protected function libRgba($args) // Handle space-separated syntax: rgba(255 255 255 50%) $args = $this->normalizeColorArgs($args, 4); if ($color = $this->coerceColor($args[0])) { - $num = isset($args[3]) ? $args[3] : $args[1]; + $num = isset($args[3]) && is_array($args[3]) ? $args[3] : $args[1]; $alpha = $this->assertNumber($num); if ($alpha > 1) { $alpha = $alpha / 100; From 7aa610118a2585470b1192b9e962ef8171b2a912 Mon Sep 17 00:00:00 2001 From: Alex Ulko Date: Tue, 11 Nov 2025 17:52:11 +0100 Subject: [PATCH 3/5] add support for color args in rgba and hsla --- src/Compiler.php | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 59266485..754534f6 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4893,15 +4893,16 @@ protected function hueToRGB($m1, $m2, $h) * * @param array $args * @param int $expectedCount + * @param bool $supportColor * * @return array */ - protected function normalizeColorArgs($args, $expectedCount) + protected function normalizeColorArgs($args, $expectedCount, $supportColor = false) { // Check if first arg is a list (space-separated syntax) if (isset($args[0]) && is_array($args[0]) && $args[0][0] === Type::T_LIST) { $listItems = $args[0][2]; // Get the list items - + // Flatten the list items, handling slash-separated values $normalizedItems = []; foreach ($listItems as $item) { @@ -4916,15 +4917,20 @@ protected function normalizeColorArgs($args, $expectedCount) $normalizedItems[] = $item; } } - + + // First arg is a color + if ($supportColor && $this->coerceColor($normalizedItems[0])) { + return $normalizedItems; + } + // Pad with nulls if needed while (count($normalizedItems) < $expectedCount) { $normalizedItems[] = 0; } - + return array_slice($normalizedItems, 0, $expectedCount); } - + return $args; } @@ -5062,7 +5068,7 @@ protected function libRgb($args) protected function libRgba($args) { // Handle space-separated syntax: rgba(255 255 255 50%) - $args = $this->normalizeColorArgs($args, 4); + $args = $this->normalizeColorArgs($args, 4, true); if ($color = $this->coerceColor($args[0])) { $num = isset($args[3]) && is_array($args[3]) ? $args[3] : $args[1]; $alpha = $this->assertNumber($num); @@ -5285,7 +5291,18 @@ protected function libHsl($args) protected function libHsla($args) { // Handle space-separated syntax: hsla(180deg 50% 58% / 50%) - $args = $this->normalizeColorArgs($args, 4); + $args = $this->normalizeColorArgs($args, 4, true); + if ($color = $this->coerceColor($args[0])) { + $num = is_array($args[3] ?? null) ? $args[3] : $args[1]; + $alpha = $this->assertNumber($num); + if ($alpha > 1) { + $alpha = $alpha / 100; + } + $color[4] = $alpha; + + return $color; + } + list($h, $s, $l, $a) = $args; $color = $this->toRGB($h[1], $s[1], $l[1]); From 9384d276f8fe385f93c14dacfd14bfff466d2e2a Mon Sep 17 00:00:00 2001 From: Alex Ulko Date: Wed, 12 Nov 2025 00:23:50 +0100 Subject: [PATCH 4/5] return color if it's the first arg and no opacity needed --- src/Compiler.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 754534f6..65f32977 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -5057,6 +5057,9 @@ protected function libRgb($args) { // Handle space-separated syntax: rgb(255 255 255) $args = $this->normalizeColorArgs($args, 3); + if ($color = $this->coerceColor($args[0])) { + return $color; + } list($r, $g, $b) = $args; return [Type::T_COLOR, $r[1], $g[1], $b[1]]; @@ -5282,6 +5285,9 @@ protected function libHsl($args) { // Handle space-separated syntax: hsl(180deg 50% 50%) $args = $this->normalizeColorArgs($args, 3); + if ($color = $this->coerceColor($args[0])) { + return $color; + } list($h, $s, $l) = $args; return $this->toRGB($h[1], $s[1], $l[1]); From 4124935aeedbc359751879cedb90de09eb71de75 Mon Sep 17 00:00:00 2001 From: Alex Ulko Date: Wed, 12 Nov 2025 00:25:32 +0100 Subject: [PATCH 5/5] process more cases in normalize color args --- src/Compiler.php | 53 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 65f32977..cb679f65 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4888,6 +4888,22 @@ protected function hueToRGB($m1, $m2, $h) return $m1; } + protected function normalizeStringArgs($args) { + $normalizedArgs = []; + if (is_array($args[0][2] ?? null)) { + foreach ($args[0][2] as $part) { + if ($part instanceof Node\Number) { + $normalizedArgs[] = $part; + } + if (is_array($part)) { + $normalizedArgs[] = $part; + } + } + } + + return $normalizedArgs; + } + /** * Normalize color function arguments (handle space-separated syntax) * @@ -4899,22 +4915,43 @@ protected function hueToRGB($m1, $m2, $h) */ protected function normalizeColorArgs($args, $expectedCount, $supportColor = false) { + if (is_array($args[0] ?? null) && ($args[0][0] ?? null) === Type::T_STRING) { + // ['string', '', ['list', '', ['color', 123, 123, 123]], '', '/', '', Node\Number] + $args = $this->normalizeStringArgs($args); + } + // Check if first arg is a list (space-separated syntax) - if (isset($args[0]) && is_array($args[0]) && $args[0][0] === Type::T_LIST) { + if (is_array($args[0] ?? null) && ($args[0][0] ?? null) === Type::T_LIST) { $listItems = $args[0][2]; // Get the list items // Flatten the list items, handling slash-separated values $normalizedItems = []; - foreach ($listItems as $item) { - if (is_array($item) && $item[0] === Type::T_STRING && is_array($item[2] ?? [])) { - // This is a string like "48% / 50%" - extract just the numbers - foreach ($item[2] as $part) { - if ($part instanceof Node\Number) { + + if (is_array($listItems[0] ?? null) && ($listItems[0][0] ?? null) === Type::T_LIST) { + // ['list', '', ['list', '', ['color', 123, 123, 123]]] + $normalizedItems = $this->normalizeColorArgs($listItems, $expectedCount, $supportColor); + } elseif (is_array($listItems[0] ?? null) && ($listItems[0][0] ?? null) === Type::T_COLOR) { + // ['color', 123, 123, 123] + $normalizedItems[] = $listItems[0]; + // Check list items + if (($listItems[1] ?? null) instanceof Node\Number) { + $normalizedItems[] = $listItems[1]; + } + // Proceed with opacity from normal args if not empty + if (($args[1] ?? null) instanceof Node\Number) { + $normalizedItems[] = $args[1]; + } + } else { + foreach ($listItems as $item) { + if (is_array($item) && ($item[0] ?? null) === Type::T_STRING && is_array($item[2] ?? null)) { + // This is a string like "48% / 50%" - extract just the numbers + $normalizedItem = $this->normalizeStringArgs($args); + foreach ($normalizedItem as $part) { $normalizedItems[] = $part; } + } else { + $normalizedItems[] = $item; } - } else { - $normalizedItems[] = $item; } }