简体   繁体   中英

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

I have a .scss file that, among other things contains this:

nav {
  font-size: 0;
  ul {
    margin: $padding/3;
  }
  li {
    z-index: 10;
    position: relative;
    display: inline-block;
    font-size: $fontSize;
    /**
     * If we want separated, Uncomment!

    margin: $padding/3;
    @include border-radius(5px);

    */
    &:first-child {
      @include border-radius(0 5px 5px 0);
    }
    &:last-child {
      @include border-radius(5px 0 0 5px);
    }
    padding: $padding/3 0;
    @include background(linear-gradient(lighten($textColor, 10%), $textColor));
    border: 1px solid lighten($textColor, 20%);
    a {
      color: $brightColor;
      padding: $padding/3 $padding;
      font-weight: bold;
      text-decoration: none;
      @include transition(.2s all);

    }
    //Nested menues
    ul {
      opacity: 0;
      //display: none;
      position: absolute;
      margin: 0;
      top: 0;
      left: 0;
      right: 0;
      z-index: 5;
      pointer-events: none;
      @include transition(.2s all);
      li {
        @include background(linear-gradient(darken($brightColor, 10%), darken($brightColor, 30%)));
        display: block;
        border: 1px solid lighten($textColor, 20%);
        &:first-child {
          @include border-radius(0);
        }
        &:last-child {
          @include border-radius(0 0 5px 5px);
        }
        a {
          color: $textColor;
        }
      }
    }
    &:hover ul {
      pointer-events: all;
      top: 100%;
      opacity: 1;
      //display: block;
    }
  }
}

How bad/harmful it is in practice? I've heard many talks about "Don't go over 3 nested selectors!" But how harmful is it really? Does it have any visible impact on page loads? The benchmarks I've done say no, but is there anything I'm missing?

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).

Just think about how you would want to write the actual css selector. Don't nest everything just because it is a child of the element.

nav li ul li a { 
    /* over specific, confusing */
}
.sub-menu a {
    /* add a class to nested menus */
}

Once you start chaining that many selectors, it becomes a pain to override and can lead to specificity issues.

Don't nest CSS. We feel comfortable nesting css because that closely mirrors what we do in HTML. Nesting gives us context that .some-child is inside .some-parent . It gives us some control over the cascading. But not much else.

As SMACSS suggests, I would nest in class names instead. ie, use .child-of-parent instead of .parent .child or .parent > .child

Nesting badly in practice can lead to extremely slow pages. See how github speeded up their diff pages.The least you should do is follow the inception rule which states that you shouldn't be nesting beyond 4 levels.

However, I would go one step further and say that we shouldn't nest CSS at all. I wrote a blog post with my opinions. Hope this is useful.

Just to chime in and enforce what others have said. It's a bad practice not necessarily from a performance point of view (It's probable you'll get better paint time increases from removing blurs/shadows and rounded corners than optimising selectors) but from a maintainability point of view.

The more heavily nested a selector, the more specific the resultant CSS rule (which you know already). Therefore, when you want to 'trump' that rule at some point you'll have to write a rule of equal (or greater) specificity further down the cascade to overrule the first. If you have an ID in there, that's going to make it FAR more specific too (so avoid unless you need them and know you won't need to override down the line).

To follow this to its logical conclusion, don't nest unless you need to. Don't have a rule like this:

.selector .another .yeah-another {}

When this would do the same job:

.yeah-another {}

It just makes life easier for everyone (including you) down the line.

My opinion:

You tell me which is worse on your eyes

from the op

nav li ul li a {color: $textColor;}

or as has been suggested

.nav-menuitem-menu-menuitem-link {color: $textColor;}

So...

The question is "Is it bad practice to hypernest in SCSS?" (or is it SASS?) I say no. But it's an ancillary argument.

The WORSE practice lies in leaving the SASS (or is it SCSS?) output, in it's machine-driven state, for production.

S*SS is a only a tool in your bag of tricks, no different than Notepad++ or Git or Chrome. It's role is to make your life a little easier by bringing some very general programming concepts to the point of building some css. It's role is NOT building your css. You can't expect it to do your job for you and create completely usable, readable, performing output.

Nest as much and as deep as you want, then follow Good Practice...

...which would be going thru your css afterwards and hand tweaking. Test, build, etc with your hypernested output. And when S*SS creates my first example above, give that anchor a class and call it with nav .class .

Although not directly an answer to your question, you can keep highly nested sass for your own purposes but still use @at-root . Check it out here .

.parent {
  @at-root {
   .child1 { ... }
   .child2 { ... }
  }
} 

// compiles to ... 

.child1 { ... }
.child2 { ... }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM