Introduction
CSS preprocessors are scripting languages that extend CSS and compile into regular CSS syntax. They provide features that don’t exist in pure CSS like variables, nesting, mixins, inheritance, and other powerful functions that make writing CSS more efficient and maintainable. This cheatsheet covers the major CSS preprocessors: Sass/SCSS, Less, and Stylus.
Core Concepts & Features
Feature | Description |
---|---|
Variables | Store values for reuse throughout stylesheets |
Nesting | Write selectors inside other selectors to reduce repetition |
Mixins | Reusable chunks of CSS declarations |
Functions | Calculate values dynamically |
Importing | Split styles into multiple files |
Inheritance | Share properties between selectors |
Operators | Perform calculations within stylesheets |
Control directives | Apply styles conditionally or in loops |
Preprocessor Comparison
Feature | Sass/SCSS | Less | Stylus |
---|---|---|---|
File Extension | .sass / .scss | .less | .styl |
Syntax | Indented (.sass) / CSS-like (.scss) | CSS-like | Flexible, optional colons/semicolons |
Variables | $variable | @variable | variable |
Nesting | Supported | Supported | Supported |
Mixins | @mixin / @include | .name() | name() |
Functions | @function | .name() | name() |
Inheritance | @extend | :extend() | @extend |
Operators | Supported | Supported | Supported |
Language | Ruby (original) / Node.js (Dart Sass, Node Sass) | JavaScript | JavaScript |
Variables
Sass/SCSS
// SCSS Syntax
$primary-color: #3498db;
$font-stack: Helvetica, sans-serif;
body {
color: $primary-color;
font: 100% $font-stack;
}
// Sass Syntax
$primary-color: #3498db
$font-stack: Helvetica, sans-serif
body
color: $primary-color
font: 100% $font-stack
Less
@primary-color: #3498db;
@font-stack: Helvetica, sans-serif;
body {
color: @primary-color;
font: 100% @font-stack;
}
Stylus
primary-color = #3498db
font-stack = Helvetica, sans-serif
body
color primary-color
font 100% font-stack
Nesting
Sass/SCSS
nav {
background: #333;
ul {
margin: 0;
padding: 0;
list-style: none;
}
li { display: inline-block; }
a {
display: block;
padding: 6px 12px;
text-decoration: none;
&:hover {
color: #fff;
}
}
}
Less
nav {
background: #333;
ul {
margin: 0;
padding: 0;
list-style: none;
}
li { display: inline-block; }
a {
display: block;
padding: 6px 12px;
text-decoration: none;
&:hover {
color: #fff;
}
}
}
Stylus
nav
background #333
ul
margin 0
padding 0
list-style none
li
display inline-block
a
display block
padding 6px 12px
text-decoration none
&:hover
color #fff
Mixins
Sass/SCSS
// Define mixin
@mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
border-radius: $radius;
}
// Use mixin
.box {
@include border-radius(10px);
}
// Mixin with default parameter
@mixin box-shadow($x: 0, $y: 2px, $blur: 5px, $color: rgba(0,0,0,.4)) {
-webkit-box-shadow: $x $y $blur $color;
-moz-box-shadow: $x $y $blur $color;
box-shadow: $x $y $blur $color;
}
// Use mixin with some parameters
.card {
@include box-shadow(2px, 2px, 10px);
}
Less
// Define mixin
.border-radius(@radius) {
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
border-radius: @radius;
}
// Use mixin
.box {
.border-radius(10px);
}
// Mixin with default parameter
.box-shadow(@x: 0, @y: 2px, @blur: 5px, @color: rgba(0,0,0,.4)) {
-webkit-box-shadow: @x @y @blur @color;
-moz-box-shadow: @x @y @blur @color;
box-shadow: @x @y @blur @color;
}
// Use mixin with some parameters
.card {
.box-shadow(2px, 2px, 10px);
}
Stylus
// Define mixin
border-radius(radius)
-webkit-border-radius radius
-moz-border-radius radius
border-radius radius
// Use mixin
.box
border-radius(10px)
// Mixin with default parameter
box-shadow(x = 0, y = 2px, blur = 5px, color = rgba(0,0,0,.4))
-webkit-box-shadow x y blur color
-moz-box-shadow x y blur color
box-shadow x y blur color
// Use mixin with some parameters
.card
box-shadow(2px, 2px, 10px)
Inheritance/Extend
Sass/SCSS
.message {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
.success {
@extend .message;
border-color: green;
}
.error {
@extend .message;
border-color: red;
}
Less
.message {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
.success {
&:extend(.message);
border-color: green;
}
.error {
&:extend(.message);
border-color: red;
}
Stylus
.message
border 1px solid #ccc
padding 10px
color #333
.success
@extend .message
border-color green
.error
@extend .message
border-color red
Functions and Operations
Sass/SCSS
// Built-in functions
$base-color: #3498db;
$darker-color: darken($base-color, 10%);
$transparent-color: rgba($base-color, 0.5);
// Math operations
$container-width: 100%;
$sidebar-width: 30%;
$content-width: $container-width - $sidebar-width;
// Custom function
@function calculate-width($col-count, $total-cols: 12) {
@return percentage($col-count / $total-cols);
}
.col-4 {
width: calculate-width(4); // Returns 33.33333%
}
Less
// Built-in functions
@base-color: #3498db;
@darker-color: darken(@base-color, 10%);
@transparent-color: fade(@base-color, 50%);
// Math operations
@container-width: 100%;
@sidebar-width: 30%;
@content-width: @container-width - @sidebar-width;
// Custom function/mixin
.calculate-width(@col-count, @total-cols: 12) {
@result: percentage(@col-count / @total-cols);
width: @result;
}
.col-4 {
.calculate-width(4);
}
Stylus
// Built-in functions
base-color = #3498db
darker-color = darken(base-color, 10%)
transparent-color = rgba(base-color, 0.5)
// Math operations
container-width = 100%
sidebar-width = 30%
content-width = container-width - sidebar-width
// Custom function
calculate-width(col-count, total-cols = 12)
return (col-count / total-cols * 100)%
.col-4
width calculate-width(4)
Control Directives
Sass/SCSS
// If/Else Statement
$theme: 'dark';
header {
@if $theme == 'dark' {
background-color: #222;
color: #fff;
} @else {
background-color: #fff;
color: #222;
}
}
// For Loop
@for $i from 1 through 3 {
.item-#{$i} {
width: 100px * $i;
}
}
// Each Loop
$social-colors: (
twitter: #55acee,
facebook: #3b5998,
linkedin: #0077b5
);
@each $platform, $color in $social-colors {
.btn-#{$platform} {
background-color: $color;
}
}
// While Loop
$i: 1;
@while $i <= 5 {
.col-#{$i} {
width: 100% / $i;
}
$i: $i + 1;
}
Less
// If/Else Statement (Mixin Guards)
@theme: 'dark';
.themify() when (@theme = 'dark') {
background-color: #222;
color: #fff;
}
.themify() when (@theme = 'light') {
background-color: #fff;
color: #222;
}
header {
.themify();
}
// For Loop (Loop Pattern)
.loop(@i) when (@i <= 3) {
.item-@{i} {
width: 100px * @i;
}
.loop(@i + 1);
}
.loop(1);
// Each Loop
@social-colors: {
twitter: #55acee;
facebook: #3b5998;
linkedin: #0077b5;
}
each(@social-colors, {
.btn-@{key} {
background-color: @value;
}
});
Stylus
// If/Else Statement
theme = 'dark'
header
if theme == 'dark'
background-color #222
color #fff
else
background-color #fff
color #222
// For Loop
for i in (1..3)
.item-{i}
width 100px * i
// Each Loop
social-colors = {
twitter: #55acee,
facebook: #3b5998,
linkedin: #0077b5
}
for platform, color in social-colors
.btn-{platform}
background-color color
// While Loop
i = 1
while i <= 5
.col-{i}
width (100% / i)
i = i + 1
Importing and Partials
Sass/SCSS
// _variables.scss (partial)
$primary-color: #3498db;
$secondary-color: #2ecc71;
// _mixins.scss (partial)
@mixin border-radius($radius) {
border-radius: $radius;
}
// main.scss
@import 'variables';
@import 'mixins';
body {
color: $primary-color;
}
Less
// variables.less
@primary-color: #3498db;
@secondary-color: #2ecc71;
// mixins.less
.border-radius(@radius) {
border-radius: @radius;
}
// main.less
@import "variables.less";
@import "mixins.less";
body {
color: @primary-color;
}
Stylus
// variables.styl
primary-color = #3498db
secondary-color = #2ecc71
// mixins.styl
border-radius(radius)
border-radius radius
// main.styl
@import "variables"
@import "mixins"
body
color primary-color
Common Challenges and Solutions
Challenge: CSS Vendor Prefixing
Solution: Use mixins to handle vendor prefixes.
// Sass/SCSS
@mixin transform($property) {
-webkit-transform: $property;
-ms-transform: $property;
transform: $property;
}
.box {
@include transform(rotate(30deg));
}
Challenge: Media Query Management
Solution: Use mixins for responsive design patterns.
// Sass/SCSS
@mixin respond-to($breakpoint) {
@if $breakpoint == "small" {
@media (max-width: 576px) { @content; }
} @else if $breakpoint == "medium" {
@media (max-width: 768px) { @content; }
} @else if $breakpoint == "large" {
@media (max-width: 992px) { @content; }
}
}
.container {
width: 1200px;
@include respond-to(large) {
width: 970px;
}
@include respond-to(medium) {
width: 750px;
}
@include respond-to(small) {
width: 100%;
}
}
Challenge: Creating a Grid System
Solution: Use functions and loops to generate grid classes.
// Sass/SCSS
$grid-columns: 12;
@for $i from 1 through $grid-columns {
.col-#{$i} {
width: percentage($i / $grid-columns);
}
}
Best Practices
Organize Files with Partials
- Split code into logical modules (_variables.scss, _mixins.scss, etc.)
- Use a consistent naming convention
- Group related styles
Optimize Variable Usage
- Define global variables in a central file
- Use descriptive variable names
- Create variable hierarchies (e.g., $brand-primary, $brand-primary-light)
Write Efficient Mixins
- Keep mixins focused on a single responsibility
- Use parameters with sensible defaults
- Document complex mixins
Use Nesting Judiciously
- Avoid nesting more than 3 levels deep
- Consider the compiled CSS output
- Use the parent selector (&) for pseudo-classes and modifiers
Maintain Compatibility
- Test compiled CSS in target browsers
- Use autoprefixer for vendor prefixes
- Be aware of preprocessor version differences
Optimize Build Process
- Use source maps for debugging
- Minify CSS for production
- Implement watch tasks for automatic compilation
Tools for CSS Preprocessors
Compilation Tools
- Node-sass/Dart Sass
- Less.js
- Stylus Compiler
- LibSass
Build Systems Integration
- Webpack
- Gulp
- Grunt
- Parcel
IDE Plugins
- VSCode extensions
- Sublime Text packages
- WebStorm/PhpStorm support
Framework Integration
- React (with CSS modules)
- Vue (with single-file components)
- Angular (with component styles)
- Next.js/Gatsby