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-hiddenmixin for screen-reader-only text and include it twice. - Inspect compiled CSS and confirm the function inlined values while the mixin duplicated rules.

