A LESS file is an outline. The CSS is the bill.
Compiling looks like translation. It behaves like duplication. A mixin called in nine rules writes its declarations nine times. A block nested four levels deep becomes a four part descendant selector with the specificity to match. None of it raises a warning, because none of it is an error. The report under the output pane puts numbers on the parts you would otherwise notice weeks later, when one override refuses to stick.
This page runs the official Less 4 compiler in your tab. Division follows the Less 4 default, so a slash outside parentheses stays a slash. If you need to compare that behaviour against Less 3, the LESS to CSS Converter has a division switch built for the comparison.
Specificity grows while you are looking at the source
Nesting reads as structure in LESS and compiles to descendant chains in CSS. Four levels of indentation feels tidy. The browser receives this:
.page {.sidebar {.widget {.title {color: #1d365d;}}}}.page .sidebar .widget .title {color: #1d365d;}Score 40, four parts. Any later rule wanting a different colour on that title needs 40 or more of its own, or the markup needs to keep every ancestor in place forever. Move the widget into a footer and the rule stops matching. The Deep nesting sample loads a small component that produces exactly this shape, so you see the table fill with high scores from a file under thirty lines.
Two levels is a workable ceiling in most component work. Below that, prefer a written out class name over another level of indentation. &__title compiles to a single class with a score of 10 and no ancestry requirement.
What the parts column tells you that the score does not
Score and parts measure different failures. #app .card .title scores 120 with three parts. .a .b .c .d .e scores 50 with five. The first is hard to override. The second is brittle, because five ancestors have to keep their relationship for the rule to apply. High score with low parts is a specificity problem. Low score with high parts is a structure problem. Both columns are in the table for that reason.
Mixin calls copy, they do not reference
LESS has no runtime. Calling .pill() inside twelve rules writes those declarations into twelve blocks. There is no shared class behind the output, and nothing links the copies back to the definition.
.pill() {border-radius: 999px;padding: 4px 12px;font-size: 12px;}
.tag-new { .pill(); background: #0f7a5f; }
.tag-beta { .pill(); background: #a15c00; }
.tag-old { .pill(); background: #6b7280; }Three calls, nine copied declarations. Gzip handles the bytes well, since repeated text compresses hard. The real cost lands on maintenance. Change the padding in the mixin and every copy moves, which is the point. Change it in one compiled rule by hand and the next compile erases the edit.
The repeated declarations panel counts pairs appearing three times or more. Seeing border-radius: 999px at a count of forty is a signal worth acting on, not a defect. Ask whether those forty rules want a shared class in the markup instead of a copied block in the CSS. Sometimes the answer is no, because the markup belongs to a template you do not own. The count still tells you which mixins carry the weight of the sheet.
Parentheses decide whether the definition ships
A ruleset written with empty parentheses is a mixin and stays out of the output. Drop the parentheses and it becomes a class, emitted on its own line and copied into every caller. Both compile without complaint. The only tell is an extra rule in the CSS, which the rule count in the report will show.
Strict units turns silent nonsense into a stopped build
By default, Less resolves arithmetic across mismatched units by taking the unit of the first operand. 2px * 3em gives 6px. No warning, no error, and a number nobody meant. Turn on Strict units in the rail and the same expression fails the compile with a message naming the operation.
@gap: 2px;@scale: 3em;.row { margin: @gap * @scale; }
.row { margin: 6px; }@gap: 2px;@scale: 3em;.row { margin: @gap * @scale; }
Operation on an invalid typeThe flag is off by default here, because switching it on breaks a lot of working stylesheets on their first compile. Load the Unit math sample, compile with the box clear, then compile again with it ticked. The difference between the two runs is a list of expressions worth reading. Multiply a length by a plain number rather than by another length and the ambiguity disappears.
The IE compatibility box is narrower than it sounds
It does one thing: values above 4095 units in properties Internet Explorer 8 mishandled get held back rather than emitted. Almost nobody targets that browser now. The box stays ticked by default to match the compiler default, and clearing it removes the guard. If a computed value keeps coming out lower than your arithmetic predicts, this flag is worth checking before you doubt the maths.
Reading a compile error with no file open
Less errors arrive with a line number, a column, and three lines of surrounding source. The panel between the editors prints all of it, and the jump button moves the cursor to the failing line in the source pane. Four messages cover most failures:
- Unrecognised input
- Usually a missing brace or semicolon above the reported line. Less reports where parsing gave up, not where the mistake was typed. Read upward from the marked line.
- variable @name is undefined
- The definition sits in a partial the page cannot read, or in a scope the usage falls outside. Paste the partial above your source and compile again.
- No matching definition was found for .mixin(...)
- Argument count or a guard condition. A mixin defined with two parameters and called with three has no match, and guards narrow the signature further.
- Operation on an invalid type
- Arithmetic across incompatible values, most often with strict units on, or maths applied to a colour and a length.
Where this compiler stops
Everything runs in the page and nothing is uploaded, which sets the boundaries:
- No
@import. There is no project folder behind a browser tab. Import statements fail or pass through as plain CSS imports. Paste partials inline, in the order the build would read them. - No
@pluginand no inline JavaScript. Less 4 disabled JavaScript evaluation by default for security reasons and this build keeps it disabled. - No source maps. Output is CSS text. Tracing a compiled line back to its LESS origin needs the command line compiler.
- No vendor prefixes. LESS never added them. Run the output through Autoprefixer if older browsers are in scope.
- The report reads text, not intent. Specificity scoring parses the compiled selectors. It knows nothing about which rules your markup reaches, so a high score on a deliberate override is a number, not a verdict.
- Whole file compiles. A few thousand lines is fine. A full theme dump will pause the tab while it runs, since the work happens on the main thread.
For a single quick conversion with live typing, the LESS to CSS Converter is the lighter page. Tidy an inherited file first with the LESS Beautifier, and shrink the finished sheet with the CSS Minifier, which goes further than the compress flag here.
