BEM and the long class name
Ugly class names, on purpose, for a reason I didn't understand until about a month in.
.site-nav__item--active is an unpleasant thing to type and a worse thing to look at, yet I’ve been writing class names like that for about eight months now and I’m not going back.
I want to try and explain why, because the usual pitch for BEM is “consistency” and “scalability” and those words are so worn down they don’t mean anything. The actual reason 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 every selector I write is competing with every other selector anyone has ever written in this project.
What that produces, on any codebase past a certain size, is an arms race. 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, and eventually you get fed up and write !important, and then after two years the stylesheet ends up in a state where changing anything breaks something else on the site.
I’ve been in that codebase. The failure isn’t any one selector, it’s that the specificity of a rule encodes where it sits in the document, so moving a component moves its styling out from under 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 identical specificity, so the cascade stops being a tiebreaker and source order does the work. And to make single classes sufficient, 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 { }
Block is the component, __element is a part of it, --modifier is a variant. That’s the entire convention. It took me ten minutes to learn and about three months to stop giving it the side-eye.
Embrace the uggo class names
I get a couple of different things out of this:
The first is that 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.
The second is deletion, 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, so 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, and on a template with a lot of elements named like that it’s genuinely noisy.
The names get long, and 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, which is that Sass lets you generate these 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 the string .card__title now appears nowhere in your codebase, so 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, because 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 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 utility classes, a handful of them, 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 it for everything. Typographic defaults, form element resets, the base layer generally, that’s all bare element selectors and it’s fine. BEM is for components. A stylesheet that is nothing but components has other problems.
Convention over configuration as applied to class names
The bigger thing I’ve taken from this, which I’m still turning over, is that the convention isn’t really about CSS. It’s a way of making a decision once so that nobody has to have an opinion about it later. Half the value is that when someone inherits a codebase, there’s an answer to “what do I call this” that isn’t entirely subjective opinion.
I’d guess that’s true of most conventions and it’s the part that’s hard to argue for, because the benefit is the absence of an argument you’d otherwise have and you can’t point at an absence.
So: ugly class names, on purpose, for a reason. I’d try it on one component before you commit to it across a project. That’s what I did, and it’s how I got over the aesthetic objection. It stops looking bad after about a week, I promise.