Chepter 7: Sass @mixin
1. What Are @mixin and @include? (Super Simple First)
- @mixin = Define a reusable block of styles (the “recipe”).
- @include = Paste/use that block wherever you want (the “cook & serve”).
Mixins let you:
- Avoid repeating the same CSS 20 times
- Create parameterized styles (like functions)
- Add logic, loops, conditionals inside reusable chunks
- Inject custom styles from the caller (via @content)
This is not possible (or very clumsy) with native CSS custom properties or nesting alone in 2026.
2. Basic Syntax – No Arguments First
Define (usually in _mixins.scss or _utils.scss)
|
0 1 2 3 4 5 6 7 8 9 10 |
@mixin reset-list { margin: 0; padding: 0; list-style: none; } |
Use (in any file after @use ‘mixins’;)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
nav ul { @include reset-list; } .social-links { @include reset-list; display: flex; gap: 1rem; } |
→ Compiles to normal CSS without duplication.
3. Mixins with Arguments (The Real Power)
Arguments make mixins like mini-functions.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Positional arguments (order matters) @mixin border-radius($radius) { border-radius: $radius; -webkit-border-radius: $radius; // if you still need old prefixes in 2026 } // Optional default value @mixin button-style($bg-color, $text-color: white, $padding: 0.8rem 1.6rem) { background: $bg-color; color: $text-color; padding: $padding; border: none; border-radius: 6px; cursor: pointer; font-weight: 600; transition: background 0.2s; &:hover { background: darken($bg-color, 12%); } } |
Using them (keyword args = very readable!)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
.primary-btn { @include button-style(#6c5ce7); // only required arg } .danger-btn { @include button-style($bg-color: #e74c3c, $text-color: white); } .large-outline { @include button-style( $bg-color: transparent, $text-color: #6c5ce7, $padding: 1rem 2rem ); border: 2px solid #6c5ce7; &:hover { background: #6c5ce7; color: white; } } |
Keyword arguments tip (2026 best practice): Always use keywords when >2–3 args or when skipping optionals — makes code self-documenting.
4. Argument Lists (…) – Flexible & Advanced
Capture any number of extra args.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
@mixin box-shadows($shadows...) { box-shadow: $shadows; } .card { @include box-shadows(0 4px 12px rgba(black, 0.08), inset 0 1px 0 white); } |
Or combine with fixed args:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@mixin flex($direction: row, $items...) { display: flex; flex-direction: $direction; @each $item in $items { > #{$item} { flex: 1; } } } .gallery { @include flex(column, .item1, .item2, .item3); } |
5. The Famous @content Block – Pass Custom Styles In
This is where mixins become magical — let the caller inject their own rules.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
@mixin hover { &:not([disabled]):hover, &:not([disabled]):focus { @content; // ← injects whatever is inside {} } } @mixin card-hover { transition: transform 0.25s ease, box-shadow 0.25s ease; @include hover { transform: translateY(-6px); box-shadow: 0 12px 35px rgba(black, 0.15); } } .card { @include card-hover; // You can add more inside the include if needed @include hover { border-color: #6c5ce7; } } |
Modern twist (Dart Sass ≥ 1.15.0 – fully supported 2026): Pass arguments to the @content block!
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@mixin respond-to($breakpoints...) { @each $bp in $breakpoints { @media (min-width: map-get($breakpoints-map, $bp)) { @content($bp); // pass the current breakpoint name } } } $breakpoints-map: (sm: 576px, md: 768px, lg: 992px); .hero { font-size: 2rem; @include respond-to(md, lg) using ($bp) { font-size: if($bp == md, 2.8rem, 3.5rem); } } |
6. Real-World Example – Button Family Mixin (Very Common Pattern)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
// _mixins.scss @use 'sass:color'; // modern way to access built-ins @mixin button($variant: primary, $size: md) { // Base styles display: inline-flex; align-items: center; justify-content: center; font-weight: 600; border: none; cursor: pointer; transition: all 0.2s; // Size modifier @if $size == sm { padding: 0.5rem 1rem; font-size: 0.875rem; } @else if $size == lg { padding: 1rem 2rem; font-size: 1.125rem; } @else { padding: 0.75rem 1.5rem; font-size: 1rem; } // Variant @if $variant == primary { background: #6c5ce7; color: white; &:hover { background: color.adjust(#6c5ce7, $lightness: -10%); } } @else if $variant == outline { background: transparent; border: 2px solid #6c5ce7; color: #6c5ce7; &:hover { background: #6c5ce7; color: white; } } @else if $variant == ghost { background: transparent; color: #6c5ce7; &:hover { background: rgba(#6c5ce7, 0.1); } } // Disabled state &[disabled] { opacity: 0.6; cursor: not-allowed; } } |
Usage:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@use 'mixins' as m; .btn-primary { @include m.button(primary); } .btn-outline-lg { @include m.button(outline, lg); } |
7. 2026 Best Practices & Gotchas
- Put mixins in partials: _mixins.scss, _helpers.scss
- Use @use ‘mixins’; (never old @import)
- Prefer keyword arguments for clarity
- Keep mixins focused — one concern per mixin (single responsibility)
- Use @content sparingly — only when caller needs to inject styles
- Avoid deep nesting inside mixins unless necessary
- For deprecation: wrap old mixins and @warn (official pattern)
- Watch out for mixed declarations breaking change (Dart Sass 1.77+): Don’t mix declarations after @include in the same block without wrapping in & { … } — or you’ll get warnings.
Example fix:
|
0 1 2 3 4 5 6 7 8 9 |
.foo { @include some-mixin(); // if mixin has nested rules background: white; // ← this triggers warning in some cases } |
→ Better:
|
0 1 2 3 4 5 6 7 8 9 10 11 |
.foo { @include some-mixin(); & { background: white; } } |
You’ve now got @mixin + @include mastered — this is where Sass really starts feeling like programming!
Next — want to combine mixins with maps for theme systems? Or build a full responsive typography mixin family? Or see how to handle vendor prefixes / fallback in modern Sass?
Just tell me — class is rolling! 🚀
