Re: [csswg-drafts] [css-mixins-1] A var()-based model for mixin parameters (#12927)

The CSS Working Group just discussed `[css-mixins-1] A var()-based model for mixin parameters`, and agreed to the following:

* `ACTION: Add a suggestion to the spec for the inspector to flag selectors that can't match`
* `RESOLVED: Accept the proposal`
* `RESOLVED: Add an unscoped mixin variant (syntax TBD)`

<details><summary>The full IRC log of that discussion</summary>
&lt;bramus> TabAtkins: next 3 issues are all linked up here<br>
&lt;bramus> … large rewrite of how weare doing mixins<br>
&lt;ChrisL> s/cyril/cyrl/<br>
&lt;bramus> … gonna do a rundown<br>
&lt;bramus> … more specific discusssions can be done in the issue<br>
&lt;bramus> … At TPAC I discussed som eof the change, but did not have visual presentation<br>
&lt;bramus> … this should help show what is going on<br>
&lt;TabAtkins> https://docs.google.com/presentation/d/1i-wOkEp9w91rmxTy2CaKzc0Sr_LC_qrko13TRnSjRMs/edit?usp=sharing (i'll send to www-archive in a bit)<br>
&lt;bramus> … so, first topic is to make syntax more parallel to custom fns<br>
&lt;bramus> … custom fns look like this<br>
&lt;bramus> … name, params, local variables (custom props in the fn body, not exposed outside the fn), then result descriptor<br>
&lt;bramus> … use var() inside the fn body to get to local vars, and then reach out outside<br>
&lt;bramus> … old mixin syntax looked like this:<br>
&lt;bramus> … params are the same, but then there wre local variables (scoped env var), and then you referenced both using env but for outside params you needed var()<br>
&lt;bramus> … the entireity of the mixin body got subbed in, so tehre is no way to hide things to the outside<br>
&lt;bramus> … this is going away<br>
&lt;bramus> … (next slide)<br>
&lt;bramus> … params the same now, local vars too. there is now a @result at-rule to indicate what gets exposed<br>
&lt;bramus> … clear distinction between local and exposed<br>
&lt;bramus> … can now use var() everywhere<br>
&lt;bramus> … exactly like fns<br>
&lt;bramus> … in vritually every way, mixins and fns now work the same way<br>
&lt;bramus> … easier for authors to reason about<br>
&lt;bramus> florian: so when you are trying to use insde the mixin tthings that are not locally or params, we fetch them from where?<br>
&lt;bramus> TabAtkins: variables not catugh by local or arg, and get resolved based on where they are used<br>
&lt;bramus> … they will take the correct --foo from the element where it is being styled<br>
&lt;bramus> … so this thing here, is thing about making the return value more epxlicit using the @result<br>
&lt;bramus> … does make small mixins more verbose to write<br>
&lt;bramus> … but dont think this is a huge deal<br>
&lt;bramus> … larger than it would be in sass, but gain in clarity to distinguish local vars vs vars you want to write in the styles is more clear now<br>
&lt;bramus> … worth the cost<br>
&lt;bramus> … (next slide)<br>
&lt;iank_> Ding dong<br>
&lt;bramus> … swapping variables from env to var was more complicated<br>
&lt;bramus> … must be in a way that is transparant and intuitive to authors<br>
&lt;bramus> … example here that is a mixin to set an element and its child to the same size<br>
&lt;bramus> … passes 1em down to the child<br>
&lt;bramus> … in the old model, it woudl sub the 1em into the styles for both<br>
&lt;bramus> … would end up with parent being a calc with resulting different sizes<br>
&lt;bramus> … old rough desugaring would result in 100px and 200p<br>
&lt;bramus> … new way of doing it (this happens internally) is that we sub in th estyles but the props that get set (width) get rewritten in a new anonymous fhta reeturns that value<br>
&lt;bramus> … let me walk through it:<br>
&lt;bramus> … size gets internally renamed<br>
&lt;bramus> … to magic-arg1 here<br>
&lt;bramus> … same syntax and inherits<br>
&lt;bramus> … then generate an anonymous fn<br>
&lt;bramus> … takes the arg and produces the value<br>
&lt;bramus> … produce the second one as well<br>
&lt;bramus> … when we rewrite we start with the parent, lifts the argument value into the magic thing which gets resolved against the font size<br>
&lt;bramus> … then the widt value is rewritten as the anonymous fn with the arg, returninng 100px<br>
&lt;astearns> q+<br>
&lt;bramus> … child does same thing, has font-size of 20px but we dont care … replace with the magic value returned<br>
&lt;florian> q+<br>
&lt;bramus> … so the two values are now identical<br>
&lt;bramus> emilio: you would still get 1em in both if you omit the lenght right?<br>
&lt;bramus> TabAtkins: yes<br>
&lt;astearns> q-<br>
&lt;bramus> florian: so you convince me that the geenrated code does the rigth thing here<br>
&lt;florian> q+<br>
&lt;bramus> … how did you get to it?<br>
&lt;bramus> TabAtkins: essentially 3 bits<br>
&lt;bramus> … take the body, all their values, and replace with anon fn, very trivial<br>
&lt;bramus> … lets you resolve the references well based on the info<br>
&lt;bramus> … then we also lift out the argument<br>
&lt;bramus> florian: so you do 1 per invocation of the fns, but not the args?<br>
&lt;bramus> TabAtkins: yes, only want to do that once<br>
&lt;bramus> … if there are already local vars, that is part of the fn body as well<br>
&lt;bramus> … which is why we have to fns, to carry around other stuff<br>
&lt;bramus> … that is the big thing that andruud prototyped and is working very well<br>
&lt;astearns> ack florian<br>
&lt;bramus> florian: im convinced that it solves this use case/ how convinced are we that other use cases need this too?<br>
&lt;bramus> … here you say you want the same length … and the hypothesis is that you want it<br>
&lt;bramus> TabAtkins: correct. people could reasonably want other things<br>
&lt;bramus> … solution now is to not type your variables<br>
&lt;bramus> florian: reasonable answer<br>
&lt;bramus> TabAtkins: could in the future add syntax for that<br>
&lt;bramus> … there is space to allow for that<br>
&lt;bramus> … most languages have an opt out<br>
&lt;bramus> miriam: this relates to the issue I brought up yesterday, related to typing<br>
&lt;bramus> TabAtkins: one aspect that is unfortunately required here is that mixins have to be scoped<br>
&lt;bramus> … this example here does not work as written<br>
&lt;bramus> … it takes a value and sets the widt on the element, child, and all ancestors<br>
&lt;bramus> … kinda worked in the old model (probablly what you didnt want) but now no longer does<br>
&lt;bramus> florian: doe snot work because the variable resolution uss the tree?<br>
&lt;bramus> TabAtkins: var is resolved on l2. l1 cannot receive that (cyclic). doe snot work for 2 bc all selectors are implictly scoped to the element being applied … so this wont style the ancestors<br>
&lt;bramus> … will only match and style l2 and 3<br>
&lt;bramus> … in this model width of el2 and el3 will be 20px<br>
&lt;bramus> … there might be reasonable reasons to style a parent or sibling<br>
&lt;bramus> … there are ways around that<br>
&lt;bramus> … the most stragithforward way is to rewrite the selectors so that you apply to the higher up element<br>
&lt;bramus> … not perfect, but should work most of the time<br>
&lt;bramus> … unfrotunately a strict requirement to makes this work<br>
&lt;bramus> … possiblity to have ??? but then would lose all the other things<br>
&lt;bramus> … would stop working<br>
&lt;bramus> florian: why cant you?<br>
&lt;astearns> s/???/unscoped mixins/<br>
&lt;bramus> emilio: because you dont know what the em is until you style the parent<br>
&lt;bramus> florian: the fact that you resolve the var on an el and its no tavalvaiable, i see<br>
&lt;bramus> … but the fact that you have to apply scope?<br>
&lt;bramus> TabAtkins: technically you woulndt have to have to<br>
&lt;bramus> … you could styl e aparent aas long as you are not using any of the args<br>
&lt;bramus> … here the var arg wouldnt be alloew to be set on the parent<br>
&lt;miriam> q+<br>
&lt;bramus> florian: this makes it predicatble and understanbable<br>
&lt;emilio> q+<br>
&lt;astearns> ack fantasai<br>
&lt;bramus> fantasai: confused about the @scope stuff …interesting thing with the cascased, whcih author would not expect here<br>
&lt;bramus> TabAtkins: I think I have it correctly in the spec (yesterda). intention is that we dont carry the cascading implications, only do the scoping<br>
&lt;bramus> … (the filtering part)<br>
&lt;bramus> fantasai: what is the benefit?<br>
&lt;bramus> TabAtkins: prevents confusion<br>
&lt;bramus> … (missed)<br>
&lt;bramus> … that var wouldn texist<br>
&lt;bramus> florian:so you uare making sure the rule isnt available whwere the variables are not<br>
&lt;romain> q+<br>
&lt;bramus> TabAtkins: so there is a chance we could add an tlernative form withotu the scoping thing (but prolly also without args)<br>
&lt;bramus> … if we end up needing those, that is possible<br>
&lt;bramus> fantasai: but seems unlikley<br>
&lt;bramus> TabAtkins: yes, because most of the time you can lift things up<br>
&lt;bramus> fantasai: gonna have to make that clear in the dfn<br>
&lt;astearns> ack miriam<br>
&lt;bramus> miriam: same logic does apply to siblings<br>
&lt;bramus> … even if we had sibling scope, because we talk abou tscope of a var<br>
&lt;bramus> … same solution as well?<br>
&lt;bramus> TabAtkins: yes<br>
&lt;florian> q+<br>
&lt;astearns> ack emilio<br>
&lt;bramus> emilio: this example here might not be amazing (its not typed) but if it were typed it would make it more clear<br>
&lt;bramus> TabAtkins: yes, lack of typing<br>
&lt;bramus> emilio: makes sense<br>
&lt;bramus> … need to explain this very well<br>
&lt;bramus> TabAtkins: yes, still some more spec clarification and examples that need to be done<br>
&lt;bramus> … this model is been implemented by andruud and he’s happy with it<br>
&lt;bramus> andruud: desugaring into custom fns is implemented, happy with that. scoping stuff and hygeinic rewrite not yet<br>
&lt;bramus> emilio: a bit weird, because you cant really use th ecustom props machinery (to prevent leaks on css om)<br>
&lt;bramus> … a bit messy<br>
&lt;bramus> TabAtkins: I have a line that says “they are unobservable” that needs more details<br>
&lt;bramus> emilio: stashin ganother hashmap makes me sad<br>
&lt;bramus> TabAtkins: quite often you dnt need to dom ost of this … if its not typed, you can do direct style spamming<br>
&lt;bramus> emilio: hard to prove though<br>
&lt;bramus> … like weir dislinb combinator<br>
&lt;bramus> … maybe not realistic<br>
&lt;kizu> q+<br>
&lt;bramus> TabAtkins: good thing is you dont need to be 100% correct … you can miss some<br>
&lt;bramus> emilio: you can avoid optimizing some of the things, as long as you end up correct<br>
&lt;bramus> TabAtkins: can andruud elaborate on how we omit some of the fns?<br>
&lt;bramus> andruud: only case where we have an optimization right now is where there are no arbitrary substituion funcions<br>
&lt;bramus> … not a lot yet<br>
&lt;bramus> … would expect that this is fairly cacheable<br>
&lt;bramus> … long mixing with complicated logic … all of those declarations are using same sset of locals<br>
&lt;bramus> … could be cached<br>
&lt;bramus> emilio: yes<br>
&lt;TabAtkins> https://docs.google.com/presentation/d/e/2PACX-1vQvEiYo4d9N3-uL8vWIAfbipOfF42hE06lJ-l04HOaCfXiBHuhOmDFHFOvPHq9wjoXu6PprikhYsjVg/pub?start=false&amp;loop=false&amp;delayms=3000<br>
&lt;bramus> … tricky, bu tnot all too concerned<br>
&lt;TabAtkins> this work?<br>
&lt;astearns> ack romain<br>
&lt;bramus> romain: unfortuante about the scoping I think styling the next element or parent is the first thing authors would like to do<br>
&lt;bramus> … since these vars arent addressable by authros, couldnt these be stored on a global map?<br>
&lt;bramus> TabAtkins: no, the point of storing is that (if they are typed) they would be resolved on that element<br>
&lt;bramus> romain: sure, but you could still uniquely address them<br>
&lt;bramus> … but store in global map<br>
&lt;bramus> emilio: effectively that ??? would be cyclic because value of thing would be maybe on you rchild and you need the parent style to inherit from<br>
&lt;bramus> romain: but this invisible rsolved variable value would not inherit<br>
&lt;bramus> florian: (agreeing with the q)<br>
&lt;bramus> … resolve on element and sintead of stashing on the element, you use global var map<br>
&lt;bramus> emilio: no. if you want to affect an ancestor, as soon as ancestor style affects … its not abou tthe var, anything that …<br>
&lt;bramus> florian: fact that various props affect each other change tht<br>
&lt;bramus> emilio: no, doesnt work<br>
&lt;bramus> romain: dont we already have this problem without mixins?<br>
&lt;bramus> TabAtkins: no, because custom props arent available on parents<br>
&lt;bramus> emilio: this resolves at a very specific time<br>
&lt;bramus> TabAtkins: if you want to style siblings, just lift the mixin 1 higher and put it on the parent<br>
&lt;bramus> … bc you can use has to target the parent element, you should always be able to make that transform and move it 1 level up<br>
&lt;bramus> romain: dont agree<br>
&lt;bramus> … many cases where you dont know the surroundings but want to style the next one<br>
&lt;bramus> TabAtkins: yes<br>
&lt;bramus> … the selector for the parent is :has(parent)<br>
&lt;fantasai> Archived Slide Deck:<br>
&lt;fantasai> https://lists.w3.org/Archives/Public/www-archive/2026Jan/0003.html<br>
&lt;bramus> … otherwise I could agree, but now we have has to solve that<br>
&lt;astearns> ack florian<br>
&lt;bramus> florian: thanks for explanations<br>
&lt;bramus> … do suspsec thtat scoping makes some selectors not work would be mysterious … would be nice if there would be warnings about that (by tools)<br>
&lt;astearns> @scoped-mixin<br>
&lt;bramus> TabAtkins: agree that since mixin systems like that dont have this, this will be a point of confusion<br>
&lt;bramus> … inspectorrs and notes inthe spec could help<br>
&lt;bramus> fantasai: a note or suggestion would be helpful<br>
&lt;bramus> TabAtkins: happy to take that as feedback to go<br>
&lt;astearns> ack kizu<br>
&lt;fantasai> ACTION: Add a suggestion to the spec for the inspector to flag selectors that can't match<br>
&lt;bramus> kizu: i think i amo reagainst the scoping<br>
&lt;bramus> … have situation where we dont have a good way that is not confusing to authors<br>
&lt;bramus> … the selectors/rule sthat dont apply<br>
&lt;bramus> … 2 points of confusion<br>
&lt;bramus> … some reules for has() that dont work<br>
&lt;bramus> … or the magically dispparing thing if we dont do scoping, only specific values are local vars<br>
&lt;bramus> … in both cases we would use devtools help<br>
&lt;bramus> … from athor pov I thin kmost wont use typed vars<br>
&lt;bramus> … often use mixins without args<br>
&lt;bramus> … and use stuf flike * + *<br>
&lt;bramus> … or copy some css snippet<br>
&lt;miriam> q+<br>
&lt;bramus> … between tose two, having variable that resolve like for :has() or at IACT is better<br>
&lt;bramus> … IACVT for non scoped … for things tha twould be cyclic,so you would have IACVT<br>
&lt;bramus> … you would not have this var from anywhere, as its from el2<br>
&lt;miriam> q-<br>
&lt;bramus> … I think there are more use cases. if you omit length it will work, but don tknow if authors would know<br>
&lt;bramus> … not scoping is sth that authors will expect more I think, rather than being scoped<br>
&lt;bramus> … potentially throwing in more styles<br>
&lt;miriam> q+<br>
&lt;bramus> TabAtkins: point of correction: if we omitted length here, ti would not work in the other case<br>
&lt;bramus> … wha twould happen here is tha twe take the uninterpreted 1em but bc of the desugaring, we try to apply to el1 and get a failed var reference making it IACVT<br>
&lt;bramus> … it is not the typing hta tinvalides, its the desugaring<br>
&lt;bramus> … &lt;missed> we need it to desugar differently. a mixin unscoped that changes the desugaring rules (no arg lifiting, no fn rewrite) could work but we cant do it automatically<br>
&lt;bramus> … cant have anything that works reasonably in the desugaring work<br>
&lt;bramus> … ???<br>
&lt;bramus> kizu: this case here could b esolved with the global hash<br>
&lt;bramus> TabAtkins: yes, in theory<br>
&lt;bramus> kizu: you could potentially do romain’s global hash thing. Just store token 1em and width on el1 would get value from env var<br>
&lt;bramus> … not simply and differentiation<br>
&lt;bramus> andruud: wasnt romain saying tha twe shoul dnot scope at all here?<br>
&lt;bramus> kizu: yeah<br>
&lt;bramus> andruud: tha twould apply some width here?<br>
&lt;bramus> kizu: I think tab says that we desugar as magical var<br>
&lt;bramus> … and bc we define on el2 it is not avilable on parent<br>
&lt;bramus> … unelss we could desugar to apply on eery element that we define here<br>
&lt;bramus> andruud: if we dropped scoping, then the width on el1 should resovle to something here<br>
&lt;bramus> TabAtkins: el1 10px, el2 20px, …<br>
&lt;bramus> andruud: if we drop scoping then el1 should ge ta valid width here<br>
&lt;bramus> TabAtkins: yes<br>
&lt;astearns> ack miriam<br>
&lt;bramus> miriam: somewhat related<br>
&lt;bramus> … in terms of what we talked about: controls over changing the type<br>
&lt;bramus> … once you capture a value, you cant uncapture<br>
&lt;bramus> … no way to get back from px to 1em<br>
&lt;bramus> … i think the orig proposal here over-indexes on that we want the same value here<br>
&lt;bramus> … dont think that is ncessariy the common use case<br>
&lt;bramus> … I like an approach that prioritizes this sort of thing and if that helps avoid scoping tha tis great<br>
&lt;bramus> … and then can use the capturing fns in a more limited way<br>
&lt;bramus> … and those can be IACVT<br>
&lt;bramus> TabAtkins: today you can get that behavior if you dont type your args<br>
&lt;bramus> … they are left uninterpreted<br>
&lt;bramus> … 1em stays 1em<br>
&lt;bramus> miriam: I think that is good<br>
&lt;bramus> TabAtkins: we still end up applyin gth escoping, bc of desugraging parent would fail to get all styles (IACVT)<br>
&lt;bramus> … if we did want to go fully unscoped, we need an implicit flag because it changes desugaring<br>
&lt;bramus> … cant automatifally do it<br>
&lt;bramus> … as long as you are ok with scoping applying, you can choose an arg to be left as is or not, on a case to case basis<br>
&lt;bramus> miriam: definingint he typ eon the param is basically a shorhtand for at call time using a fn to set the type<br>
&lt;bramus> astearns: so we had the overall presentation<br>
&lt;bramus> … been having a good conversation about scoping ¬ which is a sepaate issue<br>
&lt;bramus> TabAtkins: the three issues were tied into this one presentation<br>
&lt;bramus> astearns: sounds like there is a preference to remove the scoping requirement<br>
&lt;bramus> TabAtkins: dont think we heard that<br>
&lt;bramus> astearns: romain and miriam?<br>
&lt;bramus> miriam: what I way saying is that if scoping is mostly abou tgetting taht ability to capture, i don thtink that is important enough to add the scoping restriction<br>
&lt;bramus> TabAtkins: it is required for a few subtle aspects of this<br>
&lt;bramus> … not jsut type capturing<br>
&lt;astearns> s/romain and miriam/romain, roman, and miriam/<br>
&lt;bramus> … in order to make them work at all levels, we also need tha tsort of scoping, some things otherwise get lifted there<br>
&lt;bramus> … the desugaring I described that ends up effectivley scoping is needed for sevearl apsect<br>
&lt;bramus> … doing the unscoped is possible, bu tthen some things do not work the same way<br>
&lt;bramus> … large change<br>
&lt;kizu> q+<br>
&lt;bramus> … like I said, totally possible to add a flag to mixins that causes them to desugar differently, but means that if you are not careful you could very easily end up writing bad code that does not do what you want it to do, but can also write sass-like mixins<br>
&lt;bramus> … this goes beyond ems<br>
&lt;bramus> … if you use var(--foo) on el2, you want the value from el2, not the rest<br>
&lt;bramus> … this is the exact same problem as ems here<br>
&lt;bramus> … in general the desugaring tha timply scoping is almost always th eright thing to do, and you can escape it by using untyped variables<br>
&lt;bramus> … if we still end up wanting a fully unscoped thing, that is possible … want to try and see how far we can go without that first<br>
&lt;astearns> q+<br>
&lt;bramus> … it is in scope of the spec to do an unscoped mixin<br>
&lt;bramus> kizu: random idea<br>
&lt;astearns> ack kizu<br>
&lt;bramus> … would be great to have both<br>
&lt;bramus> … one thought how to express this, is maybe … we were thinkgina bout making @result optional … so we need @result for local vars<br>
&lt;bramus> … only posible when its scoped<br>
&lt;bramus> … so what if you omit @result it is unscoped<br>
&lt;bramus> … if you add it, it is scoped<br>
&lt;bramus> … once you add it, its mcuh easier to say that it behavs differenly<br>
&lt;bramus> … cannot use siblings and stuff<br>
&lt;bramus> … in addition could disallow thyping when there is no @result<br>
&lt;bramus> TabAtkins: not just local vars, also how args need to resolve<br>
&lt;miriam> that feels a bit implicit to me, doesn't clearly express what's happening<br>
&lt;bramus> … even her in this case iwth an untyped argument with a variable, you would expect it toread it from the element<br>
&lt;bramus> … with unscoped it would not do that<br>
&lt;bramus> … you are very loathe to make it implicit<br>
&lt;bramus> … want it to be explicit<br>
&lt;bramus> … bc you cant have local vars when unscoped<br>
&lt;romain> agree that it is better to be explicit, easier to teach authors and look it up if you encounter it in source code<br>
&lt;bramus> …we proally can omit the @result (prolly with @unscoped-mixin)<br>
&lt;bramus> … if we do it, we could make them virtually identical to how you would write them in sass<br>
&lt;bramus> … module the naming patterns<br>
&lt;bramus> kizu: if we do both, it would be important to ship both at the same time<br>
&lt;bramus> … so that people dont shoot themselves in the foot with the other (shipped) version<br>
&lt;bramus> TabAtkins: fair and given that we dont do a bunch of fancy rewriiting in the unscoped one, that sounds feasible<br>
&lt;bramus> astearns: here to reinfoced kizus point<br>
&lt;bramus> … if we do both, we might want to make them unscoped by default<br>
&lt;bramus> TabAtkins: disagree<br>
&lt;bramus> astearns: from a learning perspective … could make @mixin unscoped and add @scoped-mixin<br>
&lt;bramus> TabAtkins: reason I disagree I thik people will do things like accessing custom prop in a mixnig form an alement<br>
&lt;bramus> astearns: peole are already use to using vars in different context and having them resolve differently<br>
&lt;astearns> ack astearns<br>
&lt;bramus> TabAtkins: when those contexts are visible, yes<br>
&lt;romain> q+<br>
&lt;bramus> …with an @apply you dont see the context<br>
&lt;bramus> … would probably expec that I am writing there to ???<br>
&lt;astearns> ack fantasai<br>
&lt;bramus> fantasai: kizu argumented that unscoped does the simple thing … so kizu was saying @result could be a trigger<br>
&lt;bramus> … what is your objection?<br>
&lt;bramus> TabAtkins: very subtle syntax distinction. easy to do by accident. a lot of times forgot to add @result myself (because its a new thing to me)<br>
&lt;bramus> fantasai: why is that less of an obivius signal?<br>
&lt;bramus> … rahter than a keyword<br>
&lt;bramus> TabAtkins: a keyword there would say scoped or unscoped or sth like that<br>
&lt;bramus> fantasai: so @result by virtue of behing an at-rule that contains the rules that get returned … like blocks that createa  scope for some kind of purpose<br>
&lt;bramus> … not sure if that is non-obvious<br>
&lt;bramus> … scope as akeyword here could get mixed up with @scope<br>
&lt;bramus> … so if we use scoping here with a keyword, it might imply we do @scope but we dont<br>
&lt;bramus> … leaning towards more what kizu is saying: the @result is the trigger<br>
&lt;bramus> TabAtkins: the @result doe snot indicate anything<br>
&lt;bramus> … nothing about the word says that it is processed differently<br>
&lt;bramus> … the name mirrors the result descriptor from fns<br>
&lt;romain> +1 to using an explicit and self explaining flag<br>
&lt;bramus> fantasai: is there any other effect other htan cutting of what matches? doe sit change computation?<br>
&lt;bramus> TabAtkins: yes, massively: int his case here --foo is not resovled ont he element that its being applied to (el2) but now will resolve to whathever the var is<br>
&lt;bramus> fantasai: so resolving things differnelty and trimming the selector<br>
&lt;bramus> florian: these are not two independent effect<br>
&lt;kizu> `@resolve {}`<br>
&lt;bramus> fantasai: so the fact that its applying scoping is prolly not the biggest difference but the computation<br>
&lt;bramus> TabAtkins: yes<br>
&lt;bramus> fantasai: so its not really about scoping …<br>
&lt;bramus> TabAtkins: right. if you have a better word here …<br>
&lt;bramus> fantasai: should come up with a better one<br>
&lt;bramus> … dont think we need an extra keyword int he prelude<br>
&lt;bramus> andruud: should we do something on the apply side instead?<br>
&lt;bramus> florian: is there vocabulary for this?<br>
&lt;bramus> TabAtkins: standard macro<br>
&lt;astearns> ack romain<br>
&lt;bramus> romain: the @result didnt get much detail in the presentation, but is in the issue … how does it interact with apply? only allowed iwhtin results? are local vars outside of @result? avialble in nested mixins? stuff like that …<br>
&lt;florian> q+<br>
&lt;bramus> TabAtkins: can definitely @apply more mixins insid ea mixin … needs to be in @result<br>
&lt;bramus> … its a nested declarations block<br>
&lt;bramus> … it can take things like @apply<br>
&lt;bramus> … outside of @result its not<br>
&lt;bramus> … simliar to fns, limted to what it can accept<br>
&lt;lea> q?<br>
&lt;lea> q+<br>
&lt;bramus> … when you sub in (fi you call a mixin) insnide @result, if that one exepected a var from higher up the chain it can see a local var from the outer mixin<br>
&lt;astearns> zakim, close queue<br>
&lt;Zakim> ok, astearns, the speaker queue is closed<br>
&lt;bramus> … it an intercept outer vars  just like in fns<br>
&lt;bramus> romain: is it required to use @result?<br>
&lt;bramus> … but people might find it annyoing<br>
&lt;bramus> … could we allow omission?<br>
&lt;bramus> TabAtkins: that is the confusion part I want to avoid<br>
&lt;bramus> … could have a mixin whose only effect is to define a few variable<br>
&lt;bramus> … thus ther ei sno syntactic distinction between local vars and custom props you want to apply on the element<br>
&lt;bramus> … need a syntactic flag: that is the @result block<br>
&lt;bramus> romain: for CSS OM it is better tha tthese are well defined and not dynamic<br>
&lt;bramus> TabAtkins: very much so<br>
&lt;bramus> … the rule prolly gonna be readonly<br>
&lt;bramus> … regardless certainly a lot easier<br>
&lt;florian> q?<br>
&lt;astearns> ack florian<br>
&lt;bramus> florian: trying to find different ways of talking about this<br>
&lt;bramus> … hygeienic is wrong word<br>
&lt;bramus> … one is a literal mixin, and the other one a resolved one<br>
&lt;bramus> … distinguish not in the definition, but in the application<br>
&lt;bramus> … (like andruud suggested)<br>
&lt;bramus> TabAtkins: plausible<br>
&lt;bramus> … should add the mechanism for usncoped, and should discuss how to do that<br>
&lt;bramus> … should figure out what the synatx should be there<br>
&lt;bramus> miriam: might want to distinguish at defintion time<br>
&lt;bramus> TabAtkins: yeah, double opt-in … difference are important enough to call out<br>
&lt;bramus> fantasai: dont think user casers about how its computing. its the person defining it most likely<br>
&lt;bramus> florian: fair<br>
&lt;bramus> TabAtkins: not looking to decide on that now. can talk about it later<br>
&lt;astearns> ack lea<br>
&lt;bramus> lea: the @result is userful for syntacitcally distinquicshing local vars<br>
&lt;bramus> … vs what is exposed<br>
&lt;bramus> … many mixins have no such distinction<br>
&lt;bramus> … only reason you creat ethe mixin is to abstarct stuff<br>
&lt;bramus> … would be very annoyoing to have to nest that twice<br>
&lt;bramus> … becomes boilerplate<br>
&lt;bramus> TabAtkins: agree<br>
&lt;bramus> lea: wonder if we could just assume the entire mixin is exposed if there is no @result<br>
&lt;bramus> … even can do it in cssom<br>
&lt;kizu> +1 to that<br>
&lt;bramus> TabAtkins: for unscoped agree that we should not require it<br>
&lt;bramus> … for scoped im at least weakly to moderata against that syntax-??? would not object, but would prefer to keep syntax consistent<br>
&lt;bramus> … so that its obvious for the person reading it what it does<br>
&lt;bramus> … condition can be in the body, and result in the @result<br>
&lt;bramus> … not clear immediately otherwise to know what syntax branch you are in<br>
&lt;bramus> lea: seems like the only things we can put outside are varaibles?<br>
&lt;bramus> TabAtkins: and conditionals<br>
&lt;bramus> TabAtkins: the @result can also be in the condition<br>
&lt;bramus> lea: ????<br>
&lt;bramus> TabAtkins: anything like you put in a custom fn<br>
&lt;dbaron> s/in the condition/in the conditional/<br>
&lt;bramus> lea: but custom fns are only varaibles?<br>
&lt;bramus> TabAtkins: you can define them, and use condtiioanls to define the result condtionally<br>
&lt;bramus> lea: its still basically only private vars<br>
&lt;bramus> astearns: the conditianls can also result a result<br>
&lt;bramus> lea: i am talking about outside the result<br>
&lt;bramus> … eseentailly in terms of observable things, its only vars that we are hidning<br>
&lt;astearns> s/also result/also include/<br>
&lt;bramus> … so that instead of requiring an @result, one could mark a variable as private<br>
&lt;bramus> … so no nesting<br>
&lt;bramus> TabAtkins: that is a suggestion in the thread<br>
&lt;bramus> … suggestion there was to write locals as `@local --foo: bar`<br>
&lt;miriam> q+<br>
&lt;bramus> … possible<br>
&lt;bramus> … but I think its useful for authors to have overlap between how you write custion fucntions and mixins<br>
&lt;bramus> lea: I thkn that something around this would be better, because mental model for mixins is ???<br>
&lt;bramus> … @result is an additional step<br>
&lt;bramus> … designed around you ahve private things, and want to expose<br>
&lt;astearns> with @local you can interleave local things and the output<br>
&lt;bramus> … I think mixins are the other way around<br>
&lt;bramus> TabAtkins: agree that using local vars in a mixin is prolly gonna be less common thatn reslyin gon args<br>
&lt;bramus> miriam: Anecdtoally I already saw someone doing a video about it, and their reaction was that @result does the same as the result descriptor.<br>
&lt;bramus> … was chris coyier<br>
&lt;bramus> TabAtkins: that is exactly te mental recotextualization I want to hit here<br>
&lt;bramus> lea: seems like a nice to have<br>
&lt;bramus> TabAtkins: neither trumps the other<br>
&lt;bramus> lea: important that we get it right and that its effcitient<br>
&lt;bramus> TabAtkins: remember that I mentioend we proabley should add unscoiped, which dont have local vars<br>
&lt;bramus> lea: but having two types seems more inconsistent<br>
&lt;bramus> TabAtkins: not particularly<br>
&lt;bramus> astearns: what can we accomplish today?<br>
&lt;lea> s/lea: but having two types seems more inconsistent/lea: but having two types of mixins that are inconsistent with each other seems like a bigger inconsistency than mixins not being consistent with functions/<br>
&lt;bramus> TabAtkins: propose reso are: we accept these changes (still early spec) and then additionally that I add the unscoped variant and open an for how we name/invoke these two concepts<br>
&lt;bramus> … and to what degree their syntax is identitical or diverges<br>
&lt;bramus> florian: so resolving on accepting the behavior, but keeping the syntax open<br>
&lt;bramus> TabAtkins: yes<br>
&lt;lea> +1 to exploring the problem space, but strong reservations on the syntax<br>
&lt;bramus> florian: works for me<br>
&lt;bramus> astearns: any concerns about accepting the scoped mixin along with anunscoping getting specified?<br>
&lt;bramus> … this is covering all thre issues?<br>
&lt;bramus> TabAtkins: 3/4<br>
&lt;bramus> … relevant, but ???<br>
&lt;bramus> astearns: objections?<br>
&lt;bramus> RESOLVED: Accept the proposal<br>
&lt;bramus> RESOLVED: Add an unscoped mixin variant (syntax TBD)<br>
&lt;bramus> astearns: and outlining the differences needs to be very clear<br>
</details>


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


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

Received on Thursday, 29 January 2026 18:11:30 UTC