skip to content

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%

answer

  1. the modifier is a delta, not a component
  2. base class supplies everything shared
  3. two selectors, one class each
  4. later rule wins, not later attribute token
  5. doubling the class escalates specificity

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.

solid answer

~50 s

The base class carries everything common to the component and the modifier carries only the delta, so dropping the base class would strip the shared styles rather than override them — `.btn--primary` alone has no padding, no radius, no font. Keeping both classes also means the two selectors have identical specificity, one class each. Nothing about the modifier makes it stronger; it wins only because it appears **later in the source order**, which is why modifier rules are authored immediately after the block they modify. A consequence people get wrong: when two modifiers set the same property, the order of the classes in the HTML `class` attribute is irrelevant — stylesheet order decides. The tempting fix, writing `.btn.btn--primary` to force a win, does work by raising specificity to two classes, but it breaks BEM's flat-specificity property and starts an escalation that later overrides have to match.

code

css · 13 lines
css
.btn {
  display: inline-flex;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  background: #eee;
  color: #222;
}

/* authored after the block: equal specificity, later wins */
.btn--primary {
  background: #0057d8;
  color: #fff;
}

go deeper

for a junior

Know that both classes go on the element — the base plus the modifier — and that the modifier rule contains only what differs from the base.

for a middle

Explain the mechanics: identical single-class specificity means source order decides, so modifier rules are authored right after their block and the HTML attribute order is irrelevant.

for a senior

Show how you diagnose a losing modifier — competing descendant selectors, a re-declared base further down — instead of escalating to a doubled class that everyone downstream must then match.

for a principal

Own the codebase-wide consequence: once one team doubles classes to win, overrides ratchet upward everywhere. Set the convention and enforce the ordering rather than relying on individual restraint.

## What a modifier rule is allowed to contain In BEM a modifier expresses a *difference*, not a whole component. The base block rule holds every declaration shared by all variants; the modifier holds only what changes: ```css .btn { display: inline-flex; padding: 0.5rem 1rem; border: 1px solid transparent; border-radius: 4px; background: #eee; color: #222; } .btn--primary { background: #0057d8; color: #fff; } ``` Given that split, a node carrying only `btn--primary` would render as blue text-coloured nothing: no padding, no border radius, no inline-flex. The base class is not decoration, it is the component. That is the whole reason both classes ride together: ```html <button class="btn btn--primary">Save</button> ``` The alternative design — a self-contained `.btn-primary` that repeats every shared declaration — is what BEM is rejecting. It duplicates the component for each variant, so a change to the padding has to be made in every variant rule, and one of them will be missed. ## The specificity consequence `.btn` and `.btn--primary` are both single class selectors: identical specificity. Nothing in the naming makes the modifier stronger. When two rules of equal specificity in the same origin and layer both apply, the cascade falls through to **order of appearance**, and the later rule wins. So the modifier only overrides the base because it is written after it. This is a real ordering constraint, not a theoretical one. Author each block's modifiers immediately after that block, and never let a base rule be re-declared further down the sheet — a late `.btn { background: #eee; }` will silently defeat every modifier above it. ## The mistake that follows: attribute order Engineers frequently believe `class="btn--primary btn"` behaves differently from `class="btn btn--primary"`. It does not. The order of names inside the `class` attribute has no meaning to the cascade at all; it is an unordered set of tokens. Only the order of the *rules in the stylesheet* matters. The same applies when two modifiers collide: ```css .btn--primary { background: #0057d8; } .btn--danger { background: #c92a2a; } ``` ```html <button class="btn btn--primary btn--danger">Delete</button> ``` The button is red — not because `btn--danger` is second in the markup, but because its rule is second in the stylesheet. Reverse the two rules and the same markup turns blue. That fragility is a design signal: two modifiers that set the same property are mutually exclusive states and should be modelled as one modifier with different values (in the original Yandex syntax, a key/value modifier like `btn_theme_danger`) or simply never applied together. ## Why teams reach for `.btn.btn--primary`, and what it costs When a modifier appears not to work, the quick fix is to double the class: ```css .btn.btn--primary { background: #0057d8; } /* two classes, wins regardless of order */ ``` This genuinely works — two class selectors outweigh one — and it removes the ordering dependency. The cost is that it abandons the flat-specificity property that made BEM predictable in the first place. Every later rule that needs to override this one must now also reach two classes, and the next person will double again to win. If a modifier is losing, the honest diagnosis is almost always source order or a stray descendant selector elsewhere, and fixing that is cheaper than escalating. ## Modifiers on elements The same rules apply one level down. An element modifier is `block__element--modifier`, applied beside the element class: ```html <h2 class="card__title card__title--truncated">…</h2> ``` Again the modifier holds only the delta — the overflow and ellipsis declarations — and the element class holds the shared typography. ## Boolean versus key/value The two-dashes style treats modifiers as booleans: present or absent. The original Yandex syntax also supports key/value pairs, where a name encodes a property and its value. Key/value shines exactly where boolean modifiers get dangerous — mutually exclusive variants such as theme or size, where only one value can be true at a time. Whichever spelling you adopt, pick one for the codebase: a reader cannot parse a name whose delimiters mean different things in different files.

  • Does the order of class names inside the HTML class attribute affect which rule wins?
    No. The `class` attribute is an unordered token list; the cascade never looks at it. When two rules of equal specificity both match, the winner is the one written later in the stylesheet. Swapping `class="btn btn--primary"` to `class="btn--primary btn"` changes nothing, which is why a modifier that is not applying is nearly always a source-order or stray-selector problem.
  • A modifier is being ignored even though it is written after the base rule. What do you check?
    Look for a competing selector with more weight — a descendant chain such as `.toolbar .btn`, an id selector, or a rule that re-declares `.btn` further down the file. Also check that both classes are actually on the node and spelled with the codebase's delimiter. Escalating the modifier to two classes hides the cause rather than fixing it.
  • When would you prefer a key/value modifier over separate boolean modifiers?
    When the variants are mutually exclusive — theme, size, visual state. Boolean modifiers make it possible to apply two conflicting flags at once, and the result then depends on stylesheet order rather than intent. A single key with a value makes the exclusivity explicit and gives templating code one variable to set instead of a set of flags to keep consistent.

saying these in an interview costs you the question

  • Thinks the modifier class alone carries the component's styles
  • Believes class-attribute order decides which rule wins
  • Says a modifier beats the base because it is more specific
  • Repeats all base declarations inside every modifier rule
  • Chains .btn.btn--primary as the routine fix for overrides

context