Closed SebastianZ closed 8 months ago
This is due to the historical use of 0..255 integers in rgb()
. When we wanted to add a bit-depth-independent way to specify the RGB components, we leant on percentages. But we required you to use all one or all the other. So nothing like
rgb(17.5% 127 54.8%)
for example.
I agree that this collides with the user expectation that 0% and 0 mean the same thing.
At this point these are both legacy syntactic forms.
Even in CSS Color 5, relative color syntax does allow you to mix numbers and percents but only with the relative color syntax; the historical forms we left untouched.
I guess the questions to ask are:
I suspect that lots of in-the-wild script to manipulate color is likely to break if number and percent can be freely mixed.
Also re-tagging to CSS Color 5, as Color 4 is firming up for Candidate Recommendation
(Untagging css-values-4 as we cannot make this allowance generally across CSS.)
Yeah, we absolutely cannot allow unitless percentages in general; width: 0%
has very different behavior from width: 0px
when the container has indefinite available width.
While the color syntaxes don't have a similar issue (their use of %s are relative to fixed reference values, rather than a contextual one), I don't see a good reason to allow 0, specifically, to intermix with percentages. I'd rather just relax the grammar to allow any mixture of numbers and %s across the arguments. I'm not sure why the spec required them to be all one way or the other, as that restriction doesn't do anything productive.
I'm not sure why the spec required them to be all one way or the other, as that restriction doesn't do anything productive.
Because that is what CSS1 did.
So there is an assumed Web compat issue (what content does, what scripts expect, what browsers implement since forever), although given other changes like the 0..255 form now allowing <number>
, removal of commas, and addition of none
it isn't clear that this is a strong argument.
Yeah, we absolutely cannot allow unitless percentages in general;
width: 0%
has very different behavior fromwidth: 0px
when the container has indefinite available width.
I wasn't aware of that. So yeah, then we can't generalize unitless percentages.
I'd rather just relax the grammar to allow any mixture of numbers and %s across the arguments.
So there is an assumed Web compat issue (what content does, what scripts expect, what browsers implement since forever), although given other changes like the 0..255 form now allowing
, removal of commas, and addition of none it isn't clear that this is a strong argument.
I'd be fine to relax the syntax to allow mixtures of numbers and percentages to cover the described use case (even when it looks a bit weird to me mixing numbers besides 0 with percentages).
@svgeesus As you say, there were already a lot of other more disruptive changes to the syntax. So I don't think that's a big issue. But maybe someone could make an HTTP archive query to check whether there are currently usages of number and percentage mixtures (that currently break but would work after that change and cause a visual difference).
At least tools like VS Code obviously already support mixing numbers and percentages. And SCSS and PostCSS just pass them through without errors. So the Web ecosystem seems to be already prepared for that. And if there are tools that don't allow mixing both, they can adjust easily.
Sebastian
But maybe someone could make an HTTP archive query to check whether there are currently usages of number and percentage mixtures (that currently break but would work after that change and cause a visual difference).
Happy to give this a go.
Could you clarify which color functions you're interested in? Here's the list I was planning to use:
Could you also clarify whether it's valid to have number values for everything but the alpha? Is this valid: rgba(0,0,0,5%)
?
Thank you for helping out, @rviscomi!
But maybe someone could make an HTTP archive query to check whether there are currently usages of number and percentage mixtures (that currently break but would work after that change and cause a visual difference).
Happy to give this a go.
Could you clarify which color functions you're interested in? Here's the list I was planning to use:
* rgb * rgba * hsl * hsla * color
color()
already allows to mix <number>
s and <percentage>
s as well as all the other new color functions like lch()
, lab()
, etc. So this is just about the four old color functions rgb()
, rgba()
, hsl()
and hsla()
and maybe also the new hwb()
. The syntaxes of the last three already got changed in CSS Color 5 to allow mixing numbers and percentages.
The four old color functions need to be checked using their old comma-separated syntaxes (e.g. rgb(0, 0, 100%)
, rgb(0, 0, 100%, 90%)
and hsla(0, 0, 100%, 0.9)
) and their new space- plus optional slash-separated syntaxes (e.g. rgb(0 0 100%)
, rgb(0 0 100% / 90%)
and hsl(0 0.4 60% / 0.9)
). Also note that rgb()
is syntactically identical to rgba()
since CSS Color 4, and hsl()
to hsla()
.
So all variants should be checked.
Could you also clarify whether it's valid to have number values for everything but the alpha? Is this valid:
rgba(0,0,0,5%)
?
That's already valid and implemented in browsers today. So no need to check that. But what needs to be checked are the variants above in combination with <number>
and <percentage>
alpha values.
Sebastian
Thanks @SebastianZ that was helpful.
Here's the query I came up with:
# Warning: this query consumes 3.83 TB, which costs ~$20.
CREATE TEMP FUNCTION HAS_MIXED_ARGS(fn STRING) RETURNS BOOL LANGUAGE js AS r'''
let args = fn.match(/\(\s*(.*)\s*\)/);
if (!args) {
return null;
}
args = args[1].split(/[,\s/]+/).slice(0, 3);
if (args.length < 3) {
return null;
}
return args.some(arg => arg.match(/^[\d.]+$/)) && args.some(arg => arg.match(/^[\d.]+%$/))
''';
CREATE OR REPLACE TABLE `httparchive.scratchspace.css_color_mixed_types` AS
SELECT
page,
url,
color
FROM
`httparchive.almanac.summary_response_bodies`,
UNNEST(REGEXP_EXTRACT_ALL(body, r'\b((?:rgba?|hsla?|hwb)\([^\)]+\))')) AS color
WHERE
date = '2022-06-01' AND
client = 'mobile' AND
type = 'css' AND
HAS_MIXED_ARGS(color)
(I'd appreciate if someone double checked my regular expressions. I know these types are a lot more complex, but I went with a simpler definition since I assume it will account for most usage. Also note that this approach doesn't evaluate custom properties.)
2,390,213 pages (30% of all pages in the dataset) include a color function that mixes <number>
and <percentage>
types. This appears to be primarily driven by stylesheets included with WordPress plugins, which tend to mix types in the hsla
function, eg background:hsla(0,0%,100%,.3);
. Other CMSs like Squarespace and Shopify use this pattern as well.
Color function breakdown:
fn | pages |
---|---|
hsla | 2,352,227 |
hsl | 82,635 |
rgb | 3,308 |
rgba | 1,083 |
hwb | 11 |
Examples:
hsla(0,0%,62%,.7)
hsla(0,0%,66%,.5)
hsla(0, 0%, 100%, var(--tor-border-opacity, 1)
hsl(40, 95%, 76%)
hsl(211, 100%, 85%)
hsl(170.8, 100%, 70%)
rgb(255 255 51%)
rgb(255 47 11.25%)
rgb(232 181 3.42307692%)
rgba(0,0%,0%,6%)
rgba(0,103%,143%,35%)
rgba(120,100%,75%,.3)
hwb(240 15% 83%)
hwb(0 100% 0%)
hwb(204 94% 0%)
Top 10 colors:
color | pages |
---|---|
hsla(0,0%,100%,.5) | 1,199,422 |
hsla(0,0%,100%,.3) | 1,164,973 |
hsla(0,0%,100%,.4) | 1,052,097 |
hsla(0,0%,100%,.8) | 800,744 |
hsla(0,0%,100%,.9) | 740,812 |
hsla(0,0%,100%,.6) | 625,127 |
hsla(0,0%,100%,.1) | 466,094 |
hsla(0,0%,100%,.2) | 451,834 |
hsla(0,0%,8%,.5) | 451,003 |
hsla(0,0%,93.3%,.9) | 414,776 |
Top 10 stylesheets:
Top 10 stylesheet hosts:
host | pages |
---|---|
c0.wp.com | 97,929 |
s0.wp.com | 72,357 |
static.parastorage.com | 63,244 |
onesignal.com | 41,855 |
assets.squarespace.com | 31,137 |
cdn.shopify.com | 29,540 |
code.jivosite.com | 27,960 |
code.jivo.ru | 24,225 |
static.xx.fbcdn.net | 19,840 |
static.tacdn.com | 18,344 |
I saved the results to the httparchive.scratchspace.css_color_mixed_types
table. It's 6.8 GB so much faster and cheaper to query, if anyone else wanted to dig in.
I think the query ended up not being right, since I think those "top 10 colors" are all valid today. I'm not sure exactly what the change that we'd want to consider is for hsl()
and hsla()
, though.
Ah yeah, let me know if there are any special cases like numeric hues being valid.
@dbaron wrote:
I think the query ended up not being right, since I think those "top 10 colors" are all valid today. I'm not sure exactly what the change that we'd want to consider is for
hsl()
andhsla()
, though.
This issue does not mean to change hsl()
and hsla()
, but they already got extended in CSS Color 5 to accept <number>
values for their saturation and lightness values. The same applies to hwb()
and its whiteness and blackness values.
So I thought it would be good to get some statistics on those as well because some values were invalid in CSS Color 4 but are valid in 5, e.g. hsl(0 0 0)
or hsla(200, 50%, 0.6, 0.8)
.
Ah yeah, let me know if there are any special cases like numeric hues being valid.
Numeric hues were actually always valid for hsl()
and hsla()
since their introduction back in 2002. 😃 In addition to that, the hue can also be expressed as <angle>
value, so e.g. 30deg
or 0.5turn
are valid and implemented by browsers already.
@rviscomi What has changed is that the second and third component of hsl()
, hsla()
and hwb()
can now also be a number. Previously, they were just allowed to be percentages. That menas the check for those three functions needs to be more explicit.
For reference, see the syntax of level 4 of the spec. with the one of level 5.
Sebastian
Thanks for the guidance @dbaron and @SebastianZ. Here's a revised approach that only looks at params 2 and 3 for hsl()
, hsla()
, and hwb()
. Does this look better to you?
# Warning: this query consumes 3.83 TB, which costs ~$20.
CREATE TEMP FUNCTION HAS_MIXED_ARGS(value STRING) RETURNS BOOL LANGUAGE js AS r'''
let match = value.match(/(\w+)\(\s*(.*)\s*\)/);
if (!match) {
return null;
}
let fn = match[1];
let args = match[2].split(/[,\s/]+/).slice(0, 3);
if (args.length < 3) {
return null;
}
if (!fn.startsWith('rgb')) {
// The hue can be numeric and it not be mixed, so ignore.
args = args.slice(1, 3);
}
return args.some(arg => arg.match(/^[\d.]+$/)) && args.some(arg => arg.match(/^[\d.]+%$/));
''';
CREATE OR REPLACE TABLE `httparchive.scratchspace.css_color_mixed_types` AS
SELECT
page,
url,
color
FROM
`httparchive.almanac.summary_response_bodies`,
UNNEST(REGEXP_EXTRACT_ALL(body, r'\b((?:rgba?|hsla?|hwb)\([^\)]+\))')) AS color
WHERE
date = '2022-06-01' AND
client = 'mobile' AND
type = 'css' AND
HAS_MIXED_ARGS(color)
This time only 7,625 pages are detected as having mixed types. Many of these are subdomains of substack.com.
Color function breakdown:
fn | pages |
---|---|
rgb | 3,308 |
hsla | 3,063 |
rgba | 1,082 |
hsl | 194 |
hwb | 1 |
Examples:
rgb(101 119 6.7%)
rgb(255 77 3.95%)
rgb(101 119 8.93333333%)
hsla(0,0,80%,.15)
hsla(0,0,100%,.35)
hsla(0, 0, 100%, 0)
rgba(0,0%,0%,10%)
rgba(0,0%,0%,40%)
rgba(0,0%,0%,6%)
hsl(334,29, 76%, 85%)
hsl(var(--theme-button--color,0,0%,100%)
hsl(0deg 0 54%)
hwb(0 0 100%/.2)
Top 10 colors:
color | pages |
---|---|
rgb(101 119 8.93333333%) | 2,191 |
rgb(101 119 6.7%) | 2,191 |
hsla(0,0,100%,.8) | 1,083 |
rgb(0 0 0%) | 719 |
rgba(0,0%,0%,10%) | 539 |
hsla(0,0,100%,.5) | 427 |
hsla(0,0,100%,.75) | 427 |
hsla(0,0,100%,.9) | 351 |
hsla(0,0,100%,.3) | 258 |
rgba(0,0%,0%,6%) | 257 |
Top 10 stylesheets:
Top 10 stylesheet hosts:
host | pages |
---|---|
substackcdn.com | 2,191 |
uiassets.izmocars.com | 240 |
irp.cdn-website.com | 141 |
static.zdassets.com | 86 |
www.kinoheld.de | 81 |
cdn.myshoptet.com | 50 |
resources.kvikymart.space | 48 |
static.zdravotniregistr.cz | 42 |
static.modernilekar.cz | 39 |
static.narodnizdravotniregistr.cz | 38 |
color()
already allows to mix<number>
s and<percentage>
s
It does, but this was untested in WPT to date so I have a PR for a new WPT test, specifically for mixed <number>
, <percentage>
and none
in color()
@tabatkins @SebastianZ @weinig Review would be welcome
bump, someone review my WPT test please?
@rviscomi Thank you again for your efforts! Those results look much more realistical.
With the total set of 2,390,213 pages, that's 0.319% of all pages. This is still a relatively high number of pages. Though, obviously the top 10 of stylesheets are all related to three frameworks served via CDNs. So those could probably change their stylesheets easily. It would also be interesting to know whether websites based on those frameworks actually use those rules, i.e. are affected by the spec. change.
Btw. are those 7,625 pages distinct websites or are different pages of the same website counted individually? (Sorry if that should be obvious! I'm not familiar with the HTTP archive's data structures.) It's also interesting to see that the total number of pages and the total number of color functions with mixed numbers and percentages are almost the same. That indicates that the related stylesheets generally only have one color defined that way.
Sebastian
With the total set of 2,390,213 pages, that's 0.319% of all pages. This is still a relatively high number of pages
By my count there are 7,909,918 pages in the dataset so the 7,625 that use mixed types is more like 0.1%.
It would also be interesting to know whether websites based on those frameworks actually use those rules, i.e. are affected by the spec. change.
We can answer questions like these if there's a corresponding usage counter implemented in Chrome, for example the one for accent-color
. However, given that we're measuring special cases of property values rather than usage of the properties themselves, I'm not sure if use counters are the right tool.
It's technically feasible to get the computed style of all elements at runtime in HTTP Archive, but I'm not sure if we want to go down that road either 😅
Btw. are those 7,625 pages distinct websites or are different pages of the same website counted individually? (Sorry if that should be obvious!
In this case we're only looking at websites' home pages. As of recently we're also starting to crawl one level deeper beyond home pages, but those secondary pages aren't included in this analysis.
With the total set of 2,390,213 pages, that's 0.319% of all pages. This is still a relatively high number of pages
By my count there are 7,909,918 pages in the dataset so the 7,625 that use mixed types is more like 0.1%.
Oh, ok. I just took the number of your first comment. So that number is smaller but still somewhat high.
It would also be interesting to know whether websites based on those frameworks actually use those rules, i.e. are affected by the spec. change.
We can answer questions like these if there's a corresponding usage counter implemented in Chrome, for example the one for
accent-color
. However, given that we're measuring special cases of property values rather than usage of the properties themselves, I'm not sure if use counters are the right tool.
That depends on how those use counters actually work. Ideally, it would just be counted if the value is actually be used for any element on the page. But as Chrome doesn't support this number and percentage mixing yet, it's probably not possible to check whether it would apply.
It's technically feasible to get the computed style of all elements at runtime in HTTP Archive, but I'm not sure if we want to go down that road either 😅
See my comment above. It's hard to get the right data here.
Btw. are those 7,625 pages distinct websites or are different pages of the same website counted individually? (Sorry if that should be obvious!
In this case we're only looking at websites' home pages. As of recently we're also starting to crawl one level deeper beyond home pages, but those secondary pages aren't included in this analysis.
I see. Thanks for the clarification!
Sebastian
I think this can be closed? Spec was editted, tests were added and both Blink and Gecko have already been updated.
In https://github.com/w3c/fxtf-drafts/issues/456 it was noted that the examples in Compositing 2 were wrong. The reason for that was that CSS Color 4 doesn't allow to mix
<number>
and<percentage>
values for color components.This is generally ok as mixing those two types of values doesn't make sense here, though in case of
0
as value this seems unexpected. People expect0
and0%
to be exchangable.E.g. currently
rgb(100% 0 0 / 50%)
is currently invalid and needs to be written asrgb(100% 0% 0% / 50%)
for this reason.Therefore, I suggest to allow combining unitless
0
values with percentages in all color functions. Or, they could even be allowed generally for all places that take<percentage>
values.Sebastian