Selector Inheritance the Easy Way: Introducing @extend

Posted April 26, 2010

There are often cases when designing a page when one class should have all the styles of another class, as well as its own specific styles. The most common way of handling this is to use both the more general class and the more specific class in the HTML. For example, suppose we have a design for a normal error and also for a serious error. We might write our markup like so:

<div class="error seriousError">
  Oh no! You've been hacked!
</div>

And our styles like so:

.error {
  border: 1px #f00;
  background-color: #fdd;
}
.seriousError {
  border-width: 3px;
}

Unfortunately, this means that we have to always remember to use .error with .seriousError. This is a maintenance burden, leads to tricky bugs, and can bring non-semantic style concerns into the markup.

The @extend directive in the Sass 3 release candidate avoids these problems by telling Sass that one selector should inherit the styles of another selector. For example:

.error {
  border: 1px #f00;
  background-color: #fdd;
}
.seriousError {
  @extend .error;
  border-width: 3px;
}

This means that all styles defined for .error are also applied to .seriousError, in addition to the styles specific to .seriousError. In effect, everything with class .seriousError also has class .error.

Other rules that use .error will work for .seriousError as well. For example, if we have special styles for errors caused by hackers:

.error.intrusion {
  background-image: url("/image/hacked.png");
}

Then <div class="seriousError intrusion"> will have the hacked.png background image as well.

How it Works

@extend works by inserting the extending selector (e.g. .seriousError) anywhere in the stylesheet that the extended selector (.e.g .error) appears. Thus the example above:

.error {
  border: 1px #f00;
  background-color: #fdd;
}
.error.intrusion {
  background-image: url("/image/hacked.png");
}
.seriousError {
  @extend .error;
  border-width: 3px;
}

is compiled to:

.error, .seriousError {
  border: 1px #f00;
  background-color: #fdd; }
.error.intrusion, .seriousError.intrusion {
  background-image: url("/image/hacked.png"); }
.seriousError {
  border-width: 3px; }

When merging selectors, @extend is smart enough to avoid unnecessary duplication, so something like .seriousError.seriousError gets translated to .seriousError. In addition, it won’t produce selectors that can’t match anything, like #main#footer.

For more information, see the reference documentation.

History

There have been a lot of takes on the problem of selector inheritance since the dawn of CSS compilers back in 2006. The first was Sass’s own mixins. These were never explicitly intended for inheritance; instead, they solved the more general problem of sharing styles between selectors. This is part of what inheritance is about, but not all of it.

Copying styles around can only ever get halfway to full selector inheritance. It can give .seriousError all the CSS properties that .error has, but this isn’t enough: it should be like everything with class .seriousError has class .error as well. This means that styles for .error.intrusion should apply to .seriousError.intrusion, which can’t happen with mere style-copying.

The next step came from Less, which was the first compiler to use classes as mixins. Although Less mixins were originally intended explicitly for selector inheritance, in the end they’re no more powerful than Sass mixins. They work by copying styles around, so they can’t do full inheritance.

The third step came from xCSS, which until now had the closest thing to full selector inheritance of any CSS compiler. Like Less, xCSS had explicit inheritance, but it didn’t work by copying styles. Instead, when .seriousError extends .error in xCSS, the .error CSS rule is expanded to .error, .seriousError, just like with @extend. However, xCSS still lacks a critical component for full inheritance: .seriousError is only added when the selector is exactly .error, rather than .error.intrustion or .admin .error.

Although not a CSS compiler, OOCSS certainly warrants mention here. Nicole Sullivan, its author, was the first person to codify the idea of selector inheritance and to publicly explore the benefits it provides to design. OOCSS handles inheritance by requiring the user to manually include all inherited classes in the HTML.

It was with OOCSS in mind that Chris Eppstein, my partner in Sass, originally came up with the idea of @extend as it exists in Sass today. He outlined this in a post on his blog four and a half months ago. I took that idea and wrote the code we’re releasing today.

Try @extend Now

To use the @extend directive right now, first install the Sass 3 release candidate, via the Haml gem:

gem install haml --pre

Then you’ll be able to use @extend in any of your Sass or SCSS documents.

Trevor Burnham said April 26, 2010:

Finally! This is a super-useful feature. Can’t wait to start using it. I like that the output CSS is implemented efficiently, rather than duplicating styles.

Konstantin Haase said April 26, 2010:

Does this work with stuff like “error pre” too?

Nathan said April 26, 2010:

Konstantin: Can you give an example?

Till Schumann said April 26, 2010:

Wow, this makes things a lot easier! I’m longing for the official release of SASS3

Russell Bishop said April 27, 2010:

Interesting idea, and of course the other benefit is now you’re only writing one class in your markup instead of two. Although, you said the problem there was that “we have to always remember to use .error with .seriousError” – but now you have to remember ”@extend .error” anywhere you want the rule in your CSS?

Pro’s and Con’s I spose.

Nathan said April 27, 2010:

Russell: You only need to use @extend .error once for each class that should inherit the error styles. If you do it manually, you need to use .error once for every HTML element that has a class that should inherit the error styles. Since presumably you’ll have at least one element styled with a given class, using @extend is always going to be less work.

Potsed said April 27, 2010:

I think this is great and is a logical step forward for simplifying css. The next thing that I would love to see in css is variables. Imagine setting a font size or a colour in only one place in the file. Something like:

!myRed: #F33;

.alert { 
  color: !myRed;
  border: 1px solid !myRed;
}
Nathan said April 27, 2010:

Potsed: Variables are supported in Sass already.

steve said April 27, 2010:

Nice idea, think that’s the first time seen that way. Cheers

Peng Zhong said April 30, 2010:

I haven’t given this too much thought, but isn’t this doable without @extend?

HTML
<span class="error">Just your ordinary error.</span>
<span class="intrusion error">Hey an intrusion. Meh.</span>
<span class="serious error">A SERIOUS PROBLEM</span>
<span class="serious intrusion error">INTRUSION!!</span>
Sass
span.error
  border: 1px solid #f00
  background: #fdd
  &.serious
    border-width: 3px
  &.intrusion
    background-image: url(/image/hacked.png)

It seems cleaner this way.

Nathan said May 01, 2010:

Peng: The problem there is that you have to always remember to use the .error class with .serious and .intrustion. Not only is that hard to maintain, since it becomes easy to have bugs in your HTML where you forgot to include the parent class, but it also leaks stylistic concerns – namely, the relationship between two styles – into your HTML.

alegscogs said September 16, 2010:

Just noticed this line in your post:

“This means that styles for .error.intruction should apply to .seriousError.intrusion”

whereas I think you meant

“This means that styles for .error.intrusion should apply to .seriousError.intrusion”

@extend looks great. Thanks for the great tools.

Nathan said September 16, 2010:

Typo fixed. Thanks for the tip.

Make your comments snazzy with Textile!