In CSS, selectors are patterns used to select the element(s) you want to style. But what’s the best way of using them to aid maintainability and performance in your code?
One of the most common ways to mark-up and style a web page has always been to use ID selectors to target main sections of a page, along with element and class selectors to target the content within. Here’s an example of a basic news feed module.
HTML:
<div id="news"> <h3>Latest News</h3> <ul class="news-list"> <li>News item</li> <li>News item</li> <li>News item</li> </ul> </div>
CSS:
#news { /* CSS properties here */ } #news h3 { /* CSS properties here */ } #news .news-list { /* CSS properties here */ } #news .news-list li { /* CSS properties here */ }
This usually works fine on small websites, but it starts to fall apart when applied to larger scale sites and web apps. This news module can’t be repeated anywhere else on the page, since only once occurrence of each ID element is allowed. ID selectors also carry a high specificity weight, so a long (and poor performance) selector like #news .news-list li {}
can only be overridden by a selector that is equally as specific. This makes reusing styles across your site difficult due to the varying specificity levels of your styles, and invariably leads to large, complex stylesheets with poor maintainability and slow browser processing performance. Read more about CSS specificity. Rather than ID selectors we can stick just to classes to allow multiple occurrences of modules on a page and to prevent strong ID selectors in our stylesheet. IDs can still be attached to the HTML if you prefer, but all CSS targeting is achieved through classes. The remainder of this post discusses three different targeting methods for maintainable websites, none of which use ID selectors. Using a consistent method for targeting page elements is important for the maintainability of your CSS. Let’s run through the most common methods:
Method 1: Element Selectors
An element selector allows you to target the element you want based on the name of the element. If our news feed has a class on the wrapping element of .news-feed
we can target the elements within using element selectors: HTML:
<div class="news-feed"> <h3>Latest News</h3> <ul> <li>News item</li> <li>News item</li> <li>News item</li> </ul> </div>
CSS:
.news-feed { /* CSS properties here */ } .news-feed h3 { /* CSS properties here */ } .news-feed ul { /* CSS properties here */ } .news-feed li { /* CSS properties here */ }
Pros of this method
- Cleaner markup – This method requires no classes on elements to target them. You can have nice “clean” HTML, though it’s worth noting that classes carry no semantic meaning and the number of classes applied to elements on your page usually has negligible impact on page weight.
Cons of this method
- Fragile to markup changes – If you later decide this module of content should in fact use a series of HTML5
article
elements rather than be marked up as a list, the changes need to be made in the CSS as well as the markup. If you add another list within.news-feed
you may need some way of targeting them independently, then the method’s consistency breaks when you have to start adding classes. - Gets complex very quickly – When you start applying this to more complex examples and begin including pseudo class selectors to get at the element(s) you’re after, this method becomes unwieldy and lends itself poorly to being maintained. A long chain of selectors like
.news-feed li:nth-child(2) a p:last-child {}
has poor performance, is a nightmare to read and has limited browser support. - Performance – When a browser has to navigate through multiple chained selectors it slows down the overall processing of the stylesheet. Element selectors are slower than class selectors, which are in turn slightly slower than ID selectors (
#main-nav {}
). This usually has negligible impact to page speed, but it’s worth mentioning. - Specificity levels – If you’re targeting an element with a selector like
.news-feed li
, another selector with higher specificity is required to override these styles. It’s easy to chain selectors whilst unknowingly working yourself into a corner, where an inconsistent and very high specificity selector is required to get you out of it. It’s best to keep your specificity as “flat” as possible to allow fast browser processing and easy overriding of styles.
Method 2: Class-based Selectors
A class-based method involves adding classes to most if not all the elements you want to style on a page. This makes the CSS more robust by removing reliance on no changes being made to the markup. The method might be applied like this: HTML:
<div class="news-feed"> <h3 class="title">Latest News</h3> <ul class="list"> <li class="list-item">News item</li> <li class="list-item">News item</li> <li class="list-item">News item</li> </ul> </div>
Note: It’s debatable whether the .list-item
class is necessary as a child of .list
since .list-item li {}
could be used instead. I personally prefer to use this additional class though for code style and specificity level consistency. CSS:
.news-feed { /* CSS properties here */ } .news-feed .title { /* CSS properties here */ } .news-feed .list { /* CSS properties here */ } .news-feed .list-item { /* CSS properties here */ }
Pros of this method
- Allows markup to change – A primary benefit of this method is that HTML elements can change, and as long as the class is retained the styling will still apply. Bare in mind though that different elements have different default styling set by the browser, so you may have to change your CSS to account for this anyway.
- Cleaner specificity – Elements, classes and IDs have different weights of specificity. Using only classes simplifies and allows easier maintenance of specificity levels across your site.
- More maintainable CSS – Using descriptive class names like the example above makes it easier to identify what the selector is targeting than an element-based equivalent (Method 1).
Cons of this method
- Potential class name clashes – Using generic class name like
.title
and.list
run the risk of other class-based selectors in your stylesheet interfering with them, even if they’re nestled under the.news-feed
class. For example if our news feed is within a wrapping element with an example class of.content
,.news-feed .title {}
is subject to the styling rules that are perhaps set in.content .title {}
. This often leads to high-specificity styling override hacks and selector inconsistencies which nobody wants. - Potential for code duplication – You may also have a Twitter feed module of content on your page, that differs only slightly in design from your news feed. If the CSS in the example above was duplicated for the Twitter feed it gives you simple styling control over the two modules, though a lot of CSS is duplicated in two places.
Method 3: OOCSS-based Selectors
Nicole Sullivan pioneered a more object-oriented approach to CSS selectors. Rather than looking at a design as a single entity, the OOCSS approach looks for patterns and re-uses classes as much as possible. For example a post in your Facebook News Feed is seen in similar iterations in other places around the site, like your Profile Page. Rather than creating two slightly-different blocks of CSS for these posts, OOCSS uses classes to share as much of the styling as possible. Learn more about OOCSS. Using our same example this method might be applied like this: HTML:
<div class="sidebar-module"> <h3 class="small-heading">Latest News</h3> <ul class="basic-list"> <li class="post-item">News item</li> <li class="post-item">News item</li> <li class="post-item">News item</li> </ul> </div>
CSS:
.sidebar-module { /* CSS properties here */ } .small-heading { /* CSS properties here */ } .basic-list { /* CSS properties here */ } .post-item { /* CSS properties here */ }
Pros of this method
- Code reduction – Used properly this method has the potential for massive reduction in the amount of CSS in use on your site.
Cons of this method
- Must be wielded properly – This approach if misused can lead to huge, fragmented stylesheets. Don’t make your OOCSS classes essentially equivalent to writing inline styles (if you’ve got a whole stylesheet of classes with only one property in each you’re doing it wrong) but where obvious and appropriate extract duplications in a design away into a shared class that can be applied in both places.
Method 4: Module-based Selectors
A current trend that’s working well in the industry (encouraged by open-source projects such as Bootstrap and Jonathan Snook’s SMACSS) is to combine methods 2 and 3 above to work around some of the pitfalls. A module-based selector approach namespaces classes so it’s easy to see which can be grouped to alter the design. This means the styling of our news feed can be grouped together in a similar way to Method 2, whilst retaining a flat level of CSS specificity and allowing classes to be reused in other areas similar to Method 3. Using our same example this method might be applied like this: HTML:
<div class="module module-sidebar"> <h3 class="module-title module-title-small">Latest News</h3> <ul class="module-list"> <li class="module-list-item">News item</li> <li class="module-list-item">News item</li> <li class="module-list-item">News item</li> </ul> </div>
CSS:
.module { /* CSS properties here */ } .module-sidebar { /* CSS properties here */ } .module-title { /* CSS properties here */ } .module-title-small { /* CSS properties here */ } .module-list { /* CSS properties here */ } .module-list-item { /* CSS properties here */ }
Pros of this method
- Maintainability – This method may look unnecessarily verbose, but if you’re building a large site or working with a team of developers this simplifies the styling with descriptive class names and flat specificity which can considerably ease maintenance.
- Performance – This method offers good performance as browsers only have to find just one class each time rather than navigating through a string of selectors. Don’t worry about lots of classes on your elements, this increases page weight by mere bytes and is a small price to pay for the improvement in speed of CSS processing.
Cons of this method
- Needs to be followed – Quite often developers will jump in to a site and start writing selectors using methods 1 or 2 above. This can lead to unwanted overriding of your module-based class selectors which are only one level deep. As with all methods, the styling of a site should be built consistently using one or an appropriate combination of methods.
Conclusion
Based on the sites I’ve built using each of the above methods, I start new projects these days using the module-based approach. On the face of it this might look too granular and almost a step back in terms of a clean, semantic web (bare in mind classes have absolutely no semantic meaning) but once implemented properly on a site or web app, when the time comes to change or build upon your CSS it is revealed as an absolute breeze. Combining this approach with a CSS preprocessor like LESS or Sass can lead to some extremely maintainable, modular, scalable and high performance CSS. See a good implementation of the module-based selector approach on Twitter’s Bootstrap project.
Thanks for sharing this post,
is very helpful article.