Website Markup with LESS
LESS is a CSS preprocessor with a JavaScript runtime, working both in Node.js (build) and in the browser (legacy approach). Variables, mixins, functions, conditions, color operations. Syntax is closer to CSS than SCSS. Applied in Bootstrap 3/4, Ant Design (before v5), in some legacy projects. New projects more often choose SCSS, but LESS is relevant when supporting legacy.
Installation and Configuration
npm install -D less
Vite supports .less out of the box:
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
css: {
preprocessorOptions: {
less: {
math: 'always',
relativeUrls: true,
// Automatic global import into each file
additionalData: `@import "@/styles/variables.less";`,
},
},
},
});
Variables
In LESS, variables are declared through @ — the same symbol as in CSS directives, but in a different context:
// styles/variables.less
// Colors
@color-primary: #2563eb;
@color-primary-dark: #1d4ed8;
@color-primary-light: #eff6ff;
@color-secondary: #7c3aed;
@color-success: #16a34a;
@color-danger: #dc2626;
@color-warning: #d97706;
@color-bg: #f9fafb;
@color-surface: #ffffff;
@color-text-primary: #111827;
@color-text-secondary: #6b7280;
@color-text-muted: #9ca3af;
@color-border: #e5e7eb;
// Typography
@font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
@font-size-base: 1rem;
@font-size-sm: 0.875rem;
@font-size-lg: 1.125rem;
@font-size-xl: 1.25rem;
@font-size-2xl: 1.5rem;
@font-size-3xl: 1.875rem;
@font-size-4xl: 2.25rem;
@line-height-base: 1.6;
// Spacing
@space-1: 0.25rem;
@space-2: 0.5rem;
@space-3: 0.75rem;
@space-4: 1rem;
@space-6: 1.5rem;
@space-8: 2rem;
@space-12: 3rem;
@space-16: 4rem;
// Border and rounding
@border-radius-sm: 4px;
@border-radius-md: 8px;
@border-radius-lg: 12px;
@border-radius-full: 9999px;
// Breakpoints
@breakpoint-sm: 576px;
@breakpoint-md: 768px;
@breakpoint-lg: 992px;
@breakpoint-xl: 1280px;
Mixins
// styles/mixins.less
// Flex centering
.flex-center(@direction: row) {
display: flex;
flex-direction: @direction;
align-items: center;
justify-content: center;
}
// Text truncation
.truncate(@lines: 1) when (@lines = 1) {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.truncate(@lines) when (@lines > 1) {
display: -webkit-box;
-webkit-line-clamp: @lines;
-webkit-box-orient: vertical;
overflow: hidden;
}
// Media queries
.respond-to-sm(@rules) {
@media (min-width: @breakpoint-sm) { @rules(); }
}
.respond-to-md(@rules) {
@media (min-width: @breakpoint-md) { @rules(); }
}
.respond-to-lg(@rules) {
@media (min-width: @breakpoint-lg) { @rules(); }
}
.respond-to-xl(@rules) {
@media (min-width: @breakpoint-xl) { @rules(); }
}
// Reset button
.reset-button() {
appearance: none;
background: none;
border: none;
padding: 0;
margin: 0;
cursor: pointer;
font-family: inherit;
}
Components
// components/button.less
@import (reference) "../variables.less";
@import (reference) "../mixins.less";
.btn {
.flex-center();
gap: @space-2;
font-family: @font-family;
font-weight: 500;
border: 1px solid transparent;
cursor: pointer;
white-space: nowrap;
text-decoration: none;
transition:
background-color 150ms ease,
border-color 150ms ease,
transform 100ms ease;
&:focus-visible {
outline: 2px solid @color-primary;
outline-offset: 2px;
}
&:active { transform: scale(0.98); }
&:disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
// Sizes
&.btn-sm {
height: 32px;
padding: 0 12px;
font-size: @font-size-sm;
border-radius: @border-radius-sm;
}
&.btn-md {
height: 40px;
padding: 0 16px;
font-size: @font-size-base;
border-radius: @border-radius-md;
}
&.btn-lg {
height: 48px;
padding: 0 24px;
font-size: @font-size-lg;
border-radius: @border-radius-md;
}
// Variants
&.btn-primary {
background: @color-primary;
color: #fff;
&:hover:not(:disabled) {
background: @color-primary-dark;
}
}
&.btn-ghost {
background: transparent;
color: @color-primary;
border-color: @color-primary;
&:hover:not(:disabled) {
background: @color-primary-light;
}
}
&.btn-danger {
background: @color-danger;
color: #fff;
&:hover:not(:disabled) {
background: darken(@color-danger, 10%);
}
}
&.btn-full { width: 100%; }
}
Color Operations
LESS has built-in functions for working with colors:
@base-color: #2563eb;
.palette-example {
color: lighten(@base-color, 20%); // Lighten
background: darken(@base-color, 10%); // Darken
border-color: saturate(@base-color, 20%); // Saturate
outline: desaturate(@base-color, 50%); // Desaturate
box-shadow: fade(@base-color, 30%); // Transparency
fill: mix(@base-color, #ff0000, 70%); // Mix colors
}
Nesting and Ampersand
.card {
background: @color-surface;
border: 1px solid @color-border;
border-radius: @border-radius-lg;
padding: @space-6;
transition: box-shadow 200ms ease;
// Hover on the element itself
&:hover {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
// Nested elements
&__header {
margin-bottom: @space-4;
padding-bottom: @space-4;
border-bottom: 1px solid @color-border;
}
&__title {
font-size: @font-size-xl;
font-weight: 600;
color: @color-text-primary;
}
&__body {
font-size: @font-size-base;
color: @color-text-secondary;
line-height: @line-height-base;
}
// Modifier
&--featured {
border-color: @color-primary;
box-shadow: 0 0 0 1px @color-primary;
}
}
LESS in the Context of Ant Design v4 Support
Ant Design 4.x uses LESS variables for customization. This is the main use case for LESS in new projects:
// src/antd-custom.less
@primary-color: #2563eb;
@link-color: #2563eb;
@success-color: #16a34a;
@warning-color: #d97706;
@error-color: #dc2626;
@font-size-base: 14px;
@font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
@border-radius-base: 8px;
@btn-border-radius-base: 8px;
@layout-body-background: #f9fafb;
@layout-header-background: #ffffff;
@layout-header-height: 64px;
// vite.config.ts with modifyVars for Ant Design 4
import { defineConfig } from 'vite';
import { theme } from './src/antd-custom';
export default defineConfig({
css: {
preprocessorOptions: {
less: {
modifyVars: {
'@primary-color': '#2563eb',
'@border-radius-base': '8px',
},
javascriptEnabled: true, // Required for Ant Design
},
},
},
});
Timeline
Setting up LESS configuration and basic variables: 1–2 hours. Migration from SCSS to LESS or vice versa: 0.5–1 day for a medium project. Markup of a new page: comparable to SCSS. LESS is relevant primarily in legacy projects and when integrating with Ant Design 4.







