Skip to content

Commit 76d3f78

Browse files
committed
[css-color-4] Channel keywords are no longer numer OR percentage, w3c#7876
1 parent f823185 commit 76d3f78

File tree

1 file changed

+100
-66
lines changed

1 file changed

+100
-66
lines changed

css-color-5/Overview.bs

Lines changed: 100 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -795,13 +795,21 @@ then each color channel can <em>either</em> be directly specified,
795795
or taken from the origin color
796796
(and possibly modified with [=math functions=]).
797797

798+
799+
<wpt>
800+
parsing/color-computed-relative-color.html
801+
parsing/color-invalid-relative-color.html
802+
parsing/color-valid-relative-color.html
803+
</wpt>
804+
798805
The precise details of each function's changes to accomodate [=relative colors=] are listed below,
799806
but they all follow a common structure:
800807

801808
* An [=origin color=] can be specified with a ''from <<color>>'' value at the start of the function.
802809
* If an [=origin color=] is specified,
803810
the remaining arguments can either be specified directly, as normal,
804-
be specified as a <dfn>channel keyword</dfn> referring to one of the channels of the [=origin color=].
811+
be specified as a <dfn>channel keyword</dfn>
812+
referring to one of the channels of the [=origin color=].
805813
[=Math functions=] can also use these keywords
806814
to do dynamic modifications of the [=origin color's=] channels.
807815
* [=Relative color=] syntax doesn't change whether an argument is required or optional.
@@ -810,37 +818,38 @@ but they all follow a common structure:
810818
(rather than defaulting to ''100%'', as it does in the absolute syntax).
811819
* Relative color syntax only applies to the modern
812820
(space separated components, / separator for alpha) syntax.
813-
It cannot be used with <a>legacy color syntax</a>
821+
It <em>cannot</em> be used with <a>legacy color syntax</a>
814822
and attempting to do so is an error.
815823

816-
If the [=origin color=] was originally specified with a different color function,
817-
it's first converted into the chosen color function,
818-
so it has meaningful values for the channels.
819-
820-
<wpt>
821-
parsing/color-computed-relative-color.html
822-
parsing/color-invalid-relative-color.html
823-
parsing/color-valid-relative-color.html
824-
</wpt>
824+
Except as specified for individual color functions,
825+
(for example, hues return an <<angle>>),
826+
the [=channel keywords=] return a <<number>>;
827+
if they were originally specified as a <<percentage>>,
828+
that percentage is resolved to a <<number>>.
825829

826830
<div class=example>
827-
For example, if a theme color is specified as opaque,
828-
but in a particular instance you need it to be partially transparent:
831+
For example, if a color is specified using <<percentage>>,
832+
then RCS in the same colorspace will use the resolved <<number>> form:
829833

830834
<pre highlight=css>
831-
html { --bg-color: blue; }
835+
html { --bluegreen: <span class="swatch" style="--color: teal"></span> oklab(54.3% -22.5% -5%); }
832836
.overlay {
833-
background: rgb(from var(--bg-color) r g b / 80%);
837+
background: <span class="swatch" style="--color: rgb(4.696% 39.17% 39.38%)"></span> oklab(from var(--bluegreen) calc(1.0 - l) calc(a * 0.8) b);
834838
}
835839
</pre>
836840

837-
In this example, the r, g, and b channels of the [=origin color=] are unchanged,
838-
indicated by specifying them with the keywords
839-
drawing their values from the [=origin color=],
840-
but the opacity is set to ''80%'' to make it slightly transparent,
841-
regardless of what the [=origin color's=] opacity was.
841+
In this example, the specified percentages are resolved to numbers,
842+
giving oklab(0.543 -0.09 -0.02).
843+
The resulting RCS color has l = 1 - 0.543 = 0.457,
844+
a = -0.09 * 0.8 = -0.072,
845+
and b is unchanged at -0.02:
846+
oklab(0.457 -0.072 -0.02).
842847
</div>
843848

849+
If the [=origin color=] was originally specified with a different color function,
850+
it's first converted into the chosen color function,
851+
so it has meaningful values for the channels.
852+
844853
<div class=example>
845854
By using the [=channel keywords=] in a [=math function=],
846855
an [=origin color=] can be manipulated in more advanced ways.
@@ -862,6 +871,24 @@ so it has meaningful values for the channels.
862871
due to being used in the ''lch()'' function.
863872
</div>
864873

874+
<div class=example>
875+
For example, if a theme color is specified as opaque,
876+
but in a particular instance you need it to be partially transparent:
877+
878+
<pre highlight=css>
879+
html { --bg-color: <span class="swatch" style="--color: blue"></span> blue; }
880+
.overlay {
881+
background: <span class="swatch" style="--color: rgb(0,0,255,80%)"></span> rgb(from var(--bg-color) r g b / 80%);
882+
}
883+
</pre>
884+
885+
In this example, the r, g, and b channels of the [=origin color=] are unchanged,
886+
indicated by specifying them with the keywords
887+
drawing their values from the [=origin color=],
888+
but the opacity is set to ''80%'' to make it slightly transparent,
889+
regardless of what the [=origin color's=] opacity was.
890+
</div>
891+
865892
<div class=example>
866893
While most uses of [=relative color=] syntax
867894
will use the [=channel keywords=] in their corresponding argument,
@@ -877,12 +904,12 @@ so it has meaningful values for the channels.
877904
</pre>
878905

879906
Using this,
880-
''red'' would become ''rgb(30% 30% 30%)'',
881-
''lime'' would become ''rgb(59% 59% 59%)'',
882-
and ''blue'' would become ''rgb(11% 11% 11%)''.
907+
''red'' would become ''rgb(76.5 76.5 76.5)'',
908+
''lime'' would become ''rgb(150.45 150.45 150.45)'',
909+
and ''blue'' would become ''rgb(150.45 150.45 150.45)''.
883910
A more moderate color, like ''darkolivegreen'',
884911
which has RGB values ''rgb(85 107 47)'',
885-
would become approximately ''rgb(37% 37% 37%)''.
912+
would become ''rgb(93.8 93.8 93.8)''.
886913

887914
(Rough because firstly,
888915
although this looks like a luminance calculation,
@@ -919,11 +946,11 @@ Within a [=relative color=] syntax ''rgb()'' function,
919946
the allowed [=channel keywords=] are:
920947

921948
* <dfn value for="rgb()">r</dfn>, <dfn value for="rgb()">g</dfn>, and <dfn value for="rgb()">b</dfn>
922-
are all <<percentage>>s or <<number>>s
949+
are all <<number>>s
923950
that correspond to the [=origin color's=] red, green, and blue channels
924-
after its conversion to sRGB. For <<number>>s, 255 (or 255.0)
925-
is equivalent to 100%.
926-
* <dfn value for="rgb()">alpha</dfn> is a <<number>> or <<percentage>> that corresponds to the [=origin color's=] alpha transparency
951+
after its conversion to sRGB.
952+
255.0 is equivalent to 100%.
953+
* <dfn value for="rgb()">alpha</dfn> is a <<number>> that corresponds to the [=origin color's=] alpha transparency
927954

928955
<div class="example">
929956
To manipulate color channels in the sRGB color space:
@@ -932,10 +959,12 @@ the allowed [=channel keywords=] are:
932959
rgb(from <span class="swatch" style="--color: indianred"></span> indianred 255 g b)
933960
</pre>
934961

935-
This takes the sRGB value of indianred (205 92 92) and replaces the red channel with 255 to give <span class="swatch" style="--color: rgb(255 92 92)"></span> rgb(255 92 92).
962+
This takes the sRGB value of indianred (205 92 92) and replaces
963+
the red channel with 255 to give
964+
<span class="swatch" style="--color: rgb(255 92 92)"></span> rgb(255 92 92).
936965
</div>
937966

938-
Relative color syntax is only applicable to the non-legacy RGB syntactic forms.
967+
Relative sRGB color syntax is <em>only</em> applicable to the non-legacy RGB syntactic forms.
939968

940969
<div class="example invalid">
941970
For example, this attempt to use the rgba
@@ -980,9 +1009,10 @@ the allowed [=channel keywords=] are:
9801009
after its conversion to sRGB,
9811010
normalized to a [0deg, 360deg) range
9821011
* <dfn value for="hsl()">s</dfn> and <dfn value for="hsl()">l</dfn>
983-
are <<percentage>>s or <<number>>s that correspond to the [=origin color's=] HSL saturation and lightness
1012+
are <<percentage>>s that correspond to the [=origin color's=]
1013+
HSL saturation and lightness,
9841014
after its conversion to sRGB
985-
* <dfn value for="hsl()">alpha</dfn> is a <<number>> or <<percentage>> that corresponds to the [=origin color's=] alpha transparency
1015+
* <dfn value for="hsl()">alpha</dfn> is a <<number>> that corresponds to the [=origin color's=] alpha transparency
9861016

9871017
<div class="example">
9881018
This adds 180 degrees to the hue angle, giving a complementary color.
@@ -993,7 +1023,7 @@ the allowed [=channel keywords=] are:
9931023
lightseagreen is hsl(177deg 70% 41%), so --complement is <span class="swatch" style="--color: hsl(357deg 70% 41%)"></span> hsl(357deg 70% 41%)
9941024
</div>
9951025

996-
Relative color syntax is only applicable to the non-legacy HSL syntactic forms.
1026+
Relative HSL color syntax is only applicable to the non-legacy HSL syntactic forms.
9971027

9981028
<h3 id="relative-HWB">Relative HWB Colors</h3>
9991029

@@ -1016,9 +1046,9 @@ the allowed [=channel keywords=] are:
10161046
after its conversion to sRGB,
10171047
normalized to a [0deg, 360deg) range
10181048
* <dfn value for="hwb()">w</dfn> and <dfn value for="hwb()">b</dfn>
1019-
are <<percentage>>s or <<number>>s that correspond to the [=origin color's=] HWB whiteness and blackness
1049+
are <<percentage>>s that correspond to the [=origin color's=] HWB whiteness and blackness
10201050
after its conversion to sRGB
1021-
* <dfn value for="hwb()">alpha</dfn> is a <<number>> or <<percentage>> that corresponds to the [=origin color's=] alpha transparency
1051+
* <dfn value for="hwb()">alpha</dfn> is a <<number>> that corresponds to the [=origin color's=] alpha transparency
10221052

10231053
<h3 id="relative-Lab">Relative Lab Colors</h3>
10241054

@@ -1036,31 +1066,34 @@ The grammar of the ''lab()'' function is extended as follows:
10361066
Within a [=relative color=] syntax ''lab()'' function,
10371067
the allowed [=channel keywords=] are:
10381068

1039-
* <dfn value for="lab()">l</dfn> is a <<percentage>> or <<number>>
1069+
* <dfn value for="lab()">l</dfn> is a <<number>>
10401070
that corresponds to the [=origin color's=] CIE Lightness
1041-
* <dfn value for="lab()">a</dfn> and <dfn value for="lab()">b</dfn> are <<percentage>>s or <<number>>s
1071+
* <dfn value for="lab()">a</dfn> and <dfn value for="lab()">b</dfn> are <<number>>s
10421072
that correspond to the [=origin color's=] CIELab a and b axes
1043-
* <dfn value for="lab()">alpha</dfn> is a <<number>> or <<percentage>> that corresponds to the [=origin color's=] alpha transparency
1073+
* <dfn value for="lab()">alpha</dfn> is a <<number>> that corresponds to the [=origin color's=] alpha transparency
10441074

10451075
<div class="example">
10461076
Multiple ways to adjust the transparency of a base color:
10471077

1048-
* ''lab(from var(--mycolor) l a b / 100%)'' sets the alpha of ''var(--mycolor)'' to 100% regardless of what it originally was.
1078+
* ''lab(from var(--mycolor) l a b / 100%)'' sets the alpha of ''var(--mycolor)'' to 1.0, regardless of what it originally was.
10491079
* ''lab(from var(--mycolor) l a b / calc(alpha * 0.8))'' reduces the alpha of ''var(--mycolor)'' by 20% of its original value.
1050-
* ''lab(from var(--mycolor) l a b / calc(alpha - 20%))'' reduces the alpha of ''var(--mycolor)'' by 20% of 100%.
10511080

10521081
Note that all the adjustments are lossless in the sense that no gamut clipping occurs, since lab() encompasses all visible color.
1053-
This is not true for the alpha adjustments in the sRGB based functions (such as 'rgb()', 'hsl()', or 'hwb()'), which would also convert to sRGB in addition to adjusting the alpha transparency.
1082+
This is not true for the alpha adjustments in the sRGB based functions
1083+
(such as 'rgb()', 'hsl()', or 'hwb()'),
1084+
which would also convert to sRGB
1085+
as a necessary step for calculation of HSL or HWB,
1086+
in addition to adjusting the alpha transparency.
10541087
</div>
10551088

10561089
<div class="example">
10571090
Fully desaturating a color to gray, keeping the exact same lightness:
10581091

10591092
<pre>
10601093
--mycolor: <span class="swatch" style="--color: orchid"></span> orchid;
1061-
// orchid is lab(62.753% 52.460 -34.103)
1094+
// orchid is lab(62.753 52.460 -34.103)
10621095
--mygray: <span class="swatch" style="--color: rgb(59.515% 59.515% 59.515%)"></span> lab(from var(--mycolor) l 0 0)
1063-
// mygray is lab(62.753% 0 0) which is rgb(59.515% 59.515% 59.515%)
1096+
// mygray is lab(62.753 0 0) which is rgb(59.515% 59.515% 59.515%)
10641097
</pre>
10651098
</div>
10661099

@@ -1080,11 +1113,11 @@ The grammar of the ''oklab()'' function is extended as follows:
10801113
Within a [=relative color=] syntax ''oklab()'' function,
10811114
the allowed [=channel keywords=] are:
10821115

1083-
* <dfn value for="oklab()">l</dfn> is a <<percentage>> or <<number>>
1116+
* <dfn value for="oklab()">l</dfn> is a <<number>>
10841117
that corresponds to the [=origin color's=] Oklab Lightness
1085-
* <dfn value for="oklab()">a</dfn> and <dfn value for="oklab()">b</dfn> are <<percentage>>s or <<number>>s
1118+
* <dfn value for="oklab()">a</dfn> and <dfn value for="oklab()">b</dfn> are <<number>>s
10861119
that correspond to the [=origin color's=] Oklab a and b axes
1087-
* <dfn value for="oklab()">alpha</dfn> is a <<number>> or <<percentage>> that corresponds to the [=origin color's=] alpha transparency
1120+
* <dfn value for="oklab()">alpha</dfn> is a <<number>> that corresponds to the [=origin color's=] alpha transparency
10881121

10891122
<h3 id="relative-LCH">Relative LCH Colors</h3>
10901123

@@ -1102,48 +1135,45 @@ The grammar of the ''lch()'' function is extended as follows:
11021135
Within a [=relative color=] syntax ''lch()'' function,
11031136
the allowed [=channel keywords=] are:
11041137

1105-
* <dfn value for="lch()">l</dfn> is a <<percentage>> or <<number>>
1138+
* <dfn value for="lch()">l</dfn> is a <<number>>
11061139
that corresponds to the [=origin color's=] CIE Lightness
1107-
* <dfn value for="lch()">c</dfn> is a <<percentage>> or <<number>>
1140+
* <dfn value for="lch()">c</dfn> is a <<number>>
11081141
that corresponds to the [=origin color's=] LCH chroma
11091142
* <dfn value for="lch()">h</dfn> is an <<angle>>
11101143
that corresponds to the [=origin color's=] LCH hue,
11111144
normalized to a [0deg, 360deg) range.
1112-
* <dfn value for="lch()">alpha</dfn> is a <<number>> or <<percentage>> that corresponds to the [=origin color's=] alpha transparency
1145+
* <dfn value for="lch()">alpha</dfn> is a <<number>> that corresponds to the [=origin color's=] alpha transparency
11131146

1114-
Because LCH is both perceptually uniform and chroma-preserving,
1115-
and because the axes correspond to easily understood attributes of a color,
1116-
LCH is a good choice for color manipulation.
11171147

11181148
<div class="example">
11191149
''lch(from peru calc(l * 0.8) c h)'' produces a color that is 20% darker than <span class="swatch" style="--color: peru"></span> peru or lch(62.2532% 54.0114 63.6769), with its chroma and hue left unchanged.
1120-
The result is <span class="swatch" style="--color: rgb(57.58% 32.47% 3.82%)"> </span> lch(49.80256% 54.0114 63.6769)
1150+
The result is <span class="swatch" style="--color: rgb(57.58% 32.47% 3.82%)"> </span> lch(49.80256 54.0114 63.6769)
11211151
</div>
11221152

11231153
<div class="example">
11241154
This adds 180 degrees to the hue angle, giving the complementary color.
11251155
<pre>
11261156
--accent: <span class="swatch" style="--color: lightseagreen"></span> lightseagreen;
1127-
--complement: <span class="swatch" style="--color: rgb(88.2814% 51.1047% 58.3039%)"></span> LCH(from var(--accent) l c calc(h + 180deg));
1157+
--complement: <span class="swatch" style="--color: rgb(88.2814% 51.1047% 58.3039%)"></span> lch(from var(--accent) l c calc(h + 180deg));
11281158
</pre>
1129-
lightseagreen is LCH(65.4937% 39.4484 190.1013), so --complement is <span class="swatch" style="--color: rgb(88.2814% 51.1047% 58.3039%)"></span> LCH(65.4937% 39.4484 370.1013)
1159+
lightseagreen is lch(65.4937 39.4484 190.1013), so --complement is <span class="swatch" style="--color: rgb(88.2814% 51.1047% 58.3039%)"></span> lch(65.4937 39.4484 370.1013)
11301160
</div>
11311161

11321162
<div class="example">
11331163
Fully desaturating a color to gray, keeping the exact same lightness:
11341164

11351165
<pre>
11361166
--mycolor: <span class="swatch" style="--color: orchid"></span> orchid;
1137-
// orchid is lch(62.753% 62.571 326.973)
1167+
// orchid is lch(62.753 62.571 326.973)
11381168
--mygray: <span class="swatch" style="--color: rgb(59.515% 59.515% 59.515%)"></span> lch(from var(--mycolor) l 0 h)
1139-
// mygray is lch(62.753% 0 326.973) which is rgb(59.515% 59.515% 59.515%)
1169+
// mygray is lch(62.753 0 326.973) which is rgb(59.515% 59.515% 59.515%)
11401170
</pre>
11411171

11421172
But now (since the hue was preserved) <em>re-saturating</em> again
11431173

11441174
<pre>
11451175
--mymuted: <span class="swatch" style="--color: rgb(72.710% 53.293% 71.224%)"></span> lch(from var(--mygray) l 30 h);
1146-
// mymuted is lch(62.753% 30 326.973) which is rgb(72.710% 53.293% 71.224%)
1176+
// mymuted is lch(62.753 30 326.973) which is rgb(72.710% 53.293% 71.224%)
11471177
</pre>
11481178
</div>
11491179

@@ -1235,14 +1265,18 @@ The grammar of the ''oklch()'' function is extended as follows:
12351265
Within a [=relative color=] syntax ''oklch()'' function,
12361266
the allowed [=channel keywords=] are:
12371267

1238-
* <dfn value for="oklch()">l</dfn> is a <<percentage>> or <<number>>
1268+
* <dfn value for="oklch()">l</dfn> is a <<number>>
12391269
that corresponds to the [=origin color's=] Oklab Lightness
1240-
* <dfn value for="oklch()">c</dfn> is a <<percentage>> or <<number>>
1270+
* <dfn value for="oklch()">c</dfn> is a <<number>>
12411271
that corresponds to the [=origin color's=] Oklch chroma
12421272
* <dfn value for="oklch()">h</dfn> is an <<angle>>
12431273
that corresponds to the [=origin color's=] Oklch hue,
12441274
normalized to a [0deg, 360deg) range.
1245-
* <dfn value for="oklch()">alpha</dfn> is a <<number>> or <<percentage>> that corresponds to the [=origin color's=] alpha transparency
1275+
* <dfn value for="oklch()">alpha</dfn> is a <<number>> that corresponds to the [=origin color's=] alpha transparency
1276+
1277+
Because Oklch is both perceptually uniform and chroma-preserving,
1278+
and because the axes correspond to easily understood attributes of a color,
1279+
Oklch is a good choice for color manipulation.
12461280

12471281
<div class="example" id="ex-oklch-wildly-oog">
12481282
In this example, the aim is again to produce a new color
@@ -1258,15 +1292,15 @@ again produces an out of gamut color.
12581292
oklch(from var(--mycolor) l c calc(h - 120));
12591293
</pre>
12601294

1261-
--mycolor is <span class="swatch" style="--color: rgb(86.1% 33.4% 97.6%)"></span> oklch(69.012% 0.25077 319.893).
1295+
--mycolor is <span class="swatch" style="--color: rgb(86.1% 33.4% 97.6%)"></span> oklch(0.69012 0.25077 319.893).
12621296
Subtracting 120 from the Hue gives a very high-chroma blue-green,
1263-
<span class="swatch oog" style="--color: grey"></span> oklch(69.012% 0.25077 199.893)
1297+
<span class="swatch oog" style="--color: grey"></span> oklch(0.69012 0.25077 199.893)
12641298
which is out of sRGB gamut,
12651299
color(srgb -0.6018 0.7621 0.8448)
12661300
as the negative red component indicates.
12671301
Bring this into gamut
12681302
by reducing Oklch Chroma, yeilds
1269-
<span class="swatch" style="--color: rgb(0.079% 69.282% 72.067%)"></span> oklch(69.012% 0.1173 199.893).
1303+
<span class="swatch" style="--color: rgb(0.079% 69.282% 72.067%)"></span> oklch(0.69012 0.1173 199.893).
12701304
The Oklch chroma has dropped from 0.251 to 0.117.
12711305
</div>
12721306

@@ -1339,14 +1373,14 @@ Within a [=relative color=] syntax ''color()'' function using <<predefined-rgb-p
13391373
the allowed [=channel keywords=] are:
13401374

13411375
* <dfn value for="color()">r</dfn>, <dfn value for="color()">g</dfn>, and <dfn value for="color()">b</dfn>
1342-
are all <<percentage>>s or <<number>>s
1376+
are all <<number>>s
13431377
that correspond to the [=origin color's=] red, green, and blue channels
13441378
after its conversion to the predefined RGB color space.
13451379

13461380
Within a [=relative color=] syntax ''color()'' function using <<xyz-params>>,
13471381
the allowed [=channel keywords=] are:
13481382

1349-
* <dfn value for="color()">x</dfn>, <dfn value for="color()">y</dfn>, <dfn value for="color()">z</dfn> are all <<percentage>>s or <<number>>s
1383+
* <dfn value for="color()">x</dfn>, <dfn value for="color()">y</dfn>, <dfn value for="color()">z</dfn> are all <<number>>s
13501384
that correspond to the [=origin color's=] X, Y and Z channels
13511385
after its conversion to relative CIE XYZ color space
13521386
adapted to the relevant white point.

0 commit comments

Comments
 (0)