@extend Wrapper aka Mixtend

Avatar of Kitty Giraudel
Kitty Giraudel on (Updated on )

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 Kitty 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 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>