Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Content blocks: @content

Arguments pass single values into a mixin. Sometimes you need to pass a whole block of styles — for example, "these declarations, but only on wide screens". The @content rule marks the place inside a mixin where that block is inserted.

A mixin with @content

The most common example is a breakpoint mixin. Add it to styles/abstracts/_mixins.scss:

// styles/abstracts/_mixins.scss
@mixin tablet {
  @media (min-width: 768px) {
    @content;
  }
}

When you include it, write a block in curly braces after the name. That block replaces @content:

// styles/components/_cards.scss
@use "../abstracts/mixins";
 
.cards {
  display: grid;
  gap: 16px;
 
  @include mixins.tablet {
    grid-template-columns: 1fr 1fr;
  }
}
// styles/main.scss
@use "components/cards";
.cards {
  display: grid;
  gap: 16px;
}
@media (min-width: 768px) {
  .cards {
    grid-template-columns: 1fr 1fr;
  }
}

The media query ended up outside .cards, and the selector was repeated inside it — the same as nested media queries in the lesson about nesting.

A breakpoint as an argument

Separate mixins for each size are repetitive. Pass the width as an argument with a default value:

// styles/abstracts/_mixins.scss
@mixin from($width: 768px) {
  @media (min-width: $width) {
    @content;
  }
}
// styles/components/_cards.scss
@use "../abstracts/mixins";
 
.cards {
  display: grid;
  gap: 16px;
 
  @include mixins.from {
    grid-template-columns: 1fr 1fr;
  }
 
  @include mixins.from(1024px) {
    grid-template-columns: 1fr 1fr 1fr;
  }
}
// styles/main.scss
@use "components/cards";
.cards {
  display: grid;
  gap: 16px;
}
@media (min-width: 768px) {
  .cards {
    grid-template-columns: 1fr 1fr;
  }
}
@media (min-width: 1024px) {
  .cards {
    grid-template-columns: 1fr 1fr 1fr;
  }
}
<style>
  .cards {
    display: grid;
    gap: 16px;
  }
  @media (min-width: 768px) {
    .cards {
      grid-template-columns: 1fr 1fr;
    }
  }
  @media (min-width: 1024px) {
    .cards {
      grid-template-columns: 1fr 1fr 1fr;
    }
  }
</style>
 
<div class="cards">
  <div>Card 1</div>
  <div>Card 2</div>
  <div>Card 3</div>
</div>

Change the width of the preview or the browser window: one column, then two, then three. In the lesson about maps the widths move into one list of named breakpoints.

Where @content can be used

@content is not limited to media queries. Any wrapper fits: a hover state only for devices with a mouse, a dark-theme selector, a @supports check:

// styles/abstracts/_mixins.scss
@mixin on-hover {
  @media (hover: hover) {
    &:hover {
      @content;
    }
  }
}

Inside .btn, @include mixins.on-hover { background: black; } produces .btn:hover wrapped in a media query, so touch screens do not get a "stuck" hover.

Common mistake

If you pass a block to a mixin that has no @content, Sass stops with the error Mixin doesn't accept a content block. The reverse situation is silent: a mixin with @content included without a block simply inserts nothing there. When a breakpoint mixin seems to do nothing, check that the block in braces is really there.

Thanks for reading!

Donate to the site — we work hard for you.