826Days

826 days since this post was last revised. Specific details are likely out of date.

For many months now, in response to many failed attempts to find a suitable approach to scaling the CSS on the projects I work on, I have been using ‘ECSS’, my own Frankenstein approach to large-scale CSS architecture. It’s detailed more fully in the post, Enduring CSS: writing style sheets for rapidly changing, long-lived projects.

Subsequently, in this post, I’m about to argue that an OOCSS architectural approach to a large scale, rapidly changing responsive web site or application is an ‘anti-pattern’. With such a ‘link-bait’ title I need to get some caveats out of the way first:

  1. This isn’t an attack on OOCSS, Atomic CSS or any related single responsibility principle (SRP) approaches. It’s merely my argument that a different approach, depending upon your goals can offer a preferential outcome.
  2. I’m not suggesting that the approach I advocate is a panacea to all CSS scaling problems. It is not (there are none).

What are your problems?

Writing CSS is easy. Scaling CSS and keeping it maintainable is not. That’s why many CSS authors, that work on large codebases, are fascinated with the subject. It also explains why many grasp at the first approach they read about that purports to serve their needs; CSS architectural approaches are the diet pill of front-end developers.

It’s therefore important to consider the problems that you have. My own methodology is developed around specific problems. They may or may not align with yours:

  • the need to frequently cut out large chunks of stylesheets without adversely affecting the integrity of the remaining styles
  • the need for new features and design treatments to be built rapidly, with no reliance on styles or patterns that had gone before or exist currently
  • the need to by-pass specificity problems as far as possible
  • the need for each visual component/pattern to determine its own responsive needs via media queries alone (without reliance upon JavaScript)

Responsive web design, the Achilles heel of OOCSS

For me, the two biggest problems with an OOCSS approach are:

  • Responsive Web Design
  • Frequent change and on-going maintenance

Let’s consider each:

Responsive issues

I consider Atomic CSS (not to be confused with Atomic Design) to represent OOCSS taken the the nth-degree. Let’s consider an imaginary Atomic CSS example:

<div class="blk m-10 fr">Here I am</div> 

In this OOCSS/Atomic CSS example, the visual needs of the element have been split up/abstracted into re-usable classes. One sets a block formatting context (.blk), another sets some margin (.m-10) and finally one provides a floating mechanism for the element (.fr). Un-opinionated for sure. But what happens when the viewport changes and we don’t want 10px margin or the item floating?

We could of course make some classes to do things at certain breakpoints. For example, Mplus-cc2 might change a colour at the Mplus breakpoint. But I find this slow and laborious. Making very specific changes at certain breakpoints and tying them to a class that has to be added to the HTML seems needlessly complex. Furthermore, you inevitably end up with a raft of SRP classes in your stylesheets that are obsolete. What’s the authoring mechanism for removing any cruft from the authoring stylessheets when no longer needed?

Maintenance

Continuing with our prior example. Suppose at some point in the future, we change our product to a more progressive layout mechanism; we move from float based layouts to Flexbox based layouts. We now have twice the maintenance burden. We need to change classes in the markup/templates and alter the CSS rules (or write entirely new ones). Float is redundant in a Flexbox world so either we leave .fr alone (and so it continues to exist, needlessly in our CSS) or we make .fr responsible for justify-content: flex-end. But then what happens if we change the flex-direction of our parent at a particular viewport? Hopefully you can see the inherent shortcomings of an OOCSS approach for maintenance when your designs change frequently in a responsive world?

A pure OOCSS example (added 21.1.15)

A few responses in the comments felt that using Atomic CSS as an example was unfair as it didn’t fairly represent OOCSS. However, trying to get a canonical example of OOCSS is difficult as there seems to be so much disparity between what CSS authors believe it is, and how it is implemented.

I’ll therefore provide further, OOCSS only, examples; Nicole Sullivan’s original examples (if anyone can point to a more recent canonical example I will be happy to use it) from her slides ‘Our best practices are killing us’.

I was reluctant to do this as Nicole’s original examples are now very old (2009, before RWD was even a thing) and, without wishing to speak for her, I dare say she might use a different example and approach today.

However, hopefully we can agree that the essential aims of OOCSS are separation of structure from skin, and separating content from container? Assuming we are in agreement on that, it is my conviction that OOCSS is detrimental to speed of creation and codebase maintainability in certain circumstances.

In a responsive web design, there are times where the structure is the skin. Or rather, the structure does different things in different contexts, and there is no sane way to handle this with OOCSS. You however will be the judge.

Consider this OOCSS example. First the markup:

<div class="media attribution">
    <a href="#" class="img">
        <img src="mini.jpg" alt="Stubbornella" />
    </a>
    <div class="bd">@Stubbornella 14 minutes ago</div>
</div>

Now the CSS (note, I have removed some OldIE specific property/values here):

.media { overflow: hidden; margin: 10px; }
.media .img { float: left; margin-right: 10px; }
.media .img img { display: block; }
.media .imgExt { float: right; margin-left: 10px; }

This is the ‘el clásico’ example of OOCSS; the ‘Media object’ pattern. We know how this should look.

