Chapter 4. Introducing ECSS

In the last chapter we considered existing CSS methodologies, and where, for your humble authors needs, they fell short.

I'm not about to try and convince you that the Enduring CSS approach is the 'Alpha and the Omega'. However, it does have different strengths and aims than the existing approaches. Therefore, even if taking it wholesale doesn't appeal, I'd hope there may be something you can borrow to solve your own issues.

Highlights of ECSS:

When I first wrote about Enduring CSS I was expecting a backlash of sorts. At that time (August 2014), no-one was really advocating what I was suggesting. Common perceived wisdom was to abstract visual patterns, normalise designs as much as possible and DRY out code. Enduring CSS is in some ways the antithesis of these beliefs.

Before we get into this, I think it may help to clarify the terminology that will be used. The terms used to define the visual parts of a page are known by different names in different approaches. There's nothing revelatory in what I'm suggesting or the terms I'm using, it's just important we're all on the same page before we get into this.

Defining terminology

I'm using the term 'module' to designate an area of functionality and/or the code that creates it. To exemplify, the header of a website could be considered a module. The header module would, in turn, be made up of other smaller pieces of functionality. For example, drop-down menus or search boxes. These nested pieces of functionality would be defined as components. Finally, our smallest 'items' would be the child nodes that make up a component or module.

So, to reiterate:

For brevity, for what follows, when I'm referring to modules, it could be a module or component. The difference from a ECSS authoring perspective is unimportant.

The problems ECSS solves

My primary goal with ECSS was to isolate styles as opposed to abstracting them.

Ordinarily, it makes sense to create CSS classes that are abstractions of common functionality. The benefit being that they can then be re-used and re-applied on many varied elements. That's sound enough in principle. The problem is, on larger and more complicated user interfaces, it becomes impossible to make even minor tweaks and amendments to those abstractions without inadvertently effecting things you didn't intend to. A guiding principle with ECSS therefore was to isolate styles to the intended target. Depending upon your goals, even at the cost of repetition, isolation can buy you greater advantages; allowing for predictable styling and simple decoupling of styles.

A further advantage of isolating styles is that designers can be encouraged to bring whatever they needed making, without needing them to be encumbered by existing visual patterns. Every new module that needs to be coded can be a 'greenfield'. I found that I could code out designs far faster when starting from scratch than attempting to build them from any number of vague abstractions.

Dealing with specificity

I also wanted to negate issues surrounding specificity. To this ends, I adopted the widely used approach of insisting all selectors used a single (or as close to that ideal as possible) class-based selector.

If you're having CSS problems I feel bad for you son, I got 99 problems but specificity ain't one.

https://twitter.com/benfrain/status/537339394706141184

Furthermore, structural HTML elements (with the exception of pseudo-elements) are NEVER referenced in the style sheets as type selectors. In addition ID selectors are completely avoided in ECSS. Not because ID selectors are bad per se, but because we need a level playing field of selector strength.

'Changes' to components are handled via simple overrides. However, the way they are handled from an authoring perspective makes them easy to manage and reason about.

Suppose you have an element that needs to be a different width if it is within a certain container - easy peasy, we don't need to be draconian in the manner an override can happen. We don't need a modifier applied to that specific element. We can handle very loose and typical scenarios but manage them confidently. You would write it like this in the authoring style sheets:

.my-Module_Component {
    width: 100%;
    /* If in the sidebar */
    .sw-Sidebar & {
        width: 50%;
    }
}

And it would yields this CSS:

.my-Module_Component {
  width: 100%;
}

.sw-Sidebar .my-Module_Component {
  width: 50%;
}

This may seem like a subtle benefit. After all, we may be authoring things a little differently, by nesting the overrides, but the net result is typical CSS; an element that gets different styles based upon a different and more specific selector.

However, by adopting this approach, from an authoring perspective, we create a 'single source of truth' for each key selector. Everything that will ever make a change to that key selector is nested inside that set of curly braces. Furthermore, that key selector will never be defined as a root rule again. This approach is a subject we will touch on later in the book.

This is !important

If on the odd occasion the presence of one one override isn't enough, we can rely on !important.

Although !important has little to do with specificity, you will likely be aware that in the wrong situation it should be avoided. Here's what MDN has to say about !important:

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.

https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity

However, when events beyond our control mess with our styles (e.g. a 3rd party CSS file loaded on the page) and we need some clout, we can embrace !important. Here's an example of a state change that is receiving some extra welly from !important:

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

I'll be honest, I really don't lose much sleep over using !important.

Embracing Repetition

I think it's important to deal with a possible 'elephant in the room'. I need to try and convince you that eliminating repetition of properties and values across files may not buy as much, from a maintenance perspective, as a solid and contained set of modules that are easy to remove from a codebase as needed.

The ECSS approach embraces repetition in the properties and values of the CSS.

With ECSS, every single visual module or component is written with a micro-namespace to provide isolation from other modules and components. Here is a typical example of an authored ECSS rule (the authoring syntax is very similar to Sass, but typically facilitated by PostCSS):

.ip-SubHeader_Wrapper {
    @mixin 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 color($color-grey-54 a(.5));
    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 $color-grey-33;
    }
    /* However, even on mobile, if the SubHeader Wrapper is in section 1, we want to see it */
    .ip-Classification_Header-1 & {
        display: flex;
    }
}

Those inclined towards OOCSS and Atomic CSS methodologies may look at that and shudder. Things like color and font-size are declared in most components. The @mixin Headline mixin generates a sizeable chunk of CSS to designate a particular font stack too. So, yes, there's repetition across styles.

However, the positives:

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.

The cost of repetition?

To fully reap the benefits of ECSS you need to be comfortable with the property and value repetition it creates. At this point, you may believe 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

I was curious what 'real world' difference the verbosity of repeated property/value pairs in an approach like ECSS actually made? An experiment:

A CSS file I was working on using the ECSS methodology, when gzipped (as it would be served 'over the wire'), was 42.9KB.

The most common and verbose pattern that could be abstracted from this style sheet to an OOCSS class would be a couple of Flex based rules that are used abundantly throughout 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 the test style sheet, those four lines of CSS were repeated 193 times.

That's only half of it. Many of those items need aligning within. That required 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 those blocks of code were removed and the file re-gzipped, the CSS file size dropped to 41.9 KB.

Extracting the most common and verbose visual pattern to an OOCSS class saved just 1KB of CSS over the wire. And despite just a 1KB saving in the CSS, factor in that if abstracting those styles to a class, it would also be necessary to litter the HTML with the relevant OOCSS classes to get the visual effect back.

Was it worth it?

Given that no other property combination had anything like that sort of verbosity and repetition, from a file size perspective, certainly not in my book. It would cost a lot of development agility (remember abstraction makes authoring and iteration slower as its necessary to change both 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. Its 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.

Summary

I hope I've given you enough to consider that maybe obsessing over repeated property values and pairs isn't the best pursuit if you are trying to create maintainable style sheets. In the next chapter, besides looking at the benefits of the ECSS naming convention, I'll also be arguing that a sound project organisational approach will generate far leaner style sheets in the long term than class abstraction and re-use.