Writing on the principles & practice of building digital products

Painless page states in CSS

Adding state-changing CSS rules to the body rather than specific elements makes them much easier to keep track of.

Like a weirdo in a park, the menu on this site is always there - you can't see it. It behaves like you'd expect a menu to behave; click on the icon and it fades in, click on the cross and it fades out. Simple. No surprises there. Why am I telling you this, you may ask.

Well, I've recently been playing with a new (new to me, anyway) method of dealing with CSS state changes. It's a pretty simple idea:

Apply state-changing CSS rules to the body, rather than the element you're manipulating.

Let's take the aforementioned site menu. Using this method, instead of applying an active class directly to the element like so:

.menu {
  z-index: 0;
  opacity: 0;

  &.is--active {
    z-index: 1;
    opacity: 1;
  }
}

You would instead apply an 'menu active' class directly to the body (shown here in Javascript):

$('body').addClass('is--menu-active');

Then use the Sass parent selector to apply the active state to the menu:

.menu {
  z-index: 0;
  opacity: 0;

  .is--menu-active & {
    z-index: 1;
    opacity: 1;
  }
}

That's it. It seems like a small thing, but I've found it's really helpful for a couple of reasons.

Firstly, you're effectively telling the whole page what state it's in, rather than just one element. When a UI element becomes active there are very often other elements on the page which need to change in appearance (e.g. a menu becoming visible could mean the user shouldn't be able to scroll). Using this method you can use one very descriptive class on the body to achieve what alternatively would require a lot of individual classes on different elements. This can quickly become a nightmare.

Secondly, with this information in hand it's a snap provide different interactions as needed through Javascript. For example if you wanted the same link to have different functions when the menu is active (like a toggle perhaps), you could do something like this:

$('a').bind('click', function () {

  if ($('body').hasClass('is--menu-active')) {
    // Do something only if the menu is active
  } else {
    // Do something only if the menu is inactive
  }

});

I’ve also found this approach to be more scaleable. As your applications grows you might have any number of elements in different states (think toolbars, search fields etc). But no matter now many elements you add you'll only ever have one place (i.e. the body element) to look to see what's going on.