SCSS Beautifier

Type or paste on the left and the formatted SCSS rebuilds on the right as you go. Sass maps break onto their own lines, condition chains stay together, and the panel below points out the syntax Dart Sass has moved on from.

SCSS formatting workspace

Indent
Sass maps
Else branch
Your source
Formatted
0 deepest nesting level0 style rules0 lines out

Your source stays put while the formatted copy builds beside it

Most beautifiers overwrite the box you pasted into. When the result comes back wrong, and with preprocessor syntax it sometimes does, the original is already gone. This page keeps the two apart. The left pane holds what you typed, the right pane rebuilds after every keystroke, and the Use formatted button moves the result across once you have read it. The parser is JavaScript running in the page, so your stylesheet never leaves the browser.

Below the panes sit two readouts. One lists anything worth a second look, such as a legacy import or a slash used for division. The other shows the selectors your nesting compiles to, written out in full rather than reduced to a depth number.

What a plain CSS formatter does to SCSS

SCSS is a superset of CSS, so a CSS beautifier handles most of a file correctly and then quietly damages the rest. Four constructs cause almost all of the trouble.

Pasted in
$bp:(sm:36em,md:48em);@mixin above($k){@media (min-width:map-get($bp,$k)){@content}}
.card{padding:1rem;@include above(md){padding:2rem}
.card__title{font-size:1.25rem;&:hover{color:#0f766e}}}
Formatted out
$bp: (sm: 36em,md: 48em,);@mixin above($k) {@media (min-width: map-get($bp, $k)) {@content;}}
.card {padding: 1rem;@include above(md) {padding: 2rem;}
.card__title {font-size: 1.25rem;&:hover {color: #0f766e;}}}

Maps break onto their own lines, with a comma on the last pair

A breakpoint map on one line is fine at three entries and unreadable at twelve. Switching Sass maps to one pair per line splits it, and the trailing comma after the final pair is deliberate. Add a breakpoint later and the diff shows one added line instead of two changed ones, which matters on a file five people edit. Sass accepts the trailing comma in every version that supports maps.

The limit here is nesting. A map whose value is another map stays on a single line rather than opening a second level of expansion, because the indentation Sass authors use for nested maps varies by team and guessing wrong is worse than leaving it alone.

Why the else branch hugs the closing brace by default

A three branch condition is one decision, not three blocks. Keeping } @else if on one line makes the chain readable at a glance and stops the middle branch from looking like an unrelated rule that happens to sit nearby.

@mixin badge($tone) {@if $tone == danger {background: #b91c1c;} @else if $tone == warn {background: #b45309;} @else {background: #e2e8f0;}}

Some house styles put every brace on its own line, so the setting flips. Both forms compile the same way in Dart Sass.

Legacy imports, and why the panel keeps mentioning them

Sass has two ways to pull in another file and they behave nothing alike. @import pastes the file in wherever it appears, so a partial named by three other partials compiles three times and every variable in it lands in one shared pool. @use loads a file once, no matter how many places reference it, and puts its members behind a namespace.

Behaviour@import@use
Named twice in a projectCompiled twiceCompiled once
Variable accessGlobal pool, last write winsns.$name, traceable to a file
Private membersEverything is publicA leading underscore keeps it private
Position in the fileAnywhereAbove every style rule
Status in Dart SassDeprecatedCurrent

That last row is why the note appears. Dart Sass has deprecated the import rule for Sass files and the module system is what replaces it. The panel also raises a harder error when a @use line sits below a style rule, since Sass refuses to compile that file at all.

The slash stopped meaning division

CSS uses the slash as a separator in font: 14px/1.4 and in grid shorthands. Sass used the same character for division, and the two meanings collided often enough that Dart Sass 1.33 deprecated the arithmetic reading. Division now goes through the math module.

@use "sass:math";.col {width: math.div($container, 3);}

The check only fires when a variable sits on one side of the slash, which is the shape that used to mean division. A plain font: 14px/1.4 is left alone, because there it is doing its CSS job.

Nesting depth, written out as selectors

The depth counter reports the deepest single point in the file rather than an average, and the list underneath shows what those levels compile to. Seeing .page .card .body .list .item a lands differently from reading the number five. That selector needs a longer selector to override it, and it breaks the moment somebody moves a div.

The parent selector is free. Writing &:hover, &.is-active or &__title resolves into the parent instead of adding a level, so a BEM file full of ampersands stays at depth one no matter how long it gets. Only a real descendant adds to the count. When a selector list has commas, the first entry is the one shown.

Where this page stops

Questions about formatting SCSS

What the parser protects, what the notes mean, and where the output differs from your editor.

Why does the formatted output appear before I press anything?

The right pane rebuilds about a fifth of a second after you stop typing, so there is no format button to press. Watching the output shift while you edit also makes a broken block obvious early, since a missing brace pushes the indentation sideways as soon as you type past it. Nothing is sent anywhere while this happens.

Can I format a .sass file here?

No. The indented Sass format drops braces and semicolons and relies on indentation to mark structure, which is the exact information this parser reads from braces. Converting to SCSS first gives you a file this page handles, and the two formats compile to identical CSS either way.

The panel flagged my division. Does my stylesheet still build?

It builds and prints a deprecation warning on every compile. Dart Sass 1.33 marked slash division for removal and pointed authors at math.div instead. Load the math module once at the top of the file with a use rule and swap the expressions over. Slashes in font shorthands and grid areas are not affected and are not flagged.

What counts as a nesting level in the depth number?

Every style rule that sits inside another one adds a level. A top level rule is one, a rule nested inside it is two. The ampersand parent selector adds nothing, so hover states and BEM modifiers written with an ampersand stay at the level of their parent. At-rules such as media queries and mixin definitions are skipped as well, since they do not appear in the compiled selector.

Will it break my map if I have a map inside a map?

No, it leaves it alone. The outer map still expands one pair per line and the inner map stays on a single line as one value. Expanding both levels means picking an indentation style for nested maps, and teams do that differently enough that guessing causes more churn than it saves.

Why did my carefully grouped blank lines disappear?

The formatter rebuilds whitespace rather than adjusting yours, so blank lines inside a block are lost. The separation between top level rules is added back when the checkbox is on. If grouping declarations with blank lines is part of your house style, treat this page as a repair pass for badly formatted files rather than something to run on every save.

Does it keep my comments?

Yes, both the double slash form and the block form are preserved character for character. Position is where it gets loose. A comment sitting after a declaration stays on that line and a comment on its own line keeps its place, but a block comment spanning several lines keeps its original inner indentation instead of being reflowed to the new depth.

Can it handle a file mixing SCSS and plain CSS?

Yes. Plain CSS is valid SCSS, so keyframes, supports blocks, font face rules and custom properties all format as ordinary blocks. Custom property values are the one place to look afterwards, since a value written for a browser rather than for Sass sometimes carries spacing the formatter normalises.