- From: Tab Atkins Jr. via GitHub <noreply@w3.org>
- Date: Wed, 04 Feb 2026 21:53:05 +0000
- To: public-css-archive@w3.org
tabatkins has just created a new issue for https://github.com/w3c/csswg-drafts:
== [css-mixins-1] Need a non-hacky way to force an element-dependent value on the applying element ==
Due to hygienic renaming and the mixin evaluation rules, code like the following:
```css
@mixin --triple-border(--size <length>) {
@result {
&, & > *, & > * > * {
width: var(--size);
border-width: .2em;
}
}
}
section {
font-size: 10px;
--size: 10px;
@apply --triple-border(10em);
}
section > h1 {
font-size: 20px;
--size: 20px;
}
section > h1 > small {
font-size: 15px;
--size: 15px;
}
```
Will desugar to approximately:
```css
section {
--f7bd60b7: 10em; /* and it's registered as a <length> so it turns into 100px here */
font-size: 10px;
--size: 10px;
width: var(--f7bd60b7); /* 100px;*/
border-width: .2em; /* 2px */
}
section > h1 {
font-size: 20px;
--size: 20px;
width: var(--f7bd60b7); /* 100px */
border-width: .2em; /* 4px; */
}
section > h1 > small {
font-size: 15px;
--size: 15px;
width: var(--f7bd60b7); /* 100px */
border-width: .2em; /* 3px */
}
```
That is, the `var()` referencing a Mixin argument is captured on the applying element and maintains a single stable value across all uses, but the `em` length isn't touched, and resolves based on the element it's used on.
This isn't an *undesirable* behavior in general - it'll probably often be the case that you do indeed want the styles you put on children to resolve on the children. But I think it'll also be common to want to capture a value on the applying element and use that on the children, but right now the *only* way to do that is with an argument that you just document the user shouldn't pass, so you can set a default value, like:
```css
@mixin --triple-border(--size <length>, --em <length>: 1em) {
@result {
&, & > *, & > * > * {
width: var(--size);
border-width: .2--em;
}
}
}
section {
@apply --triple-border(10em);
}
```
That's awkward and clumsy, tho. You might think you could use a local variable with a type-coercing function, like this:
```css
@function --as-length(--x <length>) returns <length> { result: var(--x); }
@mixin --triple-border(--size <length>) {
--em <length>: --as-length(1em);
@result {
&, & > *, & > * > * {
width: var(--size);
border-width: .2--em;
}
}
}
```
But that doesn't work. The evaluation rules lift that local variable into being a local variable in an anonymous function called on each element, and custom functions inherit font-size from the calling element, so the `1em` will still be evaluated based on each element's local font-size instead.
---------------
There's a few possible solutions to this. I think the most obvious is to make local variables act exactly the same as Mixin parameters, and lift+hygiene them onto the applying element. We'd have to lift the *entire* Mixin body, tho, since the local variable might be set inside an `@media`. That means even more "unobservable" things on the applying rule, but perhaps that's fine once you've already bitten the bullet of having an unobservable custom property in the first place. (This would also make the "inside-out" and "outside-in" desugarings actually identical; see Example 20 at the end of <https://drafts.csswg.org/css-mixins/#evaluating-mixins>.)
If that's no good and we have to maintain the different behavior for parameters and locals, then we can at least add something "in-between" the two, which is lifted like a parameter but solely under author control like a local. That's right, we'd be reviving the `using(...)` clause, letting it add "shadow" parameters that are evaluated *as if* they were unpassed arguments, just resolving to their default value (which would be required in the grammar for the using-list).
I'd definitely prefer the first solution, tho, as locals and parameters are otherwise pretty identical. Thoughts? @andruud @emilio (I dunno who, if anyone, from WebKit is appropriate to ping for opinions on these issues)
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/13454 using your GitHub account
--
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config
Received on Wednesday, 4 February 2026 21:53:06 UTC