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 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.
@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