[csswg-drafts] [css-mixins-1] Alternatives to using a scoping rule (#13727)

LeaVerou has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-mixins-1] Alternatives to using a scoping rule ==
## Background

From https://drafts.csswg.org/css-mixins/#result-rule 
> The [mixin result](https://drafts.csswg.org/css-mixins/#mixin-result) is a [scoped style rule](https://drafts.csswg.org/css-cascade-6/#scoped-style-rules), with the [scoping root](https://drafts.csswg.org/selectors-4/#scoping-root) being the element the [mixin’s](https://drafts.csswg.org/css-mixins/#mixin) styles are being applied to. (Unlike a traditional [@scope](https://drafts.csswg.org/css-cascade-6/#at-ruledef-scope) rule, the scoping root here can be a [pseudo-element](https://drafts.csswg.org/selectors-4/#pseudo-element), if the mixin is being applied to one.) There are no [scoping limits](https://drafts.csswg.org/css-cascade-6/#scoping-limit).

One of the motivations for `@macro` was that because it does not return a scoping style rule, it's not bound by the same restrictions and can have more complex `:has()` conditions, sibling selectors etc.

IIRC the scoping style rule was introduced to make sure that local variables can be reliably used throughout the mixin. If the mechanics were that of naïve transclusion, this would not work:

```css
@mixin --foo(--bar) {
 @result {
  border-color: var(--bar); /* works */
  & + p { 
   border-color: var(--bar) /* doesn't work! */
  }
 }
}
```

Suppose `--bar` gets hygienically rewritten to `--local-c5f2d35`, the actual applied rule would look like this (`value` is the value passed via the mixin):

```css
& {
 --local-c5f2d35: value;
 border-color: var(--local-c5f2d35);

 & + p { 
  border-color: var(--local-c5f2d35); 
 }
}
```

But `--local-c5f2d35` would not inherit within `& + p`, so it would not be available.

Hence, the scoping.

## Proposal: Inject `--rewritten-name: inherit(--rewritten-name, specifiedValue)` in every rule

But is the scoping rule the only way to address this problem? Conceptually, what authors are trying to do is pretty clear.

One solution would be to inject the rewritten properties in *every* rule within the mixin, i.e. rewrite to this:

```css
& {
 --local-c5f2d35: value;
 border-color: var(--local-c5f2d35);

 & + p {
  --local-c5f2d35: value;
  border-color: var(--local-c5f2d35);
 }
}
```

Note that this would need to be done for nested rules as well. If the local declaration were only injected at the top level, this wouldn't work as expected:

```css
@mixin --foo(--bar) {
 @result {
  border-color: var(--bar); /* works */
  & + p { 
   border-color: var(--bar) /* works! */
   & + p { 
    border-color: var(--bar) /* doesn't work! */
   }
  }
 }
}
```

The downside of this is that we lose inheritance: All values are now applied literally in every rule. However, these two facts help us:
1. We can't override local vars inside `@result` 
2. We can't have regular rules outside of it (only conditional rules)

So the main issue is not around clobbering inherited values, but that the same literal value can have different meanings when used directly vs when inherited.

One solution to this could be to ***preferentially* use the inherited value**, i.e. instead of injecting `--local-c5f2d35: value;`, inject `--local-c5f2d35: inherit(--local-c5f2d35, value);`. Since the custom property cannot possibly be present anywhere else, this naturally sets it at the topmost level of each root and lets it inherit down naturally.

Could this possibly work or am I missing something major?

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/13727 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 18:41:48 UTC