BEMantic: DRY Like You Mean It

Tim Baxter’s recent A List Apart article, Meaningful CSS: Style Like You Mean It, has once again re-ignited the debate that front-end developers who prefer to take an object oriented approach to CSS with BEM (or similar) somehow forego any concern with semantic markup and accessibility.

Personally, I think that’s a really insulting opinion to have. I don’t understand why BEM and HTML semantics are seen as mutually exclusive. I like to think I’m a good developer; I take great pride in the HTML I write (both the semantics and ARIA attributes) and in my CSS, which utilises both Sass and BEM.

I’ve been a front-end developer for a long, long time, and I’ve settled on my current approach not because I’m “one of the brainwashed masses” or because “I like to overcomplicate things”, but because they help me to write better, more maintainable and more performant web sites and apps. I didn’t start using these technologies and techniques because it was cool or because Harry said so, but because I researched, evaluated and tried these things out, gradually realising that they helped to solve real problems I was experiencing first hand and were the missing piece to my front-end sanity puzzle.

So why and how have Sass and BEM helped my UI development?

  • Consistency and predictability
  • Specificity
  • Portability and resilience
  • Performance
  • Maintenance and other benefits

Let’s address each point.

Consistency and predictability

The primary argument of those opposed to “classitis” is that you should be using HTML tags and attributes as hooks for styling. I’m sorry, but in my opinion, there just aren’t enough tags and attributes available to cater for every possible object, component and variation thereof. What happens when you run out of tags and attributes to style off? You use classes? Right, so now your code is an arbitrary mixture of different selector types. It’s harder to learn and reason about. If everything* is styled with a class, there’s less of a learning curve.

Specificity

Put your hands up if you know the specificity of `button`, `[type=”button”]` and `#main` by heart? What about `ul > li > a`? What would you need to use to override a style in `ul > li > a` in a third variation of that link? It quickly turns to soup. Now, put your hands up if you know the specificity of every element being styled by 1 class? You don’t even need to because they’re all the same. There’s no mental juggling and the order of the rules in the stylesheet can dictate overrides. The only places where extra selectors should be used are pseudo classes, such as `:focus`, because there’s no alternative, and chaining state classes, such as `.foo.is-selected`, to guarantee a specificity win.

Portability and resilience

In your control

In his article, Tim suggests that we should be using selectors like `[type=”text”]` to style text `<input>`s. By my count, there are at least 6 “text-like” HTML5 input types now, as well as `<textarea>`s. In my app, I would want all of these to look similar, so, by following a “semantic styling” approach, I’d have a massive selector like:

[type="email"], [type="number"], [type="password"], [type="search"], [type="text"], [type="tel"], textarea { }

With a meaningful class, my CSS will be:

.o-text-input { }

which is almost 5 times smaller! It’s also future-proof, because, if and when a new text-like input is added to HTML, you won’t have to change your CSS to style it — just add the class to the `<input>`.

Additionally, I’m not sure about you, but I regularly forget my saved passwords, and change a password input’s type to `text` in Dev Tools to reveal it. The amount of sites I’ve come across where doing this then completely changes the appearance of the form input is too many to count. It’s a really fragile styling practice.

Then there’s the argument that not using semantic hooks will lead to “achieving the same visual design with any arbitrary markup”. This shouldn’t be seen as negative just because bad developers can abuse it. It should be seen as a positive because your styles are completely portable.

For example, the average sighted user doesn’t know what a `<fieldset>` is, but they know what grouping form fields looks like in your app. They’ll also definitely want visual consistency when you display a “field group-like” piece of content that isn’t `<form>` elements in between 2 actual `<fieldset>`s.

We could achieve this by styling:

fieldset, .field-group { }
legend, .field-group > h2:first-child, .field-group > h3:first-child, .field-group > h4:first-child, .field-group > h5:first-child, .field-group > h6:first-child { }

or we could just create 2 classes:

.field-group { }
.field-group__legend { }

with the former being able to be applied to `<fieldset>`s, `<section>`s and `<div>`s, and the latter being able to be applied to `<legend>`s, or any heading level — whichever makes the most sense semantically in its current context.

