BEM: Self-Documenting CSS Class Names
BEM turns classes into a mini filesystem: Block__Element--Modifier. It prevents CSS collisions in large React codebases where many teams share a global stylesheet. The footgun is turning every nested tag into an element and creating comically long names.
WHY IT EXISTS: CSS is global by default. When two developers on the same React app write .title or .active, the last one loaded wins and bugs appear at runtime without type errors or build failures. BEM was created to solve this namespace collision problem without requiring JavaScript tooling. By baking the component name into every class, it creates cheap, human-readable scoping that works in any browser and any build pipeline.
THE MENTAL MODEL: Think of your interface as a set of LEGO blocks. A Block is a standalone thing like a Button or a Card. An Element is a piece that only belongs inside that block, like a Button's icon or a Card's title. A Modifier is a variant, like a primary Button or a featured Card. The class name itself tells you which block owns the element and whether it is a variant, so the HTML becomes self-documenting.
HOW IT WORKS: The syntax is strictly Block__Element--Modifier. The double underscore means belongs to. The double hyphen means looks different. For example, a search form might use .search-form, .search-form__input, and .search-form--compact. You write these as flat single classes in CSS, which keeps specificity low and avoids the specificity wars caused by deep selectors. If an element needs its own state, you attach the modifier to it, like .search-form__input--error. Every word is lowercase and separated by a single hyphen, so multi-word blocks become .user-profile__avatar--large.
WHEN TO USE IT: Reach for BEM in React or Next.js when you are using plain CSS, Sass, or CSS Modules and you want naming conventions that survive code review without extra dependencies. It is especially valuable in design systems and component libraries where many engineers touch the same stylesheets. Because the convention is visible in the DOM, debugging in browser DevTools is immediate: you see a class and you know exactly which file to open.
WHEN NOT TO USE IT: Do not use BEM inside Tailwind or utility-first workflows, because utility classes already encode scope through composition and BEM names would only add noise. It also fights against CSS-in-JS libraries like styled-components or Emotion, where the component name lives in the JavaScript and the library hashes classes for you. Finally, avoid turning every nested div into an element; if a component has its own independent meaning, promote it to a new block rather than chaining three underscores.
ONE CANONICAL EXAMPLE: Imagine a Next.js page with a ProductCard React component. Its JSX contains a root div with class product-card, an image with class product-card__image, a title with class product-card__title, and a modifier class product-card--on-sale applied to the root when discounted. The CSS lives in ProductCard.module.css or a co-located Sass file. Six months later, another developer sees product-card__title in the DOM and knows instantly that the style lives in the ProductCard stylesheet, cannot collide with a ReviewCard__title, and that product-card--on-sale is a state variant rather than a separate component.
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.