Suggest an editImprove this articleRefine the answer for “CSS preprocessors: SASS, SCSS, and LESS”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**CSS preprocessors** (SASS, SCSS, LESS) extend CSS with variables, nesting, and mixins, then compile to plain CSS for the browser. SCSS is the most widely used because it is a full superset of CSS. ```scss $color: #007bff; .nav { background: $color; } // Compiles to: .nav { background: #007bff; } ``` **Key point:** SCSS is 100% CSS-compatible - any valid CSS file is also valid SCSS.Shown above the full answer for quick recall.Answer (EN)Image**CSS preprocessors** compile an extended CSS syntax with variables, nesting, mixins, and functions into plain CSS that browsers can read. ## Theory ### TL;DR - Preprocessors work like a word processor with mail merge: write structured templates with reusable parts, compile to plain CSS for the browser. - SCSS is a strict superset of CSS (any valid CSS is valid SCSS); original SASS uses indentation instead of braces; LESS uses `@` for variables and compiles via JS. - Main syntax split: SCSS and LESS use braces and semicolons; SASS uses indentation only. - Decision rule: SCSS for teams (easiest CSS migration); LESS for dynamic JS-driven theming; skip both if CSS custom properties cover your needs. - Bootstrap 5.3, Angular 18, and Bulma all ship with SCSS. ### Quick example ```scss // input.scss $blue: #007bff; .nav { background: $blue; .item { padding: 1rem; &:hover { background: lighten($blue, 10%); // computes to #4dabf7 } } } ``` ```css /* Compiled output */ .nav { background: #007bff; } .nav .item { padding: 1rem; } .nav .item:hover { background: #4dabf7; } ``` The compiler reads the nested structure, flattens it into standard selectors, and resolves all variable references in one pass. ### Key difference SCSS is 100% CSS-compatible. Paste any existing stylesheet into a `.scss` file and it compiles without changes. That alone makes migration painless. Original SASS trades braces and semicolons for significant whitespace, which some developers prefer for its minimal look. LESS looks similar to SCSS on the surface but uses `@variable` syntax, and its JS-based compiler can struggle with complex loops in large codebases. Most React projects I've worked on settled on SCSS once the component count grew past 20, simply because existing CSS snippets drop in without edits. ### When to use - **Team project with existing CSS**: SCSS - zero friction migrating files. - **Dynamic theming via JS** (like Ant Design 5): LESS - its `@var` system integrates naturally with JS config objects. - **Small project or prototype**: skip preprocessors, use CSS custom properties (`--color: red;`) - supported in all major browsers since 2016. - **Angular or React/Vite app**: any of the three - `sass-loader` or Vite's built-in Sass support handles compilation. - **Ruby stack**: SASS - deepest ecosystem there. ### Comparison table | Feature | SASS (.sass) | SCSS (.scss) | LESS (.less) | |---|---|---|---| | Syntax | Indentation only | Braces + semicolons | Braces + semicolons | | CSS superset | No | Yes (100%) | Yes (mostly) | | Variables | `$var` | `$var` | `@var` | | Mixins | `@mixin` | `@mixin` | `.mixin()` | | Compilation | Ruby/Node | Ruby/Node/Dart | JS/Node/browser | | Loops | `@each`, `@for` | `@each`, `@for` | Guards, recursive mixins | | When to use | Solo Ruby devs | Teams, CSS migration | JS projects, dynamic vars | ### How compilation works You write `.scss`, `.sass`, or `.less` files. A compiler (Dart Sass for SCSS/SASS since v1.77+, `lessc` for LESS v4.1+) parses the source into an abstract syntax tree, resolves all variables and mixin calls in a single pass, then emits standard CSS. Tools like `sass-loader` for Webpack v14+ or Vite's built-in support watch files and recompile on save, outputting to `/dist`. Browsers never see the source files. ### Common mistakes **Nesting more than 3-4 levels deep** ```scss // Wrong - produces .a .b .c .d .e { color: red; } a { b { c { d { e { color: red; } } } } } ``` Deeply nested selectors bloat compiled output and slow down selector matching. Chrome DevTools flags selectors beyond 6 levels. Keep nesting to 3 levels max, or use BEM: `.nav__item--active { color: red; }`. **Global variable overrides across files** ```scss // _theme.scss $color: blue; // _component.scss imports _theme.scss, then: $color: red; // overrides globally ``` Webpack's partial recompilation can miss dependencies and produce inconsistent output. Use `@use 'theme' as *;` with Sass modules (v1.23+). It namespaces variables and computes each file only once. **Using `@import` instead of `@use`** `@import` dumps everything into the global namespace and re-executes the file every time it appears. `@use` loads the file once and namespaces its variables as `theme.$primary`. Dart Sass deprecated `@import` in v1.80. **Browser-compiling LESS** The old pattern of loading `less.js` in the browser produces no source maps and breaks on modern mixins. Always pre-compile with `lessc` or a bundler. **LESS lazy evaluation in loops** ```less // LESS - guard-based loops can skip values unexpectedly .loop(@n) when (@n > 0) { .w-@{n} { width: ~"@{n}%"; } .loop((@n - 5)); } .loop(25); ``` For predictable loops, SCSS `@for` is a cleaner option: ```scss @for $i from 1 through 5 { .w-#{$i * 5} { width: $i * 5%; } } ``` ### Real-world usage - **Bootstrap 5.3**: SCSS variables and mixins for theming (`$primary`, `@mixin button-variant`). - **Ant Design 5**: LESS for dynamic theming with JS-driven variable overrides. - **Angular 18**: built-in SCSS support with `:host` component nesting. - **Bulma 1.0**: pure SCSS with CSS custom property fallback. - **React/Vite**: `sass-loader` plus SCSS modules per component. ### Follow-up questions **Q:** What is the difference between `@mixin` and `@extend` in SCSS? **A:** `@mixin` copies the declared styles into every selector that includes it, which can bloat output. `@extend` makes multiple selectors share one rule block, which is leaner but order-sensitive. Use `@extend` for utility classes like `.btn`; use `@mixin` when you need to pass arguments. **Q:** How do you handle vendor prefixes with SCSS? **A:** Run Autoprefixer as a PostCSS plugin after compilation. It reads your browserslist config and adds `-webkit-`, `-moz-`, etc. automatically. The preprocessor handles logic; Autoprefixer handles prefixes. **Q:** What is the difference between `@use` and `@import` in Sass? **A:** `@import` pollutes the global namespace and re-executes the file on each import. `@use` loads the file once and exposes its members under a namespace (`theme.$primary`). Introduced in Dart Sass v1.23; `@import` is deprecated since v1.80. **Q:** How does Dart Sass differ from LibSass? **A:** Dart Sass is the current official implementation (v1.77+ as of 2024) and tracks the spec. LibSass was a C++ port deprecated in 2021 that lagged on `@use` and `@forward`. If you are still on `node-sass`, migrate to the `sass` package. **Q (senior):** In a monorepo with 100+ SCSS partials, how do you prevent rebuild cascades? **A:** Use `@use` instead of `@import` so each partial computes once. Combine with Vite's `?inline` imports for component-level HMR. Profile slow builds with `sass --trace` and avoid glob imports - they break dependency tracking. ## Examples ### Themeable navbar with breakpoints A React app needs a navbar that adapts to screen size and pulls colors from a shared theme file. ```scss // styles/navbar.scss $primary: #0d6efd; $breakpoint-md: 768px; @mixin flex-center { display: flex; align-items: center; } .navbar { @include flex-center; padding: 1rem; background: $primary; @media (min-width: $breakpoint-md) { padding: 1.5rem; } .logo { font-size: 1.5rem; } &__link { color: white; text-decoration: none; &:hover { color: lighten($primary, 20%); } } } ``` ```css /* Compiled output (simplified) */ .navbar { display: flex; align-items: center; padding: 1rem; background: #0d6efd; } @media (min-width: 768px) { .navbar { padding: 1.5rem; } } .navbar .logo { font-size: 1.5rem; } .navbar__link { color: white; text-decoration: none; } .navbar__link:hover { color: #6ea8fe; } ``` The mixin handles flex setup, `$primary` is declared once at the top, and the media query is colocated with the component it affects - not buried in a separate breakpoints file. ### Sass modules with `@use` This pattern replaces `@import` in any Dart Sass project (v1.23+) and is the current recommended approach. ```scss // _theme.scss $primary: #0d6efd; $danger: #dc3545; $font-size-base: 1rem; ``` ```scss // button.scss @use 'theme'; .btn { font-size: theme.$font-size-base; background: theme.$primary; &--danger { background: theme.$danger; } } ``` The `theme.` prefix makes it clear where each variable originates. No more hunting through 50 partials to find which file last set `$primary`. ### Control flow: SCSS vs LESS Generating spacing utility classes with a loop is a common task. Here is how both preprocessors handle it. ```scss // SCSS - predictable @for loop @for $i from 1 through 5 { .mt-#{$i} { margin-top: $i * 0.25rem; // 0.25, 0.5, 0.75, 1, 1.25rem } } ``` ```less // LESS - recursive mixin guard (equivalent result) .spacing-loop(@n) when (@n > 0) { .mt-@{n} { margin-top: (@n * 0.25rem); } .spacing-loop((@n - 1)); } .spacing-loop(5); ``` Both produce the same five classes. The SCSS version reads as a standard loop; the LESS version requires understanding guard-based recursion before you can safely modify it.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.