Advanced Mixin Patterns
Advanced Mixin Patterns
Now that you understand the basics of mixins, let's explore advanced patterns that will take your SASS skills to the next level. These patterns are used in production codebases and design systems to create powerful, flexible, and maintainable styling solutions.
Mixins with @content for Responsive Breakpoints
One of the most powerful uses of @content is creating a responsive breakpoint system. This pattern allows you to write media queries inline with your component styles, making your code more maintainable and readable.
Basic Breakpoint Mixin
// Define breakpoints
$breakpoints: (
'mobile': 480px,
'tablet': 768px,
'desktop': 1024px,
'wide': 1200px
);
@mixin respond-to($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
} @else {
@warn "Unknown breakpoint: #{$breakpoint}";
}
}
// Usage
.container {
width: 100%;
padding: 15px;
@include respond-to(tablet) {
width: 750px;
padding: 20px;
}
@include respond-to(desktop) {
width: 970px;
}
@include respond-to(wide) {
width: 1170px;
}
}
// Compiled CSS
.container {
width: 100%;
padding: 15px;
}
@media (min-width: 768px) {
.container {
width: 750px;
padding: 20px;
}
}
@media (min-width: 1024px) {
.container {
width: 970px;
}
}
@media (min-width: 1200px) {
.container {
width: 1170px;
}
}
Building a Comprehensive Responsive Breakpoint System
A production-ready breakpoint system should handle both min-width and max-width queries, as well as range queries and custom media queries.
Advanced Breakpoint System
// Breakpoint definitions
$breakpoints: (
'xs': 0,
'sm': 576px,
'md': 768px,
'lg': 992px,
'xl': 1200px,
'xxl': 1400px
);
// Media query mixin with multiple strategies
@mixin media($breakpoint, $type: 'min') {
@if $type == 'min' {
// Mobile-first: min-width
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
}
@else if $type == 'max' {
// Desktop-first: max-width
@media (max-width: map-get($breakpoints, $breakpoint) - 1px) {
@content;
}
}
@else if $type == 'only' {
// Only this breakpoint range
$next-breakpoint: null;
$breakpoint-keys: map-keys($breakpoints);
$breakpoint-index: index($breakpoint-keys, $breakpoint);
@if $breakpoint-index < length($breakpoint-keys) {
$next-breakpoint: nth($breakpoint-keys, $breakpoint-index + 1);
}
@if $next-breakpoint {
@media (min-width: map-get($breakpoints, $breakpoint)) and (max-width: map-get($breakpoints, $next-breakpoint) - 1px) {
@content;
}
} @else {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
}
}
}
// Usage examples
.element {
// Default mobile styles
font-size: 14px;
// Tablet and up
@include media(md) {
font-size: 16px;
}
// Large desktop and up
@include media(xl) {
font-size: 18px;
}
// Only on tablets (md to lg range)
@include media(md, only) {
background: lightblue;
}
// Mobile only (max-width approach)
@include media(sm, max) {
display: none;
}
}
Conditional Logic in Mixins
Mixins can use @if, @else if, and @else statements to generate different output based on arguments. This allows you to create intelligent, adaptive mixins.
Conditional Mixin Example
@mixin theme-colors($theme) {
@if $theme == 'light' {
background-color: white;
color: #333;
border-color: #ddd;
}
@else if $theme == 'dark' {
background-color: #333;
color: white;
border-color: #666;
}
@else if $theme == 'blue' {
background-color: #3498db;
color: white;
border-color: #2980b9;
}
@else {
@warn "Unknown theme: #{$theme}. Defaulting to light theme.";
background-color: white;
color: #333;
border-color: #ddd;
}
}
// Usage
.card-light {
@include theme-colors(light);
}
.card-dark {
@include theme-colors(dark);
}
.card-blue {
@include theme-colors(blue);
}
Complex Conditional Logic
@mixin button-style($size: medium, $variant: primary, $outline: false) {
// Base styles
display: inline-block;
border: none;
border-radius: 4px;
cursor: pointer;
text-align: center;
transition: all 0.3s ease;
// Size variants
@if $size == small {
padding: 5px 10px;
font-size: 12px;
}
@else if $size == medium {
padding: 10px 20px;
font-size: 14px;
}
@else if $size == large {
padding: 15px 30px;
font-size: 18px;
}
// Color variants
$bg-color: null;
$text-color: white;
@if $variant == primary {
$bg-color: #3498db;
}
@else if $variant == success {
$bg-color: #2ecc71;
}
@else if $variant == danger {
$bg-color: #e74c3c;
}
@else if $variant == warning {
$bg-color: #f39c12;
}
// Outline vs filled
@if $outline {
background: transparent;
color: $bg-color;
border: 2px solid $bg-color;
&:hover {
background: $bg-color;
color: white;
}
}
@else {
background: $bg-color;
color: $text-color;
&:hover {
background: darken($bg-color, 10%);
}
}
}
// Usage
.btn-small-primary {
@include button-style(small, primary);
}
.btn-large-danger-outline {
@include button-style(large, danger, true);
}
Mixins That Generate Multiple Selectors
Advanced mixins can generate multiple CSS selectors and rules, creating entire component systems with a single include.
Generating Selector Variants
@mixin generate-spacing-utilities($property, $prefix, $sizes) {
@each $name, $value in $sizes {
.#{$prefix}-#{$name} {
#{$property}: $value;
}
// Also generate responsive variants
@each $breakpoint-name, $breakpoint-value in $breakpoints {
@media (min-width: $breakpoint-value) {
.#{$prefix}-#{$breakpoint-name}-#{$name} {
#{$property}: $value;
}
}
}
}
}
// Define spacing scale
$spacing-scale: (
'0': 0,
'1': 0.25rem,
'2': 0.5rem,
'3': 0.75rem,
'4': 1rem,
'5': 1.5rem,
'6': 2rem,
'8': 3rem,
'10': 4rem
);
// Generate margin utilities
@include generate-spacing-utilities(margin, 'm', $spacing-scale);
// Generate padding utilities
@include generate-spacing-utilities(padding, 'p', $spacing-scale);
// This generates classes like:
// .m-0, .m-1, .m-2, etc.
// .m-md-0, .m-md-1, etc. (responsive variants)
// .p-0, .p-1, .p-2, etc.
// .p-md-0, .p-md-1, etc.
Component Generator Mixin
@mixin generate-color-variants($component, $colors) {
.#{$component} {
// Base component styles
padding: 15px;
border-radius: 4px;
margin-bottom: 10px;
@each $name, $color in $colors {
&--#{$name} {
background-color: lighten($color, 40%);
border-left: 4px solid $color;
color: darken($color, 20%);
.#{$component}__title {
color: $color;
font-weight: bold;
}
.#{$component}__icon {
color: $color;
}
}
}
}
}
// Define color palette
$alert-colors: (
'info': #3498db,
'success': #2ecc71,
'warning': #f39c12,
'danger': #e74c3c
);
// Generate alert component variants
@include generate-color-variants('alert', $alert-colors);
// This generates:
// .alert--info, .alert--success, .alert--warning, .alert--danger
// with nested .alert__title and .alert__icon variants
Mixin Libraries and Frameworks
Many popular CSS frameworks and libraries are built using SASS mixins. Understanding these can help you build your own reusable component libraries.
- Bourbon: A lightweight SASS toolkit with useful mixins
- Compass: Comprehensive SASS framework (now deprecated)
- Bootstrap: Uses extensive mixin systems for its components
- Foundation: Provides powerful mixins for responsive design
- Susy: Grid system built entirely with mixins
Example: Bootstrap-Style Grid Mixin
@mixin make-container($padding: 15px) {
width: 100%;
padding-right: $padding;
padding-left: $padding;
margin-right: auto;
margin-left: auto;
}
@mixin make-row($gutter: 30px) {
display: flex;
flex-wrap: wrap;
margin-right: -($gutter / 2);
margin-left: -($gutter / 2);
}
@mixin make-col($size, $columns: 12, $gutter: 30px) {
flex: 0 0 percentage($size / $columns);
max-width: percentage($size / $columns);
padding-right: $gutter / 2;
padding-left: $gutter / 2;
}
// Usage
.container {
@include make-container;
}
.row {
@include make-row;
}
.col-6 {
@include make-col(6);
}
.col-4 {
@include make-col(4);
}
.col-3 {
@include make-col(3);
}
Mixins vs @extend vs Functions
Understanding when to use mixins, @extend, or functions is crucial for writing efficient SASS. Each has its strengths and appropriate use cases.
Comparison Table
// MIXINS - Copy styles to each selector
@mixin button-base {
padding: 10px 20px;
border-radius: 4px;
}
.button-1 { @include button-base; }
.button-2 { @include button-base; }
// Compiled (duplicated styles):
.button-1 {
padding: 10px 20px;
border-radius: 4px;
}
.button-2 {
padding: 10px 20px;
border-radius: 4px;
}
// @EXTEND - Groups selectors together
%button-base {
padding: 10px 20px;
border-radius: 4px;
}
.button-1 { @extend %button-base; }
.button-2 { @extend %button-base; }
// Compiled (grouped selectors):
.button-1, .button-2 {
padding: 10px 20px;
border-radius: 4px;
}
// FUNCTIONS - Return values
@function calculate-rem($px) {
@return $px / 16 * 1rem;
}
.element {
font-size: calculate-rem(18); // 1.125rem
}
- Mixins: When you need to pass arguments or include dynamic styles. Use for most reusable patterns.
- @extend: When you have identical styles shared by multiple selectors. Use sparingly as it can create complex selector chains.
- Functions: When you need to calculate or return a value, not generate styles. Use for math, color manipulation, etc.
Building a Typography Mixin System
Typography is a perfect use case for a comprehensive mixin system. Let's build a production-ready typography system.
Complete Typography System
// Typography configuration
$font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
$font-family-heading: Georgia, "Times New Roman", serif;
$font-family-mono: "Courier New", Courier, monospace;
$font-weights: (
'light': 300,
'normal': 400,
'medium': 500,
'semibold': 600,
'bold': 700
);
$type-scale: (
'xs': 0.75rem, // 12px
'sm': 0.875rem, // 14px
'base': 1rem, // 16px
'lg': 1.125rem, // 18px
'xl': 1.25rem, // 20px
'2xl': 1.5rem, // 24px
'3xl': 1.875rem, // 30px
'4xl': 2.25rem, // 36px
'5xl': 3rem // 48px
);
// Base typography mixin
@mixin typography-base {
font-family: $font-family-base;
line-height: 1.6;
color: #333;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
// Heading mixin
@mixin heading($level: 1) {
font-family: $font-family-heading;
font-weight: map-get($font-weights, 'bold');
line-height: 1.2;
margin-top: 0;
margin-bottom: 0.5em;
@if $level == 1 {
font-size: map-get($type-scale, '5xl');
@include media(md) {
font-size: map-get($type-scale, '4xl');
}
}
@else if $level == 2 {
font-size: map-get($type-scale, '4xl');
@include media(md) {
font-size: map-get($type-scale, '3xl');
}
}
@else if $level == 3 {
font-size: map-get($type-scale, '3xl');
}
@else if $level == 4 {
font-size: map-get($type-scale, '2xl');
}
@else if $level == 5 {
font-size: map-get($type-scale, 'xl');
}
@else if $level == 6 {
font-size: map-get($type-scale, 'lg');
}
}
// Text style mixin
@mixin text-style($size: 'base', $weight: 'normal', $line-height: null) {
font-size: map-get($type-scale, $size);
font-weight: map-get($font-weights, $weight);
@if $line-height {
line-height: $line-height;
}
}
// Responsive text mixin
@mixin fluid-type($min-size, $max-size, $min-width: 320px, $max-width: 1200px) {
font-size: $min-size;
@media (min-width: $min-width) {
font-size: calc(#{$min-size} + (#{strip-unit($max-size)} - #{strip-unit($min-size)}) * ((100vw - #{$min-width}) / #{strip-unit($max-width - $min-width)}));
}
@media (min-width: $max-width) {
font-size: $max-size;
}
}
// Helper function
@function strip-unit($number) {
@return $number / ($number * 0 + 1);
}
// Usage
body {
@include typography-base;
}
h1 { @include heading(1); }
h2 { @include heading(2); }
h3 { @include heading(3); }
.text-small {
@include text-style('sm', 'normal');
}
.text-large-bold {
@include text-style('xl', 'bold');
}
.hero-title {
@include fluid-type(2rem, 4rem);
}
Performance Considerations with Mixins
While mixins are powerful, they can impact the size of your compiled CSS if not used carefully. Understanding performance implications helps you make better decisions.
- Mixin Duplication: Each @include copies the entire mixin's CSS, which can bloat your file size
- Deep Nesting: Mixins with nested selectors can create specificity issues
- Complex Calculations: Heavy computation in mixins can slow compilation
- Media Query Duplication: Each responsive mixin creates separate @media blocks
Good vs Bad Practices
// BAD - Will duplicate all styles 10 times
@mixin heavy-component {
padding: 20px;
margin: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
// ... 50 more lines of styles
}
.item-1 { @include heavy-component; }
.item-2 { @include heavy-component; }
// ... item-3 through item-10
// Result: Massive CSS file with duplicated styles
// GOOD - Use @extend or classes for identical styles
%component-base {
padding: 20px;
margin: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.item-1 { @extend %component-base; }
.item-2 { @extend %component-base; }
// Result: Grouped selectors, smaller file
// BEST - Use mixins for parameterized variations
@mixin component-variant($bg-color) {
background: $bg-color;
border-color: darken($bg-color, 10%);
}
.item-1 {
@extend %component-base;
@include component-variant(blue);
}
.item-2 {
@extend %component-base;
@include component-variant(green);
}
- Use @extend for identical styles shared across selectors
- Use mixins when you need parameters or dynamic styles
- Keep mixins focused and single-purpose
- Consider using utility classes instead of mixins for simple, common patterns
- Use CSS custom properties (variables) for values that change frequently
Exercise 1: Build a Complete Breakpoint System
Create a comprehensive responsive breakpoint mixin system:
- Define breakpoints for xs, sm, md, lg, xl, xxl
- Create mixins for: up($size), down($size), between($size1, $size2), only($size)
- Add support for custom media queries (print, screen, etc.)
- Build a component that uses all breakpoint types
- Add helpful @warn messages for invalid breakpoints
Exercise 2: Component Generator System
Build a mixin system that generates complete component families:
- Create a @mixin generate-button-family that creates base, variant, size, and state classes
- Support color themes (primary, secondary, success, danger, warning, info)
- Support sizes (xs, sm, md, lg, xl)
- Support states (hover, active, focus, disabled)
- Support outline and ghost variants
- Generate all combinations with a single mixin call
Exercise 3: Design System Utilities
Create utility mixins for a design system:
- @mixin spacing-utilities($properties, $scale) that generates margin/padding classes
- @mixin color-utilities($colors) that generates text, background, and border color classes
- @mixin typography-utilities($scale, $weights) that generates font size and weight classes
- Make all utilities responsive (generate breakpoint-specific variants)
- Add a toggle to enable/disable responsive variants for performance