Re: [csswg-drafts] Proposal: Custom CSS Functions & Mixins (#9350)

The CSS Working Group just discussed `Proposal: Custom CSS Functions & Mixins`, and agreed to the following:

* `RESOLVED: Start ED of css-mixins for CSS Custom Functions and Mixins`

<details><summary>The full IRC log of that discussion</summary>
&lt;miriam> https://css.oddbird.net/sasslike/mixins-functions/<br>
&lt;fantasai> miriam: explainer linked from issue<br>
&lt;fantasai> miriam: a lot of interest in mixins and functions, which have existed in preprocessors for a long time<br>
&lt;fantasai> miriam: these are two separate features that have syntax overlap and use-case overlap<br>
&lt;fantasai> miriam: they are both ways of wrapping up some CSS logic and repeating it in various places<br>
&lt;fantasai> miriam: Both have a name and take parameters, do some logic, and output some CSS<br>
&lt;fantasai> miriam: difference is that mixins exist in the declaration space, outputting entire declarations or even rule blocks<br>
&lt;fantasai> miriam: and functions exist in the value space, simlar to existing functions<br>
&lt;fantasai> miriam: Functions exist in CSS, custom functions would be a way for authors to define their own functions in a declarative way<br>
&lt;fantasai> miriam: would be limited in some ways to what you can do declaratively<br>
&lt;fantasai> miriam: takes what you can do e.g. in complex set of calcs, and grouping with a nicer syntax<br>
&lt;fantasai> miriam: mixins are a newere concept in CSS, but same idea at a declaration level, returning entire blocks<br>
&lt;fantasai> miriam: a lot of discussion in issue around syntax and details of execution<br>
&lt;fantasai> miriam: mixed interst in functions and mixins<br>
&lt;fantasai> miriam: question to group is, do we want to take any of this up on the standards track<br>
&lt;florian> q+<br>
&lt;emilio> q+<br>
&lt;fantasai> miriam: and if so where would we work on it?<br>
&lt;astearns> ack florian<br>
&lt;fantasai> florian: When we introduced CSS Variables, what was esepcially interesting was that the way they worked gave us powers you couldn't achieve in a preprocessor<br>
&lt;fantasai> florian: do you gain abilities in native CSS that you couldn't do in preprocessor?<br>
&lt;fantasai> miriam: You would be able to reference CSS conditions<br>
&lt;fantasai> miriam: preprocessor could also output conditions, so maybe not all that different?<br>
&lt;fantasai> florian: like a media query?<br>
&lt;fantasai> miriam: No, actually, main thing you get here is passing in a custom property<br>
&lt;fantasai> miriam: couldn't be able to do that in preprocessor<br>
&lt;astearns> ack emilio<br>
&lt;fantasai> TabAtkins: could maybe do that in preprocessor, but could get explosive in length<br>
&lt;fantasai> emilio: Clearly interesting for authors, but some questions<br>
&lt;fantasai> emilio: for mixins, when does the substitution happen?<br>
&lt;fantasai> emilio: e.g. applying to nested rules sounds fancy, but if you expand it after parsing then it becomes crazy complicated<br>
&lt;fantasai> emilio: how does that interact with cascade<br>
&lt;fantasai> emilio: if expanding during selector matching, then [missed]<br>
&lt;fantasai> emilio: I'm wondering if we know the answers to these questions?<br>
&lt;fantasai> emilio: So how much have these details been thought througH/<br>
&lt;fantasai> miriam: some looking into those questions. Anders has been playing around with ideas. No fixed proposals.<br>
&lt;fantasai> TabAtkins: idea is that mixins expand essentially at parse-time, but not relying on matching because that would be weird<br>
&lt;fantasai> TabAtkins: hopefully desugar into nesting<br>
&lt;fantasai> TabAtkins: functions theoretically desugar into inlining everything<br>
&lt;fantasai> TabAtkins: just some amount of variable scoping<br>
&lt;fantasai> TabAtkins: so idea right now is that mixins should resolve very early, parse-ish time<br>
&lt;fantasai> emilio: If you pass a variable in mixin, then reference..?<br>
&lt;fantasai> TabAtkins: You need to [...]<br>
&lt;Nicole> q+<br>
&lt;astearns> ack Nicole<br>
&lt;TabAtkins> s/.../uniquify the variable name so the ref can stick around to runtime/<br>
&lt;florian> q+<br>
&lt;fantasai> Nicole: Seems like something we should talk about<br>
&lt;fantasai> TabAtkins: any objection to starting a draft?<br>
&lt;fantasai> astearns: single draft, multiple drafts?<br>
&lt;fantasai> TabAtkins: mixins probably fit best in cascade, functions in variables, but since they share a lot of concepts maybe their own module<br>
&lt;fantasai> miriam: they're both kindof like a macros<br>
&lt;fantasai> s/macros/macro<br>
&lt;fantasai> matthieud: Putting them together might make things more confused.<br>
&lt;fantasai> [discussion of functions vs macros and macros that are functions]<br>
&lt;TabAtkins> LISP-4-EVA<br>
&lt;fantasai> miriam: Each would have a name-defining at-rule, and they would share defining parameters.<br>
&lt;fantasai> miriam: so might make sense to have in a shared space<br>
&lt;fantasai> astearns: Could work on them together in ED, and then decide whether to split out at FPWD<br>
&lt;chrishtr> +1 to a new draft<br>
&lt;astearns> ack florian<br>
&lt;fantasai> &lt;fantasai> +1 to that approach<br>
&lt;kbabbitt> q+<br>
&lt;fantasai> florian: We used to say CSS doesn't have functions, but functional notations that look like functions.<br>
&lt;fantasai> florian: but a bunch of them actually are functions, e.g. trig functions<br>
&lt;astearns> ack kbabbitt<br>
&lt;fantasai> florian: this goes further in that direction, but we already crossed the bridget<br>
&lt;fantasai> kbabbitt: So question of timing impacts CSSOM<br>
&lt;florian> s/the bridget/the bridge already<br>
&lt;fantasai> kbabbitt: if I walk through a stylesheet now, 1:1<br>
&lt;fantasai> kbabbitt: like C macros<br>
&lt;fantasai> kbabbitt: if I change how they expand, does that change the CSSOM, would that cause breakage?<br>
&lt;fantasai> TabAtkins: functions would get preserved in OM<br>
&lt;florian> s/look like functions./look like functions, but are just convenient syntactic groupings, without any particular notion of returning anything/<br>
&lt;fantasai> TabAtkins: wrt mixins, could expand or not<br>
&lt;fantasai> TabAtkins: but need to not be depending on run-time behavior<br>
&lt;fremy> q?<br>
&lt;fremy> q+<br>
&lt;astearns> ack fremy<br>
&lt;fantasai> fremy: Something I'm missing from the explainer, if we assume that mixins are something that you can simulate and do the replacement<br>
&lt;fantasai> fremy: would be good to have an example of what it will actually look like<br>
&lt;fantasai> fremy: I have a lot of questions better answered by examples<br>
&lt;fantasai> fremy: That's a request for next revision, a clear example of this is what author writes, and this is how it gets "compiled"<br>
&lt;TabAtkins> yeah, that's very doable<br>
&lt;fantasai> astearns: any other questions/concerns about starting this work?<br>
&lt;fantasai> astearns: proposed resolution to start an Editors Draft on mixins and functions, with Miriam and Tab editing<br>
&lt;chrishtr> q+<br>
&lt;chrishtr> custom add-ins<br>
&lt;fremy> or just mixins?<br>
&lt;fantasai> [bikeshedding spec names]<br>
&lt;bkardell_> css-fun<br>
&lt;fremy> @bkardell lol :)<br>
&lt;fantasai> emilio: css-cpp, for css pre-processor<br>
&lt;fantasai> css-custom, css-custom-functions-and-mixins, css-custom-functions<br>
&lt;kbabbitt> css-fun-mix ?<br>
&lt;florian> css-macros<br>
&lt;fremy> css-mixins +1<br>
&lt;fantasai> astearns: proposes css-mixins as the shortname<br>
&lt;fantasai> PROPOSED: Start ED of css-mixins for CSS Custom Functions and Mixins<br>
&lt;fantasai> RESOLVED: Start ED of css-mixins for CSS Custom Functions and Mixins<br>
&lt;astearns> ack chrishtr<br>
&lt;fantasai> chrishtr: Does anyone have initial feedback on miriam's syntax proposal?<br>
&lt;emilio> q+<br>
&lt;fremy> +1, I think @miriam did a great job "distilling" all the feedback in a common ground<br>
&lt;fantasai> astearns: Suggest we take what's in the issue as a starting point, and people can file specific issues on specific concerns or suggestion<br>
&lt;astearns> ack emilio<br>
&lt;fantasai> emilio: syntax seems fine offhand, but maybe we need some other function, depending on how mixins end up working<br>
&lt;fantasai> emilio: we may need to differentiate actual variables to mixin arguments<br>
&lt;fantasai> emilio: in the mixin example in the explainer, there's e.g. background: var(--space). But could be a variable set on the element<br>
&lt;fantasai> emilio: so var inside mixins vs external variables<br>
&lt;fantasai> emilio: I think if we differentiate argument vs external variables<br>
&lt;fantasai> TabAtkins: same discussion as for function<br>
&lt;fantasai> TabAtkins: my preference is lexical variables, you only get those that are specifically forwarded to you.<br>
&lt;fantasai> TabAtkins: those exist and nothing else does<br>
&lt;fantasai> TabAtkins: keeps a simpler model, you know names you have access to<br>
&lt;fantasai> TabAtkins: and makes JS-backed functions/mixins work without having to pass the entire style to the function<br>
&lt;fantasai> miriam: That's been the most active part of the discussion lately, would be great to extract into its own issue<br>
&lt;fantasai> emilio: I would see it as a nice feature, to use external variables<br>
&lt;fantasai> emilio: if you want JS-backed, then yeah you need to specify dependencies somehow, because JS can do whatever<br>
&lt;fantasai> &lt;fantasai> +1 to emilio<br>
&lt;fantasai> fremy: I think this discussion is one of the questions I had<br>
&lt;fantasai> fremy: proposal makes sense in one way, but do we use a var() function as part of the arguments then when does var() get replaced?<br>
&lt;fantasai> fremy: as an author if you use var(--background) you want the var [missed this]<br>
&lt;fantasai> fremy: and not get mixed up<br>
&lt;fantasai> emilio: I think Tab's proposal, you would replace the variable with another variable reference<br>
&lt;fremy> --function(var(--x))<br>
&lt;fantasai> emilio: as you pass it in<br>
&lt;fantasai> TabAtkins: JS-backed mixin would have typedOM concept of unresolved variable to manipulate<br>
&lt;fremy> var(--x) is from the element<br>
&lt;fantasai> TabAtkins: but functions could give a real value<br>
&lt;fremy> but for a mixin, that's less clear<br>
&lt;fantasai> TabAtkins: I would prevent lexical and dynamic scope<br>
&lt;fremy> so I would prefer arg(--x) vs var(--x) even if var(--x) is not allowed in the function otherwise<br>
&lt;fantasai> emilio: I think specifying dependencies explicitly is good, even if you don't do JS stuff.<br>
&lt;florian> q?<br>
&lt;astearns> ack fantasai<br>
&lt;emilio> fantasai: you could do the var substitution in various way<br>
&lt;emilio> ... only locally defined vars / params<br>
&lt;emilio> ... first local, then global<br>
&lt;emilio> ... you could have var() and arg()<br>
&lt;fremy> q+<br>
&lt;emilio> ... I think they're worth exploring<br>
&lt;florian> q+<br>
&lt;emilio> ... I think I tend to agree that being able to pull the variables without passing them in makes sense<br>
&lt;emilio> ... to avoid passing them over and over<br>
&lt;fantasai> emilio: I think Tab's proposal would declare the variables you want in the function definition, not to have to call them in<br>
&lt;astearns> ack fremy<br>
&lt;fantasai> s/call them in/pass them every time at the call site/<br>
&lt;fantasai> fremy: It still remains a question, if you say var() is replaced, especially for mixins, you can't replace var() before<br>
&lt;emilio> fremy: if you see var() is replace, specially for mixins you cannot replace var() before<br>
&lt;fantasai> emilio: I think you can substitute them eagerly, just not by the resolved value<br>
&lt;fantasai> emilio: you would subsitute with the variable reference<br>
&lt;fantasai> emilio: e.g. if the arg us --foo, gets passed var(--radius) then you'd replace var(--foo) with var(--radius)<br>
&lt;fantasai> fremy: then if you have another variable --radius it would get mixed up<br>
&lt;fantasai> emilio: no at that stage, all your references are unresolved element variable references, no function variable references<br>
&lt;fantasai> emilio: I think this works<br>
&lt;astearns> ack florian<br>
&lt;fantasai> florian: ...<br>
&lt;fantasai> florian: accidentally capturing variables from the environment would be bad<br>
&lt;fantasai> florian: so either don't capture at all, or capturing by opt-in seems more reasonable than just pulling everything in<br>
&lt;fantasai> astearns: any other concerns?<br>
&lt;fremy> (except if you have different functions names, then there is just no risk of conflict)<br>
&lt;fantasai> astearns: ok done with this topic for now, let's get a draft and then poke hols in it<br>
&lt;fantasai> s/hols/holes/<br>
&lt;TabAtkins> Yes, given<br>
&lt;TabAtkins>  ```<br>
&lt;TabAtkins> @mixin --foo(--arg1) using (--outer-var1) { color: var(--arg1); background: var(--outer-var1); }<br>
&lt;TabAtkins> .foo { @apply --foo(var(--my-color)); }<br>
&lt;TabAtkins> ```<br>
&lt;TabAtkins> you'd effectively expand to:<br>
&lt;TabAtkins> ```<br>
&lt;TabAtkins> .foo { color: var(--my-color); background: var(--outer-var1); }<br>
&lt;TabAtkins> ```<br>
</details>


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


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

Received on Monday, 12 February 2024 21:38:23 UTC