Sass Reference
Article
By Hugo Giraudel

Extend

By Hugo Giraudel

The extend feature, used with the @extend directive, is one of the key features of Sass and has played a large role in the popularization of CSS preprocessors a couple of years ago. To put it very simply, it makes it possible to tell Sass to style an element ‘A’ exactly as though it also matched a selector ‘B’. Needless to say this can end up being a valuable ally when writing modular CSS.

For a selector to extend another one, you only have to define the extended selector right after an @extend directive. If it exists, the extending selector will be added to all selectors where the extended selector belongs. Because of this, extending an over-used selector can have terrible side effects on your stylesheets.

.bar {
    color: red;
}

.baz .bar {
    color: blue;
}

.foo {
    @extend .bar;
    background: yellow;
}

The previous code snippet will be compiled as:

.bar, .foo {
    color: red;
}

.baz .bar,
.baz .foo {
    color: blue;
}

.foo {
    background: yellow;
}

As you can see, it is not the styles from the extended selector (in this case .bar) that are being printed at the @extend location. It works the other way around: the extending selector (here .foo) is being appended to all selectors including the extended selectors.

Optional flag

When extending a selector that does not exist, Sass will not silently fail; instead, it will throw an error, such as:

“.foo” failed to @extend “.bar”. The selector “.bar” was not found. Use “@extend .bar !optional” if the extend should be able to fail.

Along the same lines, when trying to extend a specific class with another specified class, Sass throws an error because there is no way to generate valid CSS from this.

a.foo {
  color: red;
}


b.bar {
  @extend .foo;
}

“b.bar” failed to @extend “.foo”. No selectors matching “.foo” could be unified with “b.bar”. Use “@extend .foo !optional” if the extend should be able to fail.

As you can see from the thrown errors, it is possible to add an !optional flag to Sass extend to make the engine silently fail when it is not possible to perform the selector extension correctly.

Extend in directives

Sass presents some limitations when trying to extend selectors from different contexts, such as @media or @supports. For instance, it is not possible to extend an outer selector from with a context.

@media print {
  .foo {
    @extend .bar;
  }
}

.bar {
  color: red;
}

This Sass sample throws the following error when compiling:

You may not @extend an outer selector from within @media. You may only @extend selectors within the same directive. From “@extend .bar” on line 3.

It might look dull that Sass cannot perform such a standard action but it actually makes sense when you think about it. What would you expect from such a code sample? It is very hard to say, and any implementation would be likely to create confusion to one developer or another.

Since .foo lives in a different scope, it would be fairly odd to simply move it to a completely different scope.

On the other hand, it is perfectly fine to extend a selector of a different Sass context from a selector at root level, like so:

@media print {
  .foo {
    color: red;
  }
}

.bar {
  @extend .foo;
}

In this case, Sass has no problem in generating valid and meaningful CSS. Indeed, we want .bar to be styled as if it was .foo. That means we want .bar to be red for print stylesheets. There is no possible confusion here.

Example

.message {
    padding: 0.5em;
    border: 1px solid rgba(0, 0, 0, 0.1);
    font-weight: bold;
}

.message-error {
    @extend .message;
    color: red;
}

.message-valid {
    @extend .message;
    color: green;
}

.message-warning {
    @extend .message;
    color: orange;
}

.message-info {
    @extend .message;
    color: blue;
}

Engine compatibility

The extend directive is fully compatible across all Sass engines, although LibSass happens to be wrong when using @extend is deeply nested selectors.

Furthermore, LibSass kind of supports cross-@media extends (in an incorrect way though) while Sass deliberately does not support using @extend between different media contexts.

I strongly recommend you to keep @extend usages to simple cases and to avoid extending across various media contexts in order to prevent any unexpected behaviour.