CSS Preprocessors: Sass, Less, and Stylus
CSS preprocessors extend CSS with programming features — variables, nesting, mixins, functions, and modular file organization — that compile down to standard CSS. They eliminate repetition, improve maintainability, and bring structure to stylesheets.
What Is a Preprocessor?
A preprocessor takes a custom syntax and compiles it to plain CSS:
Source (Sass/Less/Stylus) → Preprocessor → Output CSSAll three preprocessors offer similar features. The choice often comes down to ecosystem and team preference.
Sass (Syntactically Awesome Style Sheets)
Sass is the most widely used CSS preprocessor. Two syntaxes:
- SCSS (
.scss): Superset of CSS, most popular - Indented syntax (
.sass): Uses indentation instead of braces
Variables
$primary-color: #3498db;
$spacing-unit: 8px;
$font-stack: 'Inter', system-ui, sans-serif;
.header {
background-color: $primary-color;
padding: $spacing-unit * 2;
font-family: $font-stack;
}Nesting
.navbar {
background: #333;
padding: 1rem;
.nav-list {
display: flex;
list-style: none;
margin: 0;
}
.nav-item {
margin-right: 1rem;
a {
color: white;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
&--dark {
background: #111;
}
&__link {
font-weight: 600;
}
}Mixins
@mixin flex-center($direction: row) {
display: flex;
flex-direction: $direction;
align-items: center;
justify-content: center;
}
.card {
@include flex-center(column);
padding: 2rem;
}
.hero {
@include flex-center;
min-height: 80vh;
}Functions
@function rem($px, $base: 16px) {
@return ($px / $base) * 1rem;
}
@function contrast-color($bg) {
@if lightness($bg) > 50% {
@return #000;
} @else {
@return #fff;
}
}
.element {
font-size: rem(24);
color: contrast-color($primary-color);
}Partials and Imports
In modern Dart Sass, use @use and @forward instead of @import:
// _variables.scss
$primary: #3498db !default;
// main.scss
@use 'variables' as v;
@use 'mixins';
.header {
color: v.$primary;
@include mixins.card;
}Less
Less is the precursor to many Sass features. Variables use @ instead of $. Mixins use class selectors without a @mixin keyword.
@primary-color: #3498db;
@spacing: 8px;
.flex-center(@direction: row) {
display: flex;
flex-direction: @direction;
align-items: center;
justify-content: center;
}
.container {
.flex-center(column);
}Less integrates naturally with CSS syntax — valid CSS is valid Less. The .less extension is required, but the syntax is closer to standard CSS than SCSS.
Stylus
Stylus has the tersest syntax — braces, colons, and semicolons are all optional.
primary-color = #3498db
flex-center(direction = row)
display flex
flex-direction direction
align-items center
justify-content center
.container
flex-center(column)Stylus also supports transparent mixins (functions that look like properties) and interpolation for vendor prefixes. Its free-form syntax appeals to developers who value conciseness.
Comparison
| Feature | Sass (SCSS) | Less | Stylus | |
Beyond Variables and Nesting
Modern CSS preprocessors (Sass, Less) offer advanced features that significantly improve stylesheet maintainability:
Mixins with Logic
Sass mixins can include conditional logic and loops, making them far more powerful than simple property groups:
@mixin responsive-spacing($property, $min, $max) {
#{$property}: $min;
@media (min-width: 768px) {
#{$property}: calc(#{$min} + (#{$max} - #{$min}) * ((100vw - 768px) / (1440 - 768)));
}
@media (min-width: 1440px) {
#{$property}: $max;
}
}
.card {
@include responsive-spacing(padding, 1rem, 3rem);
}Placeholder Selectors
Placeholder selectors (%) define styles that are only output when extended, avoiding class duplication in compiled CSS:
%card-base {
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.product-card { @extend %card-base; background: white; }
.article-card { @extend %card-base; background: #f5f5f5; }Color Functions
Preprocessors provide powerful color manipulation functions that are not yet available in native CSS:
$primary: #3498db;
.button {
background: $primary;
&:hover {
background: darken($primary, 10%);
}
&:active {
background: darken($primary, 15%);
}
&--secondary {
background: lighten($primary, 30%);
color: $primary;
}
}Build Tool Integration
Modern build tools like Vite, Webpack, and Parcel handle Sass compilation seamlessly with minimal configuration:
# Vite — install the preprocessor, Vite handles compilation
npm install -D sass
# Webpack with sass-loader
npm install -D sass sass-loader css-loader style-loaderRename your .css files to .scss and the build pipeline processes them automatically. For production, all tools include minification and vendor prefixing through PostCSS autoprefixer.
Modular Architecture with Partials
Organize Sass files following the 7-1 architecture pattern popularized by Hugo Giraudel:
// abstracts/ — variables, functions, mixins
// vendors/ — third-party CSS
// base/ — reset, typography
// layout/ — grid, header, footer
// components/ — buttons, cards, modals
// pages/ — page-specific styles
// themes/ — theme variablesThe main file imports all partials in order, with variables first and overrides last:
@use 'abstracts/variables';
@use 'abstracts/functions';
@use 'abstracts/mixins';
@use 'vendors/normalize';
@use 'base/reset';
@use 'layout/grid';
@use 'components/button';
// ...The @use rule (Sass 3.x+) creates namespaced imports, avoiding the global namespace pollution of @import. This prevents naming conflicts and makes dependency tracking explicit.
Performance Implications
Preprocessors add a build step. For small projects, the build overhead is negligible. For large projects with thousands of partials and deep nesting, compile times can reach seconds. Consider:
- Limiting nesting depth to 3-4 levels maximum
- Avoiding mixins with large amounts of generated CSS (use
@extendor CSS custom properties instead) - Using
purgingto remove unused CSS in production (PurgeCSS, Tailwind’s built-in purging) - Precompiling critical CSS inline and deferring the rest
PostCSS and Autoprefixing
PostCSS transforms CSS with JavaScript plugins. Combined with Autoprefixer, it ensures cross-browser compatibility without manual vendor prefixes:
npm install -D postcss autoprefixer postcss-cli// postcss.config.js
module.exports = {
plugins: [
require("autoprefixer"),
require("postcss-preset-env")({ stage: 2 }),
],
};PostCSS runs as part of the build pipeline in most modern toolchains (Vite, Webpack with postcss-loader). Autoprefixer uses data from Can I Use to determine which vendor prefixes are needed for the browser support targets defined in your project’s browserslist configuration.
CSS Next: Future Features Today
PostCSS plugins enable future CSS features today. postcss-preset-env converts modern CSS (custom properties, nesting, oklch() colors, :has() selector) to browser-compatible CSS. This allows you to write modern CSS while supporting older browsers. As browsers implement these features natively, you can drop the PostCSS transforms without changing your source CSS.
—|—|—|—|
| Variables | $var | @var | var (no prefix) |
| Nesting | & | & | & |
| Mixins | @mixin / @include | Class selectors | Direct calls |
| Functions | @function | Less.js functions | Native functions |
| Conditionals | @if / @else | Guards | if / unless |
| Loops | @each / @for | Not built-in | for / while |
| Extend | @extend | :extend() | @extend |
| Ecosystem | Largest | Moderate | Smallest |
Build Integration
CSS preprocessors require build tool integration. Webpack uses sass-loader, less-loader, or stylus-loader. Vite has built-in support — just install the preprocessor package. The @use directive replaces @import in modern Sass, providing better encapsulation and namespace control.
Preprocessor vs PostCSS
PostCSS transforms CSS with JavaScript plugins rather than extending CSS syntax. It can import files (postcss-import), add vendor prefixes (autoprefixer), nest selectors (postcss-nesting), and use future CSS features (postcss-preset-env). The choice between preprocessors and PostCSS depends on team preference — both handle variables, nesting, and mixins, but PostCSS uses standards-track syntax while preprocessors introduce custom syntax.
Best Practices
- Use a naming convention: BEM with SCSS nesting (
block__element--modifier) - Limit nesting depth: 3 levels max — deeper generates overly specific selectors
- Avoid over-mixing: Do not create mixins for every repeated property
- Use variables for design tokens: Colors, spacing, fonts, breakpoints
- Organize by component: One partial per component (
_card.scss,_navbar.scss) - Output sensible CSS: Review compiled CSS for selector bloat
Summary
Sass is the industry standard with the largest ecosystem. Less is simpler and integrates naturally with CSS syntax. Stylus offers the most concise syntax. All three provide variables, nesting, mixins, and modular organization. Choose Sass for broad compatibility and tooling, Less for simple projects, and Stylus if you prefer minimal syntax.
FAQ
What is the difference between SCSS and Sass? SCSS (Sassy CSS) uses curly braces and semicolons — it is a superset of CSS. Sass (the indented syntax) uses indentation instead of braces. SCSS is more popular because it is easier to adopt incrementally in existing CSS projects.
Should I use @import or @use in Sass? Use @use and @forward. The @import directive is deprecated in Dart Sass. @use provides namespace control, better encapsulation, and prevents variable name conflicts.
Can I use CSS preprocessors with CSS-in-JS? No. CSS preprocessors work with CSS files and compile to standard CSS. CSS-in-JS libraries handle variables, nesting, and logic through JavaScript — they do not need a preprocessing step.
What is the learning curve for each preprocessor? SCSS is easiest if you know CSS — valid CSS is valid SCSS. Less has the gentlest learning curve because it stays close to CSS syntax. Stylus has the steepest curve because its syntax is significantly different.
Related: CSS Variables Guide | Responsive Design
Related Articles
Web Development
Sass Guide: CSS Preprocessor for Scalable Stylesheets
Master Sass CSS preprocessor for maintainable stylesheets. Learn variables, nesting, mixins, functions, inheritance, partials, and project organization strategies.
Web Development
CSS Animations: A Complete Guide
Master CSS animations — transitions, keyframes, transforms, performance optimization, and creating engaging user interfaces without JavaScript.
Web Development
CSS Custom Properties: A Complete Guide to CSS Variables
Master CSS custom properties — defining variables, scope, fallbacks, dynamic theming, and real-world use cases with calc().
Web Development
CSS Flexbox: The Complete Guide with Examples
Learn CSS Flexbox from basics to advanced. Covers flex-direction, justify-content, align-items, flex-wrap, and real-world layouts.
Web Development
CSS Grid: The Complete Guide with Examples
Learn CSS Grid Layout from basics to advanced. Grid containers, items, areas, and responsive layouts with real-world examples.