A short history of CSS methodologies

What's interesting about CSS is how much people tend to love it or despise it.

Most often than not, it has to do with the cascade and the way to either use it or not use it. As a CSS file is read from top to bottom, the actual placement of the code inside the file is important. But the most terrible thing for classic developers is that CSS is global and declarations are not variables and won't trigger an error if you reuse them.

This simple thing has been the source of discussion about how to better write CSS. In this article, we'll see how various people took different approaches to organize their code bases.

But first, let's explain quickly what is the cascade.

The cascade

The cascade is an algorithm that defines how to combine property values originating from different
MDN Docs

What happens if we declare the same thing two times, but with different properties?

Here's a button:

.button {
 border:1px solid red;
}
/.../
.button {
 border:1px solid blue;
}

In this example, the border's button will be blue, as the latest declaration overwrites the previous one. But it's not the name of the declaration that's getting overwritten, it's the properties located inside.

Or, more precisely, if the cascade finds declarations with the same name, it will regroup its the properties together. And if two properties are the same, then the last one wins.

.button {
 width: 30px;
 border:1px solid red;
}
/.../
.button {
 border:1px solid blue;
 color:red;
}

In this case, the border, width and color properties will be grouped together in a single .button class. It will have a blue border.

Separation of concerns and semantics

Of course CSS is nothing without HTML.

For years the rule about organizing them was to respect the separation of concern. HTML is for structure and CSS is for styles.

For example using an <h3> tag instead of an <h1> just because the <h3> is smaller is bad practice. Using CSS to style the <h1> to the size needed is good practice. This way, even if the <h3> is replaced with a proper <h1>, the style won't change.

If an existing website needs a redesign without touching the HTML, it's also possible. That's what the famous CSS Zen Garden website, which has several designs with the same HTML, popularized in 2003.

It makes sense, but how do you name these styles?

That's where the semantic part comes in. Classes should name based on their purpose and not on their properties. In the previous example, naming the title .main-title could work, but naming it .title1 or .size-30 won't. The first one implies that this title is the first one in the page. The second one does not tell us where this class is supposed to be used.

Today the separation of concern and the semantic naming are often challenged. Some projects moved to JavaScript components where each one contains both its scoped HTML and CSS in the same file. The cascade is not a problem anymore and the global styles either. But this component approach is still less used than classic templates, and has its own issues that we will discuss later.

Semantic and structure

Now let's say you start a menu and name it .menu. It has no indication of style or structure, it just says what it is: a menu. It can be used at the top, the bottom, on the side... It's reusable! Or is it?

Actually, it depends.

Let's say our menu contains a sub menu with a button inside. Our CSS might look like that:

.menu {
 color: black;
}
.menu .submenu {
 border:1px solid blue;
}
.menu .submenu button {
 color:red;
}

This code has two major problems.

First, even with a semantic name on the parent component, the inner elements are not semantic. Worse, they are nested. Which means that if we move the button outside of the .submenu, the button will lose its red color. If the HTML moves, the CSS breaks.

The other problem is related to the first one. If you need a button with the same style outside of this context, you are out of luck and need to create another context. And if you don't have another context, then you need to create an entirely new button.

Suddenly it's a mess, and that's just for a button.

Object Oriented CSS

So of course, nature found a way. Well, Nicole Sullivan found a way. In 2009 she presented the Object Oriented CSS methodology.

Object Oriented CSS or OOCSS ditches the contextual approach of doing CSS (as showed in the previous example) by considering that the way HTML is structured will change and should not have any influence on the style.

To keep our menu example:

.menu {
 color: black;
}
.submenu {
 border:1px solid blue;
}
.red-button {
 color:red;
}

Each HTML element that needs style should have its own class. This way, no matter the context, they will always have the same style.

But there's still a problem: all this code lacks organization. If you happen to work on an aging website, it could happen that the CSS file grow a lot and that everything new is written at the end of it.

SMACSS

So in 2011 Jonathan Snook released the book Scalable and Modular Architecture for CSS. It works by organizing the different CSS declarations by logical use in 5 categories, thus fixing the problem with the OOCSS approach.

The categories are:

