- From: CSS Meeting Bot via GitHub <noreply@w3.org>
- Date: Wed, 25 Mar 2026 15:56:10 +0000
- To: public-css-archive@w3.org
The CSS Working Group just discussed `[css-mixins-1] Review of @macro`, and agreed to the following:
* `RESOLVED: Add a Note to the spec and will continue discussing this`
<details><summary>The full IRC log of that discussion</summary>
<kbabbitt> TabAtkins: next issue was andruud raising explicitly a review for @macro desing in spec<br>
<kbabbitt> ... at the f2f, we discussed how mixins have to scope their rules to achieve other things mixins want to do<br>
<kbabbitt> ... specifically hygenic rewrite of args so they don't escape<br>
<kbabbitt> ... there was significant desire to have a non scoped variant<br>
<kbabbitt> ... there are issues with this, it's weaker in some ways but stronger in others<br>
<kbabbitt> ... long discussion, we didn't resolve on anything at f2f on how it would be called, syntax, etc.<br>
<kbabbitt> ... after giving it some thought I settled on design in spec<br>
<lea> q?<br>
<lea> q+<br>
<kbabbitt> .... @macro rule, roughly identical to @mixin, except doesn't have a parameter list, doesn't have local vars, because there are no locals you don't have to separate them from return value, so you don't need @result either<br>
<kbabbitt> ... just @macro, name, {}, inside there, it's just the rules that will be emitted when you apply, and that's it<br>
<kbabbitt> ... no restrictions, can put anything in there, just literally sub them in at call site<br>
<kbabbitt> ... called exactly the same way as a mixin<br>
<kbabbitt> ... at user level, mixins and macros are nearly identical<br>
<kbabbitt> ... still use @apply, 2 rules share same namespace<br>
<kbabbitt> ... an aythor could swap their definition between the two as they decide one is better than the other and don't have to adjust call sites<br>
<kbabbitt> ... one bit that I know received some pushback from lea is lack of args<br>
<kbabbitt> ... problem is we can't have them act like mixin arguments<br>
<kbabbitt> ... where they're accessible as a var9)<br>
<kbabbitt> ... magic machinery under covers that allows them to use var syntax as if they were custom properties sitting around<br>
<kbabbitt> ... but that is a thing that requires scoping on the mixin to make work<br>
<kbabbitt> ... because we don't have that we can't have same machinery work<br>
<florian> q+<br>
<kbabbitt> ... unless we radically change something intersting about how var application works<br>
<kbabbitt> ... i.e. not using var syntax but something more bizarre<br>
<kbabbitt> ... macros can't take vars<br>
<lea> q-<br>
<lea> q+<br>
<kbabbitt> ... and this is fine overall because macros just emit values<br>
<andruud> q+<br>
<kbabbitt> ... you can still set ? on macro and that accomplishes what you want<br>
<kbabbitt> ... when sass does its macro stuff, it uses vars to construct the rule directly, they exist at higher syntax level before rules are actulaly built<br>
<kbabbitt> ... didn't want to go that far with macros, make them more distinct and magical than anything else in CSS<br>
<astearns> ack florian<br>
<kbabbitt> florian: in typical other languages when you have unhygenic macros, typically you can have params and they shadow vars with same name<br>
<kbabbitt> ... what I'm taking is that doing something like that would be impractical here<br>
<kbabbitt> ... first half of my question is confirming that, second question: assuming we go that way are we losing any use case, even minor ones?<br>
<kbabbitt> TabAtkins: first question, yes, correct<br>
<kbabbitt> ... because I'm resuing exsiting machinery of custom props, normal mixins just set randomly named custom props basically that you can't see<br>
<kbabbitt> ... we rely on inheritance to carry those into rules and be accessibile via var<br>
<kbabbitt> ... but that means they have to have a common ancestor that can set these vars and inherit them to everything<br>
<kbabbitt> ... that's part of the reason why mixins require socped rule on result<br>
<kizu> q+<br>
<kbabbitt> ... in macros because we don't apply scoping you can style any element with any selectors any way you want<br>
<kbabbitt> .,. and there's no way to have an arg work where you do something a lot weirder where you install args as root vars and track them as if they were page wide<br>
<kbabbitt> ... that didn't seem worthwhile to try for, to me<br>
<kbabbitt> ... there is no hygenic rewriting here and this every var() you write is looking for a real custom prop<br>
<andruud> q-<br>
<kbabbitt> ... could write a macro that expects custom props, that's just a less convenient way of passing args<br>
<florian> q+<br>
<kbabbitt> ... but this lets you put them where you want them and you don't have to fall back to root<br>
<astearns> ack lea<br>
<kbabbitt> lea: don't think the issue is whether macros allow args but whether we need a separate construct at all<br>
<kbabbitt> ... author facing purpose is the same between mixin and macro, having to explain the difference sounds confusing<br>
<kbabbitt> ... would prefer to be able to reuse mixins for this<br>
<kbabbitt> ... seems strange to use a different construct for slightly more convenient syntax<br>
<kbabbitt> ... this should be discussed ideally after author ? resuolt<br>
<kbabbitt> ... it doesn't sound like macros offfer anything, macros are just mixins with @result<br>
<kbabbitt> ... if we resolve not to wrap in @result, I don't think this merits a completely new concept, better to wrap in @result every time<br>
<kbabbitt> ... precedence in other languages, when you want different syntax that's more convenient, concept remains the same you just have different syntax<br>
<kbabbitt> ... this should be discussed after issue of auto wrapping in @result<br>
<kbabbitt> ... worst case maybe we have some kind of flag in @mixin, it declares in advance that contents should be auto wrapped in @result<br>
<kbabbitt> ... saves us from having to explain completely different concept<br>
<romain> I think that arrow functions vs functions in JavaScript is an argument in favor of @macro, instead of against.<br>
<kbabbitt> ... it sounds extremely annoying to explain the difference between authors<br>
<kbabbitt> s/between authors/between them to authors/<br>
<astearns> ack kizu<br>
<kizu> What if<br>
<kizu> <br>
<kizu> @macro --foo(--a, --b) {<br>
<kizu> padding: var(--a) var(--b);<br>
<kizu> }<br>
<kizu> <br>
<kizu> @apply --foo(10px, 20px);<br>
<kizu> <br>
<kizu> would be same as just literal<br>
<kizu> <br>
<kizu> --a: 10px;<br>
<kizu> --b: 20px;<br>
<kizu> padding: var(--a) var(--b);<br>
<kizu> <br>
<kizu> ?<br>
<kbabbitt> kizu: what if we were able to accept args but those would just be syntactic sugar for defining custom props<br>
<kbabbitt> ... never typed, difference with proper mixins<br>
<kbabbitt> ... [points to example]<br>
<TabAtkins> @macro --foo(--a, --b) { padding: var(--a); &+* { padding: var(--b); } }<br>
<kbabbitt> ... would mean that when you call this, you're adding 2 new custom props, but your macro could already do this on its own<br>
<kbabbitt> ... you're not doing something you couldn't otherwise, but simplifying<br>
<kbabbitt> ... could be an issue but on the author to handle<br>
<kbabbitt> ... as an author you need a macro that works<br>
<kbabbitt> ... if you do inside & + * it won't work but still allows more<br>
<fantasai> That doesn't encapsulate very well, seems like it would be perhaps surprising?<br>
<kbabbitt> ... not removing the use case that could otherwise be possible<br>
<kbabbitt> ... just making it harder to do simpler things<br>
<kbabbitt> TabAtkins: the alternate I posted is the reason why<br>
<kbabbitt> ... in a mixin, you never have a problem with accessing local vars, they work everywhere in the body<br>
<lea> q+<br>
<kbabbitt> ... this is the case for custom function stoo<br>
<kbabbitt> ... generally what you would expect to happen, rules in a block, var defined in block, expect to be able to use it<br>
<kbabbitt> ... weird if certain types of rules can't access them because they aren't children of applying rule<br>
<kbabbitt> ... seem s like it's very easy to mess up and write things that accidentally depend on these vars and won't see them<br>
<kbabbitt> ... vs no args, have to set custom props yourself, it's much more obvious I think that writing a custom prop on element doesn't make it visible to others<br>
<kbabbitt> ... that's just how custom props work, if you want it visible on multiple, set it on the parent<br>
<kbabbitt> ... no way to do that on macro except by lifting whole macro up one level and applying to parent<br>
<kbabbitt> ... at which point you could just write a mixin<br>
<kbabbitt> .. that's why I'm against, not obvious they're not visible everywhere<br>
<kbabbitt> ... having author be more explicit solves those problesm<br>
<fantasai> +1<br>
<romain> q+<br>
<kbabbitt> ... it's obvious where they're visible to<br>
<kbabbitt> ... vs keeping more aligned with mixin syntax<br>
<astearns> ack florian<br>
<kbabbitt> ... they diverge sufficiently in behavior that I don't think there's benefits<br>
<kbabbitt> florian: assuming there's some usage for both solutions, I think I disagree with lea that most languages distinguish these things<br>
<kbabbitt> ... most languages have both hygenic and not and don't have to distinguish<br>
<kbabbitt> ... think it's a little unfortunate you don't have a way to declare which vars you intend to use<br>
<kbabbitt> ... if you do that and thing fails when you didn't declare them that would be an easier way to debug things than just having somethinig missing someplace<br>
<kbabbitt> ... but that's kind of equivalent to what was just proposed<br>
<miriam> q+<br>
<kbabbitt> ... one thing you said TabAtkins that I'm not finding in spec prose<br>
<kbabbitt> ... one of the advantages of @macro of @mixin is that it can be applied to more contexts or something like that?<br>
<kbabbitt> ... can you repeat/clarify?<br>
<kbabbitt> TabAtkins: didn't mean in sense of usable in more places in stylesheet, can do more things because it doesn't apply scoping<br>
<kbabbitt> ... can use it to style siblings, or use a more complicated has selector for something else in page<br>
<kbabbitt> ... without having to apply rule high up on page to make sure everything is within scope<br>
<kbabbitt> florian: I see, that makes it useful in cases hygenic wouldn't be<br>
<kbabbitt> ... don't see a need to declare params just to make sure you aren't missing anything?<br>
<kbabbitt> TabAtkins: I think it's still impossible for same reason as kizu said<br>
<kbabbitt> ... needs to be [missed] ... styleing things on page, unclear where you';re looking<br>
<kbabbitt> ... can't tell until runtime when you're actually resolving<br>
<astearns> ack lea<br>
<kbabbitt> florian: with this clarificaiton I'm okay with the way it's defined now<br>
<kbabbitt> lea: my argument had nothing to do with hygenic rewreite, languages don't distinguish for syntactic reasons<br>
<kbabbitt> ... at the time I thought macros were mixins but it sounds like they're not<br>
<kbabbitt> ... if you can use macros in more places that's justification for them existing<br>
<romain> q-<br>
<kbabbitt> ... coul dyou use macros in @page, @keyframes, top of page?<br>
<kbabbitt> ... that would point to different syntax at call site so we can determine if it's valid<br>
<florian> q?<br>
<kbabbitt> ... might not want to allow mixin at top level where macro is allowed<br>
<florian> qq+<br>
<kbabbitt> ... that in itself makes a good case for macro<br>
<astearns> ack florian<br>
<Zakim> florian, you wanted to react to lea<br>
<kbabbitt> florian: I thnk the're not more powerful the way you described, but with a mixin -- you have params, local vars are different too<br>
<kbabbitt> ... in a mixin, local var is local to mixin<br>
<kbabbitt> ... in macro, it's not local, just gets injected into page<br>
<lea> q?<br>
<kbabbitt> ... so if you want to set something in one place and use it multiple, it's less restricted<br>
<lea> qq+ to reply<br>
<kbabbitt> ... in some cases it might be undesirable because it interferes with page, other cases it might be what you want<br>
<lea> q-<br>
<lea> q+<br>
<kbabbitt> TabAtkins: mixins can indeed create actual custom props vissible in page, just need to be in @result<br>
<kbabbitt> ... as florian said, currently macros are not intended to be usable in more places than mixins<br>
<kbabbitt> ... exact same call sites with @apply<br>
<kbabbitt> ... distinction is they do not scope their contents<br>
<kbabbitt> ... you are not restricted in how you can select things inside the body<br>
<kbabbitt> ... can just emit whatever selectors you want, select anything on page with macro, mixin forces them to be scoped<br>
<kbabbitt> ... because of the mixin restriction we can do more things like args and local vars<br>
<kbabbitt> ... which we can't do in macros because we have no idea where the elements are going to style with macro live on the page<br>
<kbabbitt> ... can't do that without a dramatically different var system<br>
<kbabbitt> ... that does something very distinct from custom props<br>
<kbabbitt> ... mixins live in same place, play with same machinery<br>
<kbabbitt> ... macro args would need to do something distinct and new, don't currently want to play around with that<br>
<kbabbitt> ... in future we could expand syntax to allow that since macros currently don't take args<br>
<kbabbitt> ... leaves open the syntax space<br>
<kbabbitt> ... for now we don't have to worry about radical new concept of vars<br>
<astearns> ack miriam<br>
<kbabbitt> miriam: may be on queue to ask about brand new radical concept of vars<br>
<kbabbitt> ... seems like distinction is because of scoping, agree with lea it will be confusing for authors when to use which<br>
<kbabbitt> ... if we can't make them more like each other can we remove this weird scoping?<br>
<kbabbitt> ... mentioned wanting to scope on root, not wanting to look into it<br>
<lea> +1 miriam<br>
<kbabbitt> ... with nesting we were in a similar place, making & opotional etc.<br>
<kbabbitt> ... if we can get rid of this scoping requirement we could put these 2 features back together and that would be a lot nicer<br>
<lea> q?<br>
<kbabbitt> TabAtkins: it's not a simple thing, not a matter of performance<br>
<kbabbitt> ... wanting to lift all vars to root, it's a question of, it requires more radical changes to underlying model<br>
<kbabbitt> ... such that I'm not sure it can be defined with current processing model<br>
<kbabbitt> ... would need more radical changes to what we emit to make them work<br>
<kbabbitt> ... re: explaining difference I think the right model is, always use a mixin unless you need something more<br>
<kbabbitt> ... or, use a macro unless you need local vars somewhere<br>
<kbabbitt> ... either these 2 models work if you stick to learning one and branching to variant<br>
<kbabbitt> ... most of the time you won't be able to tell the difference<br>
<kbabbitt> ... if only styling element and children, the 2 are exactly the same in effect<br>
<kbabbitt> ... macros slightly easier to write since they don't need @result wrapper<br>
<kbabbitt> ... if you need args, you have to use mixin and adhere to scoping restriction<br>
<kbabbitt> ... if you need no scoping restriction you have to use macro and not use args<br>
<kbabbitt> ... don'tt hink there's a happy union between these two unfortunately<br>
<kbabbitt> ... we're in this situation because CSS both evaluates these at stylesheet time and at runtime for elements on page<br>
<kbabbitt> ... that gives us 2 different notions of scope<br>
<kbabbitt> ... mixins allow us to pretend they're the same, treat vars as if 2 scopes are identical<br>
<kbabbitt> ... as soon as you break the restrictions that make that possible you get complications<br>
<kbabbitt> ... if there is ever a way to unify them and allow macros to take argument,s the syntax allows for extension since it rules out args right now<br>
<kbabbitt> ... can always explore in future<br>
<kbabbitt> ... but we won't beable to if we try to land something right now<br>
<kbabbitt> astearns: on that point, not as certain we can cleanly add params to macros in the future if we're telling the story that macros and mixins are pretty similar, you just need to account for these differences<br>
<lea> +1 astearns<br>
<kbabbitt> ... if authors don't understand differences that well, I could see people substituting mixins for macros willy nilly and not understanding that they aren't working the same<br>
<kbabbitt> ... could find ourselves unable to add params to macros based on cruft in stylesheets<br>
<kbabbitt> TabAtkins: yes that's always the case, params on macros are simply invalid now which suggests it's less likely to be a problem<br>
<kbabbitt> ... usually making something non functional is sufficient to discourage cruft, not always but usually<br>
<kbabbitt> ... I'm comfortable assuming we'll be okay in the future<br>
<kbabbitt> ... but alwasy potential we get locked<br>
<andruud> q+<br>
<astearns> ack lea<br>
<kbabbitt> lea: TabAtkins said we can explain the difference as one thing does more than other but sounds like they're o0verlapping venn circles<br>
<kbabbitt> ... mixins offer one addiitonal thing, macros offer a different additional thing<br>
<kbabbitt> ... so use this unless you want something more ins't the way to explain it<br>
<kbabbitt> ... was not aware you can't use any selector in mixins, seems like a flw<br>
<kbabbitt> ... wondering if there's a way to make this work normally in mixin<br>
<kbabbitt> ... sibling selector, can you apply directly at the target?<br>
<kbabbitt> ... not going to fix it now but wondering if there's a way to fix it<br>
<kbabbitt> ... design of macros sounds like mixins with auto wrapping<br>
<kbabbitt> ... think there's a use case for macros but not with current design<br>
<kbabbitt> ... more limited than mixins in some ways but more powerful in others<br>
<kbabbitt> ... some langauges have macros with completely distinct concept<br>
<kbabbitt> ... wondering if would make sense to defer design of macros and ship later<br>
<andruud> q-<br>
<kbabbitt> ... sounds like for most use cases mixins would work, just need to write @result if we decide not to auto wrap<br>
<kbabbitt> ... if benefit of macros is unclear, don't need to ship right away, gives us more time to figure out what it should be<br>
<romain> q+<br>
<kbabbitt> TabAtkins: agree. would be fine with deferring @macro for now<br>
<kbabbitt> ... put in spec because we had a resolution for it<br>
<kbabbitt> ... people wanted an unscoped variant<br>
<kbabbitt> ... we resolved to do something to allow mixins to be unscoped<br>
<kbabbitt> ... what fell out of that was @macro design<br>
<kbabbitt> ... would be happy to defer because mixins solve 90% case<br>
<lea> q?<br>
<kbabbitt> ... cases where you want to solve something without scoping can be solved by applying to parent<br>
<lea> q+<br>
<miriam> +1 to deferring macros<br>
<kbabbitt> ... assuming we stick to resolution that we want an uscoped varioant, then macro as in spec is AFAICT the solution<br>
<kbabbitt> ... completely right that mixins and macros are not subsets<br>
<kbabbitt> ... overlapping venn diagram<br>
<andruud> +1 to deferring<br>
<kbabbitt> ... mostly overlaps in fact<br>
<kbabbitt> ... use cases where they don't overlap are corner cases mostly<br>
<astearns> https://github.com/w3c/csswg-drafts/issues/13722 for moving to a new level<br>
<kbabbitt> ... knowing precisely how to control that set of overlaps is nontrivial but I don't know a way around it<br>
<kbabbitt> ... question about there's a sibling selector in mixin body, can we just raise scope to make it work?<br>
<kbabbitt> ... answer is no we can't because there's no way to know how high a scope we need until we're styling page and evaluating selectors<br>
<kbabbitt> ... 2 concepts of scope, lexical stylesheet and page structure, and selectors cross between those<br>
<kbabbitt> ... can't determine style scoping until you run the page<br>
<kbabbitt> ... only way around that would be to lift args to root and I don't think that works well<br>
<kbabbitt> astearns: I think we should probably try to wrap this up<br>
<kbabbitt> ... we're likely going to be separate issues brought up<br>
<kbabbitt> ... that could be raised on macro design<br>
<astearns> ack romain<br>
<kbabbitt> s/we're/there are/<br>
<kbabbitt> romain: concerned about shipping at different times, authors will use the one that ships first<br>
<kbabbitt> ... and for wrong reasons, will use mixins when they should use macros<br>
<kbabbitt> ... would prefer to ship at same time, easier to teach difference when both are available<br>
<kbabbitt> ... I like @macro, for all the concerns reaised against @mixin, it solves those<br>
<lea> TabAtkins: Lifting scope is not what I was thinking, but call time is not the right use to elaborate, I'll follow up in an issue<br>
<kbabbitt> ... agree with TabAtkins, I don't see how you can do everything with @mixin<br>
<astearns> ack lea<br>
<kbabbitt> lea: wasn't talking about lifting scope but have some ideas for how we might fix it<br>
<TabAtkins> (Note that if authors use mixins "when they should use @macro", it's either fine (they act the same) or the mixin won't work properly and it'll be obvious.)<br>
<kbabbitt> ... if we want to ship @macro at the same time, it would guard us better toward future expansion to use a different way to apply the macro<br>
<kbabbitt> ... rather than use @apply which makes it seem like a degenerate mixin<br>
<kbabbitt> ... different @rule to apply macro could be expanded to be much more syntactic al towork in places mixins aren't allowed<br>
<kbabbitt> ... could ship cut down version of macro if we have different way to apply it<br>
<kbabbitt> ... could even ship before @mixin<br>
<kbabbitt> ... think we should explore lifting the scoping restriction<br>
<kbabbitt> ... nothing prevents us from having a keyword for "this is a literal mixin, wrap in @result"<br>
<kbabbitt> astearns: wondering whether we should take it back to the issue, open new specific issues for the things brought up<br>
<lea> TabAtkins: is there an open issue for the scoping restriction?<br>
<kbabbitt> ... this issue was basically a call for review of design and we have opinions<br>
<kbabbitt> ... not sure we're ready to resolve or ratify what's in the spec at this point<br>
<kbabbitt> TabAtkins: yup<br>
<kbabbitt> florian: flag in spec that this is under debate, don't rush to it?<br>
<kbabbitt> TabAtkins: sounds appropriate<br>
<kbabbitt> astearns: any objections to putting a note in spec that we're still working on it?<br>
<kbabbitt> RESOLVED: Add a Note to the spec and will continue discussing this<br>
</details>
--
GitHub Notification of comment by css-meeting-bot
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/13680#issuecomment-4127755865 using your GitHub account
--
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config
Received on Wednesday, 25 March 2026 15:56:11 UTC