Skip to content

Response to "MaintainableCSS"


@adambsilver recently published MaintainableCSS which aims to be a best-practices guide for writing CSS. It's a good read and gives generally sound advice.

I made a rather terse remark about it's similarity to BEM on twitter, and I'm hoping to clarify my thoughts in long form because I tend to be verbose and twitter isn't an appropriate medium for conveying nuanced thoughts. As this post is in response to MaintainableCSS, please be sure to go read it thoroughly before continuing otherwise much of this may not make sense.

Chapter 1: Introduction

Pretty good, standard intro.

What does scalable even mean?

This means, that as the CSS codebase increases in size, that maintaining code (see previous point) isn’t any harder.

I'd revise this to say that it means as the project increases in size, the CSS codebase stays the same. You really want to avoid the growth of CSS to call it scalable.

The rest of the chapter is reasonably concise and accurate.

Chapter 2: Semantics

This one pained me a bit. The intent was excellent, but the execution was flawed.

It starts off on the wrong foot by claiming:

Semantic HTML isn’t just about the elements we use…more importantly, it's about the class names (and IDs) we add

Generally when talking about "semantics" it's with regard to "browser semantics". To steal from a couple of my recent Stack Overflow posts:

Browser semantics* refer to how a browser will identify a DOM node and what the browser will read to users who use assistive navigation.

*a word whose definition is meaning, which makes discussing its own meaning…difficult and quite meta

To say that classes and IDs provide semantics is misleading because they don't provide any information that the browser can use to reliably parse the DOM into roles.

That's not so say that classes and IDs shouldn't provide meaning. They absolutely should. Meaningful class names help break content into usable pieces, but there is no functional difference between…

<style>.banner {...}</style>
<div class="banner"></div>


<div class="YmFubmVy"></div>

…as far as the browser is concerned.

Anyway, the bulk of this chapter could be improved by replacing the word "semantic" everywhere it's used with "meaningful".

With that correction, everything would fall into place pretty well and strongly support classes that describe content.

As I've said before

Don't add classes for styles, add styles for classes.

It's worth noting at this point that I've been seeing a number of posts recently by devs who appear to have stumbled on the concept of "classes for styles", and it's worrisome.

I've spent much of my career as a front-end developer maintaining existing projects, most of which I did not write, and most of which I had no input for planning. I had the privilege of seeing many different architectures and was able to experience first hand the repercussions of various different design decisions when it came to CSS.

The worst possible way to structure your CSS is to make classes for each and every style you want to apply.

The projects that used such a structure had the most style bugs which also took the longest to fix.

How do you fix <div class="padding-left-20"></div> to have slightly less padding, and a slight border when it's hovered?

How do you fix <div class="margin-30"></div> to have a 40px margin for larger devices, and 20px margin for smaller devices?

The correct answer is: you can't, stop trying to shoehorn bad development practices into CSS.

CSS is declarative by nature, so any attempt to treat it as an imperative language is flawed at best and could honestly compromise the overall success of a large scale web development project.

It's not HTML's job to describe how HTML should look. That's the entire point of CSS.

Enough of that rant, Chapter 2 does well enough at explaining that meaningful class names assist in making changes.

Chapter 3: Reuse

Don’t try and reuse styles. Adopt a duplication-first approach.

I disagree with this when using a preprocessor. I know that some people don't recommend preprocessors. I recommend using preprocessors in the same way I recommend owning a nice power drill. If you don't need that particular tool to solve your problems, don't use it. I find that I often have to drill holes and screw in screws, hence the power drill. I also find I often have to change sizes and colors, hence the preprocessor.

Be careful when reusing styles. Be careful using a power drill. If you're careless you can get hurt. If you're careful you can save a lot of effort.

Because visual class names don’t hold much meaning.

Take red. Does this mean a red background? Does this mean red text? Does this mean a red gradient? What tint of red does this mean?

Readers take heed of this boldRedText.

What about mixins?

…However, you have to be very careful to update a mixin because it propagates in all instances just like utility classes. You become scared to touch and then you create new mixins that are slightly different causing redundancy and maintainance problems.