Tim also suggests styling an accessible tabs component using appropriate ARIA roles, such as `[role=”tab”]` and `[role=”tabpanel”]`. This leads to issues when your app has multiple tab styles, as you’ll need to over qualify them with unnecessary wrapping elements and will probably encounter specificity issues. And, to exacerbate this problem further, if your app also uses accessible accordions, the styling and markup needs to be very different to tabs, but they use exactly the same ARIA roles and states, so targeting them with semantics alone is impossible.

Unsurprisingly, this can easily be worked around with BEM classes:

.tab-trigger {
// Styles for a tab link
    &.is-selected {
// Styles for a selected tab link
}
}
.accordion-trigger {
// Styles for an accordion link. Could also be a heading when it's a singular piece of collapsible content
    &.is-selected {
// Styles for a selected accordion link
}
}

Of course, classes aren’t a substitute for ARIA states, such as `aria-selected` and `aria-expanded `— you’ll still need to add these as well, but as I mentioned earlier, using only classes for styling improves the consistency and predictability of the codebase.

Out of your hands

Then there are circumstances where you have no control over the markup. While you may consider the CMS or framework badly broken if it forces markup changes, I see it that your CSS is too brittle if it can’t handle subtle changes. Your styles should not break just because a React component needed a wrapping element, or because Angular needs tags to `ng-repeat` on.

It’s naive to think that the markup that’s rendered will always be as pristine as you originally intended. You need to compromise, otherwise your entire codebase won’t be portable. When another developer needs to take over your project and convert it to a different platform/framework, they shouldn’t also be burdened with fixing semantic styles that now don’t quite apply for whatever reason. And this may well happen within your own team, too. I build everything “statically” (with gulp-twig) so I can focus solely on building a great UI. This then often gets converted by a colleague to React components and Angular directives depending on the project. Since using BEM, I haven’t had a single issue where pages and components were styled incorrectly in the conversion, whereas it used to be a regular occurrence beforehand as some of my idealistic markup was unachievable.

Performance

I don’t have any personal experience with profiling selector performance, but urban legend suggests that it can make a difference to the rendering of your app. MDN has a large guide on Writing Efficient CSS, which basically recommends not using semantic and overqualified selectors. Steve Souders wrote Even Faster Web Sites with a chapter on Simplifying CSS Selectors, and Harry Roberts covered it too.

More recently, Paul Irish has commented on selector performance and Marcy Sutton notes that IE11 performance tanked for users when flexbox was used with attribute selectors instead of classes.

So, even though you may not have experienced rendering performance issues (have you profiled your work?), why risk the potential of it happening by not using classes?

Maintenance and other benefits

Both CSS Guidelines and Sass Guidelines recommend splitting your source CSS/Sass into multiple files — ideally with a file for each and every BEM block/component. For both newcomers and veterans of the codebase, this massively reduces the friction in locating the styles for a particular component found in the HTML.

If you have a boilerplate framework in your organisation that every project uses, separating components into individual files also makes it a lot easier to selectively include only the ones used in a particular project — thus reducing the file size of your stylesheets. Using individual files also simplifies the process of generating pattern libraries from them.

Also, how many times have you dropped a third-party widget into your project and found that your cascading element styles are negatively affecting its layout? Ideally, the third-party should do their best to quarantine against this, but by not globally scoping your CSS with semantic selectors, you can reduce a lot of potential conflicts.

And finally, if you believe in accessibility, then you should also believe in giving end-users the ability to overwrite your styles with a custom, user stylesheet. If everything is componentised, overriding a particular element is much simpler. Want to hide live scores on a sport website so games aren’t ruined for you? Using a selector like `.score — in-progress` makes it very easy for a user to do so compared to `#scoreboard > li > b + span`, which has a higher chance of changing as the project matures.

Summary

From what I can tell, a lot of the disliking of new technologies and methodologies stems from a superficial opinion: the ugly, ”vomit inducing” syntax of BEM, or the mark-up pollution of `data-reactid`, which sadly then stops people from evaluating them properly. Change is hard and it takes time, but I think you owe it to yourself and to your peers to try — our industry is constantly changing, learning and adapting, and that’s one reason which makes it such a wonderful place to work in.

Granted, you may have come across some woefully inadequate and inaccessible sites that use BEM, but there will always be bad developers and ones that just don’t care. However, dismissing the work of a huge group of developers and clearly justified benefits of popular methodologies for other developers’ failings is not helpful.

For what it’s worth, I didn’t see any value in Sass or BEM to begin with.

This article has been cross-posted on Codepen for better code formatting.