skip to content

BEM and Naming Conventions

BEM's block__element--modifier grammar and its cousins exist to encode scope in a class name because the language never provided any. Interviewers want the tradeoffs — verbosity and rename churn against predictability — not a recital of the syntax.

part ofCSSoverview, primer and where to startread it →
on this pageshow

questions

6

In the BEM CSS naming methodology, what do "block", "element" and "modifier" mean, and how does each one appear in a class name?

level: juniorimportance: must knowfreq 72%

answer

  1. component, part, variant
  2. the name carries the scope
  3. double underscore joins block to part
  4. double hyphen marks the variant
  5. modifier rides beside the base class

basics

~10 s

BEM names classes after a block (a standalone component, .card), its elements (parts that exist only inside it, .card__title), and modifiers (variant flags, .card--featured). Two underscores mark an element, two hyphens mark a modifier.

solid answer

~40 s

BEM stands for Block, Element, Modifier, and it encodes a component's scope directly in the class name because CSS itself has no scoping. A **block** is a standalone, meaningful piece of UI named for what it is, not how it looks: `.card`, `.site-search`. An **element** is a part that has no meaning outside its block, written `block__element` with two underscores: `.card__title`. A **modifier** is a flag that changes a block's or element's appearance or state, written with two hyphens: `.card--featured`, `.card__title--truncated`; it is added *alongside* the base class, never instead of it. Every rule is then a single class selector, so specificity is flat and source order decides conflicts. The element part stays flat no matter how deep the markup nests — `.card__title` even if the heading sits three `div`s down.

code

