diff --git a/CHANGELOG.md b/CHANGELOG.md index bf3db03a..13d94fe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Please also have a look at our - `rgb` and `rgba`, and `hsl` and `hsla` are now aliases (#797} - Parse color functions that use the "modern" syntax (#800) - Render RGB functions with "modern" syntax when required (#840) + - Support `none` as color function component value (#859) - Add a class diagram to the README (#482) - Add more tests (#449) diff --git a/src/Value/Color.php b/src/Value/Color.php index 8b913d54..c4d6a4ee 100644 --- a/src/Value/Color.php +++ b/src/Value/Color.php @@ -116,15 +116,20 @@ private static function parseColorFunction(ParserState $parserState): CSSFunctio } $containsVar = false; + $containsNone = false; $isLegacySyntax = false; $expectedArgumentCount = $parserState->strlen($colorModeForParsing); for ($argumentIndex = 0; $argumentIndex < $expectedArgumentCount; ++$argumentIndex) { $parserState->consumeWhiteSpace(); + $valueKey = $colorModeForParsing[$argumentIndex]; if ($parserState->comes('var')) { - $colorValues[$colorModeForParsing[$argumentIndex]] = CSSFunction::parseIdentifierOrFunction($parserState); + $colorValues[$valueKey] = CSSFunction::parseIdentifierOrFunction($parserState); $containsVar = true; + } elseif (!$isLegacySyntax && $parserState->comes('none')) { + $colorValues[$valueKey] = $parserState->parseIdentifier(); + $containsNone = true; } else { - $colorValues[$colorModeForParsing[$argumentIndex]] = Size::parse($parserState, true); + $colorValues[$valueKey] = Size::parse($parserState, true); } // This must be done first, to consume comments as well, so that the `comes` test will work. @@ -139,10 +144,10 @@ private static function parseColorFunction(ParserState $parserState): CSSFunctio break; } - // "Legacy" syntax is comma-delimited. + // "Legacy" syntax is comma-delimited, and does not allow the `none` keyword. // "Modern" syntax is space-delimited, with `/` as alpha delimiter. // They cannot be mixed. - if ($argumentIndex === 0) { + if ($argumentIndex === 0 && !$containsNone) { // An immediate closing parenthesis is not valid. if ($parserState->comes(')')) { throw new UnexpectedTokenException( @@ -291,7 +296,8 @@ private function renderAsHex(): string } /** - * The "legacy" syntax does not allow RGB colors to have a mixture of `percentage`s and `number`s. + * The "legacy" syntax does not allow RGB colors to have a mixture of `percentage`s and `number`s, + * and does not allow `none` as any component value. * * The "legacy" and "modern" monikers are part of the formal W3C syntax. * See the following for more information: @@ -306,6 +312,10 @@ private function renderAsHex(): string */ private function shouldRenderInModernSyntax(): bool { + if ($this->HasNoneAsComponentValue()) { + return true; + } + if (!$this->colorFunctionMayHaveMixedValueTypes($this->getRealName())) { return false; } @@ -337,6 +347,11 @@ private function shouldRenderInModernSyntax(): bool return $hasPercentage && $hasNumber; } + private function hasNoneAsComponentValue(): bool + { + return \in_array('none', $this->aComponents, true); + } + /** * Some color functions, such as `rgb`, * may have a mixture of `percentage`, `number`, or possibly other types in their arguments. diff --git a/tests/Unit/Value/ColorTest.php b/tests/Unit/Value/ColorTest.php index 5c502f82..f516d313 100644 --- a/tests/Unit/Value/ColorTest.php +++ b/tests/Unit/Value/ColorTest.php @@ -114,12 +114,18 @@ public static function provideValidColorAndExpectedRendering(): array 'rgb(0% 60% 0%)', 'rgb(0%,60%,0%)', ], - /* - 'modern rgb with none' => [ + 'modern rgb with none as red' => [ 'rgb(none 119 0)', 'rgb(none 119 0)', ], - //*/ + 'modern rgb with none as green' => [ + 'rgb(0 none 0)', + 'rgb(0 none 0)', + ], + 'modern rgb with none as blue' => [ + 'rgb(0 119 none)', + 'rgb(0 119 none)', + ], 'modern rgba with fractional alpha' => [ 'rgb(0 119 0 / 0.5)', 'rgba(0,119,0,.5)', @@ -148,12 +154,10 @@ public static function provideValidColorAndExpectedRendering(): array 'rgb(0% 60% 0% / 50%)', 'rgba(0%,60%,0%,50%)', ], - /* 'modern rgba with none as alpha' => [ 'rgb(0 119 0 / none)', - 'rgba(0 119 0 / none)', + 'rgba(0 119 0/none)', ], - //*/ 'legacy rgb with var for R' => [ 'rgb(var(--r), 119, 0)', 'rgb(var(--r),119,0)', @@ -310,22 +314,26 @@ public static function provideValidColorAndExpectedRendering(): array 'hsl(120 100% 25%)', 'hsl(120,100%,25%)', ], - /* - 'modern hsl with none' => [ + 'modern hsl with none as hue' => [ 'hsl(none 100% 25%)', 'hsl(none 100% 25%)', ], - //*/ + 'modern hsl with none as saturation' => [ + 'hsl(120 none 25%)', + 'hsl(120 none 25%)', + ], + 'modern hsl with none as lightness' => [ + 'hsl(120 100% none)', + 'hsl(120 100% none)', + ], 'modern hsla' => [ 'hsl(120 100% 25% / 0.5)', 'hsla(120,100%,25%,.5)', ], - /* 'modern hsla with none as alpha' => [ - 'hsl(120 100% 25% none)', - 'hsla(120 100% 25% none)', + 'hsl(120 100% 25% / none)', + 'hsla(120 100% 25%/none)', ], - //*/ ]; } @@ -386,6 +394,18 @@ public static function provideInvalidColor(): array 'rgb(255, 0px, 0)', ], //*/ + 'legacy rgb color with none as red' => [ + 'rgb(none, 0, 0)', + ], + 'legacy rgb color with none as green' => [ + 'rgb(255, none, 0)', + ], + 'legacy rgb color with none as blue' => [ + 'rgb(255, 0, none)', + ], + 'legacy rgba color with none as alpha' => [ + 'rgba(255, 0, 0, none)', + ], 'modern rgb color without slash separator for alpha' => [ 'rgb(255 0 0 0.5)', ], @@ -407,6 +427,18 @@ public static function provideInvalidColor(): array 'legacy hsl color with 5 arguments' => [ 'hsl(0, 100%, 50%, 0.5, 0)', ], + 'legacy hsl color with none as hue' => [ + 'hsl(none, 100%, 50%)', + ], + 'legacy hsl color with none as saturation' => [ + 'hsl(0, none, 50%)', + ], + 'legacy hsl color with none as lightness' => [ + 'hsl(0, 100%, none)', + ], + 'legacy hsla color with none as alpha' => [ + 'hsl(0, 100%, 50%, none)', + ], /* 'legacy hsl color without % for S/L units' => [ 'hsl(0, 1, 0.5)'