Converting flat CSS into nested LESS
In plain CSS every rule repeats its full selector: .card, then .card .title, then .card .title a. LESS lets you nest those rules inside each other so the shared prefix is written once. This converter does that rewrite for you, and it's careful about one thing above all: the LESS it produces compiles back to CSS that behaves exactly like your input.
It reads the stylesheet properly (strings, comments, url(...) values and nested braces included), nests what can be nested safely and copies everything else through unchanged.
Example
Input:
.card { padding: 16px; border-radius: 8px; }
.card .title { font-size: 18px; font-weight: 600; }
.card .title a { color: #0a58ca; }
.card .body { color: #333; }
Output:
.card {
padding: 16px;
border-radius: 8px;
.title {
font-size: 18px;
font-weight: 600;
a {
color: #0a58ca;
}
}
.body {
color: #333;
}
}
Compiling that LESS gives back the original four selectors, because a nested selector in LESS means "descendant of the parent" (LESS nesting docs). Output is indented with four spaces, and each declaration is printed on its own line with a semicolon.
Combinators, comments and media queries
A slightly bigger stylesheet shows the rest of the behaviour:
/* Navigation */
.nav { display: flex; }
.nav > li { padding: 8px; }
.nav > li a { color: #0a58ca; }
.nav > li a:hover { color: #084298; }
@media (max-width: 600px) {
.nav { flex-direction: column; }
.nav > li { padding: 4px; }
}
becomes:
/* Navigation */
.nav {
display: flex;
> li {
padding: 8px;
a {
color: #0a58ca;
}
a:hover {
color: #084298;
}
}
}
@media (max-width: 600px) {
.nav {
flex-direction: column;
> li {
padding: 4px;
}
}
}
The child combinator stays attached to the selector that follows it, so .nav > li becomes > li inside .nav, which LESS compiles back to .nav > li. The comment is kept where it was, and the media query keeps its condition with its own rules nested inside it.
How the converter treats each kind of rule
| Input | What you get |
|---|---|
.a .b descendant selectors |
Nested .a { .b { ... } } |
.a > .b, .a + .b, .a ~ .b |
Nested .a { > .b { ... } } and so on |
| The same selector twice in a row | One block with both sets of declarations |
h1, h2 { ... } with a comma |
Kept whole as a top-level h1, h2 block |
.btn:hover |
Separate top-level .btn:hover block, not &:hover |
@media, @supports, @container, @layer |
Query kept, rules inside nested |
@font-face, @keyframes, @import |
Copied through unchanged |
/* comments */ |
Kept in place, inside or between rules |
url(data:...;base64,...) and quoted strings |
Values kept whole |
| A rule that already contains nested blocks | Copied through unchanged |
Unbalanced braces, an unclosed string or an unclosed comment are reported as errors instead of producing a half-converted file.
Why the cascade is preserved
When two CSS rules match the same element with equal specificity, the later one wins, so the order of rules matters. The converter only merges a rule into an existing block when that block is the most recent thing written. Given .a .b { color: red; }, then .c { margin: 0; }, then .a .b { color: blue; }, you get two separate .a blocks with .c between them rather than one merged block. That looks a little repetitive, but the blue still comes last and still wins. You can merge such blocks by hand once you've checked nothing in between targets the same elements.
A safe workflow
- Check the stylesheet's braces and declarations with the CSS validator.
- Paste the stylesheet and click Convert.
- Review any blocks that were copied through unchanged (selector lists, at-rules other than the grouping ones) and nest them by hand if you want to.
- Compile the LESS once and compare the generated CSS with the original, for example in a diff tool.
What it leaves for you
The converter doesn't introduce any LESS features beyond nesting. Pseudo-classes aren't moved under their parent with &, repeated values aren't extracted into @variables, and shared groups of declarations aren't turned into mixins. Those are the changes that make a LESS codebase easier to work with, and they're judgement calls a mechanical tool can't make well. The output targets LESS 4, where / in a value such as font: 12px/1.5 is left as written.
If you'd rather keep plain CSS, modern browsers also support native CSS nesting, with similar syntax. Either way, the CSS minifier shrinks the compiled result, the online CSS editor is handy for cleaning it up, and the CSS lessons cover the selector and specificity rules that nesting builds on.
Last updated