Episode 1 — Fundamentals / 1.7 — Working With SASS

1.7.e — Mixins

In one sentence: A mixin is a reusable block of CSS declarations you define once with @mixin and stamp into any rule set with @include — like a function that outputs CSS instead of returning a value.

Navigation: ← 1.7.d — Partials & Imports · 1.7.f — Inheritance & Extends →


1. Basic syntax

// Define
@mixin flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

// Use
.hero {
  @include flex-center;
  height: 100vh;
}

Compiles to:

.hero {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

The mixin's declarations are copied into every @include site. This is different from @extend, which groups selectors (see 1.7.f).


2. Mixins with arguments

@mixin button($bg, $color: white) {
  background: $bg;
  color: $color;
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 0.25rem;
  cursor: pointer;
}

.btn-primary {
  @include button(#3b82f6);
}

.btn-danger {
  @include button(#ef4444, #fff);
}
FeatureSyntax
Required argument$bg — must be passed
Default value$color: white — used if not passed
Named arguments@include button($bg: red, $color: black) — order-independent

3. @content — passing blocks

@content lets you inject arbitrary CSS into the middle of a mixin. This is how responsive breakpoint mixins work:

@mixin respond-to($breakpoint) {
  @if $breakpoint == 'md' {
    @media (min-width: 768px) { @content; }
  } @else if $breakpoint == 'lg' {
    @media (min-width: 1024px) { @content; }
  } @else if $breakpoint == 'xl' {
    @media (min-width: 1280px) { @content; }
  }
}

.sidebar {
  width: 100%;

  @include respond-to('md') {
    width: 250px;
  }

  @include respond-to('lg') {
    width: 300px;
  }
}

Compiles to:

.sidebar { width: 100%; }
@media (min-width: 768px) { .sidebar { width: 250px; } }
@media (min-width: 1024px) { .sidebar { width: 300px; } }

4. Common real-world mixins

Responsive breakpoints (map-driven)

$breakpoints: (
  'sm':  640px,
  'md':  768px,
  'lg':  1024px,
  'xl':  1280px,
);

@mixin breakpoint($name) {
  $value: map-get($breakpoints, $name);
  @media (min-width: $value) {
    @content;
  }
}

Typography shorthand

@mixin text($size, $weight: 400, $line-height: 1.5) {
  font-size: $size;
  font-weight: $weight;
  line-height: $line-height;
}

h1 { @include text(2.5rem, 700, 1.2); }

Truncate text

@mixin truncate($lines: 1) {
  @if $lines == 1 {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  } @else {
    display: -webkit-box;
    -webkit-line-clamp: $lines;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

.card__title { @include truncate(2); }

Visually hidden (accessible hide)

@mixin visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.sr-only { @include visually-hidden; }

5. Mixins vs functions

@mixin@function
PurposeOutput CSS declarationsCompute and return a value
Used with@includeInline in a property value
ReturnsNothing (emits CSS)A value via @return
Example@include flex-center;width: rem(16);

Rule of thumb: If you need declarations (property: value pairs), use a mixin. If you need a computed value, use a function.


6. Mixins vs extends

@mixin + @include@extend
OutputDeclarations copied to each include siteSelectors grouped — shared rule
ArgumentsYesNo
@content blocksYesNo
In media queriesWorks everywhereCannot extend across media query boundaries
Output sizeCan be larger (duplicated declarations)Smaller when many selectors share rules

Use mixins by default. Reach for @extend only when you have many selectors sharing identical static rules and you understand the pitfalls.


7. Organizing mixins with @use

// abstracts/_mixins.scss
@mixin flex-center { … }
@mixin breakpoint($name) { … }
@mixin truncate($lines: 1) { … }

// components/_cards.scss
@use '../abstracts/mixins';

.card {
  @include mixins.flex-center;
  @include mixins.truncate(2);
}

Namespacing prevents collisions — you always know where a mixin came from.


8. Key takeaways

  1. @mixin defines a reusable block of CSS; @include stamps it into a rule.
  2. Mixins accept arguments with optional default values and support named arguments.
  3. @content lets you pass arbitrary CSS blocks into a mixin — the foundation of responsive breakpoint patterns.
  4. Use mixins for CSS declarations, functions for computed values.
  5. Prefer mixins over @extend in most cases — they are more flexible and predictable.

Explain-It Challenge

Explain without notes:

  1. How does @content work inside a mixin — give a breakpoint example.
  2. Why would you choose a mixin over @extend for a button variant?
  3. Write a mixin from memory that accepts a background color and optional text color with a default.

Navigation: ← 1.7.d — Partials & Imports · 1.7.f — Inheritance & Extends →