css · 5 lines
css
.card { display: grid; gap: 0.5rem; }
.card__title { font-size: 1.25rem; }
.card__meta { color: #666; }
.card--featured { outline: 2px solid #b30; }
.card__title--truncated { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

go deeper

for a junior

Be able to spell the three parts confidently — block, block__element, block--modifier — and say why the prefix exists at all: CSS has one global namespace, so the name carries the scope.

for a middle

Explain the mechanics: element names are flat regardless of markup depth, every rule is a single class so specificity stays equal, and a modifier holds only the delta from the base rule.

for a senior

Show judgment about where the boundaries fall — when a part earns promotion to its own block, why components must not own their outer geometry, and how mixes keep them context-free.

for a principal

Own the enforcement question: a convention nothing checks decays, so be ready to discuss linting the class pattern, keeping one spelling across teams, and what the convention costs in rename churn.

## Why a naming convention exists at all CSS has exactly one global namespace. A selector written anywhere matches anywhere in the document, so two engineers who both write `.title` in two different stylesheets silently fight over the same elements. BEM — Block, Element, Modifier, originally from Yandex — answers that by putting the scope into the name itself: the class *is* the namespace. The whole methodology is a grammar for class names plus one structural rule (one class per rule). ## Block A block is a standalone, meaningful chunk of interface that would still make sense if you moved it somewhere else on the page: `.card`, `.menu`, `.site-search`, `.pagination`. Two naming disciplines matter here. First, name it for **what it is**, never for how it looks or where it sits — `.card`, not `.blue-box` and not `.sidebar-thing`, because the colour will change and the sidebar will move. Second, a block should not set its own external geometry (outer `margin`, `position`, a `width` that only fits one slot), because that hard-codes one context into a component meant to be reusable. ## Element An element is a part of a block that has no standalone meaning — a card's title, a menu's item, a form's submit row. It is written with the block name, two underscores, then the element name: ```css .card { } .card__media { } .card__title { } ``` Two things surprise newcomers. The element name is **flat**: it names ownership, not DOM depth, so a heading nested three levels inside the card is still `.card__title`, not a path through its ancestors. And the class is used as a **single class selector** — you write `.card__title { }`, not `.card .card__title { }`; the prefix has already done the scoping work that a descendant selector would otherwise do. ## Modifier A modifier is a flag that changes appearance, state or behaviour of the block or element it hangs off: `.card--featured`, `.button--ghost`, `.card__title--truncated`. It is applied as an extra class next to the base one: ```html <article class="card card--featured"> <h2 class="card__title">Release notes</h2> </article> ``` The modifier rule carries only the *difference* from the base, which is why both classes must be present. ## The two spellings The original Yandex syntax marks modifiers with a single underscore and optionally a value: `block__elem_mod_val`. The variant most teams use today — popularised by the getbem.com write-up — is the "two dashes" style: `block__elem--mod`. Both are legitimate BEM; what is not legitimate is mixing them in one codebase, because the delimiters are the only thing telling a reader which part of the name is which. ## What the grammar actually buys - **Readability from the markup alone.** Seeing `class="card__footer"` in a template tells you which stylesheet component owns it, without grepping. - **Flat specificity by construction.** Every selector is one class, so they all weigh the same and conflicts resolve by source order rather than by who nested deeper. - **Safe deletion.** Removing a component means removing one name prefix; nothing else in the sheet claims those names. - **Visible misuse.** A `card__title` appearing outside a `card` is an obvious defect to a reviewer, which a generic `.title` never would be. ## Mixes: putting a block inside another block Because blocks must not own their outer positioning, BEM uses a **mix** — two entities on one DOM node: ```html <ul class="feed"> <li class="feed__item"> <article class="card">…</article> </li> </ul> ``` or, collapsing the node, `<article class="card feed__item">`. The `card` class supplies the component; the `feed__item` class supplies the parent's layout slot. The card stays context-free and reusable. ## The mistakes reviewers look for Chaining element names (`card__body__title`); naming blocks after appearance; applying a modifier without its base class; and re-introducing descendant selectors such as `.card .card__title`, which throws away the flat specificity the naming already guaranteed.

  • How do you decide whether something is an element of a block or a block in its own right?
    Ask whether it means anything outside its parent. A card's footer is meaningless alone, so it is `card__footer`. An avatar, a button or a badge is reusable elsewhere, so it is its own block — and where it sits inside a card, you mix the two classes on one node (`class="avatar card__avatar"`) so the card owns the placement and the avatar owns itself.
  • Is the two-dashes syntax the only correct BEM?
    No. The original Yandex convention marks modifiers with a single underscore and supports key/value modifiers, as in `menu__item_state_active`. The two-dashes form `menu__item--active` is a later, more widely adopted variant. Either is valid BEM; mixing both in one codebase is not, because the delimiters are the only signal of which part of the name is which.
  • Why does BEM insist a block should not set its own margin or position?
    Because those properties describe a relationship to a surrounding context, not the component. Baking them in means the block only fits one place. BEM pushes outer geometry onto the parent's element class — the mix pattern — so the same block drops into a sidebar, a grid cell or a modal without overrides.

Think of it as a filesystem path flattened into one word: the block is the folder, the element is the file inside it, and the modifier is a flag you pass alongside it rather than a different file.

saying these in an interview costs you the question

  • Says the element class only works nested under the block selector
  • Names blocks by appearance, like .blue-box or .left-column
  • Applies a modifier class alone, without the base class
  • Chains element names to mirror how deep the markup nests
  • Treats block, element and modifier delimiters as interchangeable

context

open as a page

In BEM, why is a class such as `card__body__title` considered wrong for an element nested inside another element, and how should deeply nested markup be named instead?

level: middleimportance: should knowfreq 50%

basics

~20 s

BEM has only two levels: a block and a flat set of elements it owns. Element names describe ownership, not DOM depth, so the class stays card__title however deep the node sits. If a part needs its own parts, promote it to a block.

open as a page

In BEM, a modifier is written as a second class on the node (`class="btn btn--primary"`) rather than replacing the base class. Why, and what does that imply about how the two rules interact?

level: middleimportance: should knowfreq 45%

basics

~20 s

A modifier rule holds only the difference from the base, so the base class must stay on the node to supply the shared styles. Both are single-class selectors of equal specificity, so the modifier must come later in the stylesheet to win.

open as a page

What are OOCSS's two principles — separating structure from skin, and container from content — and how do they show up in the class names you write?

level: middleimportance: should knowfreq 32%

basics

~20 s

OOCSS, from Nicole Sullivan, says to split a component's box mechanics (structure) from its paint (skin) so a new variant costs one small rule, and to style objects by their own class rather than by where they sit, so the same class works anywhere.

open as a page

What are the practical costs of adopting BEM naming across a large CSS codebase, and what would make you conclude it is no longer paying for itself?

level: seniorimportance: should knowfreq 40%

basics

~20 s

BEM's costs are verbose markup, long repetitive names, and rename churn across templates, stylesheets, tests and scripts. Its bigger weakness is that nothing enforces it: one descendant selector reintroduces the coupling it removed. It stops paying when discipline cannot be sustained or tooling already guarantees scoping.

open as a page

What are the five categories SMACSS sorts CSS rules into, and what do namespace prefixes such as `l-`, `is-`, `c-` and `u-` tell a reader about a class?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

SMACSS sorts rules into Base, Layout, Module, State and Theme, prefixing layout with l- and states with is- or has-. Namespace prefixes generalise the idea: c- marks a component, o- an abstract object, u- a utility, so the prefix advertises how risky a class is to edit.

open as a page