Edit fiddle - JSFiddle - Code Playground

JSFiddle - Test your JavaScript, CSS, HTML or CoffeeScript online with JSFiddle.

Heya @tabatkins , are you open to a spread operator so vars with comma separated values fill multiple arguments in a function call like originally planned?
There's tons of exciting potential there, happy to write up a request issue to track if there wasn't a technical limitation preventing it 🤙

@JaneOri Spread operator is already defined in the spec!

https://drafts.csswg.org/css-values-5/#early-resolution

CSS Values and Units Module Level 5

CSS Values and Units Module Level 5

@tabatkins awesome, thank you 🙏
I've been traveling the world for a year, totally disconnected from social media, still haven't caught up with all tech y'all have written and shipped. if(style()) makes my space toggle concept obsolete and I couldn't be more thrilled
@JaneOri Yeah, `if()` is legitimately crazy tech. I had no expectation five years ago that it would *ever* be possible. Anders and the rest of the Norway team are fucking BEASTS of implementors.

@tabatkins I'm grateful the spread operator will open this potential but I keep coming back to feeling the inconsistentcy that csv vars spread in standard function calls but not in custom function calls

It *feels* wrong/broken from this side of using CSS since the expectation is already set. As the/a spec author do you feel any way about the discrepancy? Seemed like Anders solved it to spread before the discussion decision to ship no-spread; Without knowing why, I'm left curious

@JaneOri The inconsistency is slightly annoying, but we were locked in by compat. If I could redo it, I'd make CSS vars respect commas in normal properties too, and require ... to expand out into multiple. (Tho that *would* require being a little more specific about function and property syntax structures; *all* functions would need to define an argument grammar as well as a normal grammar, like arb-sub functions now do.)
@JaneOri The need for an explicit spread, btw, is so that we can reasonably do short-circuiting, and avoid resolving variables/etc in branches of an `if()` that we aren't taking. To do that, we need to know the overall structure of the function ahead of time, so we can control what variables need to be substituted and when. The spread op skirts around that and just eagerly resolves the arb-sub function immediately.

@tabatkins interesting... The speed benefit of not automatically spreading makes sense on the surface, but doesn't standard function auto-spread require the same evaluation?

If no, it's unexpected that the conditional tree needs to resolve what parameters might-be-set ahead of time only for custom functions. My assumption would be resolve the parameters, call the function, but it sounds like it's more interwoven like a pre compiled in-place substitution is happening?

@JaneOri The short-circuiting isn't really a speed benefit (tho it does accomplish that a little); instead, it prevents later *unused* variables from creating reference cycles that'll affect earlier variables.

Note as well that this isn't just custom functions, it's *all* "arbitrary substitution functions" - var(), if(), attr(), etc. Anything that can emit an *arbitrary* token sequence in any location has this spread behavior, now.

@JaneOri The *secondary* benefit of it is predictability. When you write `--my-func(1, var(--arg), 3)`, you very likely expect the arg to set the second argument. But in the earlier pre-spread version, it `--arg` contains commas, it would set *multiple* arguments. You could defensively wrap it like `--my-func(1, {var(--arg)}, 3)`, but we don't expect people to remember to do that.

The new spread behavior makes it work as we think people will expect, by default.

@JaneOri For example, say you're going to use the second argument for a `font-family` value. You might *expect* people to just pass a single family name, but it would also be reasonable to pass a comma-separated fallback list. If you didn't anticipate that, your function would break; now, it works automatically.
@tabatkins indeed. I created the issue a year ago specifically because I expected it to spread based on consistency to other CSS behavior - while being cautiously aware the outsider expectation would be that it doesn't spread. I overlooked that it was already in spec at the time that it WOULD spread and was hoping only to have it clarified to avoid confusion for outsider expectations not being met.
@tabatkins Since it changed, the expectation is now sometimes-met (custom vs standard).
Worse yet, it makes author custom function documentation have to clarify if you pass this as a var, you don't need "weird" {} syntax but if you pass it directly, you do. Vs "CSS spreads by default for historical reasons so no matter what, pass your argument to our function with this {} syntax" - which is less cognitive overhead.

@JaneOri Purely as an author-expectation language design issue, I think there's reasonable arguments both ways.

I came down in favor of the behavior split so we *could* do short-circuiting. There's a more ideal CSS where this is more consistent across the board, but we work with what we've got.

@tabatkins Does that mean the short circuiting doesn't/can't happen when you pass var into the arguments of a standard function call?

My density in grasping it is not understanding clearly why it's different for standard functions - or why custom function calls aren't "wired" in the same way.

I appreciate you sharing this depth of information, it's in the weeds but clarity of /why/ makes it much easier to communicate and lean on downstream - in demos, libraries, articles, docs, and hacks.

Ty!

