SCSS to CSS Compiler

The real Sass compiler, on your device.

  • Official compiler
  • Errors with lines
  • Source maps
Two blank rounded white panels on a wooden desk with a soft arrow between them and a small stack of two rounded wooden blocks

The guide

CSS Learned to Nest. Sass Still Earns Its Build Step, If the Compiler Is the Real One

Browsers now read nested rules and custom properties, yet a preprocessor still does work at build time that CSS cannot, and only the reference compiler says exactly what that work produced.

  • 2006 the year Sass first appeared
  • 1.80.0 the Dart Sass release that deprecated @import
  • 45rem what math.div(720px, 16px) * 1rem compiles to
  • 798 B the example project's CSS, expanded, with its map comment
Read the full guide Fold the guide 7 min · 19 sources

Sass first appeared in 2006. For most of the years since, a stylesheet language that offered variables and nested rules needed no defence, because CSS had neither. That argument is over. Browsers now read nested rules on their own, and custom properties have been part of CSS for years. So the fair question is not whether Sass is useful in general, but what it still does that CSS cannot, and what it costs to keep a compiler between your source and the page.

The short answer is that Sass works before the browser ever sees a file, and CSS works after. Everything worth keeping in a preprocessor follows from that one difference. So does every reason to compile with the real thing.

What Sass adds, and when it happens

Sass comes in two syntaxes. SCSS, in files ending in .scss, is with a few small exceptions a superset of CSS, so a valid stylesheet is already valid SCSS. The older indented syntax, in .sass files, drops braces and semicolons and uses indentation instead. Both describe the same language, and the compiler accepts either.

Variables are the first thing most people meet. A Sass variable such as $brand is gone by the time the CSS exists. The Sass documentation is blunt about it: Sass variables are compiled away, have one value at a time, and are imperative, so changing one later does not touch earlier uses.

Nesting came next, and it is still the most visible habit. A rule inside a rule becomes a longer selector in the output, and the parent selector & lets you build modifiers and states. Sass also treats & as text, which is why &__title inside .card can produce .card__title.

Mixins and functions do the rest of the heavy lifting. A mixin is a named block of declarations you include wherever it is needed, with arguments and even a content block of its own. Functions return values, and loops and conditions decide what gets written. None of this survives into the CSS: what arrives is the plain result.

The module system is the part people tend to undervalue. With @use, each file gets a namespace, by default the last part of its URL, and its members are visible only where they are loaded. Styles from a module appear exactly once in the output, however many files load it. Names that start with a hyphen or an underscore stay private to their file.

Then there are the built-in modules. Every one has a URL beginning with sass:, and the set is small and practical: color, list, map, math, meta, selector and string. They replaced a long list of global functions, and that change is still working its way through real codebases, as the next section shows.

What CSS now does on its own

CSS custom properties are the obvious counterpart to Sass variables, and they are not the same thing. The W3C module that defines them reached Candidate Recommendation, and its latest snapshot is dated 16 June 2022. A property such as --brand takes part in the cascade and inherits its value from the parent element, so the same name can hold different values in different parts of a page.

That is something Sass variables cannot do, and it is the reason custom properties won for theming. Switch a class on the root element and every var() reference follows. Sass would need a second compiled stylesheet to do the same.

The reverse is also true. MDN notes that custom properties do not work inside media queries or container queries. A breakpoint stored in a Sass variable, by contrast, is just text by the time the browser reads the query.

Nesting is the newer arrival. The W3C draft, a Working Draft dated 22 January 2026, lets one style rule sit inside another, with the child’s selector relative to the parent’s. MDN puts the difference from Sass plainly: the browser parses the nesting rather than a preprocessor compiling it away.

The draft also explains why the two cannot be swapped blindly. In CSS, the nesting selector behaves as if the parent selector were wrapped in :is(), which affects specificity and cannot represent pseudo-elements. And CSS will not glue selectors together as strings: the draft uses Sass’s own &Bar example and says that form is not allowed, because nesting matches elements rather than rewriting text. A BEM habit like &__title is a Sass feature, full stop.

When a preprocessor still pays for itself

If a project only needed nesting and a few shared colours, native CSS would now cover it. A preprocessor still earns its place when the stylesheet is closer to a program than a document. Generated utility classes from a map, spacing scales computed with real unit arithmetic, design tokens checked at build time, and a library split into private and public members all fall in that group.

Errors are part of that value. When a mixin is given a step that does not exist, a Sass @warn can say so during the build, with the file and line, rather than leaving a silent gap in a layout. In CSS, an invalid declaration is simply dropped by the browser, and nobody is told.