Base
The rules that sets the default CSS properties of HTML elements like a or button.
Layout
The classes that define the layouts of pages. The containers, the grids, all those things that organize others elements. Prefixed with l- or layout-.
Modules
The reusable objects of OOCSS.
State
Utility classes dedicated to Modules, used to modify the design of an object if its state changes.
Themes
Utility classes that change the design of Layout and Modules based on the choice of the user or the context, for example dark mode.

SMACSS does not seem to be used that much today, but its influence is still there. It was a precursor that paved the way to other methodologies that improved its initial proposal.

But from there another problem emerged.

Naming conventions

SMACSS gave guidelines from a macro level perspective, but kept using HTML selectors inside it's modules. Which means that if something was moved or changed inside of them, the HTML kept breaking the CSS.

The solution? More classes for the children of modules of course. But how to name them? Naming in programming is always a problem as it's too often very opinionated.

So naming conventions were created to ease the readability and discovery of code. There were various naming conventions coming out around 2013. Among them SUITCSS was one of the most popular.

But the one that really saw mass adoption was BEM. It was created around 2015 by Vsevolod Strukchinsky and Vladimir Starkov. In it's basic form it works this way:

Blocks
the base of the object we are working on. More often than not, the context provider.
Elements
the elements inside the block. They are designated using the double underscore __.
Modifiers
the modified versions of an existing object. They are designated using the double dash --.
.menu {
 color:black;
}
.menu__item {
 height:10px;
}
.menu--open {
 color:blue;
}

From a naming perspective the benefits are obvious. You can name several inner elements the same, they will still be understood as different and won't bother each other, for example .menu__item and .footer__item.

Even if the HTML is moved around the class will still work. The cascade and the context are almost irrelevant here. A developer joining a project can also quickly understand the codebase and add its own modifications without touching the existing rules.

By the way, BEM detractors often use the example of too much nesting to say BEM does not work. For example, if we had something like this:

<div class="container">
 <span class="container__subtext">
  <button class="container__subtext__button container__subtext__button--blue">Nested button</button>
 </span>
</div>

When something like this happens, it means the whole object is too big and should be restructured. Instead of multiplying the elements, multiply the blocks by dividing them. You should never have more than one __ or -- inside your class names.

So it looks like everything is good! The macro level is handled by methodologies like SMACSS and the micro level by naming methodologies like BEM. There shouldn't be any more problems right? Right?

ITCSS and the specificity war

Well yes, there were more problems, mostly performance problems. The beginning of the 2010 saw the arrival of responsive design, HTML5 and CSS3! And most often than not, the directives were to add the new responsive CSS at the end of existing files instead of redoing everything from scratch. So more code bloat! Bigger files! And also tons of ugly repaints of the dom as soon as the browser parsed the CSS!

To deal with this, Harry Roberts came up with ITCSS around 2014. It means Inverted Triangle CSS. It is, as SMACSS, a methodology based on organizing code in different categories. But where SMACSS does it purely from a logical point of view, ITCSS is focusing on reducing specificity in order to limit repaints and ease the rendering.

The three elementary rules are :

  1. Go from global to localized (* should be before .button)
  2. Go from general use the explicit use (style resets should be before classes)
  3. Go from less specific to more specific (lighter specific selectors like h1 before heavy selectors like !important)

Which means the placement of CSS inside the file matters! Here's ITCSS code organization, from top to bottom of the file:

Settings
Should contain all the variables and global configurations of the project as it will affect everything.
Tools
Mixins, helpers and functions go there and can use the settings.
Generic
Low level style rules like resets or box-sizing should go there as they are the primer coat of CSS.
Base
Styles without classes for HTML elements like h1, a. The default style.
Objects
Structural non-themed elements that wrap components. Things like grids, containers, list positions.
Components
Your classic classes like .button-red. It's of course possible to use BEM to organize their inner code.
Trumps
Helpers as utility classes for surcharges and exceptions.

Most of the time these categories are in their own files that are then imported in this specific order using SASS. This way when the browser reads the compiled file, he treats the rules from the less specific to the more specific.

ITCSS had a huge influence on CSS makers as it's easy to adapt to each project's needs, it works for projects of all sizes and scales particularly well. The drawback is probably the requirements to understand how everything is organized and why.

So are we finally good? Is it finally over and CSS is ok? You bet it's not.

The rise of CSS in JavaScript