@JaneOri Right, outside of arb-sub functions, we just treat property values (including any functions) as being opaque token streams separated by arb-sub functions. The arb-sub substitutes in, and only after that do we care about any parsing.
@tabatkins So to recap,
Anders had custom function and standard function argument behavior the same, spreading by default. A last minute call cancelled it so the behavior is split in three very unfortunate ways now.
Different behavior on the surface of the calls.
Different behavior on --custom() if the argument is a var vs passed directly.
A rare short circuiting cyclic prevention that only happens because the() --difference() was made.
So now several caveats to communicate to users and authors.

@tabatkins any chance to revert to what Anders had working? It feels exclusively unfortunate that this difference occurs.

Passing a full font family, or any other csv, as an argument is currently pointless because there's no handling built in to dissect it - so to use it, you'll just be returning it as-is, which means the function has no functionality. So there is no existing usage to worry about

It's only implemented in one browser, we could act fast to fix the giant fumble here

@tabatkins it's not the future technical debt here that's my concern - it's the massive

**comprehension debt**

added to the present that will snowball into libraries needing entire sections in documentation about being careful during use, in this case do this, in that do that, be aware of this too.

vs one simple awareness-of-css statement that arguments always spread by default so wrap it in {} to prevent it.

A million times easier to grok

@JaneOri I think you have overargued yourself into confusion here. While the slightly different syntax behavior isn't ideal, I believe it's far less of a problem or confusion than you're implying here. In general it's meant to Just Work without having to think too hard about it. (500 char comment blocks aren't the best for talking about this, tho.)
@tabatkins It's entirely possible you are right but I may need to provide a real world example for you beyond our post limits here to demonstrate how much extra documentation caveats it causes... We have to set expectations for CSS users in our --custom() library who may be using custom functions for the first time, every tiny difference between standard vs custom vs raw vs var arguments. Auto spread is a single detail w/no further scenario discrimination: CSS vars always spread, wrap the arg {}
@tabatkins Every edge case and caveat adds to comprehension debt and must be forwarded to end users of a CSS --library().
*I* don't mind the nuance, but I really don't want my users to have to consider any of it when calling my function
@JaneOri The fact that the value is used as-as is, in fact, the point. I'm not certain why you think it would be useless? It's exactly as useful to take a font list as a single font.
@tabatkins you have spec'd out several things that make single argument list digestible (spread, nth-item, etc) which will be awesome once implemented somewhere... but currently using the entire list as-is to... stitch together a font property? You don't need a custom function to do that, just using var()s does that...
There isn't a use case currently that makes csv arguments useful (without your other work implemented).
Auto spread eliminates most of the caveats to explain in docs/preferred DX
@tabatkins so the point wasn't that it won't be useful, but that it's currently not useful as implemented AND it's causing downstream DX nightmares as-is - auto spread eliminates the vast majority of caveats. Also... it makes more sense in CSS because it's currently doing something weird - for the very first time, using var() somewhere is not expanding in place, but instead expanding later, after the custom function argument... Your initial {} concept was completely rational in CSS programming
@tabatkins all that to say was simply to make the point that it's not too late to change back because it's not going to be used currently
@JaneOri It was definitealy not a "last minute" call. It was the result of a bunch of discussion, some on the list and a bunch internal, about how unused `if()` arguments (and unused fallbacks in several other arb-sub funcs) should behave. Without this behavior, there are reasonable `if()` functions you would be unable to write, as they'd be cyclic even though every possible branch, on its own, wasn't; meanwhile a similar `@container` would work fine, just be verbose.
@tabatkins the last minute remark was reading timestamps of when Anders had it detailed and working to 11 minutes later when it was decided not to:
https://issues.chromium.org/issues/390205875#comment4
The short circuiting cycle prevention happens before they spread as arguments to a standard function call like hsl() so I still don't grok the distinction that it can't happen in custom --hsl()
Chromium

@tabatkins I do see I may be needing to understand the entire implementation behind the var short circuiting tech at the boundary(/lack of) in the standard vs custom function call arguments... Which is maybe not a reasonable gap to span in little social media post lengths...
Thank you, really, for diving in so deep with me/us here. I *love* CSS and I love sharing knowledge; We're deep in reply threads but it will come w/me to the surface to help more people appreciate the technical beauty of CSS
@tabatkins yeahhhh you're right if "people" are random programmers instead of people already familiar with CSS. It's a rub that the behavior is different for standard functions, now everyone has to learn to discriminate and the newcomer expectation only works for custom function calling. I'd take consistently weird over sometimes weird every day.

@tabatkins caaaan we spread space multipliers? ...+ vs ...# (... would default to ...#)
Then, among a bunch of other potential, we could make a custom function to rotate clip path polygons partials-of-known-length* arbitrarily (if they only use <length> values).

* arbitrary length if we get ...rest param too

Integer and Space Toggle conditionals can't have the un-circular short circuit benefit so there's undoubtedly a treasure trove of potential there... That's great information to be aware of

@tabatkins oh that's what arb-sub means. Probably should have googled it first.

Fascinating...

Makes sense now!

Thank you!