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

Specificity

Portability and resilience

In your control

[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

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

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

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

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.

Lead UX Engineer at @nrl. An author, speaker, artist & open sourcerer http://github.com/stowball . Devoted to #a11y, CSS, #vuejs, alt-rock, Nintendo & Charlie P

Lead UX Engineer at @nrl. An author, speaker, artist & open sourcerer http://github.com/stowball . Devoted to #a11y, CSS, #vuejs, alt-rock, Nintendo & Charlie P