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 pointWhat happens
Name collisionsTwo developers both create .card with different intentions
Specificity creepNesting selectors to override previous nesting → !important wars
Unreadable CSS.sidebar > div > ul > li > a:first-child tells you nothing about intent
Fear of deletionNobody 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

MistakeWhy it's wrongFix
.card__body__titleDouble-nesting elements — BEM is flat, not a mirror of the DOM treeUse .card__title (element belongs to the block, not to another element)
Using BEM for layout.container__sidebar mixes page layout with component identityKeep 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--disabledGroup 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)

MethodologyCore ideaTrade-off
OOCSS (Object-Oriented CSS)Separate structure from skin; reuse "objects" across contextsLots of small utility-like classes; less self-documenting than BEM
SMACSS (Scalable & Modular Architecture)Categorise rules: base, layout, module, state, themeGood mental model; less prescriptive naming → teams diverge
ITCSS (Inverted Triangle CSS)Layer CSS by specificity: settings → tools → generic → elements → objects → components → utilitiesExcellent for large codebases; pairs well with BEM for the naming layer
Atomic CSS / Utility-firstOne 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

  1. BEM = Block __ Element -- Modifier — flat class names, zero nesting.
  2. Elements belong to the block, never to another element (no .a__b__c).
  3. Modifiers extend a base class; always include the base alongside the modifier.
  4. SASS & makes writing BEM painless without increasing specificity.
  5. BEM is one of several methodologies — the real lesson is have a naming convention and follow it consistently.

Explain-It Challenge

Explain without notes:

  1. Why does BEM use double underscores and hyphens instead of single ones?
  2. Given a navigation bar with links and a "currently active" state, write the BEM class names for the block, two elements, and one modifier.
  3. What goes wrong if you nest BEM elements like .nav__list__item__link?

Navigation: ← 1.10 Overview · 1.10.b — Component-Based CSS →