Less - Nesting Generates Bad CSS Code

LESS - Nesting generates bad CSS code?

I'm using a lot of nesting to group parent and child elements, will that generate bad CSS code in the end?

In a word, yes. In the long run this will give you highly specific, unmaintainable CSS. Let 's have a look at what your example will produce for the h1 style.

.wrap header h1{ color: red; }

So what you've ended up with here is a very specific CSS selector, that isn't really necessary. You could, for instance, just have

h1 { color: red; }

or use a class on the h1

.title { color: red; }

Why is specificity bad?

So imagine, 6 months later another developer comes along and they need to change the color of a h1, but just one of them.

First they try to add a class to the h1

.new-color { color: blue; }

But the colour doesn't change because the original CSS is so specific. So they have to do this

.wrap header h1.new-color { color: blue }

or worse still they may do this

.new-color { color: blue!important; }

And then what happens when other changes need to be made? As you can see very quickly and very easily you can end up with unmaintainable CSS, that will have everyone pulling their hair out.

Performance

People usually negate performance when it comes to CSS, but it is always good to know what is going on when a page is rendered. CSS is read from right to left. Using your example

.wrap header h1 { color: red; }

This means the browser engine will search for every h1 and check if they have a parent header and then if that has a parent class wrap. If so it will apply the style. A low specificity makes the rendering process a lot simpler.

Summary

So to sum it up, nesting, whilst it may seem great keeping your code nice and readable, should only be used when absolutely necessary. It's very easy to forget what the CSS that is actually being produced looks like. Before you know it you'll be in nesting hell.

LESS - Should I avoid unneeded nesting?

You Can Achieve Both Organization and Minimal Specificity

It is generally recommended to use the least specific selector to do the job. If your main desire is to also do some nesting in LESS for structural organization, then you can achieve the best of both worlds. Take your simplified example modified:

LESS

#FOOTER() {
#footer {
background: @footer-background;
padding: 20px 0;
margin-bottom: 20px;

.seal {
width:22%;
}

img {
display:block;
margin:0 auto;
}
}
.copyright {
margin: 20px 0 0;
}
}

#FOOTER();

CSS Output (your original shorter code)

#footer {
background: some color you set for @footer-background;
padding: 20px 0;
margin-bottom: 20px;
}
#footer .seal {
width: 22%;
}
#footer img {
display: block;
margin: 0 auto;
}
.copyright {
margin: 20px 0 0;
}

I've used a mixin with a set of parenthesis to "group" styles related to the footer area, and call that mixin to actually output the styles. This allows me to keep the code together, and insert it "wherever" in my final output css. But the styles can then be a bit more loosely grouped under that. So I have moved the img outside the .seal, and the .copyright outside the #footer. It may be true that the img is found in the html inside the .seal, or the .copyright inside the #footer, but if that is not relevant to selecting it, then simply knowing it is related to the #FOOTER() group may be enough for your organizational needs.

