Using variables in Sass has been a core feature for years now. We have all used them to endless exhaustion, and we have all seen things like this:
// establish a core color
$bravo_gray: #333;
// assign core color to semantic variable
$input-disabled-color: $bravo_gray;
// Use semantic variable as assigned to additional semantics
$input-disabled-background: lighten($input-disabled-color, 75%);
$input-disabled-border: lighten($input-disabled-color, 50%);
$input-disabled-text: lighten($input-disabled-color, 50%);
This works and it works really well. What we accomplished was a manual name spacing by convention. To use this, we would do the following:
input[disabled] {
background-color: $input-disabled-background;
border-color: $input-disabled-border;
color: $input-disabled-text;
}
The expected CSS output would be:
input[disabled] {
background-color: #f2f2f2;
border-color: #b3b3b3;
color: #b3b3b3;
}
We can do better. In Sass 3.3 we were given a new, awesome thing to play with: maps. Maps within lists, to be exact. Sass has had lists for a long time now, but these lists were very flat. Maps give us more dimension to store and retrieve values from a list.
While libsass is lacking this as a core feature, for those who use libsass there is an add-on library by Lu Nelson that you can use.
For the most part, it all works very much the same. There are some key differences in the syntax; there are no colons :
between the key:value pairs and there are no trailing commas ,
after the last item in the array.
The following code examples make use of the Lu Nelson libsass add-on.
Using List-Maps
The first really cool thing with Maps is that you can store key:value pairs (a hash) into a list for later retrieval. Using the primary variable $input
as the namespace, we can then nest the extensions in the following way:
$input: (
disabled-background lighten($input-disabled-color, 75%),
disabled-border lighten($input-disabled-color, 50%),
disabled-text lighten($input-disabled-color, 50%)
);
Now that we have all these values stored into an array with keys and values, we can start writing Sass rules that take advantage of this.
In the following example, I used the map-get
Sass function to dig into the variable of $input
and find its keys. The function map-get
takes two arguments, like so: map-get($list, key)
.
So instead of using the old standard of:
background-color: $input-disabled-background;
I can now use this:
background-color: map-get($input, disabled-background);
All together, I can update the previous input[disabled]
selector like so:
input[disabled] {
background-color: map-get($input, disabled-background);
border-color: map-get($input, disabled-border);
color: map-get($input, disabled-text);
}
And this gives us the following CSS output:
input[disabled] {
background-color: #f2f2f2;
border-color: #b3b3b3;
color: #b3b3b3; }
Nested objects in an array
Looking at the code, there is another pattern emerging. The concept of disabled
is repeated. With Maps there is a way to nest these concepts and retrieve the data as well.
The way that Maps work, the only restriction is that each key needs to have a value. Much like JSON, a value may be another set of key:value pairs. An example tree could be the following:
$variable: (
key (
key value,
key value,
key (
key value,
key value
)
)
);
Looking at that tree structure and then looking at my previous structure, instead of listing all of the keys using a manual naming convention, let’s use disabled
as a name-space, like so:
$input: (
disabled (
background lighten($input-disabled-color, 75%),
border lighten($input-disabled-color, 50%),
text lighten($input-disabled-color, 50%)
)
);
To make use of this, Maps allow us to retrieve values from a chain of keys using the map-get-z()
function. In the following example you will notice how I am retrieving a series of chained keys delimitated by a comma ,
:
background-color: map-get-z($input, disabled, background);
The whole selector would look like this:
input[disabled] {
background-color: map-get-z($input, disabled, background);
border-color: map-get-z($input, disabled, border);
color: map-get-z($input, disabled, text);
}
Using this method we will get the following familiar output CSS:
input[disabled] {
background-color: #f2f2f2;
border-color: #b3b3b3;
color: #b3b3b3; }
List-Maps are smart!
There are many very useful functions for Maps, but one I will illustrate here is the map-has-key()
function. In short, this will return a Boolean value if a list has the key you are looking for. The map-has-key()
function takes two arguments: the $list
and the key
. Using this with the @if
directive, we can do the following:
@if map-has-key($input, disabled) {
input[disabled] {
background-color: map-get-z($input, disabled, background);
border-color: map-get-z($input, disabled, border);
color: map-get-z($input, disabled, text);
}
}
This is very helpful when building out frameworks. We can conditionally respond to available keys within the project, versus throwing the all-too-familiar:
error: unbound variable $input-disabled-background
Conclusion
In short, List-Maps are a welcomed feature when creating larger and larger Sass projects. This feature will give us greater control over how we generate families of variables within a project and how we can apply them more sensibly.
For more Sass resources, check out Dale’s blog and new GitBook, Sass in the Real World.