Skip to content

Commit e0ffffc

Browse files
committed
[css-mixins-1] Add more detail about hygience when vars reach into outer scopes.
1 parent 700e940 commit e0ffffc

1 file changed

Lines changed: 111 additions & 37 deletions

File tree

css-mixins-1/Overview.bs

Lines changed: 111 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ WPT Display: open
2020
<pre class=link-defaults>
2121
spec:infra; type:dfn; text:list
2222
spec:infra; type:dfn; for:list; text:append
23+
spec:css-borders-4; type:property;
24+
text:border-width;
2325
spec:css-cascade-5; type:dfn;
2426
text:inherit
2527
text:computed value
@@ -1149,11 +1151,13 @@ to ensure that references to them work correctly across elements and inheritance
11491151
[=Hygienic renaming=] changes the names
11501152
of [=local variables=] and [=mixin parameters=]
11511153
to an unobservable, guaranteed non-clashing name,
1152-
its <dfn>hygienic name</dfn>.
1154+
its <dfn noexport>hygienic name</dfn>.
1155+
(They remember their <dfn noexport>original name</dfn>, however.)
11531156

1154-
If a ''var()'' references a [=custom property=]
1155-
whose name matches a [=hygienically renamed=] [=local variable=] or [=mixin parameter=],
1156-
it is instead interpreted as referencing the [=hygienic name=].
1157+
If a ''var()'' in the [=mixin body=]
1158+
would reference a [=local variable=] or [=mixin parameter=]
1159+
with its [=original name=],
1160+
the reference is rewritten to use the [=hygienic name=] instead.
11571161
The same applies to [=variable unit references=]
11581162
and ''style()'' references in an ''if()'' test.
11591163

@@ -1162,21 +1166,30 @@ If future features allow referencing the value of a custom property on an elemen
11621166
they will also be interpreted as referencing the [=hygienic name=]
11631167
when used inside a [=mixin=].
11641168

