Do we still need Sass and Less?

Do we still need Sass and Less?

Variables, nesting, calculations and colour manipulation used to be a convincing reason to install Sass or Less. Modern CSS now handles a substantial part of that work itself. So why are we still adding a preprocessor to every new project before writing the first selector?

For many projects, we no longer need to. But replacing a tool requires understanding what it does. Renaming style.scss to style.css is not a migration strategy, and a native feature with a familiar name does not necessarily behave the same way.

Written on 8 January 2026. This article compares features and browser support as they stood on that date. CSS, Sass, Less and browser implementations will continue to change. Treat the checklist as a dated snapshot and check current compatibility against your own browser requirements before making a decision.

Start with what you actually use

If your Sass files contain a few colour variables and nested selectors, the browser may already provide everything you need. If your design system generates hundreds of variants from maps, exposes configurable mixins and relies on a module system, the conversation is different.

I would start by inspecting a real stylesheet. List which preprocessor features it uses and why. A dependency should earn its place through useful work, rather than through the fact that the previous project also had it.

Equally, removing a compiler that is doing a useful job is not progress by definition. A smaller toolchain is attractive when it makes the project easier to maintain. It is less attractive when the same work returns as custom JavaScript nobody wanted to own.

Variables now belong in the browser too

CSS custom properties participate in the cascade and normally inherit. They can change with the theme or the element's context, and the browser resolves their values when applying styles.

:root {
  --brand: #086b9f;
  --surface: #ffffff;
  --space: 1rem;
}

[data-theme="dark"] {
  --brand: #7bc7ef;
  --surface: #10212b;
}

.card {
  color: var(--brand);
  background: var(--surface);
  padding: var(--space);
}

Place a card inside an element with data-theme="dark" and it picks up those values. You do not need to compile another copy of the card rule to change its colours.

A dollar sign is a different promise

Sass variables such as $brand, and Less variables such as @brand, are evaluated by the compiler. The browser receives the resulting CSS values, not those variables. Sass documents this distinction in its variable guide.

That makes preprocessor variables useful for values that should drive compilation. It also makes them a poor direct replacement for a value that needs to change at runtime. You can emit CSS custom properties from either preprocessor and use both layers deliberately.

There are limits in the other direction too. You cannot use var(--breakpoint) as the width condition of an ordinary media query. Nor can a custom property generate a selector name. “CSS has variables” does not mean every use of a Sass variable has a native equivalent.

Nesting is no longer a Sass feature

This is ordinary CSS in browsers that support native nesting:

.card {
  padding: 1rem;

  & .card-title {
    margin: 0;
  }

  &:focus-within {
    outline: 2px solid currentColor;
  }

  @media (min-width: 48rem) {
    padding: 1.5rem;
  }
}

The browser interprets the nested rules. There is no Sass or Less compilation step in this example. The CSS nesting guide covers the syntax and its relationship to selectors.

By the date of this article, nesting with relaxed parsing is available in Chrome and Edge 120, Firefox 117, and Safari 17.2 and later. Earlier Chrome and Safari versions shipped a more restricted form. WebKit's Safari 17.2 release notes explain that distinction. These versions describe the broad syntax milestone, not a guarantee that every later nesting edge case behaves identically.

Do not blindly copy the ampersand

In SCSS, .card { &__title { ... } } can generate .card__title. Native CSS nesting does not concatenate selector strings that way. Write the complete class selector instead. See the Sass parent selector and native nesting rules.

Specificity deserves a check as well. With a mixed parent selector list such as .card, #featured, native nesting uses the most specific parent in a way similar to :is(). That can differ from the separately expanded selectors of a preprocessor. The specificity documentation is worth reading before a large migration.

Keep nesting shallow regardless of the tool. A deeply nested selector is still difficult to override when the browser accepts it without a build step.

Let CSS calculate the layout

For values that depend on the viewport or the available space, the browser is where the relevant information exists. calc(), min(), max() and clamp() cover many calculations that once encouraged elaborate helper functions.

.page-title {
  font-size: clamp(2rem, 1rem + 3vw, 4rem);
}

.content-panel {
  width: min(100% - 2rem, 70rem);
  margin-inline: auto;
}

The title has lower and upper bounds, with a fluid value between them. The panel adapts to its containing block without needing a generated rule for every intermediate size. Still test zoom, text wrapping and your supported viewports; a compact formula is not a substitute for checking the result.

Colours can respond at runtime

CSS colour mixing can also work with a custom property:

.notice {
  background: #eaf3f8;
}

@supports (background: color-mix(in srgb, blue, white)) {
  .notice {
    background: color-mix(in srgb, var(--brand) 15%, white);
  }
}

The fallback remains available where the feature is unsupported. In supporting browsers, changing --brand changes the mixture. A preprocessor can calculate a colour while building; it cannot know an arbitrary runtime custom-property value.

Do not assume that replacing a Sass or Less colour function with a native one produces identical pixels. The selected colour space and the operation matter. Mixing with white, adjusting lightness and changing alpha are different decisions. Review the result and its contrast.

What Sass and Less still do well

Preprocessors can generate styles before the browser sees them. That remains useful when you want repeated output derived from data or reusable bundles of declarations with parameters.

Mixins and generated variants

A small Sass example can turn an agreed spacing scale into utility classes:

$spaces: (small: 0.5rem, medium: 1rem, large: 2rem);

@each $name, $size in $spaces {
  .gap-#{$name} {
    gap: $size;
  }
}

The result is three explicit CSS rules. CSS does not have a broadly supported general-purpose loop that emits those classes as of this article's date. Sass's map iteration is doing actual work here.

Less also offers iteration and parameterised mixins. For example:

.panel(@padding: 1rem) {
  padding: @padding;
  border: 1px solid currentColor;
}

.card {
  .panel(1.5rem);
}

The mixin emits declarations during compilation. A native custom property can supply a value to a declaration; it is not a drop-in mechanism for including an arbitrary bundle of declarations. The Less feature documentation and Sass mixin documentation describe their respective approaches.

Modules are another reason

Sass's module system, with @use and @forward, can organise configurable variables, functions and mixins. CSS imports stylesheets, but does not provide the same namespaced compile-time API. Less has its own imports and mixin namespaces; those are not the same module system as Sass.

This matters most when a library exposes a real authoring API to other teams. If the project is three small stylesheets, it may matter much less.

Do not confuse emerging with universal

It would be wrong to claim that custom CSS functions do not exist. Chrome 139 shipped them in August 2025, as described in the Chrome release announcement.

But on 8 January 2026, they are not a broadly supported replacement across Chrome, Firefox and Safari. Native mixins are not an established cross-browser replacement for preprocessor mixins either. A specification, an experimental implementation and a feature you can require from your audience are three different things.

That is why the comparison below distinguishes mature native functionality from limited support. The answer will change as browsers implement more of the platform.

Keep the build question separate

Removing Sass does not necessarily remove the build pipeline. You may still want bundling, minification, source maps or a standards-based CSS transformation for older browser targets.

Likewise, running Sass or Less does not automatically make every native CSS feature compatible with older browsers. A compiler may simply pass a declaration through. Always inspect the emitted CSS and the actual compatibility work your tools perform.

My approach for an existing project would be gradual: use custom properties where runtime values are useful, replace unnecessary helpers and assess which compile-time features remain. I would not rewrite a working stylesheet purely to make its extension shorter.

For a new project, plain CSS is a reasonable starting point. Add a preprocessor when there is a concrete need for its capabilities. The tool should follow the requirement.

Let LoopPress handle the build

Choosing Sass or Less does not mean every small project needs a build setup you have to assemble and maintain yourself. We built LoopPress for that practical part of the job. It compiles Sass and Less and optimises CSS locally on your Mac, so you can concentrate on the styles instead of wiring together the tools.

Keep writing SCSS, use Less or work directly in CSS: your website ultimately loads CSS either way. LoopPress handles the compilation where it is needed and the optimisation afterwards. Your deployed website does not need to know which authoring language produced that stylesheet, and it does not need LoopPress running on the server.

That does not make the languages interchangeable or remove the need to check browser support. It does make the build work less of a reason to choose one over another. Pick the approach that helps you maintain the project; let the tooling take care of producing the files. See what LoopPress can take off your hands.

The checklist

Snapshot: 8 January 2026. “Yes” means the technique supports the capability described, not that its syntax or behaviour matches the other columns. “Via CSS” means Sass or Less can emit native CSS, whose runtime support still depends on the browser. This comparison assumes modern Dart Sass and Less 4, rather than discontinued Sass implementations.

Native CSS, Sass and Less: feature checklist for January 2026
CapabilityNative CSSSassLess
Runtime variables and themesYes: custom propertiesVia CSSVia CSS
Compile-time variablesNo compiler variablesYes: $variablesYes: @variables
Nested selectorsYes, with browser requirementsYes, compiledYes, compiled
Append a class suffix with &No string concatenationYesYes
Runtime layout calculationsYes: calc(), min(), max(), clamp()Via CSSVia CSS
Compile-time arithmeticNo build-time evaluationYesYes
Colour manipulationYes, including runtime color-mix()Build-time functions; native CSS tooBuild-time functions; native CSS too
Parameterised declaration mixinsNo broad cross-browser equivalent yetYes: @mixin / @includeYes: parameterised mixins
Loops that generate selectorsNo general-purpose equivalentYes: @each, @for, @whileYes: each() or recursion
Custom author-defined functionsLimited browser supportYes: @functionPlugins; mixin-based return patterns
Named breakpoint substituted into @mediaNo, not with var()Yes, at compilationYes, at compilation
Namespaced compile-time modulesNo Sass-style module APIYes: @use / @forwardImports and mixin namespaces; different model
Container queries and cascade layersYes, subject to browser supportVia CSSVia CSS
Directly interpreted by the browserYesNo: compile to CSSNo: compile to CSS

Before choosing, check whether your project needs runtime values, compile-time generation or both; confirm your browser targets; and identify any mixins or module APIs that a migration would need to replace.

Modern CSS has made a preprocessor optional for much more work. It has not made every preprocessor feature redundant. I would stop treating Sass as a compulsory starting point, and stop treating its removal as a goal in itself. Use the layer that earns its place.

Comments

There are no comments yet, leave yours below.

Leave a comment

Do you have an addition, question or experience related to this article? Share it below.

Comments are briefly reviewed before they appear.

Read more about:

The bullshit of frontend development

The bullshit of frontend development

Frontend tooling was supposed to help us deliver. So why do straightforward websites turn into months of work? Drawing on my experience, I challenge unnecessary frameworks, custom abstractions and premature optimisation, and ask where accessibility and our users went in the process.

Continue reading

CSS-only Carousel Slider

CSS-only Carousel Slider

Does a carousel really need a JavaScript framework? This example builds a slider with HTML and CSS, then adds navigation between slides. It is an invitation to look at what the browser already provides before turning another interface element into a JavaScript component.

Continue reading

Dark mode on your website

A website can respond to the visitor's system appearance through prefers-color-scheme. This article introduces dark-mode styles and switching images with the same media query, using the browser support available at the time. A practical starting point for making more than just the page background adapt to a dark theme.

Continue reading