A case for CSS helper classes

Keep your stylesheets lightweight and modular

Before you wish hell upon me for even suggesting this practice, CSS purists please hear me out. The practice I am suggesting applies only to complex web applications, not to simple websites or blogs. In order for this technique to be worth it, you must be using a CSS pre-processor so you can use variables. This article is written for SASS, but applies to any pre-processor.


I’ve recently toyed with the idea of using CSS helper classes to control paddings, margins, and floats in single page applications. For example, we would have something like this:

.push-left        { margin-left: $margin-x-base; }
.push-left-half { margin-left: $margin-x-half; }
// etc...

.flush-left { margin-left: 0; }
// etc..

.pad-left { padding-left: $margin-x-base; }
.pad-left-half { padding-left: $margin-x-half; }
// etc...

.float-left { float: left; }
.float-right { float: right; }

And to make these responsive, you have your media queries:

@media (max-width: 480px) {
.push-left { margin-left: $margin-x-base / 2; }
  .pad-left          { padding-left: $margin-x-base / 2; }

.float-left, .float-right { float: none; }

.mobile-float-left { float: left; }
.mobile-float-right { float: right; }
}

Why on earth would you do this?

The answer, my friend, is context. Using helper classes you can account for elements that will be positioned next to items that already have their own margins AND next to those that don’t.

Depending on how your DOM is set up, it could be extremely difficult to account for all cases in CSS. It makes much more sense to account for context in templates than in CSS.

How is this better than inline styling?

If you use variables to position your content (which you should be doing anyway), the values your helper classes use will match those used in the rest of your CSS. In other words: visual consistency.

You can easily change the variables and it will affect your entire application. Since the styles are applied through classes, they are much less specific than inline styles and can therefore be overruled easily.

The margins and paddings can also be responsive to each breakpoint.

Example

Say you typically have alerts at the top of the screen or container when something goes wrong. It makes perfect sense to add margin-bottom by default to this element:

.alert {
background: red;
color: white;
margin-bottom: $margin-base-y;
}

However, a few days later, you find yourself in a position where an alert should show up underneath a widget that doesn’t have any margins. What do you do? Yes, you could account for it by using an adjacent sibling selector like so:

.widget + .alert {
margin-top: $margin-base-y;
}

But what if you encounter a similar situation? And another? And another? You can easily end up with a monstrosity like this:

.widget,
.unicorn,
.bacon,
.bread-and-milk,
.etc {
+ .alert {
margin-top: $margin-base-y;
}
}

Instead of accounting for context in your CSS, you could do it in your templates:

<div class="unicorn">...</div>
<div class="alert push-top">Hooray</div>

You can also use @extend to make this technique more flexible. For example you could have a general purpose .push class and one specific for alerts. This way you can easily do a search and replace in the future if you decide to integrate the margin in the .alert class itself:

.alert-push-top {
@extend .push-top;
}

What do you think?

I’m still toying with this idea. So far it’s been working out great. If there is any interest, I can go into more detail about my setup.