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; }
}

Example: Without @content

@mixin reset {
  margin: 0;
  padding: 0;
}

.box {
  @include reset;
  // Just gets mixin styles
}

Example: With @content

@mixin container {
  max-width: 1200px;
  @content;
}

.main {
  @include container {
    padding: 20px;
  }
}

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 @content for 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.