@extend Wrapper a.k.a Mixtend

When extending a selector with the @extend directive, Sass doesn’t take the CSS content from the extended selector to put it in the extending one. It works the other way around. It takes the extending selector and append it to the extended one.

Because of how it works, it makes it impossible to use it from different scopes. For instance, you can’t extend a placeholder that has been declared in a @media block, nor can you extend a placeholder from root if you’re within a @media directive.

Surely we can find a way to use @extend when possible, mixin otherwise. Indeed, it's doable but it's a bit tricky, I call this the mixtend hack. You might want to think twice before implementing everywhere in your project. Perhaps using mixins only would be easier. I'll leave you the judge of that.

Wrapping @extend

The idea is actually quite simple to grasp. First we define the mixin. The only parameter is $extend, which defines whether or not the mixin should try extending rather than including. Obviously, it is a boolean (default to true).

If $extend is true, we extend a placeholder named after the mixin (unfortunately, this is not automagically computed). If it's false, we dump the CSS code as a regular mixin would do.

Out of the mixin, we define the aforementioned placeholder. To avoid repeating the CSS code in the placeholder, we only have to include the mixin by setting $extend to false so it dumps the CSS code in the placeholder’s core.

/// *Mixtend* hack
/// @author Hugo Giraudel
@mixin mixtend-boilerplate($extend: true) {
  @if $extend {
    @extend %mixtend-boilerplate-placeholder;
  } @else {
    // Mixtend content
  }
}

%mixtend-boilerplate-placeholder {
  @include mixtend-boilerplate($extend: false);
}

Example

As a simple example, we will use the micro-clearfix from Nicolas Gallagher.

@mixin clearfix($extend: true) {
  @if $extend {
    @extend %clearfix;
  } @else {
    &:after {
      content: '';
      display: table;
      clear: both;
    }
  }
}

%clearfix {
  @include clearfix($extend: false);
}

Using it is quite straightforward:

.a { @include clearfix; }
.b { @include clearfix; }

@media (min-width: 48em) {
  .c {
    @include clearfix(false);
  }
}

Result CSS:

.a:after, .b:after {
  content: '';
  display: table;
  clear: both;
}

@media (min-width: 48em) {
  .c:after {
    content: '';
    display: table;
    clear: both;
  }
}

Sublime Text Snippet

If you want to save the boilerplate in order to make it highly reusable, you will be pleased to know that it is very easy to create a Sublime Text snippet for this. In Sublime, head to Tools > New snippet... and paste the content below.

Feel free to change the <tabTrigger> key to put whatever floats your boat; it is the word to type before hitting tab to expand the snippet. I went with mixtend.

<snippet>
    <content><![CDATA[
@mixin ${1:mixtend}(\$extend: true) {
  @if $extend {
    @extend %${1:mixtend};
  } @else {
    ${2}
  }
}

%${1:mixtend} {
  @include ${1:mixtend}(\$extend: false);
}
]]></content>
    <tabTrigger>mixtend</tabTrigger>
    <scope>source.scss</scope>
</snippet>

Comments

  1. User Avatar
    Federico Brigante
    Permalink to comment#

    This is awesome, it should be the default behavior.

    For future reference, when this GitHub issue is solved, this mixtend should no longer be necessary:
    https://github.com/sass/sass/issues/1050

  2. User Avatar
    Mike

    I don’t understand it. Why all the “false” setup when it outputs exactly the same?

Submit a Comment

Posting Code

You may write comments in Markdown. This makes code easy to post, as you can write inline code like `<div>this</div>` or multiline blocks of code in triple backtick fences (```) with double new lines before and after.

Code of Conduct

Absolutely anyone is welcome to submit a comment here. But not all comments will be posted. Think of it like writing a letter to the editor. All submitted comments will be read, but not all published. Published comments will be on-topic, helpful, and further the discussion or debate.

Want to tell us something privately?

Feel free to use our contact form. That's a great place to let us know about typos or anything off-topic.

icon-anchoricon-closeicon-emailicon-linkicon-logo-staricon-menuicon-nav-guideicon-searchicon-staricon-tag