All three of the most popular CSS preprocessors support extend. I don’t have any data, but in my experience this feature is confusing at first and thus a bit under used. I thought we could talk a bit about how it works, why and when to use it, some gotchas, and what it might be like in the future.
The Basic Concept
Inheritance. I want this selector to inherit the styles from this selector.
Basic Usage
I’ll use Sass here. We’ll cover all the preprocessors later.
.foo {
color: red;
}
.bar {
@extend .foo;
}
Output:
.foo, .bar {
color: red;
}
Note that it doesn’t “go get” the styles from .foo
and insert them below into .bar
, thus duplicating the styles. Instead it comma separates the original selector, applying the styles to both more efficiently.
Relationship to Mixin
You can often achieve the same result with a mixin.
@mixin stuff {
color: red;
}
.foo {
@include stuff;
}
.bar {
@include stuff;
}
This will get those styles into both selectors. This is often easier to understand and honestly has less “gotchas,” so is more common to see. Note that the output moves the styles into both places:
.foo {
color: red;
}
.bar {
color: red;
}
Which, while not that huge of a deal (especially since repetitive text is particularly easy to squish for gzip) it is less efficient.
Mixins can do things that extend cannot though, namely take parameters and process/use them in the output.
@mixin padMasterJ ($param: 10px) {
padding: $param;
}
Extend can’t do that, so a rule of thumb is: any time you’d use a mixin with no parameter, an extend will be more efficient. Of course there are exceptions to every rule, which we’ll get into later.
Another thing to note is that all top-level selectors are extendable. LESS users will understand that more instinctively because in LESS all selectors are also mixins, which is weird to Sass users.
The Sass Way
We’ve used Sass so far, but let’s note something specific about how Sass handles Extend. It extends all nested selectors as well.
.module {
padding: 10px;
h3 {
color: red;
}
}
.news {
@extend .module;
}
Outputs:
.module, .news {
padding: 10px;
}
.module h3, .news h3 {
color: red;
}
A limitation of Sass extend is that it cannot extend a nested selector. Here is an example of that:
.header {
h3 {
color: red;
}
}
.special-header {
/* Error: can't extend nested selectors */
@extend .header h3;
}
The LESS Way
LESS uses &:extend
. To port our super simple example over, it would look like:
.foo {
color: red;
}
.bar {
&:extend(.foo);
}
Which outputs:
.foo,
.bar {
color: red;
}
What is notable about LESS extend is that it doesn’t extend nested selectors by default. So if we do this:
.module {
padding: 10px;
h3 {
color: red;
}
}
.news {
&:extend(.module);
}
The h3 will not be extended, and will output:
.module,
.news {
padding: 10px;
}
.module h3 {
color: red;
}
However, if you add the all
keyword, like:
.news {
&:extend(.module all);
}
It will extend everything:
.module,
.news {
padding: 10px;
}
.module h3,
.news h3 {
color: red;
}
You can also specify a nested selector to extend in place of the all
keyword.
The Stylus Way
Stylus uses @extend
and works largely similar to Sass.
.module
padding 10px
h3
color red
.news
@extend .module
Outputs:
.module,
.news {
padding: 10px;
}
.module h3,
.news h3 {
color: #f00;
}
The one notable difference from Sass is that it can extend a nested selector. Like:
.header
padding 10px
h3
color red
.special-header
@extend .header h3
Will get you:
.header {
padding: 10px;
}
.header h3,
.special-header {
color: #f00;
}
Extending Placeholders
Sass and Stylus have placeholder selectors. In Sass:
%grid-1-2 {
float: left;
width: 50%;
}
In Stylus:
$grid-1-2
float left
width 50%
This is such a tiny thing. It just doesn’t output the placeholder in the CSS (hence, “placeholder”) it just allows you to extend them. The big advantage here is you can use internal naming schemes that don’t effect your external naming schemes. Grid classes are a good example of this. Names like “grid-1-2” and “grid-1-3” are great names internally, but not great actual HTML class names. With placeholders you can keep them internal.
.main-content {
@extend %grid-2-3;
}
.sidebar {
@extend %grid-1-3;
}
Watch Out for Selector Order
Because of the selector re-writing, you may occasionally run into a case where the selector is re-written earlier than another selector which overrides it. Note that the specificity will never change, but when the specificity is exactly the same on two selectors, the one further down in the final CSS “wins.”
For instance:
.one {
color: red;
}
.two {
color: green;
}
.three {
@extend .one;
}
Then you have an element like this:
<div class="three two">test</div>
You might assume the class name three “wins” because it’s the lowest down in the Sass, extends .one
, and thus the color will be red. But .three
actually gets re-written up above .two
:
.one, .three {
color: red;
}
.two {
color: green;
}
Since the element also has the class name .two
, the color is actually green in the final rendering.
Watch Out For Mega Output
Here’s a very reasonable scenario covered by Nicole Sullivan:
- You create a placeholder class for clearfix
- You create a generic module class that extend clearfix
- You create other module classes that extend module (“chaining” extends)
%clearfix {
&:before,
&:after {
content: " ";
display: table;
}
&:after {
clear: both;
}
}
.module {
padding: 10px;
@extend %clearfix;
h3 {
color: red;
@extend %clearfix;
span {
float: right;
}
}
}
.sports {
@extend .module;
}
.news {
@extend .module;
}
The output for just the clearfix starts looking a little thick:
.module:before, .sports:before,
.news:before, .module h3:before,
.sports h3:before, .news h3:before,
.module:after, .sports:after,
.news:after, .module h3:after,
.sports h3:after, .news h3:after {
content: " ";
display: table;
}
.module:after, .sports:after,
.news:after, .module h3:after,
.sports h3:after, .news h3:after {
clear: both;
}
And that’s just a couple of classes. I’m sure a whole big project could get way hairier. Not that it’s not going to work, it’s just you might be better of with an actual class and dropping it into the HTML as needed. Or a mixin might even produce less code in some cases depending on how deep the nesting goes.
The rule of thumb being: chose the technique that requires the least final output (or that works best for you).
Watch Out for Media Queries
None of the languages allow you to extend from inside a media query to a selector defined outside a media query.
This is OK:
@media (max-width: 100px) {
.module {
padding: 10px;
}
.news {
@extend .module;
}
}
This will not work:
.module {
padding: 10px;
}
@media (max-width: 100px) {
.news {
@extend .module;
}
}
It is the nature of the selector moving/rewriting. If you moved that selector out of the query, you are no longer honoring the author intent (it would apply regardless of the media query). Perhaps the rules could be copied inside, but then that doesn’t honor the intent of extend. Sass 3.3 will have a way to deal with it, but the real answer as native browser support.
The Future of Extend
Extend is pretty powerful at the preprocessor layer, but would be way more powerful at the browser level. No more comma-separated selectors, mega output, source order concerns, media query problems… none of that. The browser just knows to apply the rules from the other selector to this one. From what I hear the powers that be needed some convincing this was a good idea, but now have been convinced, and it’s a matter of writing up all the specs and whatnot. But I have no evidence of that to link to.
Perhaps it could be:
.module {
padding: 10px;
}
.news {
!extend .module;
}
I’m not sure… I doubt @extend could be used because those @words have kinda special meaning already (they wrap blocks conditionally). And functions like extend() are generally for values not properties. Kinda new/weird territory for the native language.
I would love to hear all your thoughts on extend. If you use it, how you use it, other problems you’ve found, how you imagine it working natively, etc.
I think that @extend is too messy at times, but that’s just me. Maybe I need to use it more but this is my current opinion.
I don’t recommend using the extend on classes…It can really get confusing at times
I find them massively helpful and they can really cut down on css bloat (compared with duplicating the styles). Stick with it.
Stephen, help me to understand more about the confusion that would be caused by using extend on classes?
agreed !!
I agree with Stephen here. The confusion is cut down if you only extend silent classes (placeholders). Knowing when to extend . (class), # (id), or % (silent class) can be troublesome. This allows you to create files of just extendable silent classes that can be imported anywhere and get on with your coding.
I had the unfortunate experience of working on a project recently where I didn’t have access to the HTML. Every page used page-unique ID’s for elements (even when the elements were shared across multiple pages).
@extend helped save my sanity as I could create reusable base classes (with my own naming conventions) and extend them across multiple IDs e.g.
.menu extended to #page1menu, #page2menu, #page3menu etc.
whilst still keeping my CSS relatively organised. Urgghh that HTML was a mess…I live @extend. I made a Sass file that has the nearly all basic properties and values. So, like I want something to float left I just use @extend %float-left. I think it keeps everything tidy by lumping the selectors together in the outputted CSS.
how do you go about extending an element using placeholder within a media query? have you figured out any cool way to do it?
as far as I am concered it is not possible at the moment.
though U can do apply media query styles inside placeholder itslef like:
Great overview Chris. I started implementing @extends where applicable some time ago and they are a great ‘tool’, especially with placeholder selectors.
What works best for me so far is creating a file called, lets say, _patterns.scss with multitude of placeholder selectors. I found myself having a lot of reusable ‘patterns’ that I could just @extend all over the stylesheets – a big improvement when it comes to code readability.
@extend is very messy and it adds up lots of selector which makes output way bigger than you would imagine and increases selector count as well.if you are using @extend for big project then forget about IE since it has limit on selector count.
We had recently remove all @extend and add class manually. Careful usage is recommended for @extend.
I had the same problem with going over the IE selector limit, but BlessCSS solved that problem. You could look for another method that splits your stylesheets, but Bless worked best in my project.
@extend can be a lifesaver when dealing with some scattered code. I use typekit and noticed lags in page rendering while fonts waited to download. I eventually switched to loading fonts asynchronously while taking advantage of the .wf-loading, .wf-inactive, and .wf-active classes typekit dynamically adds to the html tag on load to eliminate FOUT.
I wanted to share a SASS tip for how I use extends in this manner (works for google web font API as well), and I apologize in advance for the length of this comment.
There are placeholders like this that I extend all custom typography to:
One roadblock I ran into was the inability to extend these directly within the code. SASS was tripping over some of the parent references and messing up the class order (placing child before parent & duplication). I actually had to use mixins that performed the extends, such as:
For example, @include’ing the above @mixin on a h1, h2 heading would output the following:
I know the code above looks overly complicated and there’s probably a better way to do it, but I couldn’t find an alternative solution, so there it is.
One final neat thing about @extend in SASS is its ability to ignore unnecessary extends when another @extend will sufficiently cascade a style. It just skips it altogether. What I originally thought was a bug was just silent SASS efficiency.
Thanks Chris for another good one.
Can see that getting complicated very quickly.
So far I, and my colleagues in work, have stuck to normal CSS for this reason. At least it’s easy to follow ;)
Interesting overview. I tend to use @extend sparingly, pretty much only when it saves me some file size. And concerning grids, I prefer using mixins with parameters over @extending, that way I don’t have to create millions of grid classes/placeholders for each variation :P
I use @extend all the time. Its better to me than the mixins.
How would you pass a variable to
@extend
? Two are used for different purposes, so can’t really compare. I use mixins if I’m passing in variable, and extend if I’m using placeholders, I don’t usually extend classes.Use extend alot. Mainly use placeholders, and occasionally extend classes. I started using it almost right away when learning sass.
Isn’t in LESS you can do this?
This copies the
.red
styles to.alert
Your code compiles to:
Using extend:
I recently ran into a strange behavior of SASS (compass on Windows), which made it almost impossible to @extend %placeholder selectors.
Consider the following setup:
I have one file _variables.scss, which contains variables and some mixins that are used almost everywhere. There I put the placeholder selector
%clearfix
.In some other files I
@import
ed this file and@extend
ed%clearfix
.The result: the CSS contained multiple copies of the same output over and over again. It seemed like compass was chaining the selectors inside _variables.scss and printed that whereever _variables.scss was
@import
ed.Has anybody noticed that before? Or does anyone have an idea on how to avoid this problem?
Be mindful when using @extend, especially when already using Bootstrap or something similar that bloats your codebase. Chances are if you aren’t careful you’ll stumble upon the nasty IE 4096 selectors limit sooner than you’d expect.
More info: http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
Chris: Thanks for the great article. I love being able to learn about something (in this case, extending placeholder selectors) and be able to use it in my work right away.
One possible correction: I think the third code snippet in the “Extending Placeholders” section should be:
Indeed, thank you.
The downsides of extend easily outweigh the benefits for me.
I plan to avoid using it.
If you know you’re ok with it outputting as “.foo, .bar”, why wouldn’t you just do that yourself? Isn’t extend more verbose and harder to understand than that?
You can do this two ways in pure CSS.
In your stylesheet:
Or in your markup:
Also, I am not missing the point of this post using extend as another way of doing this. Just thought I would point it out.
Nevermind, I just seen you put this up. I’ma dummee.
Well thank you chirs for another great article but for now I am very confused with the @extend I am waiting for more information before I start using @extend in my websites.
Extend is useful, but this seems mad to me. Why are the different rules for class names in HTML and “internally”!?
Because classes in the HTML are there all the time. Internal-only placeholder classes could be used conditionally internally without awkward permanent naming. Imagine if we could do:
Now the only class in the HTML is “sidebar” which likely makes perfect sense, and we control its layout through internal names that also make perfect sense.
Chances are the only difference between your .grid-x-x classes are their width, you may as well extend to common ground and avoid the repetition, then if you want to change the margins or whatever (on your common grid class) you can do so in one place
Besides the “good naming” thingy, what’s the advantage compared to just adding the class (that you would get extended) directly to the div?
Sorry, forgot to refresh the page. Didn’t have latest comments.
Nice to get an insight into this in practice.
Great article, but I have an issue
@extend
ing classes across different files.I’m using Prepros to compile my Sass project. So I have declare
.btn-primary
in a button.scss file, that’s imported for a main file called project.scss (using@import
). And I want to@extend
that class in a different file called home.scss. Prepros log tell me that:failed to @extend ".btn-primary"
I’m wondering if we can
@extend
selectors across different files with SASS.cheers
Did you ever figure this out? I’m running into a similar issue
I recently started using sass and @extend for my work projects. The work I do for one of our clients requires us to write css for the same predefined html on every project so even if the project looks completely different, it always has the same html for the most part. It’s nice being able to create styling once and then just using @extend on the specific html code blocks to get the look I want. It allows for code reuse in an environment that would normally make it extremely difficult.