SCSS to CSS Converter
About this tool
Paste any SCSS block on the left — nested rules with the & parent reference, $variables, nested @media queries, same-unit math — and get plain CSS on the right. Live conversion as you type, toggle between inline-value or CSS custom-property variable modes, copy the output in one click. No build step, no npm install, no server round-trip — the compiler runs entirely in your browser. Handles the SCSS feature set that ~90% of real-world snippets use; unsupported features (mixins, @extend, color functions, control directives) surface as amber warnings so you know what to hand-audit.
Why an SCSS → CSS converter
How to use this converter
Conversion examples
/* Input */
.card {
padding: 1rem;
border-radius: 8px;
&:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.title {
font-size: 1.25rem;
font-weight: 600;
}
}
/* Output */
.card {
padding: 1rem;
border-radius: 8px;
}
.card:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.card .title {
font-size: 1.25rem;
font-weight: 600;
}/* Input */
$brand: #7c3aed;
$radius: 8px;
.button {
background: $brand;
border-radius: $radius;
color: white;
}
.link { color: $brand }
/* Output — "Inline values" mode */
.button {
background: #7c3aed;
border-radius: 8px;
color: white;
}
.link {
color: #7c3aed;
}/* Same input, "CSS variables" mode */
:root {
--brand: #7c3aed;
--radius: 8px;
}
.button {
background: var(--brand);
border-radius: var(--radius);
color: white;
}
.link {
color: var(--brand);
}
/* Now the palette is runtime-themeable
via document.documentElement.style.setProperty(). *//* Input — nested @media */
.container {
padding: 1rem;
@media (min-width: 768px) {
padding: 2rem;
max-width: 720px;
}
}
/* Output */
.container {
padding: 1rem;
}
@media (min-width: 768px) {
.container {
padding: 2rem;
max-width: 720px;
}
}Pro tips (and the sharp edges)
Frequently asked questions
Which SCSS features does this converter handle — and which does it skip?
The tool handles the SCSS feature set that ~90% of real-world SCSS snippets use: nesting (with correct & parent-reference flattening), $variables (with inline substitution OR CSS custom-property emission), nested @media queries (correctly hoisted to top level), same-unit math (12px + 4px → 16px), block comments (preserved as valid CSS), and line comments (// stripped since they'd be invalid CSS). Skipped (because they require a real Sass compiler with file-tree access + control-flow evaluation): @mixin/@include (needs argument binding + @content block resolution), @extend (multi-selector chain expansion), @import/@use/@forward (partial-file resolution — the browser can't read your project tree), Sass color functions (lighten(), darken(), mix(), adjust-hue()), control directives (@if/@each/@for/@function). When any unsupported feature appears in your input, the tool emits an amber warning listing exactly which feature was skipped so you know what to hand-audit. For those cases: install real Sass (npm install -D sass) and run a proper compile.
Should I convert my SCSS to plain CSS, or migrate to CSS custom properties?
Depends on why you're doing the conversion. Inline values mode (default) is right when you're converting a snippet from a tutorial, Stack Overflow answer, or design-system doc for immediate use — smallest output, no runtime cost, matches how Sass compiles by default. CSS variables mode is right when you're actually MIGRATING off Sass at codebase scale. CSS custom properties have been Baseline browser-supported since 2020 (Chrome 49, Safari 9.1, Firefox 31 — literally every browser except IE11), and unlike Sass $variables they can be updated at runtime, inherited through the cascade, and swapped by adding/removing classes on <html> — which is what makes light/dark themes and per-brand palettes trivial. If your codebase is using Sass primarily for the $brand/$radius/$spacing token layer, custom-properties mode gives you the migration path where you keep the theming pattern but drop the build step. Vercel, Linear, GitHub, and Stripe all migrated design systems from Sass tokens to CSS custom properties for exactly this reason.
How does nesting flatten — especially with the & parent reference?
The tool follows Sass's exact rules for nesting resolution. Given .card { &:hover { color: red } }, the & gets replaced with the parent selector before flattening: output is .card:hover { color: red } (no space — the ampersand concatenates directly). Contrast with .card { :hover { color: red } } (no ampersand): output is .card :hover { color: red } (WITH space — selects hovered descendants, not the card itself). This is the #1 place manual SCSS→CSS conversions break: devs write &:hover in SCSS but manually convert to .card :hover, which changes the selector meaning entirely. The tool handles the concatenation rules correctly. Comma-separated parents expand cartesian: .a, .b { & > c { } } becomes .a > c, .b > c { }. Deep nesting works recursively — .a { .b { .c { &:hover { } } } } becomes .a .b .c:hover { }. Every rule Sass docs cover is implemented; if you spot a case where the output diverges, that's a bug worth reporting.
Are nested @media queries actually valid to write inside a rule?
In SCSS, yes. In vanilla CSS, actually YES now too — CSS Nesting (Baseline 2024, Chrome 120+, Safari 17.4+, Firefox 117+) allows nesting including @media queries inside rules. But if you're supporting older browsers or converting for a bundle that has to work on 2022-era Safari, you want the flattened output: the SCSS pattern .container { @media (min-width: 768px) { padding: 2rem } } becomes .container { } @media (min-width: 768px) { .container { padding: 2rem } } in the output. The tool always hoists nested @media to the top level — matches how real Sass compiles it, and produces CSS that works everywhere without requiring the CSS Nesting spec. If you want the modern flat-nested CSS output instead (skipping the hoist), that's what real Sass does when you use @included nesting or when you write CSS nesting directly — but this converter treats @media the traditional Sass way.
What's the difference between this and installing Sass locally with `npm install -D sass`?
Scope. Real Sass (npm install -D sass or Dart Sass via sass-embedded) is a full compiler with file-tree resolution (@import 'partials/typography' reading actual files), function definitions (@function multiply($a, $b) { @return $a * $b }), mixin argument binding with defaults (@mixin card($radius: 8px)), the full color-function library (lighten($brand, 10%), mix($a, $b, 40%)), control flow (@each $color in $palette), and the full @use/@forward module system. This tool handles the SUBSET of SCSS you can compile deterministically in-browser without file-system access — nesting + variables + @media + math + basic @extend. That covers the vast majority of code snippets you'd paste from a tutorial or a legacy file; it doesn't cover a real Sass codebase's build. Rule of thumb: if the SCSS you're converting fits in a code-block on a docs page, this tool works. If it's a multi-file project with partials + mixins + color functions, install real Sass.
Can I use this converter offline / does it send my code anywhere?
Zero network. The SCSS → CSS conversion runs entirely in your browser via inline JavaScript — no server call, no API upload, no telemetry on the code content. The site itself uses Google Analytics for pageview counts and Mediavine for ads (both standard, both toggleable via browser extensions), but neither ever sees the SCSS or CSS text you paste into the converter. Safe for confidential design tokens, unreleased branding colors, internal-tool CSS. If you want to work fully offline, save the /tools/scss-to-css-converter/ page for offline (Chrome: three-dot menu → Save page as → Complete webpage) — the converter still works because the JavaScript is bundled into the HTML.