w3c / csswg-drafts

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

[css-variables] Are custom properties strings? #8533

Open andruud opened 1 year ago

andruud commented 1 year ago

Is a custom property a token sequence that only serializes as the original string, or is it that original string? The difference can be seen in this example:

.container {
  --x:foo 1.00;
}
.container > div {
  @container style(--x:foo 1) {
    color: green;
  }
}

If custom properties are token sequences, this style query matches, but not if they are strings.

The point of serializing using the original string was to preserve "unknown stuff", and maintaining that idea probably means that we don't transform that unknown stuff before comparisons. Otherwise e.g. style(--uuid: 12345678-12e2-8d9b-a456-426614174000) would match --uuid: 12345678-1200-8d9b-a456-426614174000 (example stolen from https://drafts.csswg.org/css-variables/#serializing-custom-props, then modified).


Note that style(--x:1) would still match e.g. --x:1.00 according to the current plan in https://github.com/w3c/csswg-drafts/issues/8376, since the literal side matches <number>.

@tabatkins

tabatkins commented 1 year ago

I don't have a strong opinion on this. I'm fine with specifying they just stay as uninterpreted character sequences (rather than token sequences, specifically), so long as it's still clear and correctly implemented that comment insertion happens when you substitute things next to each other that would reparse differently. (Chrome still doesn't do this correctly when serializing, but does at least parse correctly; --x: 1; --y: var(--x)px; is correctly treated as two tokens, a number and an ident, rather than a length, even tho we'll serialize the computed value of --y as "1px" currently.)

(The reason Anders is filing this, btw, is that currently we preserve both the original string and the tokens for every custom property, and this is a noticeable memory burden on some sites like Amazon. It would be good to preserve only one of them, and since the original string is required to be preserved for author-observable behavior, dropping the tokens seems like the more viable one.)

Loirooriol commented 1 year ago

https://w3c.github.io/csswg-drafts/css-variables/#serializing-custom-props

Specified values of custom properties must be serialized exactly as specified by the author. Simplifications that might occur in other properties, such as dropping comments, normalizing whitespace, reserializing numeric tokens from their value, etc., must not occur.

Computed values of custom properties must similarly be serialized exactly as specified by the author, save for the replacement of any var() functions.

So it seems to me that foo 1.00 and foo 1 are different.

Oh but good point about #8376, then I'm not sure what's best.

tabatkins commented 1 year ago

In #8376 there's a suggestion that we just do the "looks numeric" check for = (along with the inequalities). : queries would do normal comparison.

So given --x: 1;, style(--x: 1.0) would be false but style(--x = 1.0) would be true.

cdoublev commented 1 year ago

Given --x: /* comment */ 1, style(--x: 1) would be true? Comments are removed with leading/trailing whitespaces when consuming a declaration (but not with CSSStyleDeclaration.setProperty() though).


I think this issue also applies to properties declared with arbitrary substitution values (eg. var()).

Given --x: 1.0 and opacity: var(--x), style(opacity: 1) would be false. Right?

tabatkins commented 1 year ago

Given --x: / comment / 1, style(--x: 1) would be true? Comments are removed with leading/trailing whitespaces when consuming a declaration (but not with CSSStyleDeclaration.setProperty() though).

No, this is specifically not the case when serializing custom properties; see https://w3c.github.io/csswg-drafts/css-variables/#serializing-custom-props

Given --x: 1.0 and opacity: var(--x), style(opacity: 1) would be false. Right?

It's not yet clear if we'll allow style() queries on non-custom properties, but I can definitely say that if we do allow them, then no, that query would match. We'd at minimum base the comparison on the used-value serialization, which absolutely normalizes these things. This'll have nothing to do with whatever we do to custom properties.

cdoublev commented 1 year ago

No, this is specifically not the case when serializing custom properties; see https://w3c.github.io/csswg-drafts/css-variables/#serializing-custom-props

Ok, so this is a browser issue.

style.cssText = '--x: /* comment */ 1 /* comment */'
style.cssText; // (Chrome) `--x:  1 ;` (FF) `--x: 1 /* comment */;`
style.setProperty('--x', '/* comment */ 1 /* comment */')
style.getPropertyValue('--x') // (Chrome) ` 1 ` (FF) `1 /* comment */`

Both may store comment representations with whitespace representations. FF does not remove trailing whitespaces (cf. #6484).

If style(--x: 1) should evaluate to false against --x: /* comment */ 1 /* comment */, I guess the original string representation might have to be created somehow when consuming a declaration.

andruud commented 1 year ago

No, this is specifically not the case when serializing custom properties; see https://w3c.github.io/csswg-drafts/css-variables/#serializing-custom-props

@cdoublev is right here. Comments are effectively not dropped within the value, but the bounds of the value are still determined by tokens, and comments do not produce tokens. Example 17 in Variables is not possible per css-syntax.

--x: /* comment */ 1 becomes <whitespace-token> <whitespace-token> <number-token>, then the whitespace tokens are stripped (consume a declaration), which leaves <number-token>, and the corresponding original string is then set from that.

Ok, so this is a browser issue.

Fully acknowledged, but it's also a spec issue. :-)

cdoublev commented 1 year ago

CSS Syntax 3 says (emphasize added):

Implementations may preserve the contents of comments and their location in the token stream

https://drafts.csswg.org/css-syntax-3/#serialization

But either implementations must preserve comments, or CSS Variables should not require serializing with comments.

I also note that serializing --x: 1 !important /**/ would produce --x: 1 !important (a new whitespace is added):

If the important flag is set, append " !important"

https://drafts.csswg.org/cssom-1/#serialize-a-css-declaration

I deleted my comment where I said that serializing exactly as specified only for <declaration-value> could help evaluating style(--y: /**/ var(--x)) to true when given --x: 1 and --y: var(--x). This would work only if not preserving comments and consecutive whitespaces.

Preserving the whole declaration string would also prevent --x: mix(75%; 0; 2) from matching style(--x: 2). But here again, this would require to change how custom property values serialize.