Yet another CSS naming scheme

Every serious developer needs to brand their ideas in order for them to become Best Practices™, which used to mean something but today I’m pretty sure primarily just gives junior developers anxiety.

This one is mine.

Let’s call it  Encapsulated Components Styling System, or ECSS, because a good acronym is half the battle.1 And if we’re being honest, it contains zero new ideas. In fact, the stated goal of this system, is to steal all the ideas that make my code better and leave the rest.

This is both a joke, because after writing CSS for 15 years each new system seems equally superfluous, and a description of an architecture I’ve enjoyed using lately.

Semantics

When we talk about semantics in code, we talk about two things:

  1. Meaning as understood by computer. i.e., HTML tags, ARIA attributes, microdata. This is the stuff that has a spec and a defined meaning.
  2. Meaning as understood by humans working on the code. Since classnames have no inherent meaning to computers, the only reason they’re not UUID’s or random garbage, is so a person working on the code can discern their intent.

Obviously, you should write meaningful HTML that the computer understands.

One of the ideas that’s intrigued me in the Trello styleguide is using prefixes to communicate what a class is for to the developer. u- starts a utility class, for example. is- describes a state or some variation on an existing component. This doesn’t do anything, of course, but it makes it clear whether what the difference between a component and a helper class is.

Scale

One of the reasons people were so quick to adopt BEM is they’d been burned before, probably on a big project that had very little thought put into its CSS architecture.

Yet the key to this is not how you name your classes, it’s how you think about components. Brad Frost’s Atomic Design articulates this idea well. An application is made of parts which are in turn made up of smaller parts. Whether you think of these as blocks and elements, organisms and molecules, or modules and components doesn’t really matter, so long as you see a composition of discrete pieces with clear boundaries between them.

What BEM does well is force you to be deliberate about building with small pieces, and communicating to the developer what is and isn’t part of the block. That’s a really good idea.

Namespacing

I’m sort of baffled by the trend of abandoning the cascade feature of CSS. Sure, selectors can be abused.

body.pity-the-straw-man h1 + p > strong {...}

Of course that’s a bad idea. But this isn’t:

.site-header .logo {...}

That’s a molecule inside of an component!

Yet, it’s not obvious that, looking at the logo class in markup, is namespaced. We should consider that in our new system.

Brevity

One of the things BEM does poorly is encourage verbose classes in CSS and markup.

As your site grows, inevitably, you run out of common words for things. You could start coming up with obscure names for things. The second card is actually a slate or a tile, but that’s violating our goal of being semantic so humans can read it. My tiny human brain is very bad at remembering the distinction between a card and a tile.

One way around this is give components more verbose names, which trickle into their children, but this adds to cognitive overhead. The straw man attack on this problem is dismiss it “extra typing.” The problem isn’t the typing; verbosity is a barrier to comprehension.

Part of me misses the old ways. If I have a card with a name on it, why not just call it name? It’s brief, it’s clear, and as long as its namespaced within a component, it has no consequence on the rest of the codebase. It will scale just as well as card__name.

Rules

And finally, I should have as few rules as possible. I want to build products, not master arbitrary rules, so if it takes more than 5 minutes to learn it’s wrong.

Let’s start with these:

.c- for component, .m- for mixin, .js- for a javascript hook, .is- .has- .for- or any other descriptive phrase is a modifier, describing a variation or state.

Every selector without a prefix must be namespaced under a component, so if you see .title or p, that’s okay, it’s an atom within a larger piece, and you can change it without fear of side effects so long as you stay in that scope.

// Good
.c-card {
    .title {...}
    &.is-highlighted {...}
    footer {...}
}

.m-inverted {...}

// Bad
.c-card {...}
.title {...}  // Nope, this should be encapsulated.

Namespacing elements under mixins or states are also fine, so long as you’re mindful of their side effects.

// This is good. It will only apply to
// the element its placed on.
.m-layout-wide {
    width: calc(100% - 40px);
    max-width: 1020px;
    margin: 0 auto;
}

// This seems risky. In this situation,
// is-dark can apply multiple places, and
// makes assumptions about child li's.
.is-dark {
    background: black;
    color: white;
    li {
        margin: 0 10px;
        display: inline-block;
    }
}

// On the other hand, this is bound
// to a particular component, and even
// if there are other uses of the `is-dark`
// class, it won't have side effects
// outside of this component.
.c-media-item.is-dark {
    background: black;
    color: white;
    .metadata li {
        margin: 0 10px;
        display: inline-block;
    }    
}

If multiple components have .is-dark states, the intent is still clear.

And that’s it.

Prefixes (or lack thereof) make it clear what a class is for. It can scale with encapsulation and unique namespaces, and if the codebase is very complex, you can make namespaces verbose without cluttering all their child elements.

Should you use it?

No.

Why would you? The point of this exercise is not to follow someone else’s blog post blindly, but to learn about their ideas and adopt the good ones.

This is the the real best practice. Everything else is shameless self-promotion.


  1. This is a real problem. Aggressive marketing of MEAN stack leads beginners to think they’ll never get a job unless they become Angular experts and do everything in Mongo.