Re: [csswg-drafts] [css-values-5] Can ident() bypass dashed-ident requirements? (#12206)

The CSS Working Group just discussed `[css-values-5] Can ident() bypass dashed-ident requirements?`, and agreed to the following:

* `RESOLVED: only allow literal strings and idents, and <number>s. for validity purposes, pretend the <number>s are "0".`

<details><summary>The full IRC log of that discussion</summary>
&lt;kbabbitt> andruud: in many places in CSS parsing we allow only a restricted type of ident e.g. dashed-ident<br>
&lt;kbabbitt> ... today we rject things that don't match at parsed time<br>
&lt;kbabbitt> ... with the ident function that's not possible anymore because we're blind to what it returns parse time<br>
&lt;kbabbitt> ... question is, what do we do?<br>
&lt;kbabbitt> ... do we allow container names for example specified with an ident() that doesn't start with --, do we allow that as a kind of "escape"?<br>
&lt;kbabbitt> ... or do we make it IACVT for example?<br>
&lt;kbabbitt> ... or something else?<br>
&lt;kbabbitt> ... I think unfortunately the most reasonable answer is that we make it IACVT<br>
&lt;lea> q+<br>
&lt;weinig> q+<br>
&lt;kbabbitt> ... so if you specify container-name: ident(...) then that's always valid parse time and we apply restrictions later<br>
&lt;kbabbitt> ... this is complicated to do, because a lot of things that could not be IACVT could now become that<br>
&lt;kbabbitt> ... but I think at least theoretically it's what makes the most sense<br>
&lt;kbabbitt> ... that said i haven't found a reason why we couldn't just treat it as an escape<br>
&lt;TabAtkins> What things could not have been IACVT and now are?<br>
&lt;emilio> q+<br>
&lt;kbabbitt> ... but worried we'd regret it later if we allowed it<br>
&lt;astearns> ack lea<br>
&lt;kbabbitt> lea: I don't think ident() should be treated specially compared to just specifying an ident<br>
&lt;kbabbitt> ... confusing to tell authors they can wrap a regular ident in ident() and now it can be a dashed-ident<br>
&lt;kbabbitt> ... if a certain syntax only accepts dashed-ident, that should remain regardless of where it came from<br>
&lt;kbabbitt> ... ident() just produces an ident<br>
&lt;kbabbitt> ... when the unknown is due to a var, we already have rules about that, not a problem<br>
&lt;kbabbitt> ... we should focus discussion on where this conforms to dashed-ident when we don't have a var<br>
&lt;kbabbitt> ... for example string + sibling-index<br>
&lt;kbabbitt> ... there was a proposal where you replace math functions with 0 or something<br>
&lt;kbabbitt> ... that would work in cases where dashed-ident is required<br>
&lt;kbabbitt> ... can't think of any cases where it wouldn't<br>
&lt;kbabbitt> ... however this kind of mock eval does seem a little wonky, makes me a little uncomfortable if we encode it in the spec<br>
&lt;kbabbitt> ... wondering if normative text should be, UA should statically check at parse time whether this is a dashed-ident without specifying how they do mock eval<br>
&lt;TabAtkins> q+<br>
&lt;kbabbitt> ... could add a note that one way to do it is this way, but not encode in spec that all UAs should do it this way<br>
&lt;kbabbitt> ... don't think we should push all cases to computed-value time, makes no sense for the feature and too restrictive for authors<br>
&lt;kbabbitt> ... if you just have 2 strings joined, it's weird that resolves at computed-value time<br>
&lt;kbabbitt> ... from author point of view, no reason to defer that kind of check<br>
&lt;kbabbitt> andruud: if we have a var in there or any substitution function, computed value time is the earliers this can become invalid<br>
&lt;kbabbitt> lea: yes, variables aside where we already have established rules<br>
&lt;kbabbitt> andruud; we could say that resolve the ident as much as possible parse time and if that flattens into a known ident, then apply parse-time checks at that time<br>
&lt;kbabbitt> ... a case where that would not work is typical concatenation of literal string + sibling-index<br>
&lt;kbabbitt> ... because that resolves later<br>
&lt;kbabbitt> emilio: but doesn't sibling-index behave as arbitrary substitution function?<br>
&lt;kbabbitt> andruud: no<br>
&lt;kbabbitt> emilio: shouldn't it?<br>
&lt;kbabbitt> ... attr() is a subsetitution function and there's no problem<br>
&lt;kbabbitt> andruud: it isn't<br>
&lt;kbabbitt> emilio: shouldnt sibling-index behave like attr effectively<br>
&lt;kbabbitt> ... which would pass this whole thing<br>
&lt;TabAtkins> sibling-index() isn't important here, it's just an example "returns an integer" function<br>
&lt;kbabbitt> ... reason I wasn't sure this why this wasn't aproblem was didn't know sibling-index was abritrary sub function<br>
&lt;kbabbitt> ... if we make it one would that resolve<br>
&lt;kbabbitt> andruud: should we make ident() as sub function then?<br>
&lt;kbabbitt> emilio: would prefer not to I think<br>
&lt;kbabbitt> ... but it's the same argument<br>
&lt;kbabbitt> ... ident() doesn't depend on the element so you can get away with not making it a sub function<br>
&lt;kbabbitt> ... if you find something random like calc() in there, is that supposed to work?<br>
&lt;kbabbitt> ... my gut feeling would be ident() was naive CSS function that parses things and always resolves to an ident, nothing pending<br>
&lt;kbabbitt> ... I think that model works for most use cases if sibling-index() and co are sub functions<br>
&lt;lea> q?<br>
&lt;kbabbitt> ... right?<br>
&lt;kbabbitt> ... would that be a model that makes sense?<br>
&lt;kbabbitt> andruud: now you're requiring all future functions to be sub functions if they return ints<br>
&lt;lea> q+<br>
&lt;kbabbitt> emilio: element-dependent functions ... could make them not arb sub functions but they wouldn't work on ident()<br>
&lt;kbabbitt> ... is a use case we want something like ident("foo" calc(...))<br>
&lt;kbabbitt> ... is that something we want to support and how? sibling-index seems similar<br>
&lt;kbabbitt> ... if ident just takes strings and concats them, or strings and idents, very simple things that don't require this whole thing, we'll behave<br>
&lt;kbabbitt> ... are there use cases for things that aren't arb sub functions that you would want to stash in there? don't see a lot of use cases<br>
&lt;kbabbitt> andruud: sibling-index is one<br>
&lt;kbabbitt> emilio: don't see why sibling-index is different to attr<br>
&lt;kbabbitt> ... can you stash sibling-index anywhere and if not why not?<br>
&lt;kbabbitt> ... if you can stash it anywhere, would it be simpler to make it an arb sub fn?<br>
&lt;kbabbitt> andruud: this is a wider question<br>
&lt;astearns> ack weinig<br>
&lt;kbabbitt> weinig: to the last point, I think calc in ident is useful even wihtout sibling-index<br>
&lt;TabAtkins> I'm honestly extremely confused what this arb-sub tangent is about. I don't think it has any relevance to the discussion?<br>
&lt;kbabbitt> ... if creating a bunch of identifiers, might want some math to compute a suffix that's a number<br>
&lt;kbabbitt> ... comment about mock evaluation<br>
&lt;kbabbitt> ... if we're jst talking aout dash prefix, it's not a mock evaluation so much as just looking at first args to the ident<br>
&lt;kbabbitt> ... if they are strings we can already figure out if they're dashes<br>
&lt;kbabbitt> ... can't think of any examples other than var() where we can't<br>
&lt;lea> qq+<br>
&lt;kbabbitt> ... excluding that, can't think of any examples where it's not trivial to look for -- at the beginning<br>
&lt;astearns> ack lea<br>
&lt;Zakim> lea, you wanted to react to weinig<br>
&lt;kbabbitt> lea: I can think of an example<br>
&lt;emilio> q-<br>
&lt;kbabbitt> ... if we have a custom counter style that adds hyphens and use that counter in the beginning, then whether ident is dashed-ident depends on counter value<br>
&lt;astearns> ack TabAtkins<br>
&lt;kbabbitt> TabAtkins: one little bit, I don't like this mock evaluation idea<br>
&lt;kbabbitt> ... don't think it's necessary, but lea mentoined not specifying details and I'm extremely allergic to that<br>
&lt;kbabbitt> ... have to have dependable behavior between browsers<br>
&lt;kbabbitt> ... my feeling about this is, if we have it be IACVT anywhere, it should be dependable and not subject to subtle aspects of what you're specifying<br>
&lt;kbabbitt> ... if it's a string, can eval that immediately and turn into a literal string<br>
&lt;kbabbitt> ... but if you have some functions that might not compute yet, I'd prefer, regardless of what they are, treat them as potentially IACVT<br>
&lt;kbabbitt> ... rather than subtly slice up if we can infer this could be a dashed-ident, fail immediately or not<br>
&lt;kbabbitt> ... don't like subtle parsing details that can't help authors<br>
&lt;kbabbitt> ... still confused about arb sub fun though<br>
&lt;kbabbitt> ... andruud said this could expose IACVT behvaior to places that didn't have it before<br>
&lt;astearns> s/arb sub fun/arbitrary substitution function discussion/<br>
&lt;kbabbitt> ... don't understand why because anyplace that could accept it could accept a var()<br>
&lt;emilio> q+<br>
&lt;kbabbitt> andruud: that was a note about impl difficulty, not author experience<br>
&lt;kbabbitt> TabAtkins: okay, you refer to a wrinkle to this I don't understand<br>
&lt;kbabbitt> andruud: in order for something to be possibly IACVT today you must have an arb sub fn somewhere<br>
&lt;kbabbitt> ... now an ident is not a sub fn, suddenly can be IACVT<br>
&lt;kbabbitt> ... not saying we have to give up on it in advance, just saying it's new<br>
&lt;kbabbitt> ... we already have one other case in anchor but it's very narrow<br>
&lt;astearns> ack lea<br>
&lt;astearns> ack emilio<br>
&lt;emilio> My proposal would be: make ident() take just `&lt;string> | &lt;ident> | &lt;integer-token>` (or so), and make `sibling-index()` and co arbitrary substitution function. If you need `calc()` or what not you can get around it with `@property` / `var()`<br>
&lt;weinig> q+<br>
&lt;kbabbitt> emilio: agree with andruud that ... I would make this simpler and make sibling-index an arb sub fn<br>
&lt;TabAtkins> ??? I still dont' understand what Emilio is talking about :(<br>
&lt;kbabbitt> ... and if you need math you can use a registered custom prop and var9)<br>
&lt;kbabbitt> ... feels to me like allowing arbitrary math in there is kind of annoying, conceptually as soon as you allow int you already have a deferred thing<br>
&lt;kbabbitt> ... might have addition of em by something else and now you have to wait until em resolve<br>
&lt;kbabbitt> ... that's similarly annoying impl wise<br>
&lt;kbabbitt> ... would rather keep ident() simple, if the big use case is sibling-index, since that needs to wait until computed-value time to resolve anyway, obvious thing is to make it an arbitrary sub fun<br>
&lt;kbabbitt> ... maybe more flexible? can think of ways where a random int wouldn't be valid<br>
&lt;andruud> Another example is random()<br>
&lt;kbabbitt> ... I think that covers all the use cases and removes this problem<br>
&lt;kbabbitt> TabAtkins: if you're saying we should make ident an arb sub fn, I agree that solves things in general<br>
&lt;kbabbitt> ... but you keep talking about specific functions returning int becoming arb sub and I don't get it<br>
&lt;kbabbitt> emilio: point is, if we make ident() an arb sub fn, would also sidestep this issue but in a different way<br>
&lt;kbabbitt> ... it's nice to be able to reject ident() at parse time if you know you're not expecting an ident<br>
&lt;kbabbitt> ... to me, main difference is, ident() on its own doesn't depend on anything else other than its args<br>
&lt;kbabbitt> ... if we restrict the args and it still covers use cases, you can reject at parse time very easily<br>
&lt;kbabbitt> ... sibling-index() etc. it's impossible to not defer computation<br>
&lt;kbabbitt> ... whole reason attr() was made an arb sub fn is, it's extremely annoying to deal with this in all the places that potentially need attr()<br>
&lt;kbabbitt> ... to keep it as a regular parsed value<br>
&lt;kbabbitt> ... arb sub fns fit well with that use case of something we need to defer unconditionally<br>
&lt;lea> q+<br>
&lt;kbabbitt> ... easier to poke into that machinery than whack-amole whole codebase<br>
&lt;kbabbitt> TabAtkins: agree with that<br>
&lt;kbabbitt> emilio: this is another case where sibling-index() fits in with that, still need to deal with that everywhere you take ints<br>
&lt;kbabbitt> ... not a huge issue but if it propagates annoyance to ident() etc. maybe the easy thing is to restrict ident() to keep as simple as possible and make sibling-index() asf<br>
&lt;kbabbitt> TabAtkins: not sure doing anything to sibling-index() will solve the issue<br>
&lt;kbabbitt> emilio: my proposal had an integer token thing<br>
&lt;kbabbitt> TabAtkins: if you want sibling-index() just as reasonable to use random() for specific integers<br>
&lt;kbabbitt> emilio: not a fan of random() for a variety of reasons<br>
&lt;astearns> ack weinig<br>
&lt;kbabbitt> weinig: not super familiar with counter, wanted to understand that example better<br>
&lt;kbabbitt> ... seems on the face that counter can't evaluate until computed value time, so acts as an asf in that regard<br>
&lt;kbabbitt> emilio: counter doesn't resolve to an integer<br>
&lt;kbabbitt> ... resolves to a string<br>
&lt;kbabbitt> ... not at computed value time<br>
&lt;kbabbitt> ... only when you build layout tree do you evaluate those<br>
&lt;kbabbitt> weinig: so the counter example isn't a counter-example<br>
&lt;kbabbitt> andruud: relevant but actually a separate issue, even worswe<br>
&lt;kbabbitt> ... about stuff we can't resolve at computed value time<br>
&lt;kbabbitt> weinig: so it wouldn't be supported inside ident()<br>
&lt;kbabbitt> andruud: noe<br>
&lt;kbabbitt> weinig: think it's a trivial operation to define to figure out if there are two dashes as a prefix<br>
&lt;lea> +1 weinig<br>
&lt;kbabbitt> ... as a rule if we can avoid IACVT it's better because it's confusing for authors<br>
&lt;astearns> ack lea<br>
&lt;TabAtkins> q+ for a proposal, then<br>
&lt;kbabbitt> lea: +1 to what weinig said about avoiding IACVT if we can<br>
&lt;kbabbitt> ... woudl support any subset of cases to be determined at parse time over making everything resolved at CV time if some cases need to wait<br>
&lt;kbabbitt> ... not sure how we could rule out counter in general since it returns a string<br>
&lt;kbabbitt> ... would we just not parse counter inside ident?<br>
&lt;kbabbitt> emilio: doesn't return a string<br>
&lt;kbabbitt> ... computes to a counter() function<br>
&lt;kbabbitt> TabAtkins: kind of a used value thing<br>
&lt;kbabbitt> lea: at used value time doesn't it return a string?<br>
&lt;kbabbitt> emilio: hand wavy but yes<br>
&lt;kbabbitt> lea: if what's inside the ident needs to wait until CV time, let's wait<br>
&lt;kbabbitt> ... if everything can be checked at parsed value time that would make the most sense<br>
&lt;emilio> q+<br>
&lt;kbabbitt> ... fewer things we have to make asfs the better<br>
&lt;kbabbitt> ... fewer things that resolve at CV time the better<br>
&lt;kbabbitt> andruud: can't wait until UV time, not practical<br>
&lt;kbabbitt> emilio: scroll-timeline needs to be known at style time<br>
&lt;kbabbitt> andruud: can open separate issue for this<br>
&lt;astearns> ack TabAtkins<br>
&lt;Zakim> TabAtkins, you wanted to discuss a proposal, then<br>
&lt;kbabbitt> TabAtkins: do we have a great reason to allow non-literal strings?<br>
&lt;kbabbitt> ... outside of counter, a custom fn that returns a string, is that problematic?<br>
&lt;kbabbitt> emilio: custom fns are not problematic because they behave as vars<br>
&lt;kbabbitt> ... if we restrict everything to literals ident() is trivial to implement<br>
&lt;kbabbitt> TabAtkins: my proposal is indeed that<br>
&lt;kbabbitt> ... we restrict ident() to literal strings and numeric functoins<br>
&lt;kbabbitt> ... and do mock evaluation stuff where, for purpose of determining validity, treat function as digit 0<br>
&lt;kbabbitt> ... if that forms a valid keyword for that context<br>
&lt;kbabbitt> ... at parse time, you are good<br>
&lt;kbabbitt> ... otherwise you are invalid immediately<br>
&lt;lea> q+<br>
&lt;kbabbitt> ... we can on a case by case basis in the future, take anu non asf string fns and determie if they should be allowed<br>
&lt;kbabbitt> ... bt want to be careful about this<br>
&lt;lea> q-<br>
&lt;weinig> +1 to tab's proposal<br>
&lt;kbabbitt> ... big uses casesa are strings and dynamic integers both of which should be fine<br>
&lt;lea> +1 to TabAtkins's proposal<br>
&lt;kbabbitt> ... if we can cover those 2 use cases we should be fine<br>
&lt;lea> q+<br>
&lt;kbabbitt> emilio: not sure that works great because if you have a negative number can't you do "-" then negative number<br>
&lt;kbabbitt> TabAtkins: 1000% okay with that being invalid<br>
&lt;romain> +1 to this being invalid :D<br>
&lt;kbabbitt> TabAtkins: if you want to put a - in front of a hopefully negative number that will form a dashed-ident, what are you doing, just put -- like a normal person<br>
&lt;kbabbitt> emilio: weird to reject that at parse time when it would be valid at CV time<br>
&lt;kbabbitt> ... feels like big use case for dynamic stuff in ident() are sibling-index, random, but random already needs to wait for CV time<br>
&lt;kbabbitt> TabAtkins: sometimes<br>
&lt;kbabbitt> emilio: you cannot resolve random at parse time by definition, right?<br>
&lt;kbabbitt> TabAtkins: theoretically resolvable at parse time, but yeah<br>
&lt;astearns> ack emilio<br>
&lt;kbabbitt> astearns: we've been discussing for half hour and it's evolved into at least one other issue<br>
&lt;kbabbitt> emilio: not a fan of treating every number as 0, feels a bit weird<br>
&lt;TabAtkins> proposal: only allow literal strings, and &lt;number>s. for validity purposes, pretend the &lt;number>s are "0".<br>
&lt;kbabbitt> ... to me, simple thing is make ident() as simple as possible<br>
&lt;kbabbitt> ... can use asf thing for other things<br>
&lt;kbabbitt> ... fine if you're not a fan of that<br>
&lt;kbabbitt> ... you're making same arg I make for ints for strings<br>
&lt;kbabbitt> ... why not take it all the way, ident is always parse timeable<br>
&lt;lea> (FWIW, anecdotally none of the use cases I have come across involve sibling-index() or random() )<br>
&lt;kbabbitt> TabAtkins: always parse timeable under my proposal but if you're depending on a negative number plus a - it's a false negative<br>
&lt;kbabbitt> ... seems like a use case we don't need to worry about<br>
&lt;kbabbitt> emilio: would it be invalid if you had a var there?<br>
&lt;kbabbitt> ... suppose you had a var that resolves to a negative number<br>
&lt;kbabbitt> TabAtkins: that would still be invalid<br>
&lt;kbabbitt> emilio: as long as we're consistent, not that terrible<br>
&lt;kbabbitt> lea: +1 to TabAtkins proposal<br>
&lt;astearns> ack lea<br>
&lt;kbabbitt> ... seems reasonable to me<br>
&lt;kbabbitt> ... most use cases I've come across are strings and numbers<br>
&lt;kbabbitt> ... idents with other idents would still be alllowed?<br>
&lt;kbabbitt> TabAtkins: ye<br>
&lt;kbabbitt> lea; assuming these idents and strings can come from vars as well<br>
&lt;kbabbitt> ... taht seems to cover every use case I've come across<br>
&lt;kbabbitt> ... would still be useful to have a list of what wouldn't be allowed<br>
&lt;kbabbitt> ... but seems like a good compromise<br>
&lt;kbabbitt> astearns: my understanding is taht this would be a stepping stone, might not ever allow more stuff in ident but could on case by case basis<br>
&lt;kbabbitt> TabAtkins: yes<br>
&lt;kbabbitt> TabAtkins: all asfs work by definition<br>
&lt;kbabbitt> lea; what wouldn't be allowed<br>
&lt;kbabbitt> TabAtkins: counter and I don't know what others we have that return strings<br>
&lt;TabAtkins> proposal: only allow literal strings, and &lt;number>s. for validity purposes, pretend the &lt;number>s are "0".<br>
&lt;TabAtkins> proposal: only allow literal strings and idents, and &lt;number>s. for validity purposes, pretend the &lt;number>s are "0".<br>
&lt;kbabbitt> astearns: saw a bunch of +1s in IRC go by, any questions?<br>
&lt;kbabbitt> astearns: objections?<br>
&lt;kbabbitt> +1 from me for now<br>
&lt;kbabbitt> RESOLVED: only allow literal strings and idents, and &lt;number>s. for validity purposes, pretend the &lt;number>s are "0".<br>
</details>


-- 
GitHub Notification of comment by css-meeting-bot
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/12206#issuecomment-3998743769 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Wednesday, 4 March 2026 16:42:35 UTC