Skip to content

Commit

Permalink
[FEATURE] Support none as color function component value (#859)
Browse files Browse the repository at this point in the history
Note that this keyword is only permitted in the "modern" syntax
(and thus functions using it must be rendered in that syntax).
  • Loading branch information
JakeQZ authored Feb 1, 2025
1 parent 6cbd495 commit dcb658c
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
25 changes: 20 additions & 5 deletions src/Value/Color.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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(
Expand Down Expand Up @@ -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:
Expand All @@ -306,6 +312,10 @@ private function renderAsHex(): string
*/
private function shouldRenderInModernSyntax(): bool
{
if ($this->HasNoneAsComponentValue()) {
return true;
}

if (!$this->colorFunctionMayHaveMixedValueTypes($this->getRealName())) {
return false;
}
Expand Down Expand Up @@ -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.
Expand Down
58 changes: 45 additions & 13 deletions tests/Unit/Value/ColorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)',
Expand Down Expand Up @@ -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)',
Expand Down Expand Up @@ -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)',
],
//*/
];
}

Expand Down Expand Up @@ -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)',
],
Expand All @@ -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)'
Expand Down

0 comments on commit dcb658c

Please sign in to comment.