Sass's `@content` Directive Use Cases

Christian Reuter

Sass 3.2 added the @content directive, which allows us to pass a content block into a mixin.

@mixin apply-to-ie6-only {
  * html {
    @content
  }
}

@include apply-to-ie6-only {
  #logo {
    background-image: url(/logo.gif);
  }
}

Generates:

* html #logo {
  background-image: url(/logo.gif);
}

We can use it anywhere that declarations need to be wrapped in outer-scoped selectors, and many places where declarations are duplicated.

Media Queries

We can inline our media queries rather than maintaining separate, device-specific stylesheets, but writing queries over and over can get pretty unwieldy. We can simplify them by passing a content block into a mixin that wraps a query.

@mixin media($width) {
  @media only screen and (max-width: $width) {
    @content;
  }
}

@include media(320px) {
  background: red;
}

Generates:

@media only screen and (max-width: 320px) {
  background: red;
}

This becomes especially helpful for long, highly specific media queries, like the HiDPI mixin we use in Bourbon.

You can see our full @media mixin in Neat.

Keyframes

Keyframes are a good example of content duplication. Rather than rewriting the declarations for each vendor-specific selector, we can instead write a mixin to do it for us.

@mixin keyframes($name) {
  @-webkit-keyframes #{$name} {
    @content;
  }

  @-moz-keyframes #{$name} {
    @content;
  }

  @keyframes #{$name} {
    @content;
  }
}

@include keyframes(fadeIn) {
  from {
    opacity: 0%;
  }
  to {
    opacity: 100%;
  }
}

Generates:

@-webkit-keyframes fadeIn {
  from {
    opacity: 0%;
  }
  to {
    opacity: 100%;
  }
}

@-moz-keyframes fadeIn {
  from {
    opacity: 0%;
  }
  to {
    opacity: 100%;
  }
}

@keyframes fadeIn {
  from {
    opacity: 0%;
  }
  to {
    opacity: 100%;
  }
}

This is also used in Bourbon.

Context Specificity

I just picked up a project from Reda Lemeden, who wrote a pair of clever mixins to modify components for a given context.

Instead of creating many .component--modifiers or chaining modifying classes, we can better separate our concerns by defining a style’s context specificity.

@mixin create-context($classes...) {
  @each $class in $classes {
    .#{$class} & {
      @content;
  }
}

@mixin context--alternate-template {
  @include create-context(about, blog) {
    @content
  }
}

.header {
  height: 12em;
  background: red;

  @include context--alternate-template {
    background: green;
  }
}

Generates:

.header {
  height: 12em;
  background: red;
}

.about .header {
  background: green;
}

.blog .header {
  background: green;
}

Getting BEMy

Sass 3.3 adds the @at-root directive and improved &s. The former allows us to nest declarations in Sass, but compile them to the stylesheet’s root. The latter appends any following text directly to the parent’s selector.

These can be used with @content to simplify writing BEM syntax. Thanks to Scott Kellum for the original implementation.

@mixin element($name) {
  @at-root #{&}__#{$name} {
    @content;
  }
}

@mixin modifier($name) {
  @at-root #{&}--#{$name} {
    @content;
  }
}

.block {
  color: red;

  @include element(element) {
    color: green;

    @include modifier(modifier) {
      color: blue;
    }
  }
}

Generates:

.block {
  color: red;
}

.block__element {
  color: green;
}

.block__element--modifier {
  color: blue;
}

In Conclusion

@content is just one of many Sass directives that can empower us to remove duplication in our SCSS, and think more creatively about its organization and implementation. Learn more by reading the Sass directives documentation.