BEM and the long class name
.site-nav__item--active is an unpleasant thing to type and a worse thing to look at. But I’ve been writing class names like that for about eight months now and I’m not going back.
The usual pitch for BEM is “consistency” and “scalability.” Those words are so worn down they don’t mean anything. My actual reason for sticking with it is narrower and I think more convincing.
Why are we doing this?
CSS has one global namespace and a cascade that resolves conflicts by specificity, which means that every selector I write is competing with every other selector anyone has ever written in this project.
That produces an arms race on any codebase past a certain size. You write .button. Then you need a different button in the sidebar and you write .sidebar .button. Now that’s more specific, so when you want to override it in a modal you write .modal .sidebar .button.
Eventually you get fed up and nuke it from orbit with !important. After two years of that, the stylesheet ends up in a state where changing anything breaks something else on the site.
I’ve been in that codebase and I wouldn’t blame any one selector for it. The specificity of a rule encodes where it sits in the document, so moving a component moves its styling out from under it.
BEM’s answer is blunt: stop nesting entirely. Every rule is a single class selector so every rule has the same specificity. That means the cascade stops being a tiebreaker and source order does the work. And for a single class to be enough, the name has to carry the information the nesting used to.
/* before */
.site-nav ul li a { }
.site-nav ul li.active a { }
/* after */
.site-nav__link { }
.site-nav__link--active { }
The block is the component, an __element is a part of it, and a --modifier is a variant. That’s the entire convention. It took me 10 minutes to learn and about 3 months to stop giving it the side-eye.
Embrace the uggo class names
I get a couple of different things out of this:
First, I can now change a component without reading the rest of the stylesheet. If a rule is .card__title and nothing else in the project can be .card__title then I know the effect of my change by looking at the name. That’s what I was buying and I didn’t realize it until I noticed I’d stopped ctrl+f’ing before every edit.
Second, I can delete things, which nobody mentions. When a component gets cut, I can delete its CSS with confidence because I can search for the block name and find every rule that belongs to it.
In the old codebase I never deleted anything because I could never prove a selector was unused. The file only ever grew. I’ve now removed more CSS in three months than in the previous year.
Downsides to BEM
The markup gets uglier. There’s no way around this and I don’t understand why some people pretend otherwise. class="card__title card__title--featured" is way worse to read than class="title" and it’s more to type. On a template with a lot of elements named like that it gets really noisy.
The names get long. They get longest exactly where the component is most nested, which is where you’re already having a hard time.
And there’s a real trap with preprocessors. SCSS lets you generate these names with the parent selector and it feels great:
.card {
&__title { }
&__body { }
&--featured { }
}
Which compiles to exactly the flat selectors you want. Awesome! But… it also means that the string .card__title now appears nowhere in your codebase. When someone inspects an element, copies the class, and searches for it, they find nothing.
I did this for about a month and then stopped. The searchability was most of the value and I’d traded it for a slightly shorter source.
Loose BEM contra strict BEM
Strict BEM says that a block can’t depend on its context at all, so a card in a sidebar and a card in the main column should either be identical or be different modifiers. In practice I do sometimes write a contextual override and I’m not going to pretend I don’t.
I also use a handful of utility classes for things like text alignment and margin removal. Purists dislike this because it puts presentation in the markup. I’ve decided I don’t care much. .u-text-center is honest about what it does and it’s saved me from creating three modifiers that each mean “the same but centered.”
And I don’t use BEM for everything. Typographic defaults, form element resets, and the base layer generally are all bare element selectors and that’s fine. BEM is for components. A stylesheet that’s nothing but components has other problems.
Convention over configuration as applied to class names
I’ve taken something bigger from this that I’m still turning over. I don’t think the convention is really about CSS. It’s a way of making a decision once so that nobody has to have an opinion about it later.
When someone inherits a codebase, there’s an answer to “what do I call this” that isn’t entirely subjective opinion. That’s half the value.
I’d guess that’s true of most conventions and it’s the part that’s hard to argue for. The benefit is the absence of an argument you’d otherwise have, and you can’t point at an absence.
So the class names are ugly on purpose and for a reason. I’d try BEM on one component before you commit to it across a project. That’s what I did. It stops looking bad after about a week, which is how I got over the aesthetic objection.