LESS to SCSS Converter
Convert LESS stylesheets to SCSS syntax online. Transforms variables, mixins, guards, extend, interpolation, and functions with accurate regex-based parsing.
About
Migrating a codebase from LESS to SCSS is not a find-and-replace job. LESS uses @ for variables while SCSS uses $, but @ also prefixes CSS at-rules like @media and @keyframes. A naive substitution breaks every media query in the project. LESS mixin syntax overloads class selectors, meaning .border-radius() is both a callable mixin and a valid CSS class pattern. Guard expressions (when) have no direct SCSS equivalent and must map to @if blocks. This converter handles those edge cases: variable scoping, string interpolation (~"..." → #{...}), mixin detection with parameter preservation, extend rewriting, and function renaming (spin → adjust-hue, fade → rgba). It operates entirely in the browser with no server round-trip.
Limitations: deeply nested mixin namespaces and JavaScript-evaluated expressions (`...` backtick blocks) have no SCSS equivalent and are flagged as warnings rather than silently mangled. Programmatic LESS features like recursive mixin pattern-matching require manual refactoring. The tool approximates conversion for roughly 95% of real-world LESS codebases. Always diff the output against your original and run your test suite before committing.
Formulas
The converter applies a deterministic pipeline of regular expression transformations in strict order. Each stage mutates the source string exactly once. The pipeline sequence matters: variables must be converted before mixin parameters, and at-rule exclusion must precede variable substitution.
Stage 1 - Variable Declaration: @name: value → $name: value
Exclusion set = { @media, @import, @keyframes, @font-face, @charset, @supports, @page, @namespace, @layer }
Stage 2 - Interpolation: @{name} → #{$name}
Stage 3 - Mixin Definition: .name(params) { → @mixin name(params) {
Stage 4 - Mixin Call: .name(args); → @include name(args);
Stage 5 - Guard: when (cond) → @if (cond)
Stage 6 - Extend: &:extend(.sel) → @extend .sel;
Stage 7 - Function Renames: spin → adjust-hue, e → unquote, fade → rgba
where name = any valid CSS identifier matching [a-zA-Z_][a-zA-Z0-9_-]*, params = comma-separated parameter list with optional defaults, cond = boolean expression from LESS guard syntax, .sel = any valid CSS selector.
Reference Data
| LESS Syntax | SCSS Equivalent | Notes |
|---|---|---|
| @variable: value; | $variable: value; | Prefix swap. At-rules excluded. |
| @{variable} | #{$variable} | String interpolation in selectors & properties. |
| ~"expression" | unquote("expression") | Escape hatch for raw CSS output. |
| .mixin() { } | @mixin mixin() { } | Parenthesized LESS class = mixin definition. |
| .mixin(); | @include mixin(); | Mixin call detected by trailing semicolon. |
| .mixin(@a, @b) | @mixin mixin($a, $b) | Parameters converted recursively. |
| .mixin() when (@a > 0) | @mixin mixin() { @if ($a > 0) { ... } } | Guard → conditional block. |
| &:extend(.class) | @extend .class; | Extend syntax normalization. |
| :extend(.class) | @extend .class; | Standalone extend form. |
| spin(color, deg) | adjust-hue(color, deg) | Color function rename. |
| fade(color, 50%) | rgba(color, 0.5) | Opacity conversion: 50% → 0.5. |
| fadein(color, 10%) | lighten(color, 10%) | Approximate mapping. |
| fadeout(color, 10%) | darken(color, 10%) | Approximate mapping. |
| e("text") | unquote("text") | Direct function rename. |
| .class when (default()) | @mixin class() { @else { ... } } | Default guard → else branch. Manual review recommended. |
| #namespace > .mixin() | @include mixin(); | Namespace stripped. Flagged as warning. |
| @import "file.less" | @import "file" | Extension removed per SCSS convention. |
| @arguments | $args... | Rest parameter mapping. Context-dependent. |
| `javascript` | - | No SCSS equivalent. Flagged as error. |
| & when (...) | @if (...) { & { ... } } | Parent selector guard. |