Around 2010 Google released AngularJS, opening the door to the rise of JavaScript components frameworks. It was followed by Angular2, React, Vue and many others.

Many JavaScript developers who probably never understood stylesheets saw this as the opportunity to fix it by adding scoped HTML and CSS inside their components, thus disabling the cascade, the need for semantic naming and the global nature of CSS. Suddenly the separation of concerns was no more. The semantics were gone. You would not ship any CSS files.

This evolution was sudden and brutal. The benefits were real, but it's important to note that some ideas about how to write CSS inside JavaScript were absolutely bollocks. For example React inline styles still use a JavaScript object that does not accept -, forcing developers to write CSS in Camel Case (what the hell?!) and declare values as strings.

var divStyle = {
 color: 'white',
 backgroundImage: 'url(' + imgUrl + ')',
 WebkitTransition: 'all',
 msTransition: 'all'
};

Fortunately, the field evolved very fast, allowing to import CSS files, write normal CSS inside components, choose if the style is scoped or not, and many little things that made it quite useful. But one problem started to appear.

Those Javascript frameworks are used in the browser and render in real time what is needed. By adding the style inside the components, the browser is forced, using JavaScript, to re-render the same things a lot of time, resulting in longer loading times.

It's not a problem when used in a true application. Something used in an office with good computers and unlimited battery for example. But when the JavaScript takes too much time to render its components, public websites see their SEO deteriorate. Also, how do you treat global styles?

So the idea of compiling the CSS in its own file came back again. It allows the browser to cache it provides better performance. But how do you keep the non-semantic, cascade free, scoped aspect of CSS in JavaScript if you write CSS into another file?

Utility First CSS

You might see the OOCSS methodology and the CSS in JavaScript component approach and ask yourself this question: why not create classes who's only purpose is to set as single property? For example the color of the button? Or the color of the border ?

.red {
    color:red;
}
.border-blue {
    border-color:blue;
}

The first problem of a full utility class approach is the bloat in the html. If you need 6 properties to achieve your design, you need 6 classes inside your HTML:

<button class="red border-blue another-class another-one another-one-again omg-its-not-ending">A lot of classes</button>

The second problem is the incredible number of classes that would be needed to make it work in a truly agnostic way!

It's one class per property multiplied by the number of design tokens (colors, sizes, etc). It simply can't be done by hand. This is why utility classes are often used for recurring and simple manipulations to avoid using an element in BEM.

That's the reason why utility first approaches can't be done with just a CSS file and need JavaScript in order to create classes programmatically. Knowing this, it's not surprising that most utility first CSS frameworks gained popularity when compiling tools like Grunt, Gulp, Webpack or Parcel got more popular.

But even if you can programmatically create your classes, the problem persists: how do you deal with the bloat?

Tailwind CSS

The Tailwind CSS framework was created around 2017 by Adam Wathan. He decided to pursuit a utility first journey after realizing that he was using more and more utility classes instead of declaring inner elements using the BEM methodology.

He also felt that the semantic part was giving developers too much freedom and took too much time to refactor. He famously said that “Best practices” don’t actually work.

Tailwind's solution is to flip the BEM + Utility First formula. Write using utility classes first, then use a semantic approach when needed. It's only if the utility first declarations tend to be repeated to often, or if the HTML is bloated with classes, that an abstraction should be created.

Wathan gives the example of a navigation bar, an element that is only used once, rarely modified, that might not deserve its own semantic class. On the opposite side, a button often comes in several sizes and styles and would work well with a .button class with a neutral design and a few utility first calsses to add theming.

Since utility first classes are pre-generated and immutable by nature, a developer without access to the CSS file can still produce styled pages or components. but can't add CSS bloat to the project.

Classes are determined programmatically inside a JavaScript config file and created when compiled. Templates and components are scanned and unused classes are removed from the final CSS file, therefor shipping a smaller file.

This approach can actually work with both traditional template files or component based projects, which explains why Tailwind has been getting more popular this past years.

And of course it's a controversial approach. Not everyone that already knows CSS is ready to learn a CSS API to get a design done. Doing Tailwind when design tokens like colors, fonts or sizes are well defined is easy, but going with the flow means either using the default config file (nice for prototyping) or going back and forth between the HTML and the config file to add design tokens. As Tailwind uses NPM and several other tools such as PostCSS and PurgeCSS, it also means more configuration, more maintenance and a brittle configuration.

