Goal
You’ll understand what real problem a CSS naming system solves as a project scales, be able to read BEM-style class names correctly, and recognize when a project has genuinely outgrown ad hoc, unstructured naming.
Learn
As a project grows past a handful of pages, plain class names like .title or .button start colliding or becoming ambiguous — which title, styled how, used in which specific context? BEM (Block, Element, Modifier) is one widely used naming convention that solves this directly, by encoding structural relationships into the class name itself:
.card { } /* Block: the standalone component itself */
.card__title { } /* Element: a part nested inside the block */
.card__image { } /* Another element, same block */
.card--featured { } /* Modifier: a variation of the block */ Reading .card__title, you immediately know, just from the name itself: this is the title element, specifically belonging to a card component, not some other unrelated title elsewhere on the page that happens to share a generic name. This self-documenting quality is BEM’s real value — a new developer reading the HTML/CSS can understand structural relationships without needing to trace through nested selectors or guess from context.
BEM isn’t the only naming approach in real use — utility-first systems (like Tailwind CSS) take a different philosophy entirely, applying many small, single-purpose classes directly in HTML rather than naming custom components. Both approaches solve the same underlying scaling problem, just with different tradeoffs; BEM optimizes for descriptive, semantic class names, while utility-first optimizes for not having to write custom CSS at all for most common styling needs.
Decision Task
You have a “featured” version of a card that needs a gold border, but is otherwise identical to a normal card. Before reading on, using BEM naming: would the class be .card-featured, .card__featured, or .card--featured?
Reveal the Answer
.card--featured — the double-dash signals a modifier, a variation of the existing block, not a new sub-element inside it. __ (double underscore) is reserved specifically for elements nested inside a block, a genuinely different structural relationship than a modifier/variation of the whole block itself.
Common Mistake
Mixing naming conventions inconsistently within one project — some components named in BEM, others in ad hoc or inconsistent naming — which makes the codebase genuinely harder to navigate over time, since the naming pattern itself stops reliably communicating structure, and a developer can no longer trust that a double-underscore always means “element” throughout the whole project.
Practice Questions
1. A card component has a footer section inside it. Using BEM, what would you name that footer’s class?
Show Answer
.card__footer
2. A “compact” version of a card modal exists, smaller than the normal version but otherwise the same structure. What BEM class name fits this variation?
Show Answer
.card–compact
3. Explain the core difference in purpose between BEM and a utility-first approach like Tailwind, in your own words.
Show Answer
BEM optimizes for descriptive, self-documenting class names that describe structure directly; utility-first optimizes for rapid styling using many small, reusable, single-purpose classes without writing custom component CSS at all.
4. True or False: in BEM, double underscore and double dash mean the same structural relationship, just written differently.
Show Answer
False — double underscore signals an element (nested part), double dash signals a modifier (variation); these are genuinely different relationships.
5. Why does mixing naming conventions inconsistently within one project cause more confusion than picking one convention and staying consistent, even if that one convention isn’t perfect?
Show Answer
Because the entire value of a naming convention is that a developer can trust the pattern consistently — once a project mixes conventions, that trust breaks down, and a developer can no longer rely on a name’s structure to tell them anything reliable about the actual relationship it represents.
Try It Yourself
A card component has an image inside it. Using BEM, what would you name that image’s class?
Show Answer
.card__image — it’s a part (element) nested inside the card block, following the same pattern as .card__title.
Quick Check
1. In BEM, what does a double underscore (__) signal?
Show Answer
An element nested inside a block.
2. What does a double dash (–) signal?
Show Answer
A modifier/variation of a block.
3. What real problem does a naming convention like BEM solve as a project grows?
Show Answer
Ambiguous or colliding class names that no longer clearly communicate structure or relationship.
4. A card component has an image inside it. Using BEM, what would you name that image’s class?
Show Answer
.card__image — it’s a part (element) nested inside the card block, following the same pattern as .card__title.
5. You have a “featured” version of a card that needs a gold border, but is otherwise identical to a normal card. Before reading on, using BEM naming: would the class be .card-featured, .card__featured, or .card--featured?
Show Answer
.card--featured — the double-dash signals a modifier, a variation of the existing block, not a new sub-element inside it. __ (double underscore) is reserved specifically for elements nested inside a block, a genuinely different structural relationship than a modifier/variation of the whole block itself.