CSS Architecture

BEM Methodology

A naming convention for CSS classes that makes front-end code more readable, scalable, and maintainable — by making relationships between HTML and CSS explicit in the class names themselves.

3
Core Concepts
0
Specificity Wars
6
Sections
01 / Overview

A structured way to name CSS classes

BEM stands for Block, Element, Modifier. It is a component-based approach to web development that was pioneered by Yandex in 2005 and later open-sourced. The core idea is simple: instead of inventing arbitrary class names, you always follow the same pattern — and that pattern itself communicates the structural role of every class in your stylesheet.

Traditional CSS quickly descends into specificity conflicts. You start with .button, then need .header .button, then .header .nav .button.is-primary. Specificity climbs, selectors couple, and changing one rule breaks three others. BEM sidesteps the entire problem by keeping every selector flat: one class, zero nesting, zero specificity battles.

The methodology is purely about naming. It works with plain CSS, Sass, PostCSS, or any other tool. It requires no build step and no runtime. The only cost is the discipline of following the convention consistently.

02 / Core Concepts

Block · Element · Modifier

B Block standalone entity

A self-contained, reusable component. It is meaningful on its own and can be placed anywhere on the page. Examples: card, nav, button, search-form.

E Element __ double underscore

A part of a Block that has no standalone meaning. Always belongs to a Block and is separated by two underscores. Examples: card__title, nav__item, search-form__input.

M Modifier -- double hyphen

A flag on a Block or Element that changes its appearance or behavior. Separated by two hyphens. Examples: button--primary, card--featured, nav__item--active.

The three concepts map directly to a three-tier hierarchy: a page is composed of Blocks, each Block is composed of Elements, and both Blocks and Elements can carry Modifiers to express variants or states.

A key rule: Elements exist only inside their parent Block. You should never have an Element without a Block, and you should never chain Elements (card__body__title is wrong — use card__title instead, even if the title lives inside card__body in the HTML).

03 / Syntax

Reading the class name like a sentence

The BEM naming formula is straightforward. Words inside a single part are separated by a single hyphen. The two underscores mark the Block → Element boundary; the two hyphens mark the modifier.

block-name__element-name--modifier-name
html
<!-- Block -->
<article class="card">

  <!-- Element -->
  <img  class="card__image" src="…" alt="…">
  <div  class="card__body">
    <h2   class="card__title">Article title</h2>
    <p    class="card__desc">Summary text…</p>
  </div>

  <!-- Element + Modifier on the same node -->
  <a    class="card__cta card__cta--primary" href="…">Read more</a>

</article>

<!-- Block + Modifier on the block itself -->
<article class="card card--featured">…</article>
css
/* Block */
.card {
  border: 1px solid var(--color-neutral-200);
  border-radius: 8px;
}

/* Elements — always flat, no nesting */
.card__title  { font-size: 1.25rem; }
.card__desc   { color: var(--color-neutral-600); }
.card__cta    { padding: 8px 16px; }

/* Modifiers — add visual variants */
.card--featured     { box-shadow: var(--shadow-lg); }
.card__cta--primary { background: var(--color-blue-600); }
04 / In Practice

Common patterns and how to apply them

BEM works across any scale — from a simple button to a complex navigation. The patterns below cover the most common scenarios you will encounter.

html — navigation
<nav class="nav">
  <ul class="nav__list">
    <li class="nav__item">
      <a class="nav__link" href="/">Home</a>
    </li>
    <li class="nav__item nav__item--active">
      <a class="nav__link nav__link--active" href="/articles">Articles</a>
    </li>
    <li class="nav__item nav__item--disabled">
      <a class="nav__link" href="/coming-soon">Coming Soon</a>
    </li>
  </ul>
</nav>
html — button variants
<!-- Base block -->
<button class="btn">Default</button>

<!-- Block + size modifier -->
<button class="btn btn--lg">Large</button>

<!-- Block + color modifier -->
<button class="btn btn--primary">Primary</button>

<!-- Multiple modifiers on the same block -->
<button class="btn btn--primary btn--lg">Primary Large</button>

<!-- Element inside block -->
<button class="btn">
  <svg  class="btn__icon" aria-hidden="true">…</svg>
  <span class="btn__label">Save</span>
</button>

Mixing BEM with state classes

It is common practice to combine BEM with JS-driven state classes like .is-open, .is-active, or .has-error. These are not part of BEM itself — they are utility flags managed by JavaScript. Keep them as supplements, never as replacements for BEM modifiers on structural variants.

05 / Common Mistakes

What to write and what to avoid

✓ Do
.card { }
.card__title { }
.card__title--large { }
.card--featured { }

<div class="card card--featured">
✗ Don't
.card .title { }
.card__body__title { }
.card .card__title { }
#card-featured { }

<div class="card--featured">

The most frequent mistake is chaining elements: card__body__title seems logical in HTML structure, but it defeats the purpose of BEM. If you later move the title out of card__body, the class name becomes a lie. Always name elements relative to their Block, regardless of DOM depth.

The second common error is applying only the Modifier without the base Block or Element class. A card--featured class on its own does nothing — the Modifier class always accompanies the base class (class="card card--featured"), because the Modifier only overrides specific properties while relying on the Block for its defaults.

06 / Takeaway

Why a naming convention matters at scale

At first, BEM's double-underscore and double-hyphen syntax looks verbose. On a single-page prototype, it is. But CSS grows. A stylesheet that starts at two hundred lines reaches two thousand before a team notices, and at that point the cost of not having a convention becomes very visible: cascade surprises, dead rules no one dares delete, and specificity overrides stacked three levels deep.

BEM makes three guarantees that compound over time. First, flat specificity: every rule carries weight 0-1-0, so overriding is always predictable. Second, self-documenting markup: reading an HTML file tells you the component boundaries and variants without opening a CSS file. Third, safe deletion: searching for .card__title finds every reference in one grep — unlike .title, which could mean anything.

BEM is not the only CSS architecture — SMACSS, ITCSS, Atomic CSS, and utility-first approaches like Tailwind solve the same problems with different trade-offs. But BEM remains the most widely adopted naming convention for component-based CSS, and understanding it is foundational to reading and contributing to most large design systems.

BEM and design systems

Most mature design systems — including those from Google, Airbnb, and IBM — use BEM or a BEM-derived convention for their component CSS. Learning BEM is not just about writing cleaner code: it is about being fluent in the shared language of component-driven UI engineering.