BEM rules in HTML and CSS
Class names are half of BEM. The other half is rules for the selectors themselves. They give the main benefit: the styles of any block can be read, changed and deleted without fear of affecting the rest of the page.
Classes only
In BEM CSS, selectors consist of classes. Tag selectors and ids are not used to style components:
| Not allowed | Allowed | Why |
|---|---|---|
h3 | .card__title | the <h3> tag is all over the page |
#cart | .cart | an id is very strong, and there is only one per page |
.card h3 | .card__title | the heading becomes an <h2> — the style is gone |
A class does not depend on the chosen tag. The card heading can become an <h2> or a <p> for semantics, and the look will not change. The exception is the site's base styles like the reset and body, covered in the lesson "Organizing CSS".
Flat specificity
Almost every selector is a single class. All rules have equal strength, so the lower one wins, and nothing needs to be "overridden":
<style>
.note { padding: 8px 12px; border-left: 4px solid royalblue; background: aliceblue; }
.note__text { margin: 0; }
.note--warning { border-left-color: orange; background: oldlace; }
</style>
<div class="note"><p class="note__text">A regular note</p></div>
<div class="note note--warning"><p class="note__text">Attention: the warehouse is closed on Sunday</p></div>The modifier comes after the block and wins, although the selectors have equal specificity. This is flat specificity.
Allowed exceptions
There are two cases where a two-part selector is justified. The first is pseudo-classes and states: .button:hover, .input:focus. The second is a block modifier that changes its elements:
<style>
.card { padding: 12px; border: 1px solid silver; border-radius: 8px; max-width: 200px; margin-bottom: 8px; }
.card__price { color: seagreen; font-weight: bold; }
.card--sale .card__price { color: crimson; }
</style>
<div class="card">Backpack <span class="card__price">€40</span></div>
<div class="card card--sale">Tent <span class="card__price">€72</span></div>One modifier on the block recolored the price inside, and there was no need to put a modifier on every element.
Files by blocks
In a large project each block's styles live in their own folder. Its images and script can be kept next to them:
blocks/
button/
button.css
card/
card.css
card__price.css (optional — a separate file for an element)
header/
header.css
styles.css @import of all blocks one by oneDelete a block — delete the folder, and all its styles disappear with it. Dead CSS simply has nowhere to stay.
Common mistake
Styling tags inside a block instead of elements:
<style>
.card { padding: 12px; border: 1px solid silver; border-radius: 8px; max-width: 240px; }
.card p { margin: 0 0 8px; color: gray; }
.notice { padding: 8px; background: gold; }
.notice__text { margin: 0; color: black; font-weight: bold; }
</style>
<div class="card">
<p>Product description</p>
<div class="notice"><p class="notice__text">Only 2 left!</p></div>
</div>The .card p rule hit the paragraph of the nested notice block: the text became gray even though it has its own class — .card p is stronger than a single class. Give the card paragraph its own card__text class, and the rule stops reaching into other blocks' tags.