1165-
The ''inherit()'' function is an exception to this;
1166-
as it intrinsically reaches "outside" of an element
1167-
(and thus the mixin scope),
1168-
it cannot be referring to a [=local variable=] or [=mixin parameter=]
1169-
and is not reinterpreted.
1170-
However, if the outer context is another [=mixin=],
1171-
[=hygienic renaming=] might still apply in <em>that</em> context.
1169+
[=Hygienic renaming=] extends to nested [=mixins=] and to invoked [=custom functions=],
1170+
if they contain "unbound" variable references
1171+
that would match a [=hygienically renamed=] [=local variable=] or [=mixin parameter=].
1172+
(This preserves the ability of mixins to "override" custom properties
1173+
implicitly used by nested mixins or functions,
1174+
the same way that nested function calls can.)
1175+
1176+
Otherwise, such "unbound" references are left undisturbed,
1177+
so they'll still match the appropriate [=custom property=] in the element context.
1178+
1179+
Note: For example, the ''inherit()'' function intrinsically reaches outside of the current context,
1180+
referencing the value one "level" up.
1181+
Even if a [=local variable=] with the same name exists in the current [=mixin=],
1182+
it won't cause the ''inherit()''&apos;s reference to be rewritten.
1183+
(But if it ends up referencing a [=local variable=] higher in the "call stack",
1184+
it'll be rewritten to coordinate with that one.)
11721185

11731186
<div class=example>
11741187
For example, given the following styles and mixin:
11751188

11761189
<xmp highlight=css>
11771190
@mixin --triple-border(--size <length>) {
11781191
@result {
1179-
&, & > h1, & > h1 > small {
1192+
&, & > *, & > * > * {
11801193
border-width: var(--size);
11811194
}
11821195
}
@@ -1218,33 +1231,94 @@ However, if the outer context is another [=mixin=],
12181231
as the author intended.
12191232
</div>
12201233

1234+
Note: While [=hygienic renaming=] ensures that descendants won't accidentally pick up the wrong variable value,
1235+
and [[#evaluating-mixins]] ensures that element-dependent arguments passed to the mixin
1236+
(like ''@apply --foo(1em);'')
1237+
will resolve against the applying element too,
1238+
using <em>any other</em> element-dependent reference in the [=mixin result=]
1239+
will evaluate as normal for their placement in the styles.
1240+
1241+
<div class=example>
1242+
For example, in the following variant of the previous example:
1243+
1244+
<xmp highlight=css>
1245+
@mixin --triple-border() {
1246+
@result {
1247+
&, & > *, & > * > * {
1248+
border-width: .2em;
1249+
}
1250+
}
1251+
}
1252+
section {
1253+
font-size: 10px;
1254+
@apply --triple-border;
1255+
}
1256+
section > h1 {
1257+
font-size: 20px;
1258+
}
1259+
section > h1 > small {
1260+
font-size: 15px;
1261+
}
1262+
</xmp>
1263+
1264+
The applied mixin will be equivalent to:
1265+
1266+
<xmp highlight=css>
1267+
section {
1268+
font-size: 10px;
1269+
border-width: .2em;
1270+
}
1271+
section > h1 {
1272+
font-size: 20px
1273+
border-width: .2em;
1274+
}
1275+
section > h1 > small {
1276+
font-size: 15px;
1277+
border-width: .2em;
1278+
}
1279+
</xmp>
1280+
1281+
Which will give three different 'border-width' values: ''2px'', ''4px'', and ''3px''.
1282+
</div>
1283+
12211284
<div class=issue>
12221285

1223-
Do we need hygienic renaming for other element references, like ''em''?
1224-
Presumably, if ''@apply --foo(var(--bar))'' is potentially confusing,
1225-
then ''@apply --foo(1em)'' would be too,
1226-
if the [=mixin parameter=] ends up being used on a child element in the [=mixin result=].
1227-
But you can also want ''1em'' inside the [=mixin result=]
1228-
to apply to the element it's actually set on,
1229-
not necessarily the parent context receiving the ''@apply''.
1230-
This might need some additional syntax support,
1231-
to ensure that all use-cases are adequately addressed.
1232-
For now, no additional renaming is done;
1233-
''em''/etc values are resolved "normally",
1234-
based on the element their styles are actually used on.
1235-
1236-
While theoretically an author could also want to reference the element's value of a [=custom property=],
1237-
ignoring a [=local variable=],
1238-
[=custom functions=] already shadow in this fashion.
1239-
You can escape <em>one</em> level of the shadowing in functions via ''inherit()'',
1240-
and that feature is preserved in mixins,
1241-
but ultimately the functin/mixin stack has control
1242-
over what custom properties are visible in their bodies.
1243-
Note that if a custom property name isn't a [=local variable=] or [=mixin parameter=],
1244-
it won't be [=hygienically renamed=] within this mixin,
1245-
so an author <em>can</em> refer to an element's own style
1246-
as long as the mixin isn't invoked inside of another mixin
1247-
that shadows it intentionally.
1286+
This is likely often a desirable behavior,
1287+
but if it's not,
1288+
we should have a workaround.
1289+
The following *doesn't* work,
1290+
due to the mixin body becoming the function body of an anonymous function,
1291+
which is evaluated on each element
1292+
and thus inherits that element's ''em'' length.
1293+
1294+
<xmp highlight=css>
1295+
@function --as-length(--x <length>) returns <length> { result: var(--x); }
1296+
@mixin --triple-border() {
1297+
--em: --as-length(1em);
1298+
@result {
1299+
&, & > *, & > * > * {
1300+
border-width: calc(0.2 * var(--em));
1301+
}
1302+
}
1303+
}
1304+
</xmp>
1305+
1306+
I think the only way that works is to have an extra argument
1307+
that you don't expect the user to pass,
1308+
since arguments get lifted onto the applying element
1309+
and hygienically renamed:
1310+
1311+
<xmp highlight=css>
1312+
@mixin --triple-border(--em <length>: 1em) {
1313+
@result {
1314+
&, & > *, & > * > * {
1315+
border-width: calc(0.2 * var(--em));
1316+
}
1317+
}
1318+
}
1319+
</xmp>
1320+
1321+
But this is clumsy. :(
12481322
</div>
12491323

12501324

0 commit comments

Comments
 (0)