csswg-drafts
csswg-drafts copied to clipboard
[css-values-5] attr()'s "px"/etc keywords
In https://github.com/w3c/csswg-drafts/issues/10437#issuecomment-2377634442 we resolved to change attr()'s type argument from a bespoke list of keywords to the newly-minted "CSS grammar in CSS" syntax.
Most of the existing attr() type keywords translate over just fine (usually just wrapping them in <> characters), but the unit names don't. That is, under the current spec you can write width: attr(foo px), and given an element like <div foo=500>, it'll be parsed as a number and then be assumed to be a px value. There's no way to express this sort of behavior in CSS grammar, tho.
Note that this is basically just a convenience; one could instead write calc(1px * attr(foo <number>)) and get the same result. So the use-case is covered no matter what.
Do we want to try and allow this, tho? The issue is that bare keywords already have meaning in the <css-syntax> production (they represent those keywords, same as in any other CSS grammar), so it would be somewhat awkward to mix them in.
I propose that we just drop that functionality, and let calc() cover the issue of "interpret a number as a particular unit".
I'm a bit ambivalent about it. I can see the theoretical purity of it, but realistically we're not going to use the full grammar syntax here, only a single <type> value--so there's no conflict. And being able to just write px is pretty handy: easy to understand, easy to use.
It's actually fine to use the whole grammar here, now that attr() is an arbitrary-substitution function (aka var()-like). We don't need to know its type at parse time, so it's perfectly fine to write attr(foo <length> | <number>). This would be useful, for example, to pass a value to 'opacity', using <number> | <percentage> (which can't be done in the current spec).
And the spec already allows ident (spelled <ident> in the new syntax), to allow any identifier, so allowing only specific identifiers via literal idents seems just fine.
So I think we either need to drop the "specific unit" functionality, as suggested, or make the syntax part more distinguishable from other syntax. Or, I guess, add some branches to the <syntax> production to allow "number interpreted as unit" natively, like <number px> or something.
realistically we're not going to use the full grammar syntax here
Even if at some point we want to allow px as a syntax keyword instead of an unit, I guess we could use brackets to disambiguate (see https://drafts.csswg.org/css-values/#component-combinators). So attr(foo px) would parse as number and add the unit, while attr(foo [px]) would parse the attribute as the keyword or whatever is supposed to happen.
To a little clearer, since @fantasai was confused in some personal conversation:
Right now, per spec, you can say display: attr(foo ident) and <div foo=block> will work. In the new resolved syntax, that would be display: attr(foo <ident>). But under the new syntax you could also say display: attr(foo block | flex | grid) to allow only those specific three keywords.
That's the ambiguity with px/etc that I'm worried about here. If we just go with the resolution exactly as stated in the f2f, then writing attr(foo px) means "attempt to parse the foo attribute as the ident px, and if successful, substitute that in".
I still think we can probably just drop that functionality entirely in favor of using calc(), but if we did want to preserve it, I think we'd do it with some special syntax on the number production, like <number px> to mean "parse this as a number, then give it the px unit".
I think this will be a popular thing to do, and calc(1px * attr(foo <number>)) is kinda cumbersome.
attr(foo <number px>) sounds good to me.
And being able to just write px is pretty handy: easy to understand, easy to use.
We absolutely need simpler syntax.
attr(foo <number px>) sounds good to me too.
<number px>
Will <px> not do here?
Anything of that sort is possible, sure, but we should assume that anything we allow in this production might eventually show up in a CSS spec, or possibly collide with something in a CSS spec. Having an unbounded set of <px>, <cqw>, etc productions doesn't sound very safe in that regard, but a special syntax on the production itself (akin to the range restriction syntax) does.
And, personally, I think it communicates better. If I saw attr(foo <px>), I'd probably assume it was expecting a value like 5px (and would reject 5em). But attr(foo <number px>) is clearer that you're looking for a number, and involving px in some way; you might think of a value like 5px, but then you have to ask why it's not spelled <length> or even <length px> if we were being specific about the unit; the intended meaning (accept 5 like <number> would, but give it the px unit) is conceptually nearby and easy to hit by guessing.
Having an unbounded set of
<px>,<cqw>? , etc productions doesn't sound very safe in that regard
Yeah, I agree with that.
And, personally, I think it [
<number px>] communicates better
OK, I'll buy that too.
Ready for Agenda+?
Yup, Agenda+ to approve the <number px> syntax to be added to the <syntax> production, meaning "parse it as a number, then give it the px unit" (or whatever).
Despite my earlier preference to just push this functionality out entirely, I think this is the right way to go, since you can accept multiple types with <syntax> and only one of them might need to be unit-ified. For example, attr(foo, <number px> | <percentage>) can't be reproduced without this feature; trying to do calc(attr(foo, <number> | <percentage>) * 1px) will fail when a percentage is actually given.
Ah, an additional point from some internal conversation - the <number px> will become a <length> or whatever, semantically, depending on the unit.
To re-iterate and elaborate on my comment in the <boolean[<test>]> related issue, it might be worth considering syntax component attribute names now.
<number [0,1]> is not a valid <syntax> (or @property/syntax) at the moment, but I assume it will become valid if <number px> becomes valid. People may ask for other syntax component attributes. For example, <ident range="foo BAR" case-sensitive> could match a range of identifiers case-sensitively (related: #11124).
<number px> is shorter than <number unit=px> but it might prevent such extension to the CSS value definition syntax.
I think that's fine. CSS is always grammatically flexible, and those examples of future growth looks just fine to me. If and when we add enough specifiers for it to start getting confusing, we can worry about named arguments; until then, we can add them ad-hoc because it's shorter and easier.
We're now resolved on not doing <number px>, and instead just letting attr() accept px as a keyword again (now that that's not ambiguous with <syntax>). So, closing.
Resolution: https://github.com/w3c/csswg-drafts/issues/11035#issuecomment-2474348198