Skip to content

Battle of the architectures

A look at two popular approaches to CSS, and why I think they are not good enough.

Published on

There’s been a lot of debate in the front-end world about what “good CSS” means, and how we should be architecting our code for better maintainability, scalability, accessibility and performance. There are two extremes to this debate:

  1. On one side, we have the “semantic” approach. People who thinks code should be as vanilla as possible, arguing that methodologies like BEM and OOCSS are not good enough because of their lack of concern about semantics and accessibility.
  2. On the other side, we have the “atomic/functional” approach. People who take the single responsibility principle very very far, making heavy use of ultra-specific utility classes that make for very bloated and often messy markup.

Both extremes are a bit too extreme. The answer lies somewhere between.

Disclaimer: Long post ahead! If at some point you read something you feel like you’ve read before, check the credits section at the bottom of this post. You probably did.

What should we care about when writing CSS?

In most cases, you should be able to determine whether or not your CSS is good based on:

  • How readable and self-documenting it is: If someone new on your team was to take a look at your code, how long would it take him/her to understand which elements do what and how they are related to each other?
  • How easy to maintain it is: Maintaining bad CSS can be a nightmare. On the other hand, a well-thought CSS architecture should enable you to roll out changes and new features easily and without breaking anything in the process.
  • How scalable it is: The patterns and components you create should be abstract enough to be re-used in many instances and for different purposes. For example, does your code know the difference between Content and Display Patterns? If not, it should.
  • File size (after GZIP): When it comes to file size, less is always more. While there’s no debate on this, you’d do well to notice that because of the way GZIP works, if you compare two minified but un-compressed CSS files, the smallest of the two could actually turn out to be the largest after GZIP.

Problems with the “semantic” approach

The basic gist of the “semantic” approach is to avoid using classes unless strictly necessary. While it does suggest there are cases where classes might make sense, the defenders of this approach maintain we should always start “semantic first” (no classes). Let’s take a look at why this approach could be tricky:

Not descriptive enough

With HTML as we have it today, you will never have as many namespacing opportunities than with a classname-based approach like BEM. Unless you’re working on a small project (and you don’t plan to scale), there is no way you’re going to find as many tags and attributes for every object and component + variations. So after you’ve used all the possible tags and attributes combinations, you’ll be forced to start using classes, making everything inconsistent (why does this have a class, and this doesn’t?).

And even if you did find a combination for everything, things would still get complex (i.e, unnecessary nesting) in a short time, making your code a pain in the ass to maintain, and all because you wanted to avoid classnames? Please.

Believe me, it will get complex

Let’s say I wanted to style all my text-like inputs with the semantic approach:

[type="email"], [type="number"], [type="password"], [type="search"], [type="text"], [type="tel"], [type="url"] { }

Great. I now have 7 selectors, and will have to add selectors on a one by one basis if HTML were ever to introduce another text-like input.

Here’s how we could simplify this:

.c-input { }

Terrible, isn’t it?

You can still make your HTML accessible

There is no excuse for not keeping your HTML accessible and semantic. You can still add ARIA roles and micro-formats. You can still use meaningful markup. You just don’t target them directly in your CSS.

The trade-off, is that developers will most likely be able to achieve the same visual design while using arbitrary markup.

No one cares

Almost no one. Browsers don’t care if you use classes or not, visitors don’t care either. Those new front-end interns on your team definitely will. How easy are you making it for them?

Problems with the “functional” approach

The main idea behind this approach is the use of highly re-usable classes that provide single-units of style (basically resembling inline styles) and have low-specificity, which allows for easy overrides. The benefits are there: small, scalable and simple CSS, plus an easy way to experiment with your layouts without having to even touch a CSS file.

To be honest, this approach got me really excited when I first heard about it. But after using it on a couple projects (with decent success), I started noticing there are some flaws to it:

Not readable or self-documenting at all

With functional/atomic CSS, it’s not unusual for you to see something like this:

<div class="bg-white box-shadow pad-1 marg-1 fl-left">

If I was to tell you that was a card component, would you have figured it out from the code alone? Unless you’re the one who wrote this code, the answer is most likely no.

The code also fails to tell us which classes are crucial to our component, and which are optional or contextual. Do all cards need to have pad-1? What about that fl-left?

Here’s how we could do it instead:

<div class="c-card u-float-left">

Here, we use BEM-style syntax along with transparent namespaces (in this case .c- for components, and .u- for utility classes). The code now tells us that this piece of DOM is a card component, and also informs which bits of it are optional (.u-float-left).

In your CSS, you would then have something like this:

.c-card {
  background: #fff;
  margin: 15px;
  padding: 15px;
  box-shadow: 0 2px 2px 0 rgba(0,0,0,0.5);

Simple, straightforward and consistent. Which brings me to my next point.

Leaves too much room for inconsistencies

Because you could use any combination of classes at any given point, it doesn’t take much for a developer to either screw up something or come up with a clever variation of your original component without giving it much thought:

<!— The original “card" —>
<div class="bg-white box-shadow pad-1 marg-1 fl-left">

<!— Someone forgot to add .box-shadow to our card, so now it doesn’t even look like card anymore! —>
<div class="bg-white pad-1 marg-1 fl-left">

<!— Wouldn’t this card look great with a black background? —>
<div class="bg-black text-white box-shadow pad-1 marg-1 fl-left">

The more classes we have, the more likely someone will screw up your design.

A more BEM-like approach would not only help keep our interfaces consistent by putting a higher entry level for new component variations (since we’d have to create a new class for each), but also help us avoid some unintentional mistakes by reducing the amount of classnames required for each component.

No significant file size advantages

In order to reduce file sizes, we are told to avoid defining the same styles for common properties (like margin or padding) over and over again. Instead, we should just use a single utility class for each property, which results in DRYer, smaller stylesheets.


You can define the same styles for common properties a thousand times in our CSS, as long as you have those values stored in a single place in your codebase (as opposed to manually typing them every time). That’s what DRYness is all about: a single source of truth.

As for the rest, don’t worry: GZIP will take all the repetitive bits from your compiled CSS and compress them out. If you want to learn more about how GZIP works, take a look at this video from Kevin Khaw & Eric Higgins.

Moves maintenance over the HTML

Unless you’re dynamically generating your component markup through a solid templating system, you can see how having to search through all your templates just to globally update a component’s padding from .pad-1 to .pad-2 can be a huge pain in the ass. Add multiple websites into the mix, and there’s even higher potential for disaster.


Join my newsletter and get articles like this delivered straight into your inbox.