Fixing body and html class specificity

I like to use classes on body and html elements to provide high-level scopes for CSS on a site but I’ve run into issues with specificity in my CSS. After thinking about the problem for a while and reading the thoughts from various CSS luminaries (like Harry Roberts) I came up with a solution that I’m quite happy with.

Usage Details

html Classes

html classes usually are generally the same across the site and include things like:

  • JavaScript availability
  • font loading status
  • Modernizr detects

body Classes

body classes usually include modifiers for page-specific features or designs such as:

  • the name or type of the page
  • if the page has a sidebar
  • if the page needs to hide horizontal overflow

The Setup

In the past, I’ve always simply put a class on the html or body element and used it in CSS with the element and the class:

  html.js .c-expanding-content__body {
    // Use JS to control expanding content display when JS is available
    display: none
  }

I prefer having the element name present in the selector because it eliminates ambiguity about where this particular modifier is coming from.

The Problems

This leads to two different problems.

The Linter Problem

I have used scsslint to lint my Sass for a couple years now and it’s been a great tool for enforcing code consistency. However, it’s rules for QualifyingElement are not element-specific. This means that I need to occasionally disable linters via source comments in the places where I’m using an html or body class.

This has been annoying and had a tendency to grate at me as an indication that something really wasn’t right. My original solution was to eventually submit a pull-request to the project to support specying a list of elements that it was OK to use with other selectors; but then I started running into The Specificity Problem.

The Specificity Problem

  html.js .c-expanding-content__body {
    // Use JS to control expanding content display when JS is available
    display: none
  }

  .js__expanded .c-expanding-content__body {
    display: block;
  }

Since I have the html tag in the selector to know when “js is available”, the specificity of that selector is higher than the operational selecter just below. I’ve worked around it in the past by simply duplicating a class (.js__expanded.js__expanded .c-expanding-content__body) but that was pretty hackish.

There had to be a better way.

The Solution

Requirements

  • keep the indication that the particular class is coming from the html tag or the body tag for clarity
  • don’t use the actual tag in the selector to prevent specificity issues

Implementation

I’m a big fan of BEM and I use it in every project. I started thinking about how the BEM practices could be applied in this case and all of a sudden the answer struck me. Instead of html.js I could just use .html--js.

Every html or body class just gets the tag name as the “block” and the informational bit as the “modifier”. Occasionally I’m actually dealing with “sub-items” and those are easily handled via the “element” (e.g. .html__font-loaded--primary and .html__font-loaded--secondary).

Note, I’ve considered prefixing with some kind of global namespace like g- but haven’t found a strong need for it yet and am only just starting to incorporate CSS namespaces into my processes.

Implementation Notes

There are a few places where I’ve implemented this behavior with third party tools/solutions and can provide some implementation notes.

Modernizr

If you are generating your Modernizr from a Grunt task, it is easy to specify the classPrefix.

  modernizr: {
    dist: {
      dest: "js/libs/modernizr.min.js",
      crawl: false,
      parseFiles: false,
      tests: [
        'svg',
        'flexbox',
        'inlinesvg'
      ],
      options: [
        'setClasses'
      ],
      uglify: true,
      classPrefix: 'html--'
    }
  }

WordPress

Similarly you can hook into WordPress to ensure that your body classes are named as you want.

  function theme_prefix_body_classes($classes) {
    $func = function($class_name) {
      if ( 'body--' !== substr($class_name, 0, 6) ) {
        $class_name = 'body--' . $class_name;
      }
      return $class_name;
    };
    return array_map($func, $classes);
  }  
  add_filter( 'body_class', theme_prefix_body_classes, 20 ); // Note "increased" priority

The Conclusion

So far I’ve used this approach on a couple projects and am very pleased with the result. The CSS is cleaner and causes fewer exceptions to work around but continues to provide the clarity that I want.