Going back to the preprocessor bit; you can treat mixins as a form of object-oriented-esque inheritance. If you're going to apply the object oriented principles, then be sure to favor composition over inheritance.

What about performance?

I don’t have stats to hand

Ouch. I can confidently say that I've seen significant reductions in CSS file sizes using a modular approach because it leads to building a component library and prevents developers from reinventing the call-to-action. Unfortunately, I don't have permission to share the data and permission would be difficult to attain at this time. Feel free to ignore this as anecdotal evidence.

Chapter 4: IDs

Don’t use IDs as hooks for styling.

I used to disagree with this, but I've since changed my mind. Ideally the chapter would be just that one line. Don't style using IDs. It leads to a bad place, and your maintainers will hate you. If you must use an ID, use the [id=""] selector. It keeps specificity lower which allows overriding from :root prefixes. If you think that sounds awful and hacky: it is. Don't use IDs as hooks for styling.

Chapter 5: Conventions

This chapter is what led to my tweet. It was meant as a comment on the similarity I saw, rather than a slight toward @adambsilver. I've written about BEM a couple times so it seemed familiar.

.<moduleName>[-<componentName>][-<state>] {} is functionally equivalent to .<block>[-<element>][-<modifier>] {} with the exception that there's no way to tell the difference between a modifier and element…erm…component and state.

I'll be the first to admin that BEM syntax is often ugly. I also advocate for a flavor of an early version of BEM before it was standardized. From training others on its use, I recommend setting a simple standard and sticking to it.

You can write a shorthand to explain most BEM usage with a simple line. When I write BEM classes, I use:


The syntax in this chapter would be represented in shorthand as:


The syntax in this chapter might be better served by using:

//                   ^^;

Each of these class names are semantic.

Again with the "semantic". I'd update this to say "Each of these class names are meaningful." and then go on to mention how this structure enables composition. The .searchResults example is a collection of search results. .searchResults-heading shows that .searchResults has a heading. .searchResults-item shows that .searchResults has an item (or items).

Chapter 6: Modules

Modular content and modular design are concepts that I've watched grow significantly over the last few years. Pattern libraries such as Bootstrap or Material Design have shown that it's possible to make content from low-level building blocks in much the same way one can build a house out of Legos.

Sometimes it can be tricky to decide whether something is a component or a module…

Whether you call them "modules", "components", "atoms", "molecules", or any other synonym for "part", it makes no difference. Thinking about design in this way helps for scale because it forces you to break complex concepts down into patterns of simpler pieces.

Don’t be tempted to reuse

In the example where an "order-summary" appears similar to a "basket", it's important to understand whether or not the order summary is a basket. If it is, use a mixin:

.order-summary {
    .basket; //your order-summary is now a basket

If "order-summary" isn't a basket, but a type of basket, a modifier might be more appropriate:

.basket {
    ...normal basket stuff...

    &--summary {
        ...summary modifications...

Of course, this assumes you're using a preprocessor. The general idea is that you can save yourself a lot of time if you know the relationship between items. If you don't know the relationship…

Duplicate duplicate duplicate

Copy-pasta isn't the end of the world in declarative languages.

Chapter 7: State

Namespacing your state classes is generally a good idea, although it will make your JavaScript more bothersome to write when you have to toggle classes on and off. Use the longer namespaced classes anyway. It'll make the JavaScript easier to understand later too.

Chapter 8: Versioning

When you have multiple versions of a module in your codebase, it can be tempting to again reuse the same HTML and CSS to do this. MaintainableCSS again dictates that you duplicate and provide a unique name to aid maintainability.

If you have two pieces of content that may appear slightly differently in different circumstances, the BEM solution is to use a modifier. If you have two pieces of that are structured similarly but appear wildly differently, use a different name because they represent different things.

In Summary

Overall @adambsilver's head is in the right place. I can't say that I felt there was anything new and game changing in MaintainableCSS, but it's good to see a reasonably accurate representation what I consider to be best practices written by someone who is not me.

I think with some discussion, constructive arguments, and github pull requests MaintainableCSS could be an excellent resource on how to write CSS in a way that you won't hate in six months.