Vivian Voss

BEM: The Naming Convention That Named Itself Indispensable

css html architecture

BEM was invented to stop CSS from breaking across 100 services. The industry adopted it to name twelve classes on a landing page.

One does wonder whether anyone read the original brief.

The Original

Yandex, 2005. One hundred services. One hundred and fifty front-end developers. One global CSS scope. Selectors like .result .albums .info collided across services because nobody had agreed on naming. The cascade was not the defect. The absence of a shared vocabulary was.

That was an organisation problem. BEM treated it as a language problem.

No CSS Modules. No Shadow DOM. No @scope. No @layer. The browser was IE6. A clear nomenclature would have sufficed. Instead, an entire methodology was constructed to enforce one: .block__element--modifier. Flatten everything. No cascade, no collision.

Yandex, 2005 150 Developers 100 services 1 global CSS scope Browser: IE6 .result .albums .info Which service owns this? The actual problem: no shared vocabulary Nomenclature Would have sufficed BEM Methodology What was built instead Organisation problem. Language solution.

Even at Yandex, this was over-engineered. They got their nomenclature. Along with DOM pollution, unreadable markup, and a war against the very language they were writing in.

The Copy

Smashing Magazine, 2013. CSS-Tricks, 2015. By 2016, BEM was "best practice." The HTTP Archive found BEM patterns on 34% of websites by 2021.

Developers with messy stylesheets saw BEM as discipline. A system that forced structure. What they overlooked: it forced far more complexity than learning to write clean CSS ever would have.

The Missing Context

The gulf between the original and the copy is not a matter of degree. It is a matter of planet.

Original (Yandex, 2005) 150 engineers 100 services Shared global CSS IE6. No scoping exists Measured perf bottleneck Copy (Everyone, 2016) 3 developers 1 project Scoped styles built in CSS Modules, Shadow DOM Nobody measured "Best practice" was sufficient.

Yandex.Mail's cascade measurably slowed rendering. Flat selectors were a performance decision. Nobody in 2016 ran those benchmarks. "Best practice" was sufficient motivation, which is rather the problem with best practices in general.

The Cascade

Every methodology has a trajectory. BEM's is particularly instructive because each step defeats the previous one's purpose.

"Use BEM for consistent naming" fair enough credit-card__header__title__shadow--highlighted longer than the content The DOM fills with underscores and dashes a wall of punctuation Developers spend more time naming than styling the methodology tax Teams adopt Sass: &__element, &--modifier nesting is back They are cascading again. Immediately. defeating the purpose The convention failed its own premise before the first Sass file was saved.

BEM was adopted to escape the cascade. The industry ground it straight back into one via preprocessors. The moment a team writes &__element in a Sass file, they are nesting selectors. They are cascading. They have rebuilt the thing they were avoiding, only now with eye-gougingly ugly syntax and a preprocessor dependency.

The Simpler Answer

Nicole Sullivan published OOCSS in 2009. Separate structure from skin. Reuse through composition. No special syntax. No tooling. Just CSS, written with discipline.

BEM .card__header--active .card__title--large .card__body--highlighted Fights the cascade One class per state per element OOCSS .card .active .card .lg .card .highlight Works with the cascade Composable. Reusable. Readable

OOCSS works with the cascade. BEM works against it. The difference is not stylistic. It is philosophical: one approach trusts the language, the other assumes it is broken.

The Car, Assembled

The car dealership analogy deserves more than a passing mention. Let us build one. First in BEM, then in the CSS that exists today.

The markup, BEM style. Every element encodes its parent in the name. Every variant is a modifier glued to the element it belongs to:

<div class="car car--red-roof--yellow-doors">
  <div class="car__engine car__engine--v8">...</div>
  <div class="car__transmission car__transmission--manual">...</div>
  <div class="car__safety car__safety--airbag">...</div>
</div>

The CSS that goes with it:

.car { display: grid; }
.car--red-roof--yellow-doors { /* this exact combination */ }
.car--red-roof--blue-doors { /* and this one */ }
.car--black-roof--yellow-doors { /* and this one */ }
.car__engine { /* base engine */ }
.car__engine--v8 { /* v8 variant */ }
.car__engine--turbo { /* turbo variant */ }
.car__engine--v8-turbo { /* wait, both? new modifier */ }
.car__transmission { /* base gearbox */ }
.car__transmission--manual { /* manual variant */ }
.car__safety { /* base safety */ }
.car__safety--airbag { /* airbag variant */ }
.car__safety--airbag--abs { /* both? another modifier */ }

Twelve selectors and counting. Flat. No cascade. Every combination of variants requires its own modifier because BEM has no composition model. Red roof with blue doors? New modifier. V8 with turbo? New modifier. The naming committee is in permanent session. This example exaggerates slightly for clarity, but anyone who has inherited a mature BEM codebase knows the truth: production is worse.

Now the same car in modern CSS. The markup first:

<div class="car">
  <div class="engine v8">...</div>
  <div class="transmission manual">...</div>
  <div class="safety airbag">...</div>
  <div class="roof red">...</div>
  <div class="doors yellow metallic">...</div>
</div>

Reads like a sentence. Now the CSS, with @layer, native nesting, and @scope:

@layer components {
  .car {
    display: grid;

    .engine       { /* base engine layout */ }
    .transmission { /* base gearbox */ }
    .safety       { /* base safety systems */ }
    .roof         { /* base roof */ }
    .doors        { /* base doors */ }
  }
}

@layer variants {
  .v8     { /* v8 power delivery */ }
  .manual { /* manual shift pattern */ }
  .airbag { /* airbag deployment zones */ }
}

@layer themes {
  .red      { /* red paint */ }
  .yellow   { /* yellow paint */ }
  .metallic { /* metallic finish */ }
}

@scope (.car) {
  :scope { /* scoped to this component */ }
  .engine { /* cannot leak outside .car */ }
}

Three layers. Structure in components, options in variants, paint in themes. The cascade resolves precedence by layer order, not by selector weight or naming gymnastics. @scope guarantees .engine inside .car cannot collide with .engine anywhere else. Native nesting keeps context visible without flattening it into underscores.

Add a turbo option? One class: .turbo, in the variants layer. No naming committee. No methodology. Just CSS, written as CSS.

The Timeline

The history is rather telling. BEM arrived when nothing else existed. Then everything else arrived. BEM stayed.

2005 BEM development begins at Yandex 2009 Nicole Sullivan publishes OOCSS The simpler answer. Nobody noticed. 2013 Smashing Magazine introduces BEM to the West 2015 CSS Modules ship Scoping solved. Natively. 2016 Shadow DOM v1 2022 @layer reaches Baseline 2023 CSS Nesting ships in all browsers 2025 @scope reaches Baseline The original problem is solved. Five times over.

By 2025, CSS has @scope, @layer, :where(), native nesting, and Modules. The scoping problem that BEM was built to work around has been solved natively, multiple times, by the platform itself.

The Irony

The "C" in CSS stands for Cascading. BEM treats that as a defect. That is not simplification. That is thinking in the wrong direction entirely.

In 2005, it was the only direction available. In 2026, it is a deliberate choice to ignore twenty years of language evolution. The naming convention was copied. The problem it solved was not.

BEM was over-engineered at its origin and bloat everywhere else. The industry copied the complexity and never looked at what was already there.

BEM solved a real problem for 150 engineers sharing one CSS scope across 100 services on IE6. The critique is not the original. It is the copy. If your team has three developers and one project, you do not need a naming convention designed for a Russian search engine's global stylesheet. You need to learn CSS.