This is still an improvement over merely a /*comment*/ that sets apart a group, because it does still minimize duplication (#footer is not duplicated), and it does allow me to output the css in an organized way--one can envision this:

#RESET();
#HEADER();
#CONTENT();
#FOOTER();

Inside #HEADER(); may be some other sub-groupings:

#NAV();
#BRANDING();

It may be opinion, but in my mind rarely should selectors require more than a single level of nesting to get the specificity needed in a well structured html page. One set of exceptions are sibling relationships, where sometimes a complex set of nesting and sibling relationships are needed to select the elements.

Of course, you have to be aware that, for example, .copyright is defined in #FOOTER() but it is outputting a selector that is not necessarily constrained to that area. If you as the designer know that the copyright appears no where else, okay. If not, then subsuming it under #footer will be important.

LESS CSS: abusing the & Operator when nesting?

I also have been pondering this use further since we encountered it in that question you referenced. While I cannot answer definitively that this is a "bug" use for & (BoltClock seems to make a good argument that it is not a bug), I want to argue for the value of it (which argues it is not a bug from a logical standpoint).

However, before the logical argument, I did find another short, simple quote (in the "nested rules" section) that seems to indicate it is at least not unintended: "& represents the current selector parent." That's it. As BoltClock argues, whether prepending or appending seems irrelevant. All that was intended was that it be a placeholder for that "selector parent" that is current up to that point. The fact that it is always mentioned in conjunction with the "nesting" use of the language implies it is designed to designate the full selector string of the nest up to the point of the nest that it resides within. How that string is used (to pre- or append) would seem up to the coder.

Now, you mention (and previously mentioned) that you "would never code this way," and yet I find myself seeing what appears to be a very valuable use for this. Consider the following argument.

The HTML Setup for the Illustration

Assume, for the sake of illustration, there is a dynamic setting of three possible classes ('light', 'medium', 'dark' themes) on the body element that change the "look" of the site. We have
two columns, and some various types of links we want to style (textLink, picLink, textWithIconLink) differently in each column for each theme.

<body class="light">
<div class="leftCol">
<a class="textLink"></a>
<a class="picLink"></a>
<a class="textWithIconLink"></a>
</div>
<div class="rightCol">
<a class="textLink"></a>
<a class="picLink"></a>
<a class="textWithIconLink"></a>
</div>
</body>

Now the questions to ask are, just looking at the links, of the two following methods, which...

  1. Is less code in LESS
  2. Best organzies the code in LESS
  3. Outputs less code in CSS
  4. Best organizes the outputted CSS

"Best" may be somewhat subjective. I'll let you weigh that evidence yourself from below.

Option #1 Typical Nesting

LESS (approx. 99 lines of code)

/*Light Color Theme */
.light {
.leftCol {
.textLink {
color: fooL1;
&:hover { color: barL1;}
}
.picLink {
background-image: url(/fooL1.jpg);
&:hover { background-image: url(/barL1.jpg);}
}
.textWithIconLink {
color: fooL2;
background-image: url(/fooL2.jpg);
&:hover { color: barL2; background-image: url(/barL2.jpg);}
}
}
.rightCol {
.textLink {
color: fooL3;
&:hover { color: barL3;}
}
.picLink {
background-image: url(/fooL3.jpg);
&:hover { background-image: url(/barL3.jpg);}
}
.textWithIconLink {
color: fooL4;
background-image: url(/fooL4.jpg);
&:hover { color: barL4; background-image: url(/barL4.jpg);}
}
}
}
/*Medium Color Theme */
.medium {
.leftCol {
.textLink {
color: fooM1;
&:hover { color: barM1;}
}
.picLink {
background-image: url(/fooM1.jpg);
&:hover { background-image: url(/barM1.jpg);}
}
.textWithIconLink {
color: fooM2;
background-image: url(/fooM2.jpg);
&:hover { color: barM2; background-image: url(/barM2.jpg);}
}
}
.rightCol {
.textLink {
color: fooM3;
&:hover { color: barM3;}
}
.picLink {
background-image: url(/fooM3.jpg);
&:hover { background-image: url(/barM3.jpg);}
}
.textWithIconLink {
color: fooM4;
background-image: url(/fooM4.jpg);
&:hover { color: barM4; background-image: url(/barM4.jpg);}
}
}
}
/*Dark Color Theme */
.dark {
.leftCol {
.textLink {
color: fooD1;
&:hover { color: barD1;}
}
.picLink {
background-image: url(/fooD1.jpg);
&:hover { background-image: url(/barD1.jpg);}
}
.textWithIconLink {
color: fooD2;
background-image: url(/fooD2.jpg);
&:hover { color: barD2; background-image: url(/barD2.jpg);}
}
}
.rightCol {
.textLink {
color: fooD3;
&:hover { color: barD3;}
}
.picLink {
background-image: url(/fooD3.jpg);
&:hover { background-image: url(/barD3.jpg);}
}
.textWithIconLink {
color: fooD4;
background-image: url(/fooD4.jpg);
&:hover { color: barD4; background-image: url(/barD4.jpg);}
}
}
}

CSS Output (approx. 87 lines of output, organized by theme of course)

 /*Light Color Theme */
.light .leftCol .textLink { color:fooL1; }
.light .leftCol .textLink:hover { color:barL1; }
.light .leftCol .picLink { background-image:url(/fooL1.jpg); }
.light .leftCol .picLink:hover { background-image:url(/barL1.jpg); }
.light .leftCol .textWithIconLink {
color:fooL2;
background-image:url(/fooL2.jpg);
}
.light .leftCol .textWithIconLink:hover {
color:barL2;
background-image:url(/barL2.jpg);
}
.light .rightCol .textLink { color:fooL3; }
.light .rightCol .textLink:hover { color:barL3; }
.light .rightCol .picLink { background-image:url(/fooL3.jpg); }
.light .rightCol .picLink:hover { background-image:url(/barL3.jpg); }
.light .rightCol .textWithIconLink {
color:fooL4;
background-image:url(/fooL4.jpg);
}
.light .rightCol .textWithIconLink:hover {
color:barL4;
background-image:url(/barL4.jpg);
}
/*Medium Color Theme */
.medium .leftCol .textLink { color:fooM1; }
.medium .leftCol .textLink:hover { color:barM1; }
.medium .leftCol .picLink { background-image:url(/fooM1.jpg); }
.medium .leftCol .picLink:hover { background-image:url(/barM1.jpg); }
.medium .leftCol .textWithIconLink {
color:fooM2;
background-image:url(/fooM2.jpg);
}
.medium .leftCol .textWithIconLink:hover {
color:barM2;
background-image:url(/barM2.jpg);
}
.medium .rightCol .textLink { color:fooM3; }
.medium .rightCol .textLink:hover { color:barM3; }
.medium .rightCol .picLink { background-image:url(/fooM3.jpg); }
.medium .rightCol .picLink:hover { background-image:url(/barM3.jpg); }
.medium .rightCol .textWithIconLink {
color:fooM4;
background-image:url(/fooM4.jpg);
}
.medium .rightCol .textWithIconLink:hover {
color:barM4;
background-image:url(/barM4.jpg);
}
/*Dark Color Theme */
.dark .leftCol .textLink { color:fooD1; }
.dark .leftCol .textLink:hover { color:barD1; }
.dark .leftCol .picLink { background-image:url(/fooD1.jpg); }
.dark .leftCol .picLink:hover { background-image:url(/barD1.jpg); }
.dark .leftCol .textWithIconLink {
color:fooD2;
background-image:url(/fooD2.jpg);
}
.dark .leftCol .textWithIconLink:hover {
color:barD2;
background-image:url(/barD2.jpg);
}
.dark .rightCol .textLink { color:fooD3; }
.dark .rightCol .textLink:hover { color:barD3; }
.dark .rightCol .picLink { background-image:url(/fooD3.jpg); }
.dark .rightCol .picLink:hover { background-image:url(/barD3.jpg); }
.dark .rightCol .textWithIconLink {
color:fooD4;
background-image:url(/fooD4.jpg);
}
.dark .rightCol .textWithIconLink:hover {
color:barD4;
background-image:url(/barD4.jpg);
}

Option #2 End Target Grouping

I've named it "End Target Grouping," because that's really how I see using the & in this other way of adding parent selectors. One is now coding based off the final end target element that is actually being styled.

LESS (approx. 88 lines of code)

/*Links */
/*Text Links*/
.textLink {
.light .leftCol & {
color: fooL1;
&:hover { color: barL1;}
}
.light .rightCol & {
color: fooL3;
&:hover { color: barL3;}
}
.medium .leftCol & {
color: fooM1;
&:hover { color: barM1;}
}
.medium .rightCol & {
color: fooM3;
&:hover { color: barM3;}
}
.dark .leftCol & {
color: fooD1;
&:hover { color: barD1;}
}
.dark .rightCol & {
color: fooD3;
&:hover { color: barD3;}
}
}
/*Picture Links */
.picLink {
.light .leftCol & {
background-image: url(/fooL1.jpg);
&:hover { background-image: url(/barL1.jpg);}
}
.light .rightCol & {
background-image: url(/fooL3.jpg);
&:hover { background-image: url(/barL3.jpg);}
}
.medium .leftCol & {
background-image: url(/fooM1.jpg);
&:hover { background-image: url(/barM1.jpg);}
}
.medium .rightCol & {
background-image: url(/fooM3.jpg);
&:hover { background-image: url(/barM3.jpg);}
}
.dark .leftCol & {
background-image: url(/fooD1.jpg);
&:hover { background-image: url(/barD1.jpg);}
}
.dark .rightCol & {
background-image: url(/fooD3.jpg);
&:hover { background-image: url(/barD3.jpg);}
}
}
/*Text with Icon Links */
.textWithIconLink {
.light .leftCol & {
color: fooL2;
background-image: url(/fooL1.jpg);
&:hover { color: barL2; background-image: url(/barL1.jpg);}
}
.light .rightCol & {
color: fooL4;
background-image: url(/fooL3.jpg);
&:hover { color: barL4; background-image: url(/barL3.jpg);}
}
.medium .leftCol & {
color: fooM2;
background-image: url(/fooM1.jpg);
&:hover { color: barM2; background-image: url(/barM1.jpg);}
}
.medium .rightCol & {
color: fooM4;
background-image: url(/fooM3.jpg);
&:hover { color: barM4; background-image: url(/barM3.jpg);}
}
.dark .leftCol & {
color: fooD2;
background-image: url(/fooD1.jpg);
&:hover { color: barD2; background-image: url(/barD1.jpg);}
}
.dark .rightCol & {
color: fooD4;
background-image: url(/fooD3.jpg);
&:hover { color: barD4; background-image: url(/barD3.jpg);}
}
}

CSS (approx. 88 lines of output [due to one extra comment], organized by end target element; but notice, there is still a suborganization by theme because of the class structure)

/*Links*/
/*Text Links*/
.light .leftCol .textLink { color:fooL1; }
.light .leftCol .textLink:hover { color:barL1; }
.light .rightCol .textLink { color:fooL3; }
.light .rightCol .textLink:hover { color:barL3; }
.medium .leftCol .textLink { color:fooM1; }
.medium .leftCol .textLink:hover { color:barM1; }
.medium .rightCol .textLink { color:fooM3; }
.medium .rightCol .textLink:hover { color:barM3; }
.dark .leftCol .textLink { color:fooD1; }
.dark .leftCol .textLink:hover { color:barD1; }
.dark .rightCol .textLink { color:fooD3; }
.dark .rightCol .textLink:hover { color:barD3; }
/*Picture Links */
.light .leftCol .picLink { background-image:url(/fooL1.jpg); }
.light .leftCol .picLink:hover { background-image:url(/barL1.jpg); }
.light .rightCol .picLink { background-image:url(/fooL3.jpg); }
.light .rightCol .picLink:hover { background-image:url(/barL3.jpg); }
.medium .leftCol .picLink { background-image:url(/fooM1.jpg); }
.medium .leftCol .picLink:hover { background-image:url(/barM1.jpg); }
.medium .rightCol .picLink { background-image:url(/fooM3.jpg); }
.medium .rightCol .picLink:hover { background-image:url(/barM3.jpg); }
.dark .leftCol .picLink { background-image:url(/fooD1.jpg); }
.dark .leftCol .picLink:hover { background-image:url(/barD1.jpg); }
.dark .rightCol .picLink { background-image:url(/fooD3.jpg); }
.dark .rightCol .picLink:hover { background-image:url(/barD3.jpg); }
/*Text with Icon Links */
.light .leftCol .textWithIconLink {
color:fooL2;
background-image:url(/fooL1.jpg);
}
.light .leftCol .textWithIconLink:hover {
color:barL2;
background-image:url(/barL1.jpg);
}
.light .rightCol .textWithIconLink {
color:fooL4;
background-image:url(/fooL3.jpg);
}
.light .rightCol .textWithIconLink:hover {
color:barL4;
background-image:url(/barL3.jpg);
}
.medium .leftCol .textWithIconLink {
color:fooM2;
background-image:url(/fooM1.jpg);
}
.medium .leftCol .textWithIconLink:hover {
color:barM2;
background-image:url(/barM1.jpg);
}
.medium .rightCol .textWithIconLink {
color:fooM4;
background-image:url(/fooM3.jpg);
}
.medium .rightCol .textWithIconLink:hover {
color:barM4;
background-image:url(/barM3.jpg);
}
.dark .leftCol .textWithIconLink {
color:fooD2;
background-image:url(/fooD1.jpg);
}
.dark .leftCol .textWithIconLink:hover {
color:barD2;
background-image:url(/barD1.jpg);
}
.dark .rightCol .textWithIconLink {
color:fooD4;
background-image:url(/fooD3.jpg);
}
.dark .rightCol .textWithIconLink:hover {
color:barD4;
background-image:url(/barD3.jpg);
}

Concluding Thoughts

A few other considerations:

First, most of your theme colors (and possibly other themeing aspects) will be set up with variables, which can be grouped at the top of the LESS code by theme even using Option #2 above--so having the theme structure for the output CSS itself scattered in the code is not necessarily bad.

Second, any "standard" code for a type of element is defined above any theme code. My examples did not show this, but say all .textLink elements had text-decoration: none; set. That would occur once under Option #2 without any further selector code, and would appear above all the theme changes below. For Option #1, I need to set up a new, unnested .textLink selector (at least one other line of code) to apply it to all themed links, and that "base" code for the class will, again, be somewhere unrelated to where the rest of the code for the theme link info is.

Third, as a developer, if I am having issues with my picLinks (a specific type of element on my page), Option #2 makes it far easier to examine my code for the element I am having issues with, as all my code for all the themes is right in one spot.

Obviously, how one wants the final LESS and CSS organized is going to be a major factor in how one sees the value of this. My point here is to merely demonstrate that there is a very useful, logical reason to use the & in this way of adding parent level selectors to the & reference.

Nested selectors performance impact and LESS

That is correct. Browsers evaluate selectors from right to left. It will try to find a span inside a li.pinfo-box and so on.

One rule of thumb to folllow when writing LESS is: do not nest more than 3-4 levels.

This will prevent your selectors from growing to big, while you will still be able to benefit from the nesting feature in LESS.

A good example of "useless" nesting is when styling lists. Sometimes I write the selectors like this:

#wrapper .blog-post ul, #wrapper .blog-post ul li

Is it really necessary to specify that the li must be inside a ul? It will probably be enough writing:

#wrapper .blog-post li

All of this is good to know. BUT: This is not the first thing to dive into when trying to optimize your sites performance. Spend some time lower the number of request or something else instead.

Hierarchical / nested styles in LESS

As far as i know .item .title is the only way to select a .title inside a .item (.item > .title as mentioned by @Oriol possible also works in some situations). Less enables you to nested this relation.

When you don't need that relation, you should not nest it in your Less code too. Possible use comments to make clear that a group of selectors belongs to a certain other selector. Alternatively consider the BEM methodology, see http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/, then .item .title can be written as item__title {}.

How bad is it in practice to over-nest selectors in SASS/SCSS?

It depends on how much dynamic manipulation of the DOM and styles will go on after page load. It's not page loads (mostly) or slow selectors on initial layout that are at issue, it's repaints/reflows.

Now, Steve Souders says that on the average site it's simply not a real concern. However, on a web app or highly interactive site, poorly performing CSS rules can make your repaints slower than they have to be. If you have a lot of repaints...

Experts such as Nicole Sullivan, Paul Irish, and Steve Souders have covered the way CSS interacts with with JavaScript and how to write highly performant CSS selectors. It's more than depth (different selectors have different performance), but a good rule of thumb is to limit both depth and complexity to keep yourself out of trouble--but not so much performance trouble, read on.

However, as jankfree.org notes, it's not so much descendant or specific selectors as it is certain properties in certain contexts (html5rocks.com) that make paints expensive. I see long or complicated selectors more as a maintainability issue (Nicolas Gallagher) than a performance issue--keeping in mind that maintainability interacts with performance. Highly maintainable code can iterate faster and is easier to debug (helping you find and fix performance issues).

Now, as to Sass optimization. Yes, Sass can optimize your CSS. But it cannot optimize your selectors. A 4 level nested block will be output as a 4 level nested selector. Sass cannot change it without possibly making your CSS not work. You, as the author, have to optimize the way you write Sass to optimize your output. I, personally, use nesting only in a limited way (a killer feature in Sass for me is composing styles with @extend and placeholders). However, if you really love nesting you might be able to tweak your output to some degree using the Sass parent selector reference (or the newer @at-root).

So far as I know, neither Sass nor Compass has a built-in tool to analyze selectors and warn about them. It's probably possible to create a tool to do that (set a max depth and have your pre-processor warn you) utilizing an AST. More directly, Google Page Speed does have an existing feature that provides some information. SCSS Lint has a nesting option. There's also CSS Lint. (These can theoretically be added to run in your Compass config's on_stylesheet_saved if you're not already using something like Grunt or Gulp).

Questions about nested direct descendants in LESS

It's actually as simple as:

Nested selector elements are always combined with whitespace. E.g. [1a]:

a {
b {}
}

results in:

a b {}

Same way, [1b]:
:

a {
> b {}
}

results in:

a > b {}

etc.

---

& has to be used only if you need to suppress that whitespace, as in [2a]:

a {
&:hover {}
}

for:

a:hover {}

Or if parent selector elements are not to be placed in front, [2b]:

a { 
b & {}
}

c {
& & {}
}

d {
& {}
}

e {
f ~ g:not(&) > & & + &&& {}
}

// etc.

resulting in:

b a {}
c c {}
d {}
f ~ g:not(e) > e e + eee {}

---

Combinators never affect anything and the only requirement is that a combinator must be followed by an identifier, e.g:

a { > b {} } // ok
a > { b {} } // error

Make LESS remove useless IDs when compiling

Your assumption is wrong that multiple IDs in CSS are redundant. Imagine, as an example, a site where the CMS generates the page type into the output, like that it's the contact page:

<body id="contact">
<section id="content">Blah</section>
</body>

According to your logic, the following piece of CSS would be a candidate for 'optimization':

#contact #content {
background:red;
}

Now however, your home page has <body id="home"> of course in this imaginary CMS. And suddenly the content of your homepage has a red background because you decided to erroneously optimize that #contact selector out of the CSS, while it most certainly shouldn't have a red background according to this rule.

So no, LESS cannot do this because it would break code. If you don't want the selectors, don't use them and don't put them in your code.



Related Topics



Leave a reply



Submit