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

> * My understanding (that [@tabatkins](https://github.com/tabatkins) confirmed) is that scoping was introduced to restrict rules to descendants, and rules had to be restricted to descendants only so that arguments could be used everywhere and still inherit as expected.

Perhaps there was a misunderstanding? Arguments would be available everywhere anyway; inheritance is not needed for that. We introduced scoping _only_ due to hygienic rewrites.

> * `--double-it` would be called with an `--arg` of `100px`

OK, good, just checking if there was a fundamental disagreement about resolving things at the call site.

---

Even if we subtract hygiene from the current spec, the following _mixin_ still gives `width:200px` on `<div>`:

```css
@mixin --double-it(--arg) {
  --mywidth: 300px;
  @result {
    width: calc(var(--arg) * 2);
  }
}

div {
  --mywidth: 100px;
  @apply --double-it(var(--mywidth));
}
```

That's because the final `calc(var(--arg) * 2)` value gets evaluated with a "synthetic" `@function` call stack above it which mirrors the `@apply` call stack. In other words, it evaluates as if you instead did:

```css
@function --anonymous-generated-function(--arg) {
  --mywidth: 300px;
  result: calc(var(--arg) * 2);
}

div {
  --mywidth: 100px;
  width: --anonymous-generated-function(var(--mywidth));
}
```

(We do this to give locals/params a place to live so that `var()` can reach them via existing mechanisms. Although this interpretation of mixins into functions is a "rewrite", it must not be confused with "hygienic rewrite".)

If we look at another example:

```css
@mixin --sync-size(--arg) {
  --mywidth: 200px;
  @result {
    width: var(--arg);
    & > .child {
      width: var(--arg);
    }
  }
}

.parent {
  --mywidth: 100px;
  @apply --sync-size(var(--mywidth));
}
.child {
  --mywidth: 300px;
}
```

You expected `--double-it` (function) to be called with `--arg` of `100px`, which means that you expected `--arg` to hold an already-resolved value inside the function. Do you expect the same for the `@apply --sync-size(var(--mywidth))` call?

If we again subtract hygiene from the current spec, we get a computed `width` of `100px` on `.parent` and `300px` on `.child` -- the value of `var(--arg)` (a parameter) _shifted_ according to the element being targeted. This seems very bad.

If we maintain hygiene, we get a computed `width` of `100px` on both `.parent` and `.child`, because `var(--arg)` resolves to the same thing everywhere in the mixin. In order to do this, we need to _somehow_ resolve any `var(--arg)` (regardless of which element is targeted) in the context of the applying element. This is only realistically possible if the applying element is an ancestor of the element being targeted.










-- 
GitHub Notification of comment by andruud
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/13727#issuecomment-4141259864 using your GitHub account


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

Received on Friday, 27 March 2026 09:25:12 UTC