Skip to content

Commit dc08503

Browse files
committed
[css-mixins-1] First draft of @mixin (and supporting @env). Will probably change!
1 parent a3b68e8 commit dc08503

File tree

1 file changed

+362
-4
lines changed

1 file changed

+362
-4
lines changed

css-mixins-1/Overview.bs

Lines changed: 362 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,15 +247,11 @@ and the optional [=custom function/return type=] is given by the <<css-type>> fo
247247
</xmp>
248248
</div>
249249

250-
251250
The name of a ''@function'' rule is a [=tree-scoped name=].
252251
If more than one ''@function'' exists for a given name,
253252
then the rule in the stronger cascade layer wins,
254253
and rules defined later win within the same layer.
255254

256-
257-
258-
259255
If the [=function parameters=]
260256
contain the same <<custom-property-name>> more than once,
261257
then the ''@function'' rule is invalid.
@@ -791,6 +787,368 @@ or acting as if nothing exists at that location otherwise.
791787
</pre>
792788
</div>
793789

790+
791+
<!-- Big Text: @mixin
792+
793+
████▌ █ █ ████ █ █ ████ █ █▌
794+
█▌ █▌ ██ ██ ▐▌ █ █ ▐▌ █▌ █▌
795+
█▌▐█ █▌ █▌█ █▐█ ▐▌ █ █ ▐▌ ██▌ █▌
796+
█▌▐█ █▌ █▌ █ ▐█ ▐▌ █ ▐▌ █▌▐█ █▌
797+
█▌ ██▌ █▌ ▐█ ▐▌ █ █ ▐▌ █▌ ██▌
798+
█▌ █▌ ▐█ ▐▌ █ █ ▐▌ █▌ █▌
799+
████▌ █▌ ▐█ ████ █ █ ████ █▌ ▐▌
800+
-->
801+
802+
<h2 id=defining-mixins>
803+
Defining Mixins</h2>
804+
805+
A [=mixin=] is in many ways similar to a [=custom function=],
806+
but rather than extending/upgrading [=custom properties=],
807+
[=mixins=] extend/upgrade [=nested style rules=],
808+
making them reusable and customizable with arguments.
809+
810+
<div class=example>
811+
For example, the following code sets up a mixin
812+
applying all the properties you need for a "gradient text" effect,
813+
including guarding it with [=supports queries=]:
814+
815+
<pre highlight=css>
816+
@mixin --gradient-text(
817+
--from type(color): mediumvioletred,
818+
--to type(color): teal,
819+
--angle: to bottom right,
820+
) {
821+
color: env(--from, env(--to));
822+
823+
@supports (background-clip: text) or (-webkit-background-clip: text) {
824+
@env --gradient: linear-gradient(env(--angle), env(--from), env(--to));
825+
background: env(--gradient, env(--from));
826+
color: transparent;
827+
-webkit-background-clip: text;
828+
background-clip: text;
829+
}
830+
}
831+
832+
h1 {
833+
@apply --gradient-text(pink, powderblue);
834+
}
835+
</pre>
836+
837+
Note that this example also uses a [=scoped environment variable=]
838+
(along with the arguments, which implicitly define [=scoped environment variables=])
839+
which is scoped to <em>the rule itself</em>
840+
(rather than being applied to the element, like a [=custom property=] would be)
841+
to hold a temporary value to aid in readability of the [=mixin=],
842+
without polluting the element's styles with unwanted [=custom properties=].
843+
844+
This is exactly equivalent to writing a [=nested style rule=] literally into the `h1` styles:
845+
846+
<pre highlight=css>
847+
h1 {
848+
& {
849+
@env --from: pink;
850+
@env --to: powderblue;
851+
@env --angle: to bottom right;
852+
color: env(--from, env(--to));
853+
854+
@supports (background-clip: text) or (-webkit-background-clip: text) {
855+
@env --gradient: linear-gradient(env(--angle), env(--from), env(--to));
856+
background: env(--gradient, env(--from));
857+
color: transparent;
858+
-webkit-background-clip: text;
859+
background-clip: text;
860+
}
861+
}
862+
}
863+
</pre>
864+
</div>
865+
866+
Issue: The entire ''@mixin'' feature is experimental and under active development,
867+
and is much less stable than ''@function''.
868+
Expect things to change frequently for now.
869+
870+
871+
<h3 id=mixin-rule>
872+
The <dfn>@mixin</dfn> rule</h3>
873+
874+
The ''@mixin'' rule defines a <dfn>mixin</dfn>,
875+
and consists of a name,
876+
a list of [=mixin parameters=],
877+
and a [=mixin body=].
878+
(Identical to ''@function'',
879+
save that it lacks a [=custom function/return type=].)
880+
881+
<pre class="prod def">
882+
<<@mixin>> = @mixin <<function-token>> <<function-parameter>>#? )
883+
{
884+
<<declaration-rule-list>>
885+
}
886+
</pre>
887+
888+
If a [=default value=] and a [=parameter type=] are both provided,
889+
then the default value must parse successfully according to that parameter type’s syntax.
890+
Otherwise, the ''@mixin'' rule is invalid.
891+
892+
<h4 id=mixin-preamble>
893+
The Mixin Preamble</h4>
894+
895+
The <<function-token>> production
896+
must start with two dashes (U+002D HYPHEN-MINUS),
897+
similar to <<dashed-ident>>,
898+
or else the definition is invalid.
899+
900+
The name of the resulting [=mixin=] is given by the name of the <<function-token>>,
901+
the optional [=mixin parameters=]
902+
are given by the <<function-parameter>> values
903+
(defaulting to an empty set).
904+
905+
The name of a ''@mixin'' rule is a [=tree-scoped name=].
906+
If more than one ''@mixin'' exists for a given name,
907+
then the rule in the stronger cascade layer wins,
908+
and rules defined later win within the same layer.
909+
910+
If the [=mixin parameters=]
911+
contain the same <<custom-property-name>> more than once,
912+
then the ''@mixin'' rule is invalid.
913+
914+
<h4 id=mixin-body dfn lt="mixin body" export>
915+
The Mixin Body</h4>
916+
917+
The body of a ''@mixin'' rule acts as a [=nested style rule=],
918+
and accepts the same properties and rules
919+
that a normal [=nested style rule=] would.
920+
921+
In particular, further [=mixins=] can be invoked
922+
(via the ''@apply'' rule)
923+
within a [=mixin body=].
924+
925+
Unknown properties and rules are invalid and ignored,
926+
but do not make the ''@mixin'' rule itself invalid.
927+
928+
<h3 id=mixin-args dfn lt="mixin parameter" export>
929+
Mixin Parameters</h3>
930+
931+
Within a [=mixin body=],
932+
the ''env()'' function can access [=scoped environment variables=]
933+
defined within the [=mixin body=],
934+
defined by the mixin's arguments,
935+
or those defined at the <em>call site</em>
936+
(a style rule, or another [=mixin=]).
937+
938+
In that list, earlier things "win" over later things of the same name,
939+
exactly as if the [=mixin body=] was a [=nested style rule=]
940+
placed at its call site.
941+
Specifically, it desugars to <em>two</em> [=nested style rules=],
942+
to correctly reproduce the argument scope
943+
944+
<div class=example>
945+
For example, the following mixin use:
946+
947+
<xmp highlight=css>
948+
@mixin --nested(--color2: green) {
949+
@env --color3: blue;
950+
background: linear-gradient(env(--color1), env(--color2), env(--color3));
951+
}
952+
p.nested {
953+
@env --color1: red;
954+
@apply --nested();
955+
}
956+
</xmp>
957+
958+
is exactly equivalent to:
959+
960+
<xmp highlight=css>
961+
p.nested {
962+
@env --color1: red;
963+
& {
964+
@env --color2: green;
965+
& {
966+
@env --color3: blue;
967+
background: linear-gradient(env(--color1), env(--color2), env(--color3));
968+
}
969+
}
970+
}
971+
</xmp>
972+
</div>
973+
974+
<div class=example>
975+
[=Scoped environment variables=] defined in mixins can "shadow" ones defined higher up,
976+
just like they can in [=nested style rules=] normally
977+
(and like how variables can be shadowed in [=custom functions=]):
978+
979+
<xmp class='lang-css'>
980+
@mixin --z-index-a-b-c(--b, --c) {
981+
@env --c: 300;
982+
z-index: calc(env(--a) + env(--b) + env(--c));
983+
/* uses the --a from the call site's envs,
984+
the --b from the mixin parameter,
985+
and the --c from the local env */
986+
}
987+
div {
988+
@env --a: 1;
989+
@env --b: 2;
990+
@env --c: 3;
991+
@apply --add-a-b-c(20, 30); /* 321 */
992+
}
993+
</xmp>
994+
</div>
995+
996+
<div class=example>
997+
Note that [=mixin parameters=] are [=scoped environment variables=]
998+
rather than [=custom properties=],
999+
which means they exist in a separate namespace
1000+
and don't interfere with [=custom properties=].
1001+
They can even be used together.
1002+
1003+
For example, this code shows how to use a [=custom property=]
1004+
as the fallback for a [=mixin parameter=]:
1005+
1006+
<pre highlight=css>
1007+
@mixin --var-fallback(--arg: var(--arg)) {
1008+
/* TODO: come up with a believable example */
1009+
}
1010+
</pre>
1011+
</div>
1012+
1013+
1014+
Using Mixins {#using-mixins}
1015+
============================
1016+
1017+
The result of a [=mixin=] application
1018+
is substituted into the body of another [=style rule=]
1019+
as a [=nested style rule=]
1020+
via the ''@apply'' rule.
1021+
1022+
<h3 id=apply-rule>
1023+
The <dfn>@apply</dfn> Rule</h3>
1024+
1025+
The ''@apply'' rule applies a [=mixin=],
1026+
causing it to substitute into the rule
1027+
in place of the ''@apply'' rule itself.
1028+
1029+
Its grammar is:
1030+
1031+
<pre class="prod">
1032+
<<@apply>> = @apply <<dashed-function>> ;
1033+
</pre>
1034+
1035+
The ''@apply'' rule is only valid
1036+
in the body of a [=style rule=]
1037+
or [=nested group rule=];
1038+
using it in any other context causes it to be invalid and ignored.
1039+
1040+
''@apply'' rules are processed <em>before</em> any styles are applied,
1041+
as they effectively modify the stylesheet itself.
1042+
(Similar, in effect, to how [=conditional group rules=]
1043+
adjust which properties and rules are active in a stylesheet
1044+
before styles are applied.)
1045+
1046+
The ''@apply'' rule applies the [=mixin=]
1047+
named by the <<dashed-function>>'s name.
1048+
If no such [=mixin=] exists,
1049+
the ''@apply'' does nothing.
1050+
1051+
The arguments passed to the <<dashed-function>>
1052+
are mapped to the [=mixin's=] arguments;
1053+
if more arguments are passed than the length of the [=mixin's=] argument list,
1054+
the ''@apply'' application does nothing.
1055+
(Passing too few arguments is fine;
1056+
the missing arguments take their default values instead.)
1057+
1058+
1059+
<!-- Big Text: @env
1060+
1061+
████▌ █████▌ █ █▌ █▌ █▌
1062+
█▌ █▌ █▌ █▌ █▌ █▌ █▌
1063+
█▌▐█ █▌ █▌ ██▌ █▌ █▌ █▌
1064+
█▌▐█ █▌ ████ █▌▐█ █▌ ▐▌ █
1065+
█▌ ██▌ █▌ █▌ ██▌ █ ▐▌
1066+
█▌ █▌ █▌ █▌ ▐▌ █
1067+
████▌ █████▌ █▌ ▐▌ ▐█
1068+
-->
1069+
1070+
Scoped Environment Variables {#scoped-env}
1071+
============================
1072+
1073+
Issue: This section should move to [[css-env-1]]
1074+
(or level 2, whatever).
1075+
1076+
The ''env()'' function,
1077+
defined in [[css-env-1]],
1078+
allows substituting the value of [=environment variables=]
1079+
into a stylesheet.
1080+
These are "global" variables,
1081+
defined by the <l spec=infra>[=user agent=]</l>,
1082+
rather than [=custom properties=]
1083+
defined by the page author on individual elements (and their descendants).
1084+
1085+
The ''@env'' rule allows defining <dfn>scoped environment variables</dfn>,
1086+
which are <em>lexically scoped</em> to a single rule in a stylesheet
1087+
(and any [=nested style rules=] within it).
1088+
1089+
Issue: A top-level ''@env'' probably needs to still be lexically scoped to just the stylesheet itself.
1090+
(After all, you can use `media=""` to link in a stylesheet
1091+
*effectively* auto-wrapped in an ''@media'',
1092+
and it would be weird to have that act differently
1093+
from actually using a wrapping ''@media''.)
1094+
That means only JS-defined custom envs are available cross-stylesheet.
1095+
1096+
1097+
<h3 id=env-rule>
1098+
The <dfn>@env</dfn> Rule</h3>
1099+
1100+
The ''@env'' rule defines a [=scoped environment variable=],
1101+
scoped to its parent rule
1102+
(and any other nested rule within its parent rule).
1103+
It's only valid within a [=nested style rule=]
1104+
or [=nested group rule=],
1105+
or at the "top level" of a stylesheet not nested in anything;
1106+
in any other context it's invalid and ignored.
1107+
Its grammar is:
1108+
1109+
<pre class=prod>
1110+
<<@env>> = @env <<custom-property-name>> : <<declaration-value>>? ;
1111+
</pre>
1112+
1113+
This defines a [=scoped environment variable=]
1114+
with a name given by the <<custom-property-name>>,
1115+
and a value given by the <css><<declaration-value>>?</css>.
1116+
Its scope is the rule it's nested in,
1117+
or the stylesheet it's defined in
1118+
if it's not nested.
1119+
1120+
<h4 id=using-scoped-env>
1121+
Using Scoped Environment Variables</h4>
1122+
1123+
When an ''env()'' function is used
1124+
with a <<dashed-ident>> as the name of the environment variable,
1125+
it's potentially accessing a [=scoped environment variable=].
1126+
1127+
First the parent rule
1128+
of the property or rule the ''env()'' is used in
1129+
is checked to see if a [=scoped environment variable=] of that name
1130+
exists scoped to that rule.
1131+
If not,
1132+
its parent rule is checked,
1133+
recursively,
1134+
until finally the stylesheet itself is checked.
1135+
If that fails,
1136+
the global [=environment variables=] are finally checked.
1137+
1138+
Note: Custom global [=environment variables=] can be defined by the <code>CSS.customEnv</code> API.
1139+
(To be defined.)
1140+
1141+
Issue: Need some analogue to ''inherit'',
1142+
but for the parent lexical scopes.
1143+
Probably can't use ''inherit'' itself,
1144+
as that's a meaningful value that an ''env()'' could resolve to,
1145+
I guess?
1146+
1147+
1148+
1149+
1150+
1151+
7941152
<!-- Big Text: cssom
7951153

7961154
███▌ ███▌ ███▌ ███▌ █ █

0 commit comments

Comments
 (0)