Sass’s trailing ampersand and modular code
I recently learned of a feature of Sass that I think is extremely powerful, and little known about. I’m not even sure if it has a proper name*, so I’ll make one up and call it the “insert parent selector”. And it can help you deal with exceptions or variations that might make otherwise modular code become unruly.
And all it comes down to is a trailing ampersand - “&”.
If you’ve been using Sass for any length of time, you have probably used the more well known nested selectors:
p {
color: black;
a {
color: red;
}
}
Which compiles to this in CSS:
p {
color: black;
}
p a {
color: red;
}
And that’s awesome. It’s one of the most powerful features of Sass, and one of the most compelling reasons to switch to a preprocessor.
Let’s say you’re trying to create modular chunks of CSS that pertain to the components in your project. Using nested selectors like the ones above, it’s pretty easy to create self-contained chunk of code:
.search-form {
label {
color: purple;
}
input\[type="search"\] {
color: green;
}
}
But, like most things in development, it’s the exceptions that screw up your best laid plans. What happens to your search form on the search results page, and maybe it has moved from the top right of the page to the <main>
content area?
That’s where the “insert parent selector” comes in. You can nest a parent selector inside the original selector.
.search-form {
label {
color: purple;
}
input\[type="search"\] {
color: green;
}
main & {
color: black;
}
}
By inserting the trailing ampersand (“&”), you will create a compiled piece of CSS that looks like this:
main .search-form { … }
That is very powerful. This allows you to truly create modular chunks of code that are really easy to manage.
Think of the possibilities - use Modernizr? Now it is really easy to keep variations based on Modernizr-supplied body classes, e.g. .no-svg &
.
Use the HTML5 Boilerplate trick of attaching IE classes to the root HTML element? Ditto for .ie8 &
.
There are a few rules that I’ve been able to figure out around the “insert parent selector”:
Whatever preceeds the ampersand will be inserted before all selectors that it is nested within. So:
.foo {
.bar {
.test & { …
}
}
}
Will compile to:
.test .foo .bar { … }
However, it will respect nested media queries:
.foo {
@media (min-width: 600px) {
.test & {
…
}
}
}
Will compile to:
@media (min-width: 600px) {
.test .foo { … }
}
Even this:
.foo {
.test & {
@media (min-width: 600px) {
…
}
}
}
Will still compile to the same thing:
@media (min-width: 600px) {
.test .foo { … }
}
It will also work as expected within an extend directive:
%testextend {
.test & { … }
}
.foo {
@extend %testextend;
}
Will compile to:
.test .foo { … }
I only learned about this feature of Sass recently, while I was mid-project. Based on this little feature, though, I was able to refactor several large pieces of code, and probably cut down my .scss files by almost a hundred lines. This feature has had me radically rethink how I architect my .scss files, and will allow me to truly create some modular pieces of code.
Further reading:
-
The ampersand & a killer Sass feature by Joe Oliveira.
-
Referencing Parent Selectors using the ampersand (&) character by Adam Stacoviak
-
As far as I can tell, it’s only been referred to as “a trailing ampersand”, which isn’t that fun of a name.
Previous: Not Communicating is Not an Option