Codeskill

Learn to code, step by step

Mixins vs functions (rules of thumb)

Let’s clarify when to reach for a mixin and when a function is the better tool. Both reduce repetition, but they compile differently and solve different problems.

Mixins output CSS

A mixin is a reusable chunk of declarations. Each @include copies those rules into the selector where you call it:

@mixin focus-ring {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

.button {
  @include focus-ring;
}

a:focus-visible {
  @include focus-ring;
}

Compiled CSS contains two separate rule blocks with the same declarations. That is fine for a few lines. It bloats output if the mixin is large and included everywhere.

Functions return values

Functions compute and return a single value (colour, length, string). They do not emit CSS by themselves:

@function rem($px, $base: 16) {
  @return calc($px / $base) * 1rem;
}

.sidebar {
  width: rem(280);
  padding: rem(12) rem(16);
}

Use functions for maths, unit conversion, map lookups, and colour manipulation. The compiled CSS shows plain values:

.sidebar {
  width: 17.5rem;
  padding: 0.75rem 1rem;
}

Rules of thumb

  • Need CSS rules? Mixin (or a placeholder + extend – use sparingly).
  • Need a calculated value? Function.
  • Media queries? Mixin (functions cannot wrap blocks).
  • Colour from a map? Function that returns a colour.
  • Same three declarations in two places? Mixin is fine. In twenty places? Consider a utility class or a CSS custom property instead.

Mixin parameters with defaults

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

.card-title {
  @include truncate;
}

.excerpt {
  @include truncate(3);
}

Default arguments keep call sites readable. Avoid ten optional parameters – that usually means the mixin is doing too much.

When mixins hurt

Mixins that only wrap a single property are often better as variables or functions:

// Avoid
@mixin color-brand {
  color: $color-brand;
}

// Prefer
color: $color-brand;
// or
color: brand('primary');

Also watch nested mixins that include other mixins – the compiled CSS can explode. We cover output size again in the specificity tutorial.

Try it

  • Write a rem() function and use it for padding and font sizes in one component.
  • Write a visually-hidden mixin for screen-reader-only text and include it twice.
  • Inspect compiled CSS and confirm the function inlined values while the mixin duplicated rules.
PreviousToken systems (colour, space, type scales)