I have a nested ruleset (map) like the one below.
@typography: {
@h1: {
font: roboto;
font-weight: 300;
font-size: 9.6rem;
I'm afraid it's not going to work the way (specifically the map itself) it is. Intuitively it would be something like:
#usage {
@typography[@h1]();
}
But currently this feature (cascading ()
and []
operators) is not implemented.
A first-guess workaround like "assign a ruleset of interest to a temporary variable and then 'call' it" also fails:
#usage {
@temp: @typography[@h1];
@temp(); // error: not callable value
}
(This one actually to be counted as a bug - I created a dedicated ticket).
This all brings us to the next section:
Notice that while "variable-based maps" (aka DRs) seem to be a more wide-spread pattern by now, there are five different methods to define a map in Less (and infinite number of these methods permutations to define an N-dimensional (aka "nested") map).
Each method has its pros and cons, and so far it's not clear which one to be chosen as the "go-to" one (in a long run there's tendency to unify them as tidy as possible but so far it's far from that).
Now look at the structure you're trying to represent w/o sticking to "variable -> @variable
" stereotype. Does not it look like a regular CSS ruleset:
.typography {
.h1 {
font: roboto;
font-weight: 300;
font-size: 9.6rem;
line-height: 9.6rem;
text-transform: none;
}
}
?
And this way you've already got a "mixin-based map" you can use pretty much the same way you'd use a "variable-based map". (Actually the current documentation for "Maps" also suggest both methods w/o enforcing either one as "the primary").
The only modification you'll need for this "CSS" structure is to make its inner or outer (or both) rulesets to be a parameteric mixin (by adding ()
) so that the rules won't appear in the compiled CSS by default.
E.g. like this:
.typography {
.h1() {
...
Or like this:
.typography() {
.h1 {
...
(Also if you prefer for these identifiers you can use #
instead of .
).
Now getting back to your use-case (The Solution):
.typography {
.h1() {
font: roboto;
font-weight: 300;
font-size: 9.6rem;
line-height: 9.6rem;
text-transform: none;
}
}
#usage-1 {
// "expand" the set of rules:
.typography.h1(); // OK
}
#usage-2 {
// use individual value from the map:
r: .typography.h1[font]; // OK
}
#usage-3 {
// iterate through:
each(.typography.h1(), <...>); // OK
}
// etc.
Not a surprise counting that expanding a set of rules is what the mixins were invented for in the first place.
The only fundamental difference (beside current limitations/issues on how they can be used) between "variable-based" and "mixin-based" maps to keep in mind is that "variables (and properties) override" and "rulesets (and thus mixins) cascade". This may affect some particular details when you'll need your CSS data to be customized/modified by "external code" (e.g. as in "theming/subtheming" etc.) - but that's another big story so I won't get into it here, although see the next section for some tips.
And one more important thing to understand about mixins (in context of the use-case).
If we'll think of variables as an abstract programming thing, i.e. "an identifier (symbolic name) associated with a value" we quickly see that a mixin is just that: a variable.
A "mixin" (its name) is really nothing but an identifier to refer to a value, i.e. -> variable.
It's just the identifier characters (#
or .
in front) plus a limitation on what kind of values it can hold is what makes it to be referred to by a different title, i.e. "mixin" instead of a "variable" (as in "Less @variable
").
In other words, when it comes to "I have some data and I need something (i.e. "a variable") to hold/represent it", it's important to not automatically fall into the "a variable (in a generic sense) -> @variable
" trap.
So getting back to the Q, another trick to have in mind is to know that mixin and variable values (specifically if it's a "ruleset" value) can be (almost) freely assigned/reassigned to each other. I.e. basically, you can create a variable to refer to a mixin-based map and create a mixin to refer to a variable-based map.
This may be valuable to overcome current issues/limitations (mostly in usage) of both methods (or if you just prefer more of @
, .
or #
"code-look" where maps are used).
Here're a few tips:
// ................
// "Universal" map:
.typography {
.h1() {
font: roboto;
font-weight: 300;
font-size: 9.6rem;
line-height: 9.6rem;
text-transform: none;
}
@h1: {.typography.h1}; // assign mixin to variable
.h2() {@h1()} // assign variable to mixin
.h3() {.typography.h1} // assign mixin to mixin
@h2: @h1; // assign variable to variable
}
@typography: {.typography}; // assign mixin to variable
.graphytypo {.typography} // assign mixin to mixin
// etc.
// ................
// Usage:
#usage-1 {
// use individual values from the map (all roboto):
1: .typography.h1[font];
2: .typography[@h1][font];
3: .typography.h2[font];
4: .typography.h3[font];
5: .typography[@h2][font];
6: @typography[@h1][font]; // <- like your original map
7: .graphytypo.h3[font];
// etc.
}
#usage-2 {
// expand a set of .h1 rules (all the same):
.typography.h1();
.typography.h2();
.graphytypo.h3();
// etc.
}