CSS PREprocessors

Parent Reference

by

So you've discovered that preprocessors support nesting stuff, you use it here and there and after a while you want to be able to do more than setting a child selector.

Well, you can.

It's called a parent reference - &, and it allows you to do some pretty cool stuff.

Selector append

With the parent reference, you can easily attach a pseudo class to the selector:

.selector-append {
  foo: bar;
  &:hover {
    baz: qux;
  }
}
.selector-append {
  foo: bar;
  &:hover {
    baz: qux;
  }
}
.selector-append
  foo: bar
  &:hover
    baz: qux
.selector-append
  foo: bar
  &:hover
    baz: qux
.selector-append {
  foo: bar;
  &:hover {
    baz: qux;
  }
}
.selector-append {
  foo: bar;
}
.selector-append:hover {
  baz: qux;
}

You can also add class, id, and attribute selectors in this way.

Extend the name of the selector

If you need to extend the name of a selector you can do it in the same way as above:

.extend-name {
  &-one {
    foo: bar;
  }
  &two {
    baz: qux
  }
}
.extend-name {
  &-one {
    foo: bar;
  }
  &two {
    baz: qux;
  }
}
.extend-name
  &-one
    foo: bar
  &two
    baz: qux
.extend-name
  &-one
    foo: bar
  &two
    baz: qux
.extend-name {
  &-one {
    foo: bar;
  }
  &two {
    baz: qux;
  }
}
.extend-name-one {
  foo: bar;
}
.extend-nametwo {
  baz: qux;
}

This allows for BEM-like names in your CSS. You can read more about this technique from these articles:

Parent selector

We can also have a trailing ampersand - when we put it after...

By having a trailing parent reference, you can add a parent selector:

.parent-selector {
  foo: bar;
  .ie7 & {
    baz: qux;
  }
}
.parent-selector {
  foo: bar;
  .ie7 & {
    baz: qux;
  }
}
.parent-selector
  foo: bar
  .ie7 &
    baz: qux
.parent-selector
  foo: bar
  .ie7 &
    baz: qux
.parent-selector {
  foo: bar;
  .ie7 & {
    baz: qux;
  }
}
.parent-selector {
  foo: bar;
}
.ie7 .parent-selector {
  baz: qux;
}

This can be used to target browsers if we use the classic ie targeting solution as described by Paul Irish.

Another case where the parent selector can be useful is for parent element states where the children get specific classes.

Selector prepend

Appending selectors might work when you are appending a class, id, or attribute selector, but what if we need to add a html element like ol to .test? With selector appending we will end up with .testol which is not what we want. For cases like this we need to prepend the selector.

.selector-prepend {
  foo: bar;
  ol& {
    baz: qux
  }
}
.selector-prepend {
  foo: bar;
  @at-root ol#{&} {
    baz: qux
  }
}
.selector-prepend
  foo: bar
  @at-root ol#{&}
    baz: qux
.selector-prepend
  foo: bar
  ol&
    baz: qux
.selector-prepend {
  foo: bar;
  ol& {
    baz: qux
  }
}
.selector-prepend {
  foo: bar;
}
ol.selector-prepend {
  baz: qux;
}

Multiple parent references

Need something weirder with a repeating class in the selector for example? No problem, just use several ampersands and chain the appropriately:

.multi-reference {
  foo: bar;
  & + & {
    baz: qux;
  }
}
.multi-reference {
  foo: bar;
  & + & {
    baz: qux;
  }
}
.multi-reference
  foo: bar
  & + &
    baz: qux
.multi-reference
  foo: bar
  & + &
    baz: qux
.multi-reference {
  foo: bar;
  & + & {
    baz: qux;
  }
}
.multi-reference {
  foo: bar;
}
.multi-reference + .multi-reference {
  baz: qux;
}

Combinatorial explosion

The idea for combinatorial explosion comes from Less and it can be useful for covering many combinations of selectors. Simulating it in Sass and Stylus is also possible.

p, ul, ol {
  foo: bar;
  & + & {
    baz: qux;
  }
}
$selectors: "p, ul, ol";

#{$selectors} {
  foo: bar;
  & + {
    #{$selectors} {
      baz: qux;
    }
  }
}
$selectors: "p, ul, ol"

#{$selectors}
  foo: bar
  & +
    #{$selectors}
      baz: qux
$selectors = "p, ul, ol"

{$selectors}
  foo: bar
  & +
    {$selectors}
      baz: qux
p,
ul,
ol {
  foo: bar;
}
p + p,
p + ul,
p + ol,
ul + p,
ul + ul,
ul + ol,
ol + p,
ol + ul,
ol + ol {
  baz: qux;
}

This technique might be useful not only for adding space between elements that are direct siblings (the + selector), but also for covering direct descendants (the > selector).

Root directive

The root directive/reference will move anything below it to the root level - therefore it will not inherit the selector it's nested under.

One use case inspired by the excellent Stu Robson is positioning an animation declaration only where it's used. If we also put the animation name in a variable we end up with code that is orderly and less prone to errors.

.text {
  $anim-name: bounce;
  margin-top: 0;
  &:hover {
    animation: $anim-name 0.8s infinite ease-in alternate;
  }
  @at-root {
    @keyframes #{$anim-name} {
      50% {
        margin-top: -.5em;
      }
    }
  }
}
.text
  $anim-name: bounce
  margin-top: 0
  &:hover
    animation: $anim-name .8s infinite ease-in alternate
  @at-root
    @keyframes #{$anim-name}
      50%
        margin-top: -.5em
.text
  $anim-name = bounce
  margin-top: 0
  &:hover
    animation: $anim-name .8s infinite ease-in alternate
  /
    vendors = official
    @keyframes $anim-name
      50%
        margin-top: -.5em
.text {
  $anim-name: bounce;
  margin-top: 0;
  &:hover {
    animation: $anim-name 0.8s infinite ease-in alternate;
  }
  @at-root {
    @keyframes $anim-name {
      50% {
        margin-top: -.5em;
      }
    }
  }
}
.text {
  margin-top: 0;
}
.text:hover {
  animation: bounce 0.8s infinite ease-in alternate;
}
@keyframes bounce {
  50% {
    margin-top: -0.5em;
  }
}

Interactive

Leave a comment below or suggest improvements on GitHub.