However, maybe this needs to be laid out differently at a 300px wide viewport. You could set a media query to make it a column based layout in that situation. But let’s say you have the same ‘object’ in a different context at the same viewport width? And in that context, it shouldn’t be in a column layout. To surmise:

  1. One media object needs to be a column based layout at 300px wide (let’s call this ‘media1’)
  2. A second media object needs to be a row based layout at 300px wide (as it is within another context/container, we will call this ‘media2’)

Mmmm. Let’s make a class that separates more concerns. It makes a media object a column layout at a certain viewport:

@media (min-width: 18.75rem) {
    .media-vp-small {
        /* Styles */
    }
}

That gets added to any element that needs to be a column at that viewport (‘media1’) so you’ll need to head over to the templates/HTML to make that change, adding the class where needed.

Furthermore, ‘media2’ needs to have a different background colour at a larger viewport. Let’s add another class to separate that concern:

@media (min-width: 60rem) {
    .draw-focus {
        /* Styles */
    }
}

Head into the HTML/template to add that style where needed.

Oh, and ‘media1’ needs the .img to be wider at the larger viewport and not have the margin. We can make another class for that:

@media (min-width: 60rem) {
    .expand-img {
        width: 40%;
        margin-right: 0!important;
    }
}

Back into the HTML/templates to make that change happen.

Hopefully now, you can see how this is headed?

I fail to appreciate how this is making a responsive code base more maintainable. We’re getting more and more SRP classes for more and more edge cases/situations. This means in these eventualities we have to go hunting for the particular SRP class for the specific situation and head back to the markup too. Which makes me question:

Why can’t the thing, just be the thing? We’ll look at that shortly.

For now, you may counter with, ‘this is a daft example, if you’re design has so many eventualities, you should normalise it’. At which point I would counter that I shouldn’t need to. I code the front-end, why should I hobble designers creativity, or the need to prototype new features rapidly?

When I’ve used OOCSS to do this, my speed to build new visuals decreases and the amount of SRP classes increases; often a class is used just once or twice on an entire project.

With a ECSS approach neither occurs. The thing is just the thing. Singular and isolated; free to change as is needed without disturbing others. Indulge me.

Let me state once more, in case you skipped it before; I’m not trying to convince you that OOCSS is a bad thing. If it works for you and solves your problems, fantastic. There are already plenty of articles out there that can extol the virtues of OOCSS. However, it doesn’t work for my scenario. I’m therefore merely trying to convince you that there are other, equally valid, and effective approaches to scaling CSS for large projects that don’t have anything to do with OOCSS principles.

Alternate approaches to maintenance

One of the principal arguments for an OOCSS approach is that it removes duplication of code and therefore results in a more maintainable CSS codebase. You build a set of CSS ‘Lego’ pieces you can then use in your HTML/templates to quickly build out designs. The hope is that once your OOCSS stylesheet is built it shouldn’t grow (much). You re-use where possible and extend where needed.

My own approach gains maintainability by isolating each visual pattern. It keeps file size minimal by virtue of the fact that you can cut out sections/features/components from the codebase simply and with impunity.

However, even then, there are plenty of repeated properties and values across any components ‘live’ in the CSS at any time. What does this cost us in real-terms?

ECSS

The ECSS system I’m advocating results in A LOT of repetition in the CSS.

Every single component lives in a namespace to provide isolation from other styles and components. Here is a typical example of an authored ECSS rule (in Sass, prior to Autoprefixer doing its thing):

.ip-SubHeader_Wrapper {
    @include Headline;
    align-items: center;
    // We want the subheader hidden by default on mobile
    display: none;
    font-size: $text12;
    background-color: $color-grey-54;
    border-bottom: 1px solid hsbbrighten($color-grey-54, -3);
    min-height: $size-fine-quadruple;
    @include MQ(Mplus) {
        display: flex;
        background-color: $color-grey-a7;
        color: $color-grey-54;
        font-size: $text13;
        min-height: 1.5rem;
        border-bottom: 1px solid $color-grey-54;
        border-top: 1px solid transparentize($color-white, .85);
    }
    // However, even on mobile, if the SubHeader Wrapper is in section 1, we want to see it
    [data-section-id="id-1"] ~ & {
        display: flex;
    }
}

Those inclined towards OOCSS and Atomic CSS methodologies may look at that and baulk. Things like color and font-size are declared in most components. The @include Headline mixin generates a sizeable chunk of CSS to designate a particular font stack too. The positives:

  • It’s verbose yet it relies on nothing.
  • It’s generally context agnostic (save for the size context of where it is placed), any changes due to viewport are defined within it.
  • A namespaced component exists once and once only. When this component needs to change, you only need to look in this one place.
For a detailed explanation of the naming convention employed in ECSS, you can visit this post: The ECSS naming convention explained.

Property/value repetition due to zero component abstractions

With ECSS, if a component needs to be made that is similar, yet subtly different to an existing component, we would not abstract or extend from this existing component. Instead, a new one would be written. Yes, I’m serious. Even if 95% of it is the same.

