Mixins and Reusable Code Patterns
1. Mixin Definition with @mixin Directive
| Concept | Syntax | Example | Notes |
|---|---|---|---|
| Basic Mixin | @mixin name { ... } |
@mixin reset { margin: 0; padding: 0; } |
Reusable style block |
| With Arguments | @mixin name($arg) { ... } |
@mixin size($w) { width: $w; } |
Parameterized styles |
| Multiple Arguments | @mixin name($a, $b) { ... } |
@mixin margin($top, $right) { ... } |
Multiple parameters |
| Default Values | @mixin name($arg: default) { ... } |
@mixin border($w: 1px) { ... } |
Optional parameters |
| Naming Convention | kebab-case or camelCase | @mixin flex-center |
Descriptive names preferred |
| Scope | Can use outer variables | Access global/local variables | Closures supported |
Example: Mixin definitions
// Basic mixin without arguments
@mixin reset {
margin: 0;
padding: 0;
box-sizing: border-box;
}
// Mixin with single argument
@mixin border-radius($radius) {
border-radius: $radius;
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
}
// Mixin with multiple arguments
@mixin box-shadow($x, $y, $blur, $color) {
box-shadow: $x $y $blur $color;
-webkit-box-shadow: $x $y $blur $color;
-moz-box-shadow: $x $y $blur $color;
}
// Mixin with default parameters
@mixin button($bg: blue, $color: white, $padding: 10px 20px) {
background: $bg;
color: $color;
padding: $padding;
border: none;
cursor: pointer;
}
// Mixin accessing outer variables
$base-font-size: 16px;
@mixin responsive-font($multiplier) {
font-size: $base-font-size * $multiplier;
}
// Complex mixin
@mixin flex-center($direction: row) {
display: flex;
justify-content: center;
align-items: center;
flex-direction: $direction;
}
2. Mixin Inclusion with @include Directive
| Usage Pattern | Syntax | Example | Notes |
|---|---|---|---|
| Basic Include | @include mixin-name; |
@include reset; |
No arguments |
| With Arguments | @include name($val); |
@include border-radius(5px); |
Positional arguments |
| Named Arguments | @include name($arg: val); |
@include button($bg: red); |
Explicit parameter names |
| Mixed Arguments | @include name(pos, $named: val); |
Positional then named | Positional must come first |
| Nested Include | Inside selectors or other mixins | .btn { @include button; } |
Most common pattern |
| Top-level Include | Outside selectors | Generates global CSS | Use cautiously |
Example: Mixin inclusion patterns
// Define mixins
@mixin reset {
margin: 0;
padding: 0;
}
@mixin button($bg, $color, $size: medium) {
background: $bg;
color: $color;
padding: if($size == small, 5px 10px,
if($size == medium, 10px 20px, 15px 30px));
}
// Basic inclusion
.box {
@include reset;
width: 100%;
}
// With positional arguments
.primary-btn {
@include button(blue, white);
}
// With named arguments (any order)
.secondary-btn {
@include button($color: black, $bg: gray, $size: large);
}
// Mixed positional and named
.danger-btn {
@include button(red, white, $size: small);
}
// Multiple includes in one selector
.card {
@include reset;
@include border-radius(8px);
@include box-shadow(0, 2px, 4px, rgba(0,0,0,0.1));
}
// Nested within media query
.responsive {
width: 100%;
@media (min-width: 768px) {
@include flex-center(column);
}
}
// Include within mixin (composition)
@mixin fancy-button {
@include button(blue, white);
@include border-radius(20px);
text-transform: uppercase;
}
Note: Named arguments allow any order and improved readability for mixins with many parameters.
3. Mixin Arguments and Default Parameters
| Feature | Syntax | Behavior | Example |
|---|---|---|---|
| Required Argument | @mixin name($arg) |
Must be provided | @mixin size($width) { ... } |
| Default Value | @mixin name($arg: default) |
Optional, uses default if omitted | @mixin btn($bg: blue) { ... } |
| Multiple Defaults | @mixin name($a: 1, $b: 2) |
All optional with defaults | Can override selectively |
| Mixed Required/Optional | @mixin name($req, $opt: val) |
Required first, then optional | Best practice pattern |
| Null as Default | @mixin name($arg: null) |
Can check for user override | Conditional property output |
| Expression Defaults | @mixin name($a: $var * 2) |
Computed default values | Dynamic defaults |
Example: Argument patterns and defaults
// All optional with defaults
@mixin padding($top: 10px, $right: 10px, $bottom: 10px, $left: 10px) {
padding: $top $right $bottom $left;
}
// Usage variations
.box1 { @include padding; } // Uses all defaults
.box2 { @include padding(20px); } // Override first only
.box3 { @include padding($bottom: 30px); } // Named override
// Mixed required and optional
@mixin button($text, $bg: blue, $color: white) {
content: $text;
background: $bg;
color: $color;
}
.btn { @include button('Click Me'); } // Required provided
.btn2 { @include button('Submit', green); } // Override one optional
// Null default for conditional properties
@mixin border($width: 1px, $style: solid, $color: null) {
border-width: $width;
border-style: $style;
@if $color != null {
border-color: $color;
}
}
.element {
@include border; // No color set
}
.element2 {
@include border($color: red); // Color explicitly set
}
// Expression as default
$base-size: 16px;
@mixin font($size: $base-size * 1.5, $weight: normal) {
font-size: $size;
font-weight: $weight;
}
// Complex default with function
@mixin box-size($width, $height: $width) {
width: $width;
height: $height; // Defaults to same as width (square)
}
.square { @include box-size(100px); } // 100px × 100px
.rect { @include box-size(100px, 50px); } // 100px × 50px
// Boolean defaults
@mixin text($uppercase: false, $bold: false) {
@if $uppercase {
text-transform: uppercase;
}
@if $bold {
font-weight: bold;
}
}
.normal { @include text; }
.shouting { @include text($uppercase: true, $bold: true); }
4. Variadic Arguments (...) and @rest
| Feature | Syntax | Description | Use Case |
|---|---|---|---|
| Variadic Parameter | @mixin name($args...) |
Accepts any number of arguments | Flexible argument count |
| Rest After Required | @mixin name($req, $rest...) |
Required + variable args | At least one argument |
| Spread List | @include name($list...) |
Unpack list as arguments | Pass list items individually |
| Spread Map | @include name($map...) |
Unpack map as named args | Pass map as named parameters |
| Access Variadic | nth($args, $n) |
Get specific argument | Index into variadic list |
| Keyword Arguments | keywords($args) |
Get named args as map | Advanced meta-programming |
Example: Variadic arguments and rest parameters
// Accept any number of arguments
@mixin box-shadow($shadows...) {
box-shadow: $shadows;
-webkit-box-shadow: $shadows;
}
// Usage with multiple shadows
.card {
@include box-shadow(
0 2px 4px rgba(0,0,0,0.1),
0 4px 8px rgba(0,0,0,0.1),
0 8px 16px rgba(0,0,0,0.1)
);
}
// Required argument + rest
@mixin transition($property, $rest...) {
transition: $property $rest;
}
.button {
@include transition(background, 0.3s, ease-in-out);
// → transition: background 0.3s ease-in-out;
}
// Spread list into arguments
@mixin margin($top, $right, $bottom, $left) {
margin: $top $right $bottom $left;
}
$spacing: 10px 20px 10px 20px;
.box {
@include margin($spacing...);
// Unpacks list: margin(10px, 20px, 10px, 20px)
}
// Spread map as named arguments
@mixin button($bg, $color, $padding) {
background: $bg;
color: $color;
padding: $padding;
}
$btn-config: (
bg: blue,
color: white,
padding: 10px 20px
);
.btn {
@include button($btn-config...);
// Maps to named parameters
}
// Iterate over variadic arguments
@mixin generate-classes($prefix, $values...) {
@each $value in $values {
.#{$prefix}-#{$value} {
#{$prefix}: $value;
}
}
}
@include generate-classes(color, red, green, blue);
// Generates: .color-red, .color-green, .color-blue
// Combined regular and variadic
@mixin flex($direction, $rest...) {
display: flex;
flex-direction: $direction;
@if length($rest) > 0 {
justify-content: nth($rest, 1);
}
@if length($rest) > 1 {
align-items: nth($rest, 2);
}
}
.container {
@include flex(row, center, center);
}
// Advanced: keyword arguments
@mixin advanced($args...) {
$named: keywords($args);
// $named is a map of named arguments
@if map-has-key($named, color) {
color: map-get($named, color);
}
}
Note: The
... syntax can collect arguments (in
definition) or spread them (in inclusion).
5. Content Blocks and @content Directive
| Concept | Syntax | Description | Use Case |
|---|---|---|---|
| @content Directive | @mixin name { ... @content } |
Inject caller's content block | Wrapper mixins |
| Pass Content | @include name { content } |
Provide styles to inject | Custom styles within mixin |
| Multiple @content | Multiple @content in mixin | Inject same content multiple times | Repetitive patterns |
| Content Arguments NEW | @content($arg) |
Pass values to content block | Advanced meta-programming |
| Using Passed Values | @include name using ($var) |
Receive arguments in content | Dynamic content generation |
| Empty Content | @content without passed block | Nothing injected (no error) | Optional content blocks |
Example: @content directive patterns
// Basic content injection
@mixin media-query($breakpoint) {
@media (min-width: $breakpoint) {
@content;
}
}
// Usage
.sidebar {
width: 100%;
@include media-query(768px) {
width: 300px;
float: left;
}
}
// Output:
// .sidebar { width: 100%; }
// @media (min-width: 768px) {
// .sidebar { width: 300px; float: left; }
// }
// Wrapper mixin with @content
@mixin hover-focus {
&:hover,
&:focus {
@content;
}
}
.button {
background: blue;
@include hover-focus {
background: darkblue;
transform: scale(1.05);
}
}
// Multiple @content (same content injected twice)
@mixin vendor-prefix {
-webkit-@content;
-moz-@content;
@content;
}
// Advanced: Content with arguments (Sass 3.5+)
@mixin context($name) {
.#{$name} {
@content($name);
}
}
@include context('header') using ($ctx) {
background: #{$ctx}-color; // header-color
}
// Media query mixin library
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
);
@mixin respond-to($breakpoint) {
$value: map-get($breakpoints, $breakpoint);
@media (min-width: $value) {
@content;
}
}
.container {
padding: 1rem;
@include respond-to(md) {
padding: 2rem;
}
@include respond-to(lg) {
padding: 3rem;
}
}
// Pseudo-selector wrapper
@mixin pseudo($pseudo) {
&:#{$pseudo} {
@content;
}
}
a {
color: blue;
@include pseudo(hover) {
color: red;
}
@include pseudo(visited) {
color: purple;
}
}
// Context-based styling
@mixin when-inside($selector) {
#{$selector} & {
@content;
}
}
.button {
background: white;
@include when-inside('.dark-theme') {
background: black;
color: white;
}
}
// Output: .dark-theme .button { background: black; color: white; }
// Keyframe wrapper
@mixin keyframes($name) {
@keyframes #{$name} {
@content;
}
}
@include keyframes(fade-in) {
from { opacity: 0; }
to { opacity: 1; }
}
6. Dynamic Mixin Generation and Library Patterns
| Pattern | Technique | Description | Example Use |
|---|---|---|---|
| Loop-generated Mixins | @each with mixin calls | Generate utilities from data | Spacing, color utilities |
| Conditional Mixins | @if inside mixin | Behavior based on arguments | Responsive variations |
| Mixin Composition | Mixins calling mixins | Build complex from simple | Component libraries |
| Configuration Maps | Map-driven mixin logic | Data-driven styling | Theme systems |
| Mixin Libraries | Reusable mixin collections | Shareable across projects | Framework development |
| Meta Mixins | Mixins generating CSS rules | Advanced code generation | BEM, atomic CSS |
Example: Dynamic mixin patterns
// Utility generator mixin
@mixin generate-spacing-utilities($property, $sides, $sizes) {
@each $side-key, $side-value in $sides {
@each $size-key, $size-value in $sizes {
.#{$property}#{$side-key}-#{$size-key} {
#{$property}-#{$side-value}: $size-value;
}
}
}
}
// Usage
$sides: (t: top, r: right, b: bottom, l: left);
$sizes: (0: 0, 1: 0.25rem, 2: 0.5rem, 3: 1rem);
@include generate-spacing-utilities(margin, $sides, $sizes);
@include generate-spacing-utilities(padding, $sides, $sizes);
// Generates: mt-0, mt-1, mr-0, pt-0, etc.
// Conditional responsive mixin
@mixin responsive-font($min, $max, $min-vw: 320px, $max-vw: 1200px) {
font-size: $min;
@media (min-width: $min-vw) {
font-size: calc(#{$min} + (#{$max} - #{$min}) *
((100vw - #{$min-vw}) / (#{$max-vw} - #{$min-vw})));
}
@media (min-width: $max-vw) {
font-size: $max;
}
}
// Mixin composition (building blocks)
@mixin reset {
margin: 0;
padding: 0;
box-sizing: border-box;
}
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
@mixin card-base {
@include reset;
border: 1px solid #ddd;
border-radius: 4px;
padding: 1rem;
}
@mixin card-hoverable {
@include card-base;
transition: transform 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
}
// Map-driven button system
$button-variants: (
primary: (bg: #007bff, color: white, hover-bg: #0056b3),
secondary: (bg: #6c757d, color: white, hover-bg: #545b62),
success: (bg: #28a745, color: white, hover-bg: #1e7e34),
danger: (bg: #dc3545, color: white, hover-bg: #bd2130)
);
@mixin button-variant($variant) {
$config: map-get($button-variants, $variant);
background: map-get($config, bg);
color: map-get($config, color);
border: none;
padding: 10px 20px;
cursor: pointer;
&:hover {
background: map-get($config, hover-bg);
}
}
// Generate all button variants
@each $name, $config in $button-variants {
.btn-#{$name} {
@include button-variant($name);
}
}
// BEM generator mixin
@mixin bem-block($block) {
.#{$block} {
@content;
}
}
@mixin bem-element($element) {
&__#{$element} {
@content;
}
}
@mixin bem-modifier($modifier) {
&--#{$modifier} {
@content;
}
}
// Usage
@include bem-block('card') {
border: 1px solid gray;
@include bem-element('header') {
font-weight: bold;
}
@include bem-element('body') {
padding: 1rem;
}
@include bem-modifier('featured') {
border-color: gold;
}
}
// Advanced: Breakpoint mixin library
$breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
);
@mixin media-up($name) {
$min: map-get($breakpoints, $name);
@media (min-width: $min) {
@content;
}
}
@mixin media-down($name) {
$max: map-get($breakpoints, $name) - 1px;
@media (max-width: $max) {
@content;
}
}
@mixin media-between($lower, $upper) {
$min: map-get($breakpoints, $lower);
$max: map-get($breakpoints, $upper) - 1px;
@media (min-width: $min) and (max-width: $max) {
@content;
}
}
Mixin Best Practices
- Use mixins for reusable patterns, not single properties
- Prefer
@contentfor wrapper patterns (media queries, pseudo-selectors) - Use default parameters for common use cases
- Name mixins descriptively based on purpose, not implementation
- Combine simple mixins into complex ones (composition)
- Use maps and loops to generate utility classes from data
- Document mixin parameters and expected usage
- Keep mixins focused on single responsibility
Warning: Overusing mixins can lead to CSS bloat. Each @include
duplicates styles. Consider @extend or utility classes for shared styles.