Saved
Expert Guide

Choosing a CSS Strategy

Decision framework for selecting between BEM, CSS-in-JS, Utility-first, and CSS Modules.

Icon for Choosing a CSS Strategy
Level
Intermediate
Type
Framework

By Den Odell

Framework Guide

Choosing a CSS Strategy

How you write CSS affects whether your team ships features fast or drowns in specificity wars. Let’s cut through the marketing and figure out which approach actually fits your situation.

Assess Your Situation First

Before you cargo-cult whatever framework your Twitter timeline is hyping this week, consider:

  • Team structure: Design and engineering teams actually talking to each other? BEM works. Teams operating in parallel universes? You need CSS Modules or stricter isolation before someone’s .button class ruins someone else’s day.
  • Build tooling: Modern bundlers (Vite, Next, Remix) make CSS Modules trivial. Serverless or custom rendering? Carefully evaluate CSS-in-JS performance costs or accept that your users will stare at unstyled content during hydration.
  • Design system maturity: Got an actual design system with tokens? Utility-First CSS or CSS-in-JS shine here. Still figuring out if buttons should be blue or “brand-primary-interactive-500”? A simple naming convention prevents chaos while you figure it out.
  • Performance requirements: CSS Modules and Utility-first ship only what you use. CSS-in-JS can add runtime overhead that makes your Core Web Vitals look like a JavaScript conference’s carbon footprint.

Evaluate the Core Options

BEM Class Naming

BEM Class Naming uses a structured naming system (Block__Element—Modifier) for predictable, globally scoped CSS. Yes, the class names look ridiculous. No, that’s not the point.

Choose BEM when:

  • You need CSS to work without JavaScript (progressive enhancement still matters)
  • Multiple teams collaborate on shared UI components without stepping on each other
  • Designers already think in terms of reusable blocks and variations

Keep it manageable: BEM without governance turns into .header__nav__list__item__link--active--hover--mobile. Combine it with Design Tokens and Folder Organization or watch your stylesheets become unmaintainable faster than you can say “technical debt.”

CSS Modules & Scoped Styles

CSS Modules automatically scope class names to individual components, preventing style conflicts. It’s scoped styles without the framework lock-in.

Choose CSS Modules when:

  • You want to write normal CSS without playing “guess which .button class is conflicting now”
  • You’re building component libraries that’ll be used across multiple projects (because global styles and reusability mix like oil and water)
  • Your build tool already supports CSS module imports (import styles from "./button.module.css"), and if you’re using anything modern, it does

Extra protection: Use Style Isolation to keep third-party widget styles from bleeding into your components. Because that email capture modal you embedded? It ships with 50KB of global CSS that thinks h1 should be Comic Sans.

Utility-First CSS

Utility-First CSS uses small, single-purpose classes (like p-4 or text-blue-500) to build UIs quickly. It’s polarizing. People either love it or think you’ve lost your mind.

Choose Utility-First when:

  • Teams build tons of UI variations and you’re tired of bikeshedding over class names
  • Your design system already uses tokenized spacing and colors (otherwise you’ll end up with 50 shades of blue)
  • You want rapid iteration with automatic unused-code removal (because yes, shipping 3MB of CSS “just in case” is still a bad idea)

Prevent chaos: Without discipline, you’ll end up with <div class="mt-4 md:mt-6 lg:mt-8 xl:mt-10 2xl:mt-12 hover:mt-3 focus:mt-5">. Use linting and Design Token governance or accept that your HTML will look like line noise.

CSS-in-JS

CSS-in-JS writes styles directly in JavaScript, enabling dynamic styling based on props, themes, or state. It solves real problems but creates new ones if you’re not careful.

Choose CSS-in-JS when:

  • Components need truly dynamic styles based on themes, user preferences, or runtime data (not just “I want my styles colocated with my components”)
  • Your SSR setup supports zero-runtime CSS extraction, or you’ve made peace with the performance hit
  • You already use a Theme Provider and need programmatic access to design tokens

Performance tip: That “style button based on props” pattern? It re-generates styles on every render. Memoize and cache or watch your component tree become a CSS generation factory. For large lists, this kills performance faster than nested .map() calls.

Combine Strategies When It Makes Sense

Real talk: you don’t have to pick just one approach and die on that hill.

  • Foundation + Layout: Use BEM or CSS Modules for your design system components (the stuff that doesn’t change), then add Utility-first classes for page layouts that change every sprint when stakeholders have “just one more idea.”
  • Static + Dynamic: Keep most components in CSS Modules but reach for CSS-in-JS when you actually need dynamic theming or animations that respond to runtime data. Not every component needs JavaScript to style itself.
  • Always use design tokens: This isn’t optional. Regardless of your CSS strategy, maintain one source of truth for spacing, colors, and typography. Otherwise you’ll end up with #3b82f6, #2563eb, #1d4ed8, and “that blue from the prototype” all meaning “primary blue.”

Quick Decision Guide

Your SituationBest StrategyReason
Refactoring existing global CSSBEM Class NamingIncrementally improve without rewriting your entire build pipeline
Building a shared component libraryCSS ModulesPrevents conflicts across projects without requiring consumers to adopt your framework
Rapid prototyping and iterationUtility-First CSSShip UI changes without bikeshedding over semantic class names
Dynamic theming or personalizationCSS-in-JSEnables runtime styling decisions (but measure the performance cost)

Next steps: Document your decision before three different approaches emerge organically. Update linting rules so the team actually follows the strategy. Add examples to your style guide that developers will actually read. Then review your choice yearly, because your team, product, and performance needs will change, and clinging to old decisions because “that’s how we’ve always done it” is how you end up with jQuery in 2025.

Stay Updated

Get New Patterns
in Your Inbox

Join thousands of developers receiving regular insights on frontend architecture patterns

No spam. Unsubscribe anytime.