The benefit of this is that each component is then independent and isolated. One can exist without the other. One can change however it needs to, independently from the other. Despite their apparent aesthetic similarity at the outset, they can mutate as needed with no fear of infecting or tainting any other similar looking component. To extend the biological metaphor, we have gained components that are ‘self-quarantining’ by virtue of their unique namespace.

A further analogy: a BMW 3 series has a lot in common with a BMW 5 series. But they are not the same. They may share some/many parts (the property and value combinations) but that doesn’t make them the same. Their differences define them. They cannot be made of exactly the same parts because there is something inherently different about them. Same thing with components in FUN. The CSS language IS the abstraction. The property/value pairs of CSS already mean we can build what we want from individual parts.

Before I address the duplication issue, I’d like to talk briefly about specificity (or the lack thereof).

Specificity is not an issue

A while back I tweeted:

The reason being that with a ECSS approach, specificity is almost never an issue.

As an aside, I should make it clear that with ECSS, structural HTML elements (with the exception of pseudo-elements) are NEVER referenced in the stylesheets – only classes/attributes are used for selection.

The context of a component is irrelevant, changes to components generally only occur through a state change. For example, a nav area may change based upon aria-expanded="true" appearing above it in the DOM via JavaScript. Other than that, the hierarchy of selectors is flat and where states are changed, if on the odd occasion that the presence of one element providing the specificity isn’t enough, we can rely on !important.

Although !important has nothing to do with specificity, you will likely be aware that in the wrong situation it should be avoided:

When an !important rule is used on a style declaration, this declaration overrides any other declaration made in the CSS, wherever it is in the declaration list. Although, !important has nothing to do with specificity, using !important is bad practice because it makes debugging hard since you break the natural cascading in your stylesheets.

However, if a ‘state change’ class above the element in question (in the DOM) isn’t possible/preferable for whatever reason, and we need some clout, we can embrace !important:

[aria-expanded="true"] {
    transform: translate3d(0, -$super-height, 0)!important;
}

ECSS is the antithesis of OOCSS

In many ways, ECSS is the antithesis of OOCSS. Rather than abstract and share, we contain and isolate styles. Rather than the component being made up of many ‘atomic’ and abstracted CSS Lego parts brought together in the HTML, a ECSS component is merely constructed by the atomic and abstracted property/value Legos of the CSS language itself.

Embrace repetition

To fully reap the benefits of ECSS you need to be comfortable with the repetition it creates. At which point, I’d forgive you for believing me deluded. With all this duplication, how can this ECSS approach be a viable option? I’ll address that concern with one word: gzip.

OK, I lied. I’d like to qualify that further.

gzip is incredibly efficient at compressing repetitive strings

What ‘real world’ difference does the verbosity of repeated property/value pairs in an approach like FUN actually make? An experiment.

At the time of writing this, a CSS file I’m working on using the ECSS methodology, when gzipped (as it would be served ‘over the wire’), weighs in at 42.9KB.

The most common and verbose pattern that could be abstracted from this stylesheet to an OOCSS class would be a couple of Flex based rules that are used abundantly throughout the project to vertically centre content within their container. They are even more verbose thanks to the fact that there is considerable code added by Autoprefixer to enable support on older devices. For example, the resultant CSS would be:

.flex {
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
}

In my test stylesheet, those four lines of CSS were repeated 193 times.

That’s only half of it. Many of those items need aligning within. That requires this in the CSS too:

.flex-center {
    -webkit-box-align: center;
    -webkit-align-items: center;
    -ms-flex-align: center;
    align-items: center;
}

That block was repeated 117 times. Doesn’t seem like any better reason to abstract to an OOCSS class, right? That must be causing some serious bloat right there? Not so fast, Batman!

If I remove those blocks of code and re-gzip, my CSS file size drops to 41.9 KB.

Extracting the most common and verbose visual pattern to an OOCSS class has saved just 1KB of CSS over the wire (but I’ll obviously have to litter the HTML with the relevant OOCSS class to get the visual effect back so factor that in).

Was it worth it?

Given that no other property combo has anything like this sort verbosity and repetition from a file size perspective, certainly not in my book. I’m loosing a lot of development agility (I need to change templates and CSS) and responsive flexibility (what if I want this thing to do something different in a different viewport) for a minuscule gain in CSS weight. It’s the CSS equivalent of ‘robbing Peter to pay Paul’.

Let me be quite clear. Despite the efficacy of gzip, if your priority is having the smallest possible CSS file size, ECSS isn’t your best choice. Instead, go take a look at Atomic CSS. It’s creators are smart people, indeed, Thierry Koblentz is one of the smartest CSSers I know of. I’m sure ACSS will serve your needs well.

On the other hand, the priorities of ECSS are developer ergonomics (understandable class naming conventions), easy maintainability (styles organised by component and simple to delete) and style encapsulation (namespacing prevents leaky abstractions). Different problems, different solution.

What is your CSS architecture buying you?

Is the approach you are using to author your CSS actually solving the problems you have? Do you work on a large responsive design? Do you work on a project that with visuals that change frequently and need to cater for similar, yet subtly different design treatments? If you do, I would argue that an OOCSS approach, that attempts to abstract common visual patterns for re-use and extension, with a strict adherence to a single responsibility principal is a an anti-pattern.