A stylesheet written against Less 3 compiles differently under Less 4, and the change is quiet. width: @grid / 3 produced 320px for years. Under Less 4 the same line ships @grid / 3 through to the CSS untouched, and the layout collapses with no error to point at. The Division control above switches between both rule sets so you see which one your source was written for.
What a compile pass actually does to your code
LESS is not a smaller language sitting on top of CSS. It is a text expander. Four things happen when you press Compile:
- Variables get inlined. Every
@brandis replaced by its literal value at each use site. Nothing about the variable survives into the output. - Nesting gets flattened. A rule three levels deep becomes one long descendant selector. Deep nesting is where specificity problems are born, and the right pane shows the damage immediately.
- Mixins get copied. Calling
.pill()in six places writes those declarations six times. LESS has no shared class behind the scenes. - Guards and loops resolve. A recursive mixin unrolls into real rules before anything reaches the browser.
The counter under each pane tracks this. Load the Mixins sample and watch the byte count climb on the right. Output growing by 40 percent is normal, and it is the reason a minifier belongs at the end of a LESS build rather than the start.
Division: the one migration that breaks silently
Less 4 shipped with math: parens-division as the default. A slash outside parentheses is treated as a CSS separator, not an operator, because CSS itself uses slashes in font, grid-area, border-radius, and aspect-ratio.
@grid: 960px;.col { width: @grid / 3; }
.col { width: 320px; }@grid: 960px;.col { width: @grid / 3; }
.col { width: 960px / 3; }Wrap the expression in parentheses and both modes agree: width: (@grid / 3) gives 320px everywhere. That is the fix worth applying across a codebase, since it survives whichever compiler version the build lands on. The third option in the dropdown, parens, goes further and requires parentheses around every operation, multiplication included, which surfaces arithmetic you forgot was there.
Legacy stylesheets are the common case for the Less 3 setting. Compile once under each mode and diff the two outputs. Any rule that differs is a division depending on the old behaviour, and those are the lines needing parentheses before you upgrade the build.
Parentheses decide whether a mixin appears in the output
This trips people up more than any other rule in the language. A ruleset defined with empty parentheses is a mixin and stays out of the CSS. The same ruleset without them is a class and gets emitted alongside every call site.
.rounded() {border-radius: 10px;}
.card { .rounded(); }
.card { border-radius: 10px; }.rounded {border-radius: 10px;}
.card { .rounded; }
.rounded { border-radius: 10px; }
.card { border-radius: 10px; }Both compile without complaint, so the only signal is dead weight in the output. If a utility class was meant to be reusable markup, drop the parentheses on purpose. If it exists only for other rules to pull in, keep them.
Arguments use semicolons when values contain commas
LESS accepts both separators in a mixin signature, and the comma loses whenever an argument holds a comma of its own. .shadow(0 1px 2px #000, 0 4px 8px #333) reads as two arguments. Write .shadow(0 1px 2px #000, 0 4px 8px #333;) with a trailing semicolon, or separate parameters with semicolons throughout, and the whole list arrives as one value.
The ampersand is a selector, not just a nesting shortcut
Inside a nested block, & stands for the full parent selector, and it works anywhere in the child selector rather than only at the front. Load the Nesting sample to see the three patterns worth knowing:
&:hoverattaches a state to the parent with no descendant space.&--stackedbuilds BEM modifier names by string concatenation. The compiled selector is.nav--stacked, and searching your codebase for that literal string finds nothing, which is the known cost of the pattern..theme-dark &puts the parent last, producing.theme-dark .nav. Theming hooks live here.
Repeating it doubles specificity: && compiles to .nav.nav. Useful for winning a fight with a third party stylesheet, and worth a comment when you use it.
Variables resolve at the end, not in order
LESS uses lazy evaluation. A variable takes the value of its last definition in the current scope, not the one above the line using it. This compiles to color: green:
@color: red;.box { color: @color; }
@color: green;Redefining a variable inside a block scopes the change to that block and everything nested under it. There is no mutation and no assignment order to reason about, which makes theme overrides straightforward and makes debugging a stray value harder. When the output surprises you, search for a second definition of the same name before anything else.
Interpolation and escaping
A variable used as a value needs no ceremony. A variable used inside a selector, a property name, a URL, or a media query needs @{braces}:
@side: left;@min: ~"(min-width: 48em)";.margin-@{side} { margin-@{side}: 12px; }
@media @min { .nav { gap: 24px; } }The tilde form passes a string through with no parsing. Media query conditions need it because LESS otherwise reads the colon and parentheses as its own syntax. The same escape covers CSS features the compiler predates, such as aspect-ratio: ~"16 / 9", where an unescaped slash meets the division rules described above.
Where this converter stops
Everything runs in your browser through the official Less compiler. Your source is never uploaded, which also sets the limits:
- No
@import. There is no filesystem to read from, so an import statement fails or passes through as a plain CSS import. Paste the imported partials above your main file, or work one file at a time. - No plugins.
@plugindirectives and inline JavaScript are disabled. Less 4 turned JavaScript evaluation off by default for security reasons, and this build keeps it off. - No source maps. Output is CSS text only. Debugging a large stylesheet back to its LESS line needs the CLI compiler with the source map flag.
- No vendor prefixes. LESS never added them, and neither does this page. Run the result through Autoprefixer or PostCSS if you support older browsers.
- Large files feel it. Compile-while-typing waits for a pause and then recompiles the whole document. Above a few thousand lines, switch the toggle off and press Compile when you are ready.
The minify option uses the compiler's own compressor, which strips whitespace and comments and nothing more. For real size reduction, feed the result to a dedicated minifier afterwards.
Related work in this category: the LESS Beautifier tidies the source before compiling, and the CSS Minifier squeezes the output afterwards.
