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

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 you adopt a CSS strategy, it’s worth thinking about your context:

  • Team structure: If your design and engineering teams collaborate closely, BEM works well. If they work more independently, CSS Modules or stricter isolation prevents unintended style conflicts.
  • Build tooling: Modern build tools and frameworks such as Vite and Next.js 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: If you already have an established design system and tokens, Utility-First CSS or CSS-in-JS work well. If you don’t have standardized design tokens yet, 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, because you’re building with progressive enhancement
  • 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. If you pair it with Design Tokens and Folder Organization, your stylesheets stay 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, where global styles get in the way of reuse
  • Your build tool supports CSS module imports like 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, because without tokens inconsistency builds up 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 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 or runtime data, not 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 values like spacing and color. 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, and updating linting rules so the team follows the strategy. You’ll also want to revisit the choice as your team 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.