Episode 1 — Fundamentals / 1.10 — CSS Architecture and Project Structure
1.10.a — BEM Naming Convention
In one sentence: BEM (Block, Element, Modifier) is a flat, predictable naming system that turns class names into self-documenting mini-sentences — eliminating specificity wars and making stylesheets grepable.
Navigation: ← 1.10 Overview · 1.10.b — Component-Based CSS →
1. The problem BEM solves
Open any old codebase and you will see classes like .header .left .item a. The problems multiply fast:
| Pain point | What happens |
|---|---|
| Name collisions | Two developers both create .card with different intentions |
| Specificity creep | Nesting selectors to override previous nesting → !important wars |
| Unreadable CSS | .sidebar > div > ul > li > a:first-child tells you nothing about intent |
| Fear of deletion | Nobody knows which classes are still used, so dead CSS stays forever |
BEM addresses all four by encoding context into the class name itself.
2. The BEM formula
.block → standalone, meaningful component
.block__element → a child that only makes sense inside its block
.block--modifier → a variation or state of the block
.block__element--modifier → a variation of an element inside the block
Rules:
- Block — a self-contained, reusable piece of UI (
.card,.nav,.form). - Element — a part that has no standalone meaning outside its block (
.card__title,.card__image). - Modifier — a flag that changes appearance or behaviour (
.card--featured,.card__title--large).
3. BEM in practice
HTML
<article class="card card--featured">
<img class="card__image" src="hero.jpg" alt="Hero shot">
<div class="card__body">
<h2 class="card__title">Launch Day</h2>
<p class="card__text">We shipped the new dashboard.</p>
<a class="card__link card__link--primary" href="/post/1">Read more</a>
</div>
</article>
CSS
.card { border: 1px solid #ddd; border-radius: 8px; overflow: hidden; }
.card--featured { border-color: #f59e0b; box-shadow: 0 4px 12px rgba(0,0,0,.1); }
.card__image { width: 100%; display: block; }
.card__body { padding: 1rem; }
.card__title { font-size: 1.25rem; margin: 0 0 .5rem; }
.card__text { color: #555; }
.card__link { text-decoration: none; font-weight: 600; }
.card__link--primary { color: #2563eb; }
Notice: every selector is a single class — specificity is always 0-1-0. No nesting. No wars.
4. BEM with SASS nesting
SASS's & operator makes BEM ergonomic to write without increasing specificity:
.card {
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
&--featured {
border-color: #f59e0b;
box-shadow: 0 4px 12px rgba(0,0,0,.1);
}
&__image {
width: 100%;
display: block;
}
&__title {
font-size: 1.25rem;
margin: 0 0 .5rem;
&--large { font-size: 1.75rem; }
}
&__link {
text-decoration: none;
font-weight: 600;
&--primary { color: #2563eb; }
}
}
The compiled output is still flat single-class selectors.
5. Common BEM mistakes
| Mistake | Why it's wrong | Fix |
|---|---|---|
.card__body__title | Double-nesting elements — BEM is flat, not a mirror of the DOM tree | Use .card__title (element belongs to the block, not to another element) |
| Using BEM for layout | .container__sidebar mixes page layout with component identity | Keep layout classes separate (.l-sidebar, grid utilities) |
| Modifier without base | <div class="card--featured"> (missing .card) | Always include the base block class alongside the modifier |
| Too many modifiers | .btn--primary--large--rounded--disabled | Group unrelated axes: .btn--primary.is-disabled or use data attributes for state |
6. When BEM class names feel too long
The double underscore and double hyphen look verbose — that is the point. They create visual separators you can scan instantly, and they are trivially searchable with grep or "Find in Files."
If verbosity truly hurts, consider:
- SASS mixins that generate the classes for you
- CSS Modules or Scoped styles (framework-level), which let you use short local names that get hashed at build time (see 1.10.b)
7. BEM alternatives (brief overview)
| Methodology | Core idea | Trade-off |
|---|---|---|
| OOCSS (Object-Oriented CSS) | Separate structure from skin; reuse "objects" across contexts | Lots of small utility-like classes; less self-documenting than BEM |
| SMACSS (Scalable & Modular Architecture) | Categorise rules: base, layout, module, state, theme | Good mental model; less prescriptive naming → teams diverge |
| ITCSS (Inverted Triangle CSS) | Layer CSS by specificity: settings → tools → generic → elements → objects → components → utilities | Excellent for large codebases; pairs well with BEM for the naming layer |
| Atomic CSS / Utility-first | One class = one declaration (.mt-4, .flex) | Covered in 1.10.c |
BEM remains the most widely recognised naming convention and appears in many existing codebases. Understanding it makes every alternative easier to learn.
8. Key takeaways
- BEM = Block
__Element--Modifier — flat class names, zero nesting. - Elements belong to the block, never to another element (no
.a__b__c). - Modifiers extend a base class; always include the base alongside the modifier.
- SASS
&makes writing BEM painless without increasing specificity. - BEM is one of several methodologies — the real lesson is have a naming convention and follow it consistently.
Explain-It Challenge
Explain without notes:
- Why does BEM use double underscores and hyphens instead of single ones?
- Given a navigation bar with links and a "currently active" state, write the BEM class names for the block, two elements, and one modifier.
- What goes wrong if you nest BEM elements like
.nav__list__item__link?
Navigation: ← 1.10 Overview · 1.10.b — Component-Based CSS →