In order to fix the naming, cascade and global problems of CSS, Tailwind choose to displace the organizational complexity of CSS inside configuration files and APIs. It's a way of thinking that is very different from traditionnal CSS development.

But what if trying to fix the cascade with more classes, more utilities was actually a dead end?

Cube CSS

The Composition, Utility, Block, Exception or CUBE CSS methodology was created in 2019 by Andy Bell. It's a different approach than the previous methodologies as it's trying to see CSS more on a macro level than a micro level.

Most importantly, it tries to reunite with the cascade instead of trying to make it disappear. Overall the goal is to write CSS that makes the right choices by itself instead of micro-coding every aspect of it. This is called intrisic webdesign, a term invented by Jen Simmons in 2018.

If you remember correctly at the start of the article we talked about how OOCSS tried to part ways with the parent class and its HTML child elements, as moving them could break CSS. More classes were added in a top to bottom approach to organize the code and make CSS detached from HTML. But an approach like BEM makes you think about objects in isolation.

The multiplication of viewports and contexts in which CSS has to work leave developers with two choices: either going more deep inside the objects to micro-manage things like responsive design, or take a step back to try to establish a set of rules and trust the browser into making the right choice. The latest actually already exist: with things like flexbox, the developer establishes a set of behaviors and let the browser decide.

CUBE CSS tries to take this approach to a new level by making traditionnal OOCSS or BEM the last step in the development of an interface.

Here are the steps and details about them:

Composition
Composition classes are used to create layouts, flow and consistent rhythm to any parent of elements. They set behavior rules only and are design agnostic.
Utility
A set of utility classes that do a single thing well. They are used to apply single or near single properties. They are also used to declare design tokens like colors or sizes. Their goal is the same as the utility classes used with BEM to avoid creating elements, but they come first.
Blocks
The classic block approach inherited from OOCSS and BEM. Those classes should only add the missing CSS rules that Composition and Utility classes did not applied. It can be done with BEM, with single classes or even using HTML selectors.
Exception
A modification of existing classes to change the rules set inside Blocks. CUBE CSS uses data attributes for its exceptions. Since exceptions are events often triggered by JavaScript, it's simplier to change the data-attribute and having the CSS react immediately than inserting a class. Also it avoids creating a specific class for the exception.

Let's look at an example of a composition class:

.flow > * + * {
 margin-top: 10px;
}

This .flow class has a single purpose. It detects any child element right under the parent and add a top margin of 10px only between two elements.

A more traditionnal OOCSS with BEM approach would be:

.flow {
 \...\
}
.flow__item {
 margin-top:10px;
}
.flow__item:first-child() {
 margin-top:0;
}

But those two approaches are not exclusive, because of the cascade. It's perfectly possible to manage the flow outside of the blocks or components while adding customization to them, thus the Block of CUBE CSS.

As to how organize the code, Andy Bell suggest this order:

  1. Config files
  2. Design tokens classes
  3. Resets
  4. Global styles for HTML elements
  5. Utility classes for Composition and Utility
  6. Block classes

This organization is also good for specificity. The more specific and nested code is located inside the Block classes, that are served at the end of the file. It will avoid creating repaints.

Andy Bell also suggests regrouping classes in HTML using separators that the browser ignores like [ or |.

// Using [ ]
<div
 class="[ card ] [ section box ] [ bg-base color-primary ]"
 data-state="reversed">
</div>
// Using |
<div
 class="card | section box | bg-base color-primary"
 data-state="reversed">
</div>

The order he designed follows this pattern:

  1. The elements’s primary block class (here it's card)
  2. Any subsequent block classes (section and box)
  3. Standard utility classes (bg-base)
  4. Design token utility classes (color-primary)

This way it's easier to know how the code is organized.

Conclusion

That's all!

This short history wasn't short in the end. I hope you found it useful. Feel free to contact me if you find any error or mistake or simply want to discuss this topic.


Initially published: January 13th, 2021
Generated: March 13th, 2022
Statistics for: CSS methodologies Time spent
Time spent
10 hours total
Started
week 2 of 2021
Last update
week 2 of 2021
All times entries for this page
YearWeekHours
2021210