From 42f181c0311b51b8325d8038f01d972907653459 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Mon, 20 Jan 2025 02:58:57 +0000 Subject: [PATCH 1/3] [FEATURE] Support rgba and rgb (etc.) being aliases As of CSS Color Module Level 4, `rgba` is an alias of `rgb`; likewise, `hsla` is an alias of `hsl`. This change allows any of the above color functions to contain either three or four arguments, with alpha assumed as the fourth, and allowed to be absent. --- CHANGELOG.md | 2 ++ src/Value/Color.php | 37 ++++++++++++++++++++++++++++------ tests/Unit/Value/ColorTest.php | 8 +++----- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1f9a7e9..d3704586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ Please also have a look at our ### Added +- Partial support for CSS Color Module Level 4 syntax: + - `rgb` and `rgba`, and `hsl` and `hsla` are now aliases (#797} - Add official support for PHP 8.4 (#657) - Support arithmetic operators in CSS function arguments (#607) - Add support for inserting an item in a CSS list (#545) diff --git a/src/Value/Color.php b/src/Value/Color.php index 07ec5b13..fbf9169c 100644 --- a/src/Value/Color.php +++ b/src/Value/Color.php @@ -72,23 +72,48 @@ public static function parse(ParserState $oParserState, bool $bIgnoreCase = fals $oParserState->consumeWhiteSpace(); $oParserState->consume('('); + // CSS Color Module Level 4 says that `rgb` and `rgba` are now aliases; likewise `hsl` and `hsla`. + // So, attempt to parse with the `a`, and allow for it not being there. + switch ($sColorMode) { + case 'rgb': + case 'hsl': + $colorModeForParsing = $sColorMode . 'a'; + $mayHaveOptionalAlpha = true; + break; + case 'rgba': + case 'hsla': + $colorModeForParsing = $sColorMode; + $mayHaveOptionalAlpha = true; + break; + default: + $colorModeForParsing = $sColorMode; + $mayHaveOptionalAlpha = false; + break; + } + $bContainsVar = false; - $iLength = $oParserState->strlen($sColorMode); + $iLength = $oParserState->strlen($colorModeForParsing); for ($i = 0; $i < $iLength; ++$i) { $oParserState->consumeWhiteSpace(); if ($oParserState->comes('var')) { - $aColor[$sColorMode[$i]] = CSSFunction::parseIdentifierOrFunction($oParserState); + $aColor[$colorModeForParsing[$i]] = CSSFunction::parseIdentifierOrFunction($oParserState); $bContainsVar = true; } else { - $aColor[$sColorMode[$i]] = Size::parse($oParserState, true); + $aColor[$colorModeForParsing[$i]] = Size::parse($oParserState, true); } - if ($bContainsVar && $oParserState->comes(')')) { - // With a var argument the function can have fewer arguments + // This must be done first, to consume comments as well, so that the `comes` test will work. + $oParserState->consumeWhiteSpace(); + + // With a var argument, the function can have fewer arguments. + // And as of CSS Color Module Level 4, the alpha argument is optional. + $canCloseNow = + $bContainsVar || + $mayHaveOptionalAlpha && $i >= $iLength - 2; + if ($canCloseNow && $oParserState->comes(')')) { break; } - $oParserState->consumeWhiteSpace(); if ($i < ($iLength - 1)) { $oParserState->consume(','); } diff --git a/tests/Unit/Value/ColorTest.php b/tests/Unit/Value/ColorTest.php index 08df8573..b4799aa8 100644 --- a/tests/Unit/Value/ColorTest.php +++ b/tests/Unit/Value/ColorTest.php @@ -62,15 +62,15 @@ public static function provideValidColorAndExpectedRendering(): array 'rgba(0, 119, 0, 50%)', 'rgba(0,119,0,50%)', ], - /* 'legacy rgb as rgba' => [ 'rgba(0, 119, 0)', - 'rgb(0,119,0)', + '#070', ], 'legacy rgba as rgb' => [ 'rgb(0, 119, 0, 0.5)', 'rgba(0,119,0,.5)', ], + /* 'modern rgb' => [ 'rgb(0 119 0)', 'rgb(0,119,0)', @@ -112,16 +112,14 @@ public static function provideValidColorAndExpectedRendering(): array 'hsla(120, 100%, 25%, 50%)', 'hsla(120,100%,25%,50%)', ], - /* 'legacy hsl as hsla' => [ 'hsla(120, 100%, 25%)', 'hsl(120,100%,25%)', ], 'legacy hsla as hsl' => [ 'hsl(120, 100%, 25%, 0.5)', - 'hsla(120,100%,25%,0.5)', + 'hsla(120,100%,25%,.5)', ], - //*/ ]; } From 3f54b77745d05130b8df498ca5e08497c6fa9cd7 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Mon, 20 Jan 2025 22:32:15 +0000 Subject: [PATCH 2/3] Changes suggested in code review. --- src/Value/Color.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Value/Color.php b/src/Value/Color.php index fbf9169c..d5d4313a 100644 --- a/src/Value/Color.php +++ b/src/Value/Color.php @@ -76,10 +76,14 @@ public static function parse(ParserState $oParserState, bool $bIgnoreCase = fals // So, attempt to parse with the `a`, and allow for it not being there. switch ($sColorMode) { case 'rgb': + $colorModeForParsing = 'rgba'; + $mayHaveOptionalAlpha = true; + break; case 'hsl': - $colorModeForParsing = $sColorMode . 'a'; + $colorModeForParsing = 'hsla'; $mayHaveOptionalAlpha = true; break; + // These two cases are handled identically. case 'rgba': case 'hsla': $colorModeForParsing = $sColorMode; @@ -88,7 +92,6 @@ public static function parse(ParserState $oParserState, bool $bIgnoreCase = fals default: $colorModeForParsing = $sColorMode; $mayHaveOptionalAlpha = false; - break; } $bContainsVar = false; @@ -105,7 +108,7 @@ public static function parse(ParserState $oParserState, bool $bIgnoreCase = fals // This must be done first, to consume comments as well, so that the `comes` test will work. $oParserState->consumeWhiteSpace(); - // With a var argument, the function can have fewer arguments. + // With a `var` argument, the function can have fewer arguments. // And as of CSS Color Module Level 4, the alpha argument is optional. $canCloseNow = $bContainsVar || From 95ce6d12ef7868cb2666b701292d61129c1a3f77 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Wed, 22 Jan 2025 17:49:53 +0000 Subject: [PATCH 3/3] Further changes suggested in review. --- src/Value/Color.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Value/Color.php b/src/Value/Color.php index d5d4313a..f62bda3c 100644 --- a/src/Value/Color.php +++ b/src/Value/Color.php @@ -83,8 +83,8 @@ public static function parse(ParserState $oParserState, bool $bIgnoreCase = fals $colorModeForParsing = 'hsla'; $mayHaveOptionalAlpha = true; break; - // These two cases are handled identically. case 'rgba': + // This is handled identically to the following case. case 'hsla': $colorModeForParsing = $sColorMode; $mayHaveOptionalAlpha = true; @@ -112,7 +112,7 @@ public static function parse(ParserState $oParserState, bool $bIgnoreCase = fals // And as of CSS Color Module Level 4, the alpha argument is optional. $canCloseNow = $bContainsVar || - $mayHaveOptionalAlpha && $i >= $iLength - 2; + ($mayHaveOptionalAlpha && $i >= $iLength - 2); if ($canCloseNow && $oParserState->comes(')')) { break; }