A while back, in #12927, we defined the evaluation model of mixins to, under the covers, wrap all the property values set by the mixin into anonymous functions. At the time, this let us move away from env()-based parameters/locals and instead use the existing custom-function var()-based parameters/locals. Functions had a nice "hypothetical element" model that let us use the author-chosen parameter names in var() without clashing with real custom properties already on the element.
Since then, we've made changes to mixins for a number of reasons, most recently #14004 to use the new generic @private rule for defining lexically-scoped custom properties, which lets us resolve mixin parameters/locals directly on the applying element and get the desired behavior wrt element-relative values like em.
As far as I can tell, this completely obviates the previous need for the function-based evaluation model. Am I right? Or is there still some significant detail I'm missing that the function-based model gives us?
/cc @andruud, as the originator of the function-based evaluation idea
As an example, take the --colorized-squish() example Anders gives in #14004, which shows off nested mixin application:
@mixin --squish(--left-color <color>,
--right-color <color>: var(--left-color)) {
&::before {
content: "🡆";
background-color: var(--left-color);
}
&::after {
content: "🡄";
background-color: var(--right-color);
}
}
@mixin --colorized-squish(--color <color>) {
background-color: var(--color);
border: 2px solid oklch(from var(--color) calc(l - 0.1) c h);
@apply --squish(oklch(from var(--color) calc(l - 0.3) c h),
oklch(from var(--color) calc(l - 0.2) c h));
}
div {
@apply --colorized-squish(tomato);
}
Under the new spec model, this would desugar to approximately1:
/* First step, desugaring the `@apply --colorized-squish` */
div {
/* colorized-squish() passed args */
/* magically chosen unique name, only used once */
@private { --arg-3e296714 <color>: tomato; }
@nest { /* colorized-squish() mixin body */
@private { --color <color>: var(--arg-3e296714); }
/* only usage of this name */
background-color: var(--color);
border: 2px solid oklch(from var(--color) calc(l - 0.1) c h);
@apply --squish(oklch(from var(--color) calc(l - 0.3) c h),
oklch(from var(--color) calc(l - 0.2) c h);
}
}
/* Second step, desugaring the nested `@apply squish` */
div {
@private { --arg-3e296714 <color>: tomato; }
@nest {
@private { --color <color>: var(--arg-3e296714); }
background-color: var(--color);
border: 2px solid oklch(from var(--color) calc(l - 0.1) c h);
@private {
/* squish() passed args */
/* more magical unique names */
--arg-dc8b6cab <color>: oklch(from var(--color) calc(l - 0.3) c h);
--arg-d482b77e <color>: oklch(from var(--color) calc(l - 0.2) c h);
}
@nest { /* squish() mixin body */
@private { /* again, only usage of the magic names */
--left-color <color>: var(--arg-dc8b6cab);
--right-color <color>: var(--arg-d482b77e, var(--left-color));
}
&::before {
content: "🡆";
background-color: var(--left-color);
}
&::after {
content: "🡄";
background-color: var(--right-color);
}
}
}
}
I think that just makes all the behavior we want fall right out.
- Values will resolve against the applying element.
- Parameter names are only visible within the mixin body.
- Parameter names are visible to nested mixins if they want, same as normal custom properties.
- Multiple mixins using the same parameter names will shadow each other rather than accidentally colliding, even when they're applied inside each other.
- An argument value using a
var() will see names in the outer context, a parameter default will see names in the inner context.
A while back, in #12927, we defined the evaluation model of mixins to, under the covers, wrap all the property values set by the mixin into anonymous functions. At the time, this let us move away from
env()-based parameters/locals and instead use the existing custom-functionvar()-based parameters/locals. Functions had a nice "hypothetical element" model that let us use the author-chosen parameter names invar()without clashing with real custom properties already on the element.Since then, we've made changes to mixins for a number of reasons, most recently #14004 to use the new generic
@privaterule for defining lexically-scoped custom properties, which lets us resolve mixin parameters/locals directly on the applying element and get the desired behavior wrt element-relative values likeem.As far as I can tell, this completely obviates the previous need for the function-based evaluation model. Am I right? Or is there still some significant detail I'm missing that the function-based model gives us?
/cc @andruud, as the originator of the function-based evaluation idea
As an example, take the
--colorized-squish()example Anders gives in #14004, which shows off nested mixin application:Under the new spec model, this would desugar to approximately1:
I think that just makes all the behavior we want fall right out.
var()will see names in the outer context, a parameter default will see names in the inner context.Footnotes
Using
@nestto represent a nested declarations rule. Using the proposed "inline typed custom property" syntax for argument values. ↩