w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.52k stars 674 forks source link

[css-color-5] Allow use of 0 with <percentage> color components #7338

Closed SebastianZ closed 8 months ago

SebastianZ commented 2 years ago

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 expect 0 and 0% to be exchangable.

E.g. currently rgb(100% 0 0 / 50%) is currently invalid and needs to be written as rgb(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

svgeesus commented 2 years 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.

svgeesus commented 2 years ago

Also re-tagging to CSS Color 5, as Color 4 is firming up for Candidate Recommendation

fantasai commented 2 years ago

(Untagging css-values-4 as we cannot make this allowance generally across CSS.)

tabatkins commented 2 years ago

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.

svgeesus commented 2 years ago

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.

SebastianZ commented 2 years ago

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.

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

rviscomi commented 2 years ago

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%)?

SebastianZ commented 2 years ago

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

rviscomi commented 2 years ago

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:

url pages
https://c0.wp.com/c/6.0/wp-includes/js/mediaelement/mediaelementplayer-legacy.min.css 54,866
https://onesignal.com/sdks/OneSignalSDKStyles.css?v=2 41,817
https://c0.wp.com/p/jetpack/11.0/css/jetpack.css 39,467
https://static.parastorage.com/services/chat-widget/1.2290.0/chat-widget.min.css 31,153
https://static.parastorage.com/services/chat-widget/1.2290.0/expanded-widget.chunk.min.css 30,437
https://assets.squarespace.com/universal/styles-compressed/commerce-712187450e7fc15b937be-min.en-US.css 25,287
https://code.jivosite.com/css/9cb05c5/widget.css 25,085
https://code.jivo.ru/css/9cb05c5/widget.css 22,637
https://c0.wp.com/c/5.9.3/wp-includes/js/mediaelement/mediaelementplayer-legacy.min.css 21,792
https://static.parastorage.com/services/pro-gallery-santa-wrapper/1.2629.0/staticCss.min.css 20,305

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.

dbaron commented 2 years ago

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.

rviscomi commented 2 years ago

Ah yeah, let me know if there are any special cases like numeric hues being valid.

SebastianZ commented 2 years ago

@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() and hsla(), 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

rviscomi commented 2 years ago

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:

url pages
https://substackcdn.com/theme/main.css?v=f5b1fe705c81d1960b9f63dd4eac851c 771
https://substackcdn.com/theme/main.css?v=1cd9b86b3eea08483c07544e4be2bfc8 548
https://substackcdn.com/theme/main.css?v=cdf0335dd84f37b2eb1f77675b26822e 445
https://uiassets.izmocars.com/izmo/4_0/css/forms/formbuilder.css?v=319 240
https://substackcdn.com/theme/substack.css?v=9e1dbecd54d5f0e3b4c0356b492ccfdf 125
https://substackcdn.com/theme/substack.css?v=f8372c55e60eb842bff70d9d4be72b1d 87
https://static.zdassets.com/classic/assets/zendeskgarden_modals-48f7485d39033cb19f3b25119107c2a9242b166108252e10f0023f1b2b20eb7a.css 86
https://static.zdassets.com/classic/assets/zendeskgarden_button-f46b8c2020555512d4cff7c4b28c5ad5fdef859a7560162b45aaa5bd7e55dd28.css 86
https://substackcdn.com/theme/substack.css?v=81b8c8d305de0d09f5a6b485f9bc95b8 71
https://substackcdn.com/theme/main.css?v=51254473aa7c4262de2382a4a2809638 70

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
svgeesus commented 2 years ago

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

svgeesus commented 2 years ago

bump, someone review my WPT test please?

SebastianZ commented 2 years ago

@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

rviscomi commented 2 years ago

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.

SebastianZ commented 2 years ago

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

romainmenke commented 8 months ago

I think this can be closed? Spec was editted, tests were added and both Blink and Gecko have already been updated.