The Sass Ampersand

Avatar of Richard Finelli
Richard Finelli on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

The & is an extremely useful feature in Sass (and Less). It’s used when nesting. It can be a nice time-saver when you know how to use it, or a bit of a time-waster when you’re struggling and could have written the same code in regular CSS.

Let’s see if we can really understand it.

Basic Nesting

.parent {
  .child {}
}

This compiles to:

.parent .child {}

You can nest as deep as you’d like, but it’s a good practice to keep it only a level or two to prevent overly specific selectors (which are less useful and harder to override).

Adding another class

The & comes in handy when you’re nesting and you want to create a more specific selector, like an element that has *both* of two classes, like this:

.some-class.another-class { }

You can do this while nesting by using the &.

.some-class {
  &.another-class {}
}

The & always refers to the parent selector when nesting. Think of the & as being removed and replaced with the parent selector. Like this:

The “aha” moment!

Take this Sass:

.parent {
  .child {}
}

This can actually be thought of as short-hand for nesting with the &:

.parent {
  & .child {}
}

So, these two examples both compile to the same thing:

.parent .child {}

The example with the & isn’t anything different than the example without the &. Nesting without the & is shorthand for nesting with it. We can think of the & as a mechanism that allows us to place the parent selector wherever we need it in our child selector. It allows us to nest with alterations. Let’s look at some more examples.

Using the & with pseudo classes

You can write pseudo classes on a class in a much less repetitive way with the &:

.button {
  &:visited { }
  &:hover { }
  &:active { }
}

This compiles to:

.button:visited { }
.button:hover { }
.button:active { }

The & in this case allows us to position .button directly next to pseudo classes without repetition in the authored code. If we left out the & from this example, basic nesting would put a space between them like this…

.button :hover

… which isn’t the same.

Using the & with >, +, and ~

Using the & with the child combinator >, adjacent sibling combinator +, and the general sibling combinator ~ is a breeze. At first I thought you had to use the &, but:

// You don't actually have to do this.
// Here, the ampersand is implied.
.button {
  & > span { }
  & + span { }
  & ~ span { }
}

Leaving the &‘s out of the selector works here:

// This compiles the same as above
.button {
  > span { }
  + span { }
  ~ span { }
}

Both of these examples compile into this CSS:

.button > span { }
.button + span { }
.button ~ span { }

Qualifying based on context

Nested selectors don’t necessarily have to start with the ampersand. You can qualify a selector by putting the & on the right.

.button {
  body.page-about & { }
}

We’re repositioning the parent selector exactly where we need it. This is really useful for qualifying a selector based on a different parent. This will compile to:

body.page-about .button {}

Meaning, select the button class only when a child of a body with a page-about class.

Tweaking the definition of the &

Think of the & as not only being replaced by the parent selector but as being replaced by the *compiled* parent selector.

This is important when nesting more than two levels deep, where more than one level has an &. These next two wacky examples drive this point home.

Wacky but working example #1

Do not write selectors that look like this:

.parent {
  .child {
    & div & & > a {}
  }
}

For each & it will be replaced with the compiled parent selector. Therefore, every time there is an & we’ll insert .parent .child. Here’s the compiled CSS:

.parent .child div .parent .child .parent .child > a {}

Wacky but working example #2

.parent {
  .child {
    .grand-child & {
      &.sibling { }
    }
  }
}

To mentally-compile this CSS, start at the top-most layer and work your way down pealing off the outer layers and replacing the & with the new compiled parent selector.

Here it is compiled:

.grand-child .parent .child.sibling {}

What the & isn’t

I found I was using the & for something it wasn’t from time to time. The & doesn’t allow you to selectively traverse up your nested selector tree to a certain place and only use a small portion of the compiled parent selector that you want to use. I’ve wanted to do something like this before:

.grand-parent {
  .parent {
    &(1) .child {} // Trying to get `.parent`, not `.grand-parent .parent`
  }
}

My intention was for the & to only get replaced with .parent in hopes of compiling to this:

.parent .child {}

But that doesn’t work.

The & is always the fully compiled parent selector.

@at-root to the rescue

It’s worth mentioning that @at-root allows you to break out of your nesting structure entirely to the “root” of your nesting tree.

.grand-parent {
  .parent {
    @at-root .child {}
  }
}

We’ve teleported out of the nesting tree to this compiled CSS:

.child {}

This is nice. From an organizational perspective, all the code is still grouped together, which could be noted as an unsung benefit of nesting. @at-root can help keep specificity levels low because you no longer have the compiled parent selector to increase specificity. All the while still keeping your code conceptually organized with nesting:

.grand-parent {
  .parent {
    @at-root body.page-about .child {}
  }
}

Compiled CSS:

body.page-about .child {}

There’s a few other use cases for the & that can be fun.

Doubling up specificity

Sometimes you need to beat-down the specificity of a 3rd-party CSS library to take ownership of the style:

.parent.parent {}

It’s a lot less overpowering than using and ID, inline style, or !important and it could have benefits over qualifying the selector with an arbitrary parent element. The specificity level isn’t raised based on a selector’s context, but only by itself. With the & you can do that same thing like this.

.parent {
  &#{&} { }
}

The interpolation brackets #{ } are needed as two touching ampersands are invalid Sass.

Modifying the ampersand

Even though you can’t have two ampersands touching without the interpolation brackets — as we demoed earlier in our pseudo class example — you can have another selector touch the ampersand. Touching the ampersand works well with modifier classes.

.btn {
  &-primary {}
  &-secondary {}
}

Compiled CSS:

.btn-primary {}
.btn-secondary {}

This can be quite useful if employing a naming methodology (i.e. BEM) which uses dash and underscore combinated classes rather than combined selectors.

Conclusion

The ampersand combined with nesting is a great feature. Once you know what it’s doing, authoring your Sass can become easier, faster, and less error-prone.

Here’s a couple of other articles specifically about the ampersand, for your reference pleasure: