Mastering CSS Preprocessors: Practical Less and SCSS Guide (Part 4)
This article explains why CSS preprocessors are essential for large projects, demonstrates core features of Less and SCSS—including variables, nesting, mixins, inheritance, functions, and modularization—with real code examples, compares the two syntaxes, and offers recommendations and project‑structure guidelines for modern front‑end development.
Why Use a CSS Preprocessor?
When a theme color such as #0052d9 is used in dozens of places (buttons, links, headings, borders), changing it globally requires a tedious and error‑prone search‑and‑replace. CSS preprocessors solve this by providing:
Variables : define once, reference everywhere.
Nesting : mirrors HTML structure, reduces selector duplication.
Reuse : mixins extract common styles; functions perform calculations.
Modularization : split into multiple files and import as needed.
Less in Practice
Installation & Usage
# Install
npm install less -D
# Compile
npx lessc styles.less styles.css
# Vite/Webpack have built‑in supportVariables
// Define variables (prefixed with @)
@primary-color: #0052d9;
@success-color: #52c41a;
@danger-color: #ff4d4f;
@warning-color: #faad14;
@font-size-base: 14px;
@font-size-lg: 16px;
@font-size-xl: 20px;
@border-radius: 8px;
@spacing-sm: 8px;
@spacing-md: 16px;
@spacing-lg: 24px;
// Use variables
.btn-primary {
background: @primary-color;
font-size: @font-size-base;
padding: @spacing-sm @spacing-md;
border-radius: @border-radius;
}
// Variable interpolation in selectors, property names, URLs
@theme: dark;
.@{theme}-mode {
background: #1a1a1a;
color: #fff;
}
@property: color;
.highlight {
@{property}: @primary-color;
}Nesting
.navbar {
height: 64px;
background: #fff;
display: flex;
align-items: center;
padding: 0 24px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
&__logo {
font-size: 18px;
font-weight: bold;
color: @primary-color;
}
&__menu {
display: flex;
gap: 24px;
list-style: none;
margin: 0;
padding: 0;
margin-left: auto;
}
&__item {
a {
color: #666;
text-decoration: none;
transition: color 0.2s;
&:hover { color: @primary-color; }
}
&--active a { color: @primary-color; font-weight: 500; }
}
}
// Nested media queries
.card {
width: 100%;
@media (min-width: 768px) { width: calc(50% - 8px); }
@media (min-width: 1200px) { width: calc(33.33% - 11px); }
}Mixins
// Define mixins (not output to CSS)
.flex-center() {
display: flex;
align-items: center;
justify-content: center;
}
.ellipsis() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.absolute-fill() {
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
}
// Use mixins
.modal-overlay {
.absolute-fill();
.flex-center();
background: rgba(0,0,0,0.5);
}
.button-variant(@bg, @color: #fff, @hover-bg: darken(@bg,10%)) {
background: @bg;
color: @color;
border: none;
cursor: pointer;
padding: 8px 16px;
border-radius: 6px;
transition: background 0.2s;
&:hover { background: @hover-bg; }
&:active { background: darken(@bg,15%); }
}
.btn-primary { .button-variant(@primary-color); }
.btn-success { .button-variant(@success-color); }
.btn-danger { .button-variant(@danger-color); }Extend
// Share styles with :extend()
.message {
padding: 12px 16px;
border-radius: 6px;
font-size: 14px;
border-left: 4px solid transparent;
}
.success-message:extend(.message) {
background: #f6ffed;
border-left-color: @success-color;
color: #52c41a;
}
.error-message:extend(.message) {
background: #fff2f0;
border-left-color: @danger-color;
color: @danger-color;
}Built‑in Functions
@base: #0052d9;
.element {
// Color functions
color: lighten(@base,20%); // +20% lightness
background: darken(@base,10%); // -10% lightness
border-color: fade(@base,50%); // 50% opacity
color: mix(@base, #fff, 80%); // blend with white
// Math functions
width: round(4.7px); // 5px
height: ceil(4.1px); // 5px
padding: floor(4.9px); // 4px
margin: abs(-10px); // 10px
// String function to prevent calc from being escaped
content: ~"calc(100% - 20px)";
}Conditionals & Loops
// Guard (conditional)
.mixin(@a) when (@a >= 0) { width: @a; }
.mixin(@a) when (@a < 0) { width: 0; }
// Recursive loop to generate columns
.generate-columns(@n, @i:1) when (@i <= @n) {
.col-@{i} { width: (@i * 100% / @n); }
.generate-columns(@n, (@i + 1));
}
.generate-columns(12); // creates .col-1 … .col-12SCSS in Practice
Installation & Usage
# Install Sass
npm install sass -D
# Vite/Webpack have built‑in support; just write .scss filesVariables ($ prefix)
// Basic variables
$primary: #0052d9;
$success: #52c41a;
$danger: #ff4d4f;
$warning: #faad14;
$text-color: #333;
$text-secondary: #666;
$text-placeholder: #999;
$border-color: #e8e8e8;
$border-radius: 8px;
$border-radius-sm: 4px;
$border-radius-circle: 50%;
$spacing-xs: 4px;
$spacing-sm: 8px;
$spacing-md: 16px;
$spacing-lg: 24px;
$spacing-xl: 32px;
// Maps for font sizes and breakpoints
$font-sizes: (sm:12px, base:14px, lg:16px, xl:20px, xxl:24px);
$breakpoints: (xs:0, sm:576px, md:768px, lg:992px, xl:1200px);
.title { font-size: map.get($font-sizes, xl); /* 20px */ }Nesting (more powerful than Less)
.card {
background: #fff;
border-radius: $border-radius;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
&__header { padding: $spacing-md; border-bottom: 1px solid $border-color; font-weight:600; }
&__body { padding: $spacing-md; }
&__footer { padding: $spacing-md; border-top: 1px solid $border-color; display:flex; justify-content:flex-end; gap:$spacing-sm; }
&--primary { border-top: 3px solid $primary; }
&--success { border-top: 3px solid $success; }
&--danger { border-top: 3px solid $danger; }
&:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.12); }
@media (max-width:768px) {
&__header, &__body, &__footer { padding: $spacing-sm; }
}
}Mixins (@mixin / @include)
// Parameter‑less mixins
@mixin flex-center { display:flex; align-items:center; justify-content:center; }
@mixin absolute-fill { position:absolute; inset:0; }
@mixin ellipsis { overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
// Parameterised mixin with default values
@mixin button-base($size:md) {
$sizes:(
sm:(padding:4px 12px, font-size:12px, height:28px),
md:(padding:8px 16px, font-size:14px, height:36px),
lg:(padding:12px 20px, font-size:16px, height:44px)
);
$cfg: map.get($sizes,$size);
padding: map.get($cfg,padding);
font-size: map.get($cfg,font-size);
height: map.get($cfg,height);
border:none; border-radius:$border-radius; cursor:pointer; font-weight:500;
transition:background 0.2s, transform 0.15s;
&:active { transform:scale(0.97); }
}
// Media‑query helper
@mixin respond-to($breakpoint) {
$bp-map:(mobile:"(max-width:767px)", tablet:"(min-width:768px) and (max-width:1199px)", desktop:"(min-width:1200px)");
@media #{map.get($bp-map,$breakpoint)} { @content; }
}
.layout { padding:24px; @include respond-to(mobile){ padding:16px; } }
.hero { @include flex-center; min-height:100vh; background:$primary; color:#fff; }
.btn-lg { @include button-base(lg); background:$primary; color:#fff; }Extend (@extend)
%message-base { padding:12px 16px; border-radius:6px; font-size:14px; display:flex; align-items:center; gap:8px; }
.alert-success { @extend %message-base; background:#f6ffed; color:#52c41a; border:1px solid #b7eb8f; }
.alert-error { @extend %message-base; background:#fff2f0; color:#ff4d4f; border:1px solid #ffccc7; }
.alert-warning { @extend %message-base; background:#fffbe6; color:#faad14; border:1px solid #ffe58f; }Functions
@use 'sass:color';
@use 'sass:math';
@use 'sass:map';
.element {
// Color functions
color: color.adjust($primary, $lightness:20%);
background: color.adjust($primary, $lightness:-10%);
border: 1px solid color.change($primary, $alpha:0.3);
color: color.mix($primary, white, 80%);
// Math functions
width: math.round(4.7px);
padding: math.div(16px,2);
}
@function rem($px,$base:16){ @return math.div($px,$base) * 1rem; }
@function color-level($color,$level){
$base: if($level>0, black, white);
$amount: math.abs($level) * 8%;
@return color.mix($base,$color,$amount);
}
.title { font-size: rem(24); color: color-level($primary,2); }Control Flow
// Conditional
@mixin theme-color($theme) {
@if $theme == dark { background:#1a1a1a; color:#e0e0e0; }
@else if $theme == light { background:#fff; color:#333; }
@else { background:#f5f5f5; color:#666; }
}
// Loops
$colors:(primary:#0052d9, success:#52c41a, danger:#ff4d4f, warning:#faad14);
@each $name,$color in $colors {
.btn-#{$name} { background:$color; color:#fff; &:hover { background:color.adjust($color,$lightness:-8%); } }
.text-#{$name} { color:$color; }
.bg-#{$name} { background:$color; }
.border-#{$name}{ border-color:$color; }
}
@for $i from 1 through 12 { .col-#{$i} { width: math.div(100%,12) * $i; } }
$i:1; @while $i <=5 { .mt-#{$i} { margin-top: $i * 4px; } $i: $i + 1; }Less vs SCSS Comparison
Variable Prefix : Less uses @, SCSS uses $.
Mixin Syntax : Less defines mixins as .name(); SCSS uses @mixin / @include.
Inheritance : Less uses :extend(); SCSS uses @extend.
Conditionals : Less provides Guard syntax; SCSS uses @if / @else.
Loops : Less implements recursion; SCSS offers @for / @each / @while.
Functions : Less has limited built‑in functions; SCSS supports full @function definitions.
Module System : Less has a weaker module system; SCSS uses powerful @use / @forward.
Ecosystem : Less ecosystem is average; SCSS ecosystem is richer.
Popularity : Less usage is declining; SCSS is mainstream.
Conclusion
For new projects, SCSS is recommended because of its richer feature set and stronger module system. Less is closer to native CSS, has a gentler learning curve, and suits small projects or migrations from plain CSS.
SCSS Project Architecture Example
src/scss/
├── _variables.scss # variable definitions
├── _mixins.scss # all mixins
├── _reset.scss # CSS reset
├── _global.scss # global styles
├── _utilities.scss # utility classes
└── index.scss # main entry, imports all
src/components/
└── Button/
└── Button.scss # component styles // src/scss/_variables.scss
$primary: #0052d9;
$spacing-md: 16px;
// ...
// src/scss/_mixins.scss
@use 'variables' as v;
@mixin flex-center { display:flex; align-items:center; justify-content:center; }
// ...
// src/scss/index.scss
@use 'reset';
@use 'variables';
@use 'global';
@use 'utilities';
// Component file
// src/components/Button/Button.scss
@use '../../scss/variables' as v;
@use '../../scss/mixins' as m;
.button { background: v.$primary; padding: v.$spacing-md; @include m.flex-center; }Full SCSS Component Library Snippet
// _variables.scss
$colors: (
primary: #0052d9,
success: #52c41a,
danger: #ff4d4f,
warning: #faad14,
info: #1890ff
);
$sizes: (
sm: (height:28px, padding:0 10px, font-size:12px, radius:4px),
md: (height:36px, padding:0 16px, font-size:14px, radius:6px),
lg: (height:44px, padding:0 20px, font-size:16px, radius:8px)
);
// Button.scss
@use 'sass:map';
@use 'sass:color';
@use '../variables' as v;
.btn {
display:inline-flex; align-items:center; justify-content:center; gap:6px;
border:1px solid transparent; cursor:pointer; font-weight:500; white-space:nowrap;
transition:all 0.2s; user-select:none;
// Size variants
@each $size,$cfg in v.$sizes {
&--#{$size} {
height: map.get($cfg,height);
padding: map.get($cfg,padding);
font-size: map.get($cfg,font-size);
border-radius: map.get($cfg,radius);
}
}
// Color variants
@each $name,$color in v.$colors {
&--#{$name} {
background:$color; color:#fff; border-color:$color;
&:hover { background:color.adjust($color,$lightness:5%); border-color:color.adjust($color,$lightness:5%); }
&:active { background:color.adjust($color,$lightness:-5%); border-color:color.adjust($color,$lightness:-5%); }
}
// Outline variant
&--outline-#{$name} {
background:transparent; color:$color; border-color:$color;
&:hover { background:color.change($color,$alpha:0.1); }
}
// Text variant
&--text-#{$name} {
background:transparent; color:$color; border-color:transparent;
&:hover { background:color.change($color,$alpha:0.08); }
}
}
// Disabled state
&:disabled, &--disabled { opacity:0.5; cursor:not-allowed; pointer-events:none; }
// Loading state
&--loading {
pointer-events:none;
&::before {
content:''; display:inline-block; width:1em; height:1em;
border:2px solid currentColor; border-top-color:transparent; border-radius:50%;
animation:btn-spin 0.7s linear infinite;
}
}
}
@keyframes btn-spin { to { transform:rotate(360deg); } }Key Takeaways
Suitable Scenarios : Less for simple, quick‑start projects; SCSS for complex, feature‑rich applications.
Core Value : Both provide variables, nesting, and mixins; SCSS adds strong‑typed functions and full logical control.
Recommendation Rating : Less ★★★☆☆, SCSS ★★★★★.
Learning Advice
Start with SCSS because it offers a complete feature set; the concepts translate directly to Less, so after mastering SCSS you can pick up Less in about ten minutes.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
