tulika  goyal tulika goyal

Introducing ECSS

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:

It gains maintainability by isolating each visual pattern.

File size remains minimal over long periods of time by virtue of the fact that you can cut out sections/features/components with impunity.

Rules are 'self-quarantining'.

Class names/selectors can communicate context, originating logic and variation.

All language/technology files that create a module are contained within a shared folder.

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.

In case you aren't aware of the acronym, DRY stands for Don't Repeat Yourself, a popular goal when coding so that logic is only written once in a codebase to provide a single source of truth.

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:

module is the widest, visually identifiable, individual section of functionality.

components are the nested pieces of functionality that are included within a module

child nodes are the individual parts that go to make up a component (typically nodes in the DOM)

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.

Different interpretations of DRY

I wasn't convinced that the goal of DRY code that other CSSers were pursuing and extolling the virtues of, was the same kind of DRY code I wanted. To explain that a little more - I didn't care much about repeated values and pairs across my rules, which is what most people were concentrating on DRYing out. What I cared about was key selectors not being repeated in the codebase. Key selectors were my 'single source of truth' and that was the area I wanted to DRY out.

To that ends, with ECSS, an authoring convention is enforced that prevents a key selector being defined more than once project-wide. We will get into that in much more detail in the 'Ten Commandments of Sane Style Sheets' chapter.

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 colorand 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:

It's verbose yet it relies on nothing.

It's generally context agnostic (save for the size context of where it is placed), any media queries that affect this component are defined within this single set of curly braces.

A namespaced module is written once and once only. When this module needs to change, you only need to look in this one place.

Writing rules with all overrides nested within creates a sort of micro-cascade. Where ordinarily overrides could be anywhere in the CSS, adhering to this method confines them to a very specific area. It then becomes far easier to reason about specificity as it relates to the rule.

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 equivalent of CSS 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. I'd argue it is the same case with modules and components defined with ECSS. The CSS language IS the abstraction. The property/value pairs of CSS already mean we can build what we want from individual parts.

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 Koblentzis 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).

tulika  goyal

tulika goyal Creator

B-tech 2nd year student of polymer science.

Suggested Creators

tulika  goyal