Mixins: Creating Reusable Style Blocks
Mixins: Creating Reusable Style Blocks
Mixins are one of the most powerful features in SASS. They allow you to define reusable blocks of styles that can be included anywhere in your stylesheet. Think of mixins as functions for CSS—they can accept arguments, have default values, and generate different output based on the input.
What are Mixins and Why Use Them?
A mixin is a block of code that can be reused throughout your stylesheet. Mixins help you:
- Avoid Repetition: Write once, use many times
- Maintain Consistency: Ensure the same patterns are used everywhere
- Create Abstractions: Hide complexity behind simple interfaces
- Support Variants: Create different versions of the same pattern
- Enhance Readability: Make your code more semantic and self-documenting
@mixin and @include Syntax
To create a mixin, use the @mixin directive followed by a name. To use a mixin, use the @include directive followed by the mixin name.
Basic Mixin Syntax
// Define a mixin
@mixin mixin-name {
// CSS properties here
}
// Use the mixin
.selector {
@include mixin-name;
}
Simple Mixins (No Arguments)
The simplest mixins don't take any arguments—they just output the same CSS every time they're used.
Simple Mixin Examples
// Define mixins
@mixin reset-list {
margin: 0;
padding: 0;
list-style: none;
}
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
@mixin clearfix {
&::after {
content: "";
display: table;
clear: both;
}
}
// Use the mixins
.nav {
@include reset-list;
}
.container {
@include flex-center;
height: 100vh;
}
.row {
@include clearfix;
}
// Compiled CSS
.nav {
margin: 0;
padding: 0;
list-style: none;
}
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.row::after {
content: "";
display: table;
clear: both;
}
Mixins with Arguments
Mixins become much more powerful when they accept arguments. This allows you to create flexible, configurable style patterns.
Mixin with Single Argument
@mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
border-radius: $radius;
}
// Usage
.box {
@include border-radius(10px);
}
.button {
@include border-radius(4px);
}
// Compiled CSS
.box {
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
}
.button {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
Mixin with Multiple Arguments
@mixin size($width, $height) {
width: $width;
height: $height;
}
@mixin position($position, $top, $right, $bottom, $left) {
position: $position;
top: $top;
right: $right;
bottom: $bottom;
left: $left;
}
// Usage
.square {
@include size(100px, 100px);
}
.overlay {
@include position(absolute, 0, 0, 0, 0);
}
// Compiled CSS
.square {
width: 100px;
height: 100px;
}
.overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
Default Argument Values
You can provide default values for mixin arguments. If a value isn't passed when including the mixin, the default is used.
Default Values Example
@mixin transition($property: all, $duration: 0.3s, $timing: ease) {
transition: $property $duration $timing;
-webkit-transition: $property $duration $timing;
}
// Usage - all arguments provided
.button-1 {
@include transition(color, 0.5s, linear);
}
// Usage - some defaults used
.button-2 {
@include transition(background, 0.2s);
// $timing uses default: ease
}
// Usage - all defaults used
.button-3 {
@include transition;
// Uses: all 0.3s ease
}
// Compiled CSS
.button-1 {
transition: color 0.5s linear;
-webkit-transition: color 0.5s linear;
}
.button-2 {
transition: background 0.2s ease;
-webkit-transition: background 0.2s ease;
}
.button-3 {
transition: all 0.3s ease;
-webkit-transition: all 0.3s ease;
}
Keyword Arguments
When calling a mixin with multiple arguments, you can use keyword arguments to make the call more readable and to skip arguments you want to leave at their default.
Keyword Arguments
@mixin box($width: 100px, $height: 100px, $bg: white, $border: 1px solid black) {
width: $width;
height: $height;
background: $bg;
border: $border;
}
// Positional arguments
.box-1 {
@include box(200px, 150px, blue, 2px solid red);
}
// Keyword arguments (order doesn't matter)
.box-2 {
@include box($bg: yellow, $width: 300px);
// $height and $border use defaults
}
// Mix of positional and keyword
.box-3 {
@include box(250px, $bg: green);
// $height uses default, $border uses default
}
// Compiled CSS
.box-1 {
width: 200px;
height: 150px;
background: blue;
border: 2px solid red;
}
.box-2 {
width: 300px;
height: 100px;
background: yellow;
border: 1px solid black;
}
.box-3 {
width: 250px;
height: 100px;
background: green;
border: 1px solid black;
}
Variable Arguments (...)
Sometimes you want a mixin to accept any number of arguments. Use the ... syntax to create variable-length argument lists.
Variable Arguments Example
@mixin box-shadow($shadows...) {
-webkit-box-shadow: $shadows;
-moz-box-shadow: $shadows;
box-shadow: $shadows;
}
// Usage with one shadow
.card-1 {
@include box-shadow(0 2px 4px rgba(0,0,0,0.1));
}
// Usage with multiple shadows
.card-2 {
@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)
);
}
// Compiled CSS
.card-1 {
-webkit-box-shadow: 0 2px 4px rgba(0,0,0,0.1);
-moz-box-shadow: 0 2px 4px rgba(0,0,0,0.1);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card-2 {
-webkit-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);
-moz-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);
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);
}
Variable Arguments for Vendor Prefixes
@mixin prefix($property, $value, $prefixes: webkit moz ms o) {
@each $prefix in $prefixes {
-#{$prefix}-#{$property}: $value;
}
#{$property}: $value;
}
// Usage
.element {
@include prefix(transform, rotate(45deg));
}
// Compiled CSS
.element {
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
@content Blocks
The @content directive allows you to pass a block of styles to a mixin. This is incredibly powerful for creating wrapper mixins, especially for media queries.
Basic @content Example
@mixin important {
// Styles inside the mixin
color: red;
font-weight: bold;
// Insert the content block here
@content;
}
// Usage
.alert {
@include important {
// This content is inserted where @content appears
background: yellow;
padding: 10px;
}
}
// Compiled CSS
.alert {
color: red;
font-weight: bold;
background: yellow;
padding: 10px;
}
@content for Media Queries
@mixin respond-to($breakpoint) {
@if $breakpoint == mobile {
@media (max-width: 480px) {
@content;
}
}
@else if $breakpoint == tablet {
@media (max-width: 768px) {
@content;
}
}
@else if $breakpoint == desktop {
@media (min-width: 1024px) {
@content;
}
}
}
// Usage
.sidebar {
width: 300px;
@include respond-to(tablet) {
width: 200px;
}
@include respond-to(mobile) {
width: 100%;
}
}
// Compiled CSS
.sidebar {
width: 300px;
}
@media (max-width: 768px) {
.sidebar {
width: 200px;
}
}
@media (max-width: 480px) {
.sidebar {
width: 100%;
}
}
Common Use Cases for Mixins
Flexbox Center
@mixin flex-center($direction: row) {
display: flex;
justify-content: center;
align-items: center;
flex-direction: $direction;
}
.container {
@include flex-center;
}
.vertical {
@include flex-center(column);
}
Clearfix
@mixin clearfix {
&::after {
content: "";
display: table;
clear: both;
}
}
.row {
@include clearfix;
}
Transition
@mixin transition($properties...) {
@if length($properties) == 0 {
transition: all 0.3s ease;
} @else {
transition: $properties;
}
}
.button {
@include transition(background 0.3s ease, color 0.3s ease);
}
.link {
@include transition; // Uses default
}
Responsive Font Size
@mixin responsive-font($min-size, $max-size, $min-width: 320px, $max-width: 1200px) {
font-size: $min-size;
@media (min-width: $min-width) {
font-size: calc(#{$min-size} + (#{$max-size} - #{$min-size}) * ((100vw - #{$min-width}) / (#{$max-width} - #{$min-width})));
}
@media (min-width: $max-width) {
font-size: $max-size;
}
}
h1 {
@include responsive-font(24px, 48px);
}
Aspect Ratio Box
@mixin aspect-ratio($width, $height) {
position: relative;
&::before {
content: "";
display: block;
padding-top: ($height / $width) * 100%;
}
& > * {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
.video-container {
@include aspect-ratio(16, 9);
}
.square {
@include aspect-ratio(1, 1);
}
Truncate Text
@mixin truncate($lines: 1) {
@if $lines == 1 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} @else {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
.single-line {
@include truncate;
}
.multi-line {
@include truncate(3);
}
Real-World Example: Button Mixin System
Complete Button System
// Base button styles
@mixin button-base {
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: 600;
text-align: center;
text-decoration: none;
cursor: pointer;
transition: all 0.3s ease;
&:focus {
outline: 2px solid currentColor;
outline-offset: 2px;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
// Button color variant
@mixin button-variant($bg-color, $text-color: white) {
background: $bg-color;
color: $text-color;
&:hover:not(:disabled) {
background: darken($bg-color, 10%);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba($bg-color, 0.3);
}
&:active:not(:disabled) {
transform: translateY(0);
box-shadow: 0 2px 4px rgba($bg-color, 0.3);
}
}
// Button size variant
@mixin button-size($padding, $font-size) {
padding: $padding;
font-size: $font-size;
}
// Usage
.btn {
@include button-base;
@include button-variant(#3498db);
}
.btn-success {
@include button-base;
@include button-variant(#2ecc71);
}
.btn-danger {
@include button-base;
@include button-variant(#e74c3c);
}
.btn-large {
@include button-base;
@include button-variant(#3498db);
@include button-size(15px 30px, 18px);
}
.btn-small {
@include button-base;
@include button-variant(#3498db);
@include button-size(5px 10px, 14px);
}
Exercise 1: Create a Card Mixin
Create a flexible card mixin system:
- Create a @mixin card-base with standard card styling (padding, border, shadow)
- Create a @mixin card-variant($bg-color, $text-color) for color variations
- Create a @mixin card-hover that adds hover effects
- Use these mixins to create .card, .card-primary, .card-dark classes
Exercise 2: Responsive Grid Mixin
Build a responsive grid system using mixins:
- Create @mixin grid-container($columns, $gap) for the container
- Create @mixin grid-item($span) for items that span multiple columns
- Create @mixin responsive-grid using @content for breakpoint-specific styles
- Build a 12-column grid that becomes 6 columns on tablet and 1 column on mobile
Exercise 3: Animation Mixin Library
Create a library of animation mixins:
- @mixin fade-in($duration, $delay) for fade animations
- @mixin slide-in($direction, $distance, $duration) for slide animations
- @mixin pulse($scale, $duration) for pulse effects
- Use variable arguments for complex animations with multiple steps
- Apply these to create animated button, card, and modal components