Two file extensions carry the Sass name and they are not interchangeable. A .scss file looks like CSS with extra features bolted on. A .sass file drops every brace and semicolon and uses indentation to mark structure, the way Python does. Paste the second kind into a normal SCSS compiler and you get a parse error on line one. This page reads the indented dialect directly.
Indentation is the syntax, so it has to be consistent
In a .sass file, a line indented deeper than the line above it becomes a child of that line. There is no other signal. Mix a tab into a file indented with spaces and the parser sees an indentation jump it cannot resolve, then reports something vague about an unexpected token several lines later.
When the first parse fails on whitespace, this page retries with tabs converted to spaces and says so in the status bar. Treat that message as a warning rather than a pass. A local Sass build has no such retry and will reject the same file. Set your editor to insert spaces, two per level, and fix the source rather than relying on the recovery.
Pasting SCSS here also works. The indented parse fails first, the page reads the input as SCSS on the second attempt, and the compiled CSS comes out the same.
Which pieces survive into the CSS file
Most Sass features exist only at compile time. Variables, mixin definitions, and silent comments never appear in the output. Knowing which is which saves a lot of confusion when the compiled file looks shorter than the source.
| Feature | Indented .sass | SCSS equivalent | In the CSS output? |
|---|---|---|---|
| Variable | $brand: #cd6799 | $brand: #cd6799; | No. The value gets substituted wherever you used it. |
| Nested rule | indent under the parent | { } braces | Yes, flattened to .card .title. |
| Parent reference | &:hover | &:hover | Yes, written out as .btn:hover. |
| Mixin definition | =button($bg) | @mixin button($bg) | No. A definition alone emits nothing. |
| Mixin call | +button(#cd6799) | @include button(#cd6799); | Yes. Declarations get copied into each caller. |
| Placeholder | %chip with @extend | %chip with @extend | Yes, as one grouped selector list instead of repeated blocks. |
| Loop | @each, @for | same | Yes, one rule per iteration. |
| Silent comment | // note | // note | No, stripped during compile. |
| Loud comment | | | Yes, except in minified output. |
Load the Mixins example above and watch the difference in practice. The source defines =button once and calls it twice. The output contains two full rule blocks with no trace of the mixin, which is why heavy mixin reuse grows a CSS file faster than people expect. Placeholders with @extend group selectors instead of duplicating declarations, so they stay smaller at the cost of selector order you control less directly.
Mixin syntax trips up almost everyone
The indented dialect gives mixins their own shorthand. =name defines one and +name calls it. Both @mixin and @include also work in .sass files, so you will meet both styles in older codebases:
=card-shell
border-radius: 8px
padding: 16px
.pricing-card
+card-shell
border: 1px solid #e5e7ebDefine before you call. The compiler reads top to bottom, so a +card-shell above its =card-shell definition fails with a missing mixin error even though the name is spelled correctly.
The three compiler errors worth recognizing
- Invalid CSS after "...": expected expressionNearly always an indentation problem, not a value problem. Check the line above the reported one for a stray tab or an extra space level.
- File to import not found or unreadableAn
@importpointing at a partial such as_variables. This converter has no file system, so partials cannot resolve. Paste the partial contents above the code that needs them. - No mixin named xEither the call sits above the definition, or the file used
+xwhile the definition lives in a separate partial that never got pasted in.
Empty output is not always a failure. A file holding only variables and mixin definitions compiles cleanly and produces zero bytes of CSS, because nothing in it applies to a selector. The status bar says so rather than showing a blank pane with no explanation.
What runs here and what still needs a build step
Compiles in this tab
- Variables, arithmetic, and interpolation with
#{} - Nesting of any depth, plus
&parent references - Mixins with defaults and arguments, written as
=/+or@mixin/@include - Control directives:
@if,@else,@each,@for,@while - Legacy color functions such as
lighten(),darken(),mix(),rgba() - Nested
@mediaand@supportsblocks - Maps, lists,
@extend, and placeholder selectors
Needs Dart Sass on your machine
@useand@forward. The module system arrived after this compiler engine froze.- Namespaced built-ins like
math.div()orcolor.adjust() @importof separate partial files- Source maps for browser debugging
- Vendor prefixing, which belongs to Autoprefixer rather than Sass
- Watching a folder and rebuilding on save
The engine behind this page is LibSass, compiled to run in the browser. LibSass stopped at the Sass 3.5 feature set and was retired in favor of Dart Sass. For converting a legacy .sass file, checking what a snippet expands to, or grabbing output during a code review, that gap rarely matters. For a project you actively ship, install Dart Sass and keep the build local. Division with / still works here while newer Dart Sass versions want math.div(), so code copied from this page into a modern build may raise deprecation warnings.
Moving off indented Sass
Teams migrating away from .sass usually go to SCSS first rather than straight to plain CSS, because SCSS keeps the variables and mixins intact. Compiling to CSS the way this page does throws that structure away, which is what you want when handing a stylesheet to a system with no build pipeline: a CMS theme field, an email template, or a plugin that accepts CSS only.
If you want to keep the features and only change the punctuation, use the SASS to SCSS converter instead. If you want the compiled result smaller, switch the output selector above to Minified, or run the expanded output through the CSS minifier for finer control over what gets stripped.
Privacy and file size
Compilation happens in JavaScript inside your browser tab. No stylesheet text is sent anywhere, which makes the tool usable on client work under NDA. The practical ceiling is around a few thousand lines. Past that, the browser main thread blocks noticeably during compile, and a local Dart Sass run finishes faster anyway.
