Skip to main content
Saved
Guide
Category strategy
Difficulty Intermediate

Choosing a CSS Strategy

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

Den Odell
By Den Odell Added
In this article
  1. Assess Your Situation First
  2. Evaluate the Core Options
  3. BEM Class Naming
  4. CSS Modules & Scoped Styles
  5. Utility-First CSS
  6. CSS-in-JS
  7. Combine Strategies When It Makes Sense
  8. Quick Decision Guide

Choosing a CSS Strategy

How you write CSS affects whether your team ships features efficiently or struggles with specificity conflicts. This guide helps you evaluate which approach fits your situation.

Assess Your Situation First

Before adopting a CSS strategy, consider your context:

  • Team structure: When design and engineering teams collaborate closely, BEM works well. When teams work more independently, CSS Modules or stricter isolation prevents unintended style conflicts.
  • Build tooling: Modern build tools and frameworks (Vite, Next.js, React Router) make CSS Modules straightforward to implement. For serverless or custom rendering setups, CSS-in-JS needs a careful look at its runtime cost, especially its effect on hydration timing.
  • Design system maturity: With an established design system and tokens, Utility-First CSS or CSS-in-JS work well. Without standardized design tokens, a simple naming convention can prevent inconsistency while the design system matures.
  • Performance requirements: CSS Modules and Utility-first approaches ship only the styles you use. CSS-in-JS can add runtime overhead that affects Core Web Vitals.

Evaluate the Core Options

BEM Class Naming

BEM Class Naming uses a structured naming system (Block__Element—Modifier) for predictable, globally scoped CSS. The class names can be verbose, but the convention makes it clear what each class belongs to and helps avoid conflicts.

Choose BEM when:

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

Keep it manageable: BEM without governance can produce deeply nested class names like .header__nav__list__item__link--active--hover--mobile. Pairing it with Design Tokens and Folder Organization keeps the stylesheets readable as they grow.

CSS Modules & Scoped Styles

CSS Modules automatically scope class names to individual components, preventing style conflicts. They give you scoped styles without tying you to a framework.

Choose CSS Modules when:

  • You want to write standard CSS without worrying about class name conflicts
  • You’re building component libraries used across multiple projects (global styles and reusability often conflict)
  • Your build tool supports CSS module imports (import styles from "./button.module.css"), which most modern bundlers do

Extra protection: Style Isolation keeps third-party widget styles from bleeding into your components. Embedded widgets sometimes ship with global CSS that may override your styles.

Utility-First CSS

Utility-First CSS uses small, single-purpose classes (like p-4 or text-blue-500) to build UIs quickly. Developers tend to love or hate this approach.

Choose Utility-First when:

  • Teams build many UI variations and want to avoid debates over class names
  • Your design system already uses tokenized spacing and colors (without tokens, inconsistency accumulates quickly)
  • You want rapid iteration with automatic unused-code removal

Keep markup readable: Without discipline, class lists get very long: <div class="mt-4 md:mt-6 lg:mt-8 xl:mt-10 2xl:mt-12 hover:mt-3 focus:mt-5">. Linting and Design Token governance keep them under control.

CSS-in-JS

CSS-in-JS writes styles directly in JavaScript, enabling dynamic styling based on props, themes, or state. It handles dynamic styling well, but it needs careful implementation to keep runtime overhead down.

Choose CSS-in-JS when:

  • Components need dynamic styles based on themes, user preferences, or runtime data (beyond just colocating styles with components)
  • Your SSR setup supports zero-runtime CSS extraction, or you’ve accepted the performance trade-off
  • You already use a Theme Provider and need programmatic access to design tokens

Performance tip: Styles generated from props can be recalculated on every render. Memoizing and caching dynamic styles helps, especially in large lists where that work adds up.

Combine Strategies When It Makes Sense

In my experience, you don’t have to stick to one approach.

  • Foundation + Layout: BEM or CSS Modules for design system components that stay stable, with Utility-first classes for page layouts that change frequently.
  • Static + Dynamic: Most components stay in CSS Modules, and CSS-in-JS comes in only where you need dynamic theming or runtime-responsive styles. Not every component requires JavaScript for styling.
  • Design tokens everywhere: Whatever CSS strategy you choose, it helps to keep one source of truth for spacing, colors, and typography. Without one, similar values drift apart over time.

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 debates over semantic class names
Dynamic theming or personalizationCSS-in-JSEnables runtime styling decisions (but measure the performance cost, and check whether CSS custom properties already cover your theming)

Next steps: It’s worth documenting the decision before different approaches appear independently across the codebase, updating linting rules so the team follows the strategy, and adding examples to your style guide. The choice also needs revisiting as teams and requirements change.

Newsletter

A Monthly Email
from Den Odell

Behind-the-scenes thinking on frontend patterns, site updates, and more

No spam. Unsubscribe anytime.