- From: Tab Atkins Jr. via GitHub <noreply@w3.org>
- Date: Tue, 31 Mar 2026 22:54:46 +0000
- To: public-css-archive@w3.org
> My assumption was that both mixin arguments, as well as any variables defined outside the `@result` would be transparently rewritten to guaranteed unique names. Is that not possible? It's of course possible. My point is that, without scoping, it's a nonsense rewrite. The point of macro hygiene is that you analyze what variables are *defined* inside of a macro and detect what references are *to* those definitions, and then use renaming to ensure they're safe and not accidentally clashing (while leaving other definitions alone). If we blindly rename all `var(--a)` refs, without using scoping to ensure that they are *actually* referring to the `--a` arg/local, then we're not doing macro hygiene. Some of those refs are just going to be to unbound variables. > > If one wants (a) element-relative values in `@apply` arguments to resolve based on the element that's applying (I think one definitely does) > > Yes, we want element-relative values to resolve based on the element that's applying, but the second best thing is them resolving _at all_, using _any_ sensible mechanism. All resolution algorithms have their pros and cons, but having entire rules be ignored, and thus, major capabilities being impossible to define as a mixin, is the worst possible alternative. Especially since there is no escape hatch: all-untyped mixins still have to go through the scoped path _as well_. I strongly disagree that "resolving *at all* using *any* sensible mechanism" is better. Authors are well-served by explainable, predictable behavior, with an easy-to-understand boundary around what's allowed. The current spec satisfies that: mixin contents are scoped, so your rules are restricted to what you can do inside of `@scope`. (Easy rule to apply, and matches an existing feature's restrictions.) As a result of this restriction, authors also get the behavior that all their `var(...)` refs that refer to args/locals get the same value, resolved against the applying element. (Simple to understand, straightforward beahvior.) Your proposal instead would have more complex behavior: * if you're styling the applying element or a descendant, it can use `var()` to refer to the arg/local, resolved against the applying element. * if you're styling a different element, your `var()` *does not* refer to the arg/local. Depending on the exact behavior being proposed, they'll either be invalid refs (making the property IACVT) or will resolve the literal arg/local value against a completely different element (potentially many different elements). > > and (b) those resolved values to be usable inside the mixin without confusing restrictions (also something I think one wants), > > Having entire rules be dropped _is_ a confusing restriction! I mean "confusing" as "difficult to understand", not "unexpected". The rules aren't dropped, they're scoped, exactly identical to being inside of `@scope`. That might be unexpected, sure, if you're brand new to the feature, but it's not difficult to predict and understand the effects. > First, my assumptions about how hygienic variable rewriting worked have been: > > * Variables get hygienically rewritten to guaranteed unique names, per mixin, and there is an internal mapping to the original name. I.e. if `--bar` inside a mixin is rewritten to `--bar-c432fad39`, you cannot reference an outside `--bar` **anywhere** in the same mixin, both declarations AND `var()` references are transparently rewritten throughout. If you want access to an outside `--bar`, rename your local variable! I would find it _extremely_ confusing if a mixin had a `--bar` argument and sometimes that took precedence over an outside `--bar` and other times it didn't. > * These mappings are scoped to each mixin separately. Correct. > As a corollary: > > * Applying the same mixin multiple times to the same element (e.g. via different rules) would reuse the same mappings. Otherwise simple CSS rewrites such as combining rules or rewriting selectors can end up having unintended side effects. No, the rewriting is per-application. Executing the mixin injects rules into the stylesheet, with specific prop/vals in those rules. If you apply a mixin multiple times, those are separate invocations. (This matches how macro-ish things work in every language that has them.) > * Scoping would be shallow per mixin: if the mixin uses `@apply` to invoke another mixin, and the other mixin uses `--bar` as well, that's rewritten to a different `--bar`. If the parent mixin wants to use the same value, they can pass _their_ `--bar` as an argument. We do track "stack frames" - if the inner mixin defines its *own* `--bar` arg/local, then yes, its `var(--bar)` refs are rewritten to *its* `--bar` arg/local. If it doesn't have a `--bar` arg/local (and thus it's expecting to refer to the outside world's `--bar`, then it'll get caught by the outer mixin and rewritten to match.) > 1. Simply leave these properties undefined on these elements. At least declarations that don't use them still work, and authors can always provide fallbacks. > 2. Resolve on the element AND on every other topmost root, then let inheritance handle the rest. > 3. Resolve on the element, any top-most root, AND if one of these is an ancestor of the applying element, also resolve separately on every ancestor between the element and that top-most root. All of these mean that `var(--foo)` *sometimes* refers to the `--foo` arg/local, and sometimes refers to a completely different value. That's what I've referred to as "confusing", and don't want to do. Even within a *single rule*, you can get different behaviors if the rule is styling both descendants of the applying element and non-descendants. > > Either we lean on the variable mechanism that exists today in CSS, which _requires_ scoped rules and _implies_ hygienic rewrites for convenience, or we invent something entirely novel (likely similar to Sass) which doesn't have a concept of the applying element at all and is just rewriting at the stylesheet level. > > Rewriting at the stylesheet level with no concept of an applying element is not the actual author need though. I think. There's no other way to talk about this concept without going with something dramatically different and novel. If we want to use the concept of style rules and selectors, it has to be done at the stylesheet level; putting rules into the value space is the "something totally novel" I was referring to. > But the current mechanism is _also_ not how they work. The mental model of "just take the `@result`, replace arguments, and substitute `@apply` fails if they return a scoping root because entire rules get dropped. Here's an example: Rules don't get dropped. They get scoped and thus might not apply to any elements, same as putting that same selector in an @scope. The mental model is "just take the `@result`, replace arguments/locals, and substitute `@apply` with an @scoped (&)". That is an *extremely* close desugaring (I'd have to think about what the actual corner-case differences might even be...), including for some of the less-expected corner behaviors! > Literally almost everybody in all polls I posted was **baffled** about the options where `& + p` was dropped (C & D). They found it _more_ surprising than any resolution mechanism whatsoever. Sure, people who aren't familiar with the internal mechanics of how this all needs to work can make assumptions that aren't actually possible to satisfy. That's the problem with making a social media poll that provides minimal context and just asks people for reactions to a context-less bit of new proposed syntax. Sometimes we can get useful information out of that, sometimes we can't. > While none of the alternative resolution mechanisms is perfect, I would be surprised if authors found having entire rules ignored less surprising. Perhaps a simple rename could fix this (`@macro` → `@mixin`, `@mixin` → `@scoped-mixin` or something). I have no particular attachment to any naming. If your concerns end up getting totally resolved by just a rename along these lines, then (a) great! and also (b) I'm not really sure what your complaints were in the first place then. ^_^ -- GitHub Notification of comment by tabatkins Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/13727#issuecomment-4166160490 using your GitHub account -- Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config
Received on Tuesday, 31 March 2026 22:54:47 UTC