Here is a worked example you can reproduce in the compiler on this page. The example project has three files: main.scss, a _tokens.scss partial holding colours and a spacing map, and a _mixins.scss partial with a panel mixin and a tint function. One line in main.scss sets a maximum width with math.div(720px, 16px) * 1rem. Dividing pixels by pixels leaves the plain number 45, and multiplying by 1rem gives 45rem.

Compiled in the expanded style, the output holds 9 style rules, 22 declarations and one @media block. It is 760 bytes, or 798 B once the one-line comment pointing at main.css.map is added. The compressed style brings the same rules down to 611 bytes on a single line. A silent %focus-ring rule extended in three places turns into one rule listing all three selectors, and the .badge rule asks for a spacing step named xl that the map does not have. The compiler reports that warning at line 8, column 5 of _mixins.scss, called from line 47 of main.scss, and falls back to the default step.

The cost side is real, though. A build step means a tool to install, configure and keep current, and source maps to keep the browser’s developer tools honest. Teams that need little of the above often do better with plain CSS and a few custom properties.

Why the reference compiler matters

Sass is defined by its behaviour, and that behaviour has had more than one implementation. The Sass team declared LibSass and Node Sass deprecated on 26 October 2020, saying they no longer recommended LibSass for new projects and advising Dart Sass instead. The Dart Sass repository describes itself as the reference implementation of Sass, and the same code is compiled to JavaScript for the npm package.

The language keeps moving, too. As of Dart Sass 1.80.0 the @import rule is deprecated, along with the global versions of built-in functions that now live in sass: modules. Removal is planned for Dart Sass 3.0.0, no sooner than two years after 1.80.0. A compiler that is even a little behind will accept files the real one warns about, or reject files it now accepts.

That is the case for compiling with the package itself rather than a lookalike. Rounding of numbers, the order of extended selectors, which colours stay in hex, and the exact wording and position of a deprecation are all decided by the compiler. A homemade converter can get the easy cases right and still drift on the ones that matter when a release goes out.

Running that package inside a browser has its own rules. The Dart Sass documentation says the file-reading compile() function is not available there, so other files have to be supplied through a custom importer. The importer page spells out how such an importer should find a file: try the name with .sass and .scss, then with a leading underscore for partials, report an error if more than one candidate exists, and then look for an index file in a folder of that name.

Source maps tie the result back to the input. The command-line tool writes one by default for every CSS file, and the map format, now an Ecma specification, lists the original sources, can carry their text in a sourcesContent field, and encodes every position in a compact mappings string. With a map, a declaration that came from a mixin points at the mixin’s file and line, not at the line that included it.

What this compiler cannot do

This page runs the real package, version 1.105.2, in a background worker, and only project files can be loaded. Package imports, load paths and remote URLs are not available, because nothing is fetched. Output from unique-id() changes on every run by design, and the size and rule counts come from the page’s own reading of the compiled CSS, not from a browser engine. It compiles; it does not lint or reformat arbitrary CSS.

Sources

Every source consulted for this guide. Links checked October 18, 2026.

  1. Sass: Syntax Sass team, sass-lang.com (MIT License)
  2. Sass: Variables Sass team, sass-lang.com (MIT License)
  3. Sass: Style Rules Sass team, sass-lang.com (MIT License)
  4. Sass: @mixin and @include Sass team, sass-lang.com (MIT License)
  5. Sass: @function Sass team, sass-lang.com (MIT License)
  6. Sass: Flow Control Rules Sass team, sass-lang.com (MIT License)
  7. Sass: @use Sass team, sass-lang.com (MIT License)
  8. Sass: Built-In Modules Sass team, sass-lang.com (MIT License)
  9. Sass: Breaking Change: @import and global built-in functions Sass team, sass-lang.com (MIT License)
  10. Sass: LibSass is Deprecated Natalie Weizenbaum, Sass blog, 26 October 2020
  11. Sass: Importer | JS API Sass team, sass-lang.com (MIT License)
  12. Sass: Dart Sass Command-Line Interface Sass team, sass-lang.com (MIT License)
  13. GitHub - sass/dart-sass: The reference implementation of Sass, written in Dart. Sass project on GitHub (MIT License)
  14. CSS Nesting Module Level 1 W3C, Working Draft, 22 January 2026
  15. CSS Custom Properties for Cascading Variables Module Level 1 W3C, Candidate Recommendation Snapshot, 16 June 2022
  16. CSS nesting - CSS | MDN MDN Web Docs contributors (CC BY-SA 2.5 or later)
  17. Using CSS custom properties (variables) - CSS | MDN MDN Web Docs contributors (CC BY-SA 2.5 or later)
  18. Source map format specification Ecma International, TC39 (ECMA-426 draft)
  19. Sass (style sheet language) - Wikipedia Wikipedia contributors (CC BY-SA 4.0)