Back to stories
<Frontend/>

Component-Driven CSS: BEM, Utilities, and When to Use Each

Share by

Component-Driven CSS: BEM, Utilities, and When to Use Each

How you name and structure CSS for components affects consistency, refactors, and bundle size. BEM, utility-first (e.g. Tailwind), and scoped/component CSS each solve different problems. This post compares them and when to use (or mix) each.


BEM: Block Element Modifier

BEM gives you a naming convention: .block, .block__element, .block--modifier. The block is the component; elements are its parts; modifiers are variants.

Pros: Clear ownership (no leaking), predictable names, no need for build-time scoping. Cons: Long class names, repetition, and you must keep the convention strict or it drifts.

Use BEM when: You want component-scoped naming without a framework, or you’re in a legacy codebase where utilities aren’t adopted yet.


Utility-first (e.g. Tailwind)

Utility-first means many small, single-purpose classes (e.g. flex, gap-4, text-gray-700). You compose them in the template instead of writing custom CSS per component.

Pros: Fast UI building, consistent spacing/colors from a config, small production bundle with purging. Cons: Markup can get noisy; complex components may need @apply or a small number of component classes, which can blur the line.

Use utility-first when: You want design-system consistency and fast iteration, and the team is okay with class-heavy markup.


Scoped / component CSS

Scoped CSS (e.g. Vue’s scoped, CSS Modules) generates unique class names so component styles don’t leak. You write normal CSS (or BEM-like names) and the tooling handles scope.

Pros: No global namespace clashes, component-focused files. Cons: Overriding from outside is harder; deep selectors can couple you to structure.

Use scoped/modules when: You’re in a component framework and want styles colocated with components without fighting specificity.


Mixing approaches

You can combine: tokens and utilities for layout and spacing, BEM or scoped for complex components with many states, and semantic classes for high-level layout (e.g. .page, .sidebar). Decide up front what is global (tokens, resets, utilities) and what is component-local (BEM or scoped).


Summary

  • BEM gives clear, component-level naming without tooling; good when you’re not all-in on utilities.
  • Utility-first speeds up UI and enforces design system; good for greenfield or when the team likes composing in markup.
  • Scoped/component CSS keeps styles local to components; pair with tokens and optionally utilities. Pick one primary approach and use the others only where they clearly help.