Skip to main content

When to Use Each CSS Methodology: The Decision Guide

Choosing the wrong CSS methodology wastes weeks of refactoring. Choosing the right one at the start of a project means your CSS stays maintainable as the project grows. This guide maps every realistic project scenario to the methodology it fits best.


1. The Quick Decision Matrix

┌─────────────────────────────────────────────────────────────────────┐
│ Your Situation → Use This │
├─────────────────────────────────────────────────────────────────────┤
│ WordPress site, vanilla CSS, single dev → BEM │
│ Rapid prototype, AI-assisted, quick ship → Utility-First │
│ Component library with heavy reuse → OOCSS │
│ Large team, need shared CSS structure → SMACSS │
│ Enterprise / very large codebase (50+ files)→ ITCSS │
│ Any project that scales over time → ITCSS + BEM │
│ Tailwind-based project → Utility-First + @apply│
│ WordPress + no build step → BEM + SMACSS states │
│ WooCommerce or form-heavy site → SMACSS + BEM │
│ Design system / token-driven → OOCSS + ITCSS │
└─────────────────────────────────────────────────────────────────────┘

2. Use BEM When...

✅ Ideal Scenarios for BEM

Scenario A: Single WordPress Site (No Build Step)

You're building a GeneratePress or Bricks Builder site with a child theme and writing vanilla CSS in the WordPress customizer or an external stylesheet.

Project: Custom WordPress business site
Team: 1 developer
Stack: WordPress + GeneratePress + Additional CSS
Build step: None
Duration: 1–4 weeks

Best choice: BEM ✅
Why: Self-documenting class names, no tooling needed, predictable structure,
easy to debug in browser DevTools, resistant to WP plugin CSS conflicts.
<!-- Your BEM component in a WP page template -->
<article class="feature-card feature-card--highlighted">
<div class="feature-card__icon"></div>
<h3 class="feature-card__title">Fast Delivery</h3>
<p class="feature-card__text">We ship in 48 hours.</p>
<a class="feature-card__btn" href="#">Learn More</a>
</article>

Scenario B: Component Library

You're building reusable components that will be copy-pasted across multiple client projects.

Project: Personal design system (shared across 10+ client sites)
Team: 1–3 developers
Stack: Any — JS framework, WordPress, or static HTML
Goal: Reuse without naming conflicts across many projects

Best choice: BEM ✅
Why: Block names are unique and self-contained. Dropping .card CSS into
any project does not conflict with anything. This is BEM's superpower.

Scenario C: Team Onboarding

New developers join a project and need to understand the codebase within 1 day.

/* A new developer reads this and instantly knows: */
.pricing-card {} /* This styles a pricing card */
.pricing-card__price {} /* The price inside a pricing card */
.pricing-card--featured {} /* A special variant of the pricing card */

/* vs. the confusion of: */
.price {} /* Which element? What context? */
.featured { } /* Featured what? */
.card-2 { } /* Is this the second card or a variant? */

❌ Don't Use BEM When

  • You need to prototype rapidly (too slow to brainstorm names for every element)
  • You're using a Tailwind-first project (clash of paradigms)
  • Your project has almost no components — just a few styled pages ← overkill
  • You need utility-level overrides frequently (BEM lacks utilities by design)

3. Use Utility-First (Tailwind) When...

✅ Ideal Scenarios for Utility-First

Scenario A: Rapid Prototype / Vibe Coding / AI-Generated

You're using AI (Claude, ChatGPT, Cursor) to generate UI components quickly and want to paste them directly into a browser without a CSS file.

Project: Landing page prototype for a client pitch
Timeline: 2–8 hours
Team: 1 developer + AI assistant
Stack: Static HTML or React/Next.js

Best choice: Utility-First ✅
Why: AI generates Tailwind by default. Utility classes require zero CSS file
setup. You see the design in the HTML. Iterate at maximum speed.
<!-- AI generates this in seconds — no CSS file needed -->
<section class="min-h-screen flex items-center justify-center bg-gray-950 px-6">
<div class="text-center max-w-3xl mx-auto">
<h1 class="text-6xl font-bold text-white mb-6">Your Idea Here</h1>
<p class="text-xl text-gray-400 mb-10">Short compelling description.</p>
<a href="#" class="px-8 py-4 bg-purple-600 text-white rounded-full font-semibold hover:bg-purple-500 transition-colors">
Get Started
</a>
</div>
</section>

Scenario B: React / Next.js / Astro Application

Your project has a JavaScript frontend framework with a build pipeline already in place.

Project: SaaS application dashboard, marketing site, Next.js blog
Team: 1–5 developers
Stack: React, Next.js, Astro, Vue, Svelte
Build step: Yes (Vite, Webpack, Turbopack)

Best choice: Utility-First ✅
Why: Tailwind's JIT compiler is purpose-built for component-based frameworks.
Each component contains its own styling — no stylesheet to maintain.
Team members don't fight over the global CSS file.

Scenario C: Design System With Strict Token Constraints

Your design uses a rigid spacing/color scale and you need to enforce that nobody uses arbitrary values.

Best choice: Utility-First with customized config ✅
Why: tailwind.config.js enforces the design token system.
Developers can ONLY use approved values — no more "margin: 13px".
No need for code reviews to catch off-system values.

Scenario D: Multiple Developers / No Naming Agreement

Your team can't agree on class names, and maintaining a shared CSS file causes conflicts.

Best choice: Utility-First ✅
Why: No custom class names = no naming conflicts. Every developer uses
the same Tailwind vocabulary. CSS file stays minimal.
Works especially well with CSS Modules in React.

❌ Don't Use Utility-First When

  • WordPress without a build step — Tailwind requires Node.js + build pipeline
  • Long-term content site — HTML becomes hard to maintain when classes span 5+ lines
  • Client edits HTML manually — utility class soup is unreadable to non-developers
  • You need to override third-party styles — utility classes have low specificity and can't target external component classes

4. Use OOCSS When...

✅ Ideal Scenarios for OOCSS

Scenario A: Design System With Many Visual Variants

You have one structural pattern (a button, a badge, an alert) that needs 8–15 visual variants without duplicating the structure code.

Project: Internal component library / design system
Team: 1–3 developers
Goal: Single source of structural truth + unlimited skins

Best choice: OOCSS ✅
Why: Structure defined once, skins added by combining classes.
Adding a new color variant is one new CSS rule — not a copy-paste.
/* Structure once */
.badge { display: inline-flex; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 700; }

/* Skin n times — no structural code repeated */
.badge-primary { background: rgba(108,99,255,0.15); color: var(--color-primary); }
.badge-success { background: rgba(56,161,105,0.15); color: var(--color-success); }
.badge-danger { background: rgba(229,62,62,0.15); color: var(--color-danger); }
.badge-warning { background: rgba(237,137,54,0.15); color: var(--color-warning); }
.badge-outline { background: transparent; border: 1.5px solid currentColor; }
/* → Adding a 5th color: 1 line of CSS. No structure copy-paste. */

Scenario B: WordPress Patterns Plugin / Reusable Block Patterns

You're creating WordPress block patterns that are inserted into many different pages and containers.

Project: WordPress patterns library (saved block compositions)
Team: WP developer/designer
Stack: WordPress + block editor

Best choice: OOCSS ✅
Why: OOCSS' container/content separation principle means your pattern
looks correct whether placed in a wide section, narrow sidebar,
or inside a column block. Location-independent styling.

Scenario C: Extending an Existing Framework

You're building on top of Bootstrap or another framework that already uses structural classes.

Best choice: OOCSS ✅
Why: Bootstrap IS OOCSS. .btn is structure, .btn-primary is skin.
Understanding OOCSS helps you extend Bootstrap cohesively.

❌ Don't Use OOCSS Alone When

  • Your project needs naming structure for a team → pair with BEM
  • You have complex stateful components (OOCSS has no state convention)
  • The project has very few reusable patterns → overkill

5. Use SMACSS When...

✅ Ideal Scenarios for SMACSS

Scenario A: Team Project With JavaScript State Changes

Your UI has lots of states toggled by JavaScript — open menus, loading states, active items, form validation.

Project: Dynamic web application with complex interactions
Team: 2–5 developers (CSS + JS)
Need: Clear boundary between CSS and JavaScript-driven changes

Best choice: SMACSS ✅
Why: SMACSS's State category defines exactly how JS touches CSS.
JS adds .is-open, .is-loading, .has-error — never custom class names.
Every developer knows exactly which classes JS will toggle.
// With SMACSS, every developer knows what JS does:
nav.classList.toggle('is-open'); // State
form.classList.add('has-error'); // State
submitBtn.classList.add('is-loading'); // State
menuItem.classList.add('is-active'); // State

// Never this ambiguity:
nav.classList.toggle('nav-open-menu'); // ❌ Unclear — is this BEM? Custom?

Scenario B: Multi-Developer WordPress Theme

A team is building a WordPress theme where different developers own different sections.

Project: Custom WordPress theme for a media company
Team: 4 developers — layout, components, states, themes
Stack: WordPress + Sass (child theme)
File count: 30+ CSS files

Best choice: SMACSS ✅
Why: Each developer owns a SMACSS category:
Dev 1 → base/ and layout/
Dev 2 → modules/ (card, nav, hero)
Dev 3 → modules/ (forms, modals, sidebar)
Dev 4 → state/ and themes/
No file conflicts. Clear ownership by category.

Scenario C: Site With Multiple Themes

A site that needs a light/dark toggle, or multiple brand themes (blue/green/orange).

Project: SaaS platform with white-label for 3 clients
Need: Default theme + 3 brand overrides

Best choice: SMACSS (Theme category) ✅
Why: SMACSS's Theme layer is explicitly designed for this. All color,
typography, and decorative properties live in theme files —
swapping the theme is swapping one CSS file.
/* Default theme */
:root { --color-primary: #6c63ff; }

/* Client A theme */
.theme-client-a { --color-primary: #0066cc; }

/* Client B theme */
.theme-client-b { --color-primary: #00a86b; }

/* Applied by PHP based on site option */

❌ Don't Use SMACSS Alone When

  • Solo developer short project → too much overhead defining categories
  • Utility-first stack → categories don't map cleanly
  • Very small site (< 500 lines CSS) → overkill

6. Use ITCSS When...

✅ Ideal Scenarios for ITCSS

Scenario A: Large Codebase With Specificity Wars

Your codebase is mature, has 3,000+ lines of CSS, and your team keeps fighting with !important just to override styles from 2 years ago.

Problem: CSS file has grown to 80KB. Nobody knows what overrides what.
Adding new styles breaks old ones. Everything needs !important.

Best choice: ITCSS ✅
Why: ITCSS systematically prevents specificity wars by defining
exactly where each type of CSS belongs and what its specificity
ceiling is. Utilities at the bottom always win cleanly.
Refactoring to ITCSS is the cure for "CSS spaghetti."

Scenario B: Long-Term Product (2+ Years)

You're starting a project that you know will be maintained for years — a SaaS platform, a large brand site, an agency's framework.

Project: SaaS dashboard + marketing site
Expected: Maintained for 5+ years, team grows from 2 → 12
Stack: React/Next.js or WordPress + Sass

Best choice: ITCSS ✅
Why: ITCSS is designed for longevity. Its layer system prevents the
specificity drift that makes CSS unmaintainable after 2 years.
New team members understand where every CSS rule belongs.

Scenario C: Design System for Multiple Products

You're building a CSS framework used by multiple independent applications.

Project: Enterprise design system consumed by 5 different web properties
Need: Predictable cascade, configurable tokens, no conflicts between apps

Best choice: ITCSS ✅ (especially with @layer)
Why: ITCSS's layer system maps exactly to CSS @layer, giving
consuming applications full control over the cascade.
Settings layer exports tokens. Objects export structure patterns.
Components export styled UI. Utilities are the safety valve.

Scenario D: WordPress Gutenberg Theme With Full Site Editing

You're building a block-based WordPress theme using Full Site Editing (FSE) where theme.json provides settings, PHP provides structure, and CSS provides components.

Project: WordPress FSE block theme
Stack: theme.json (Settings) + PHP templates (Structure) + CSS (Components)

ITCSS layer mapping:
Settings → theme.json tokens
Generic → reset.css
Elements → editor-style.css base elements
Objects → theme.css layout patterns
Components → theme.css block overrides
Utilities → theme.css emergency overrides

Best choice: ITCSS ✅

❌ Don't Use ITCSS When

  • Quick project, < 2 weeks → too much upfront setup
  • Solo developer with < 20 CSS files → overhead is not worth it
  • You're using Tailwind exclusively → ITCSS layers are replaced by the utility pipeline
  • Client "Additional CSS" only → can't control file structure

7. Methodology Combination Guide

Real-world projects almost always combine methodologies. Here are the most effective pairings:

Combination 1: ITCSS + BEM (The Professional Standard)

Best for: Any serious long-term project

ITCSS provides: The layer order (where each file goes)
BEM provides: The naming convention (how classes are named)

ITCSS Layer 5 (Objects): .o-container, .o-grid (structure only)
ITCSS Layer 6 (Components): .c-card, .c-btn, .c-nav (BEM-named)
ITCSS Layer 7 (Utilities): .u-hidden, .u-text-center

This is the architecture used by GOV.UK, many large design systems,
and is Harry Roberts' own recommendation.
/* @layer-based ITCSS with BEM component naming */
@layer settings, generic, elements, objects, components, utilities;

@layer components {
/* BEM naming INSIDE the ITCSS components layer */
.c-card { background: var(--color-surface); border-radius: var(--radius-lg); }
.c-card__title { font-size: var(--text-xl); }
.c-card--featured { border-color: var(--color-primary); }

.c-btn { display: inline-flex; padding: 0.7rem 1.5rem; }
.c-btn--primary { background: var(--color-primary); }
}

Combination 2: BEM + SMACSS States (The WordPress Standard)

Best for: WordPress theme development with JavaScript interactions

BEM provides: Component naming and structure
SMACSS provides: State class convention (.is-*, .has-*)

Components: .nav, .nav__link, .nav--scrolled (BEM)
States: .is-open, .is-active, .has-dropdown (SMACSS)
Layout: .l-container, .l-sidebar (SMACSS)
// WordPress child theme JS
document.querySelector('.nav__hamburger').addEventListener('click', () => {
document.querySelector('.nav').classList.toggle('is-open'); // SMACSS state on BEM block
});

document.addEventListener('scroll', () => {
document.querySelector('.nav').classList.toggle('nav--scrolled', window.scrollY > 80); // BEM modifier
});

Combination 3: Utility-First + BEM (@apply extraction)

Best for: Tailwind project where some components repeat heavily

/* In your Tailwind CSS file */
@layer components {
/* Extract repeated Tailwind utilities into a BEM component */
.btn {
@apply inline-flex items-center justify-content-center gap-2 px-6 py-3
rounded-full font-semibold transition-all duration-200
focus:outline-none focus:ring-2 focus:ring-offset-2
disabled:opacity-50 disabled:pointer-events-none;
}
.btn--primary {
@apply bg-purple-600 text-white hover:bg-purple-500 focus:ring-purple-500;
}
}

Combination 4: OOCSS + SMACSS (The CSS Framework Pattern)

Best for: Building a reusable CSS framework for multiple sites

/* OOCSS: structure/skin objects */
.btn { display: inline-flex; padding: 0.7rem 1.5rem; border-radius: 9999px; }
.btn-primary { background: var(--color-primary); color: white; }
.btn-outline { border: 2px solid var(--color-primary); color: var(--color-primary); }

/* SMACSS states: JS toggles */
.btn.is-loading { pointer-events: none; opacity: 0.6; }
.btn.is-disabled { opacity: 0.4; pointer-events: none; }

/* SMACSS layout: page structure */
.l-container { max-width: 1200px; margin: 0 auto; }
.l-grid-3 { display: grid; grid-template-columns: repeat(3,1fr); gap: 1.5rem; }

8. Project-Type Decision Tree

Starter Questions

Q1: Do you have a build step (Node.js, webpack, Vite)?

├── NO → WordPress / Static HTML / PHP
│ │
│ ├── Q2: Is this a long-term product (1+ year)?
│ │ ├── YES → ITCSS + BEM
│ │ └── NO → BEM (simple, fast)
│ │
│ └── Q3: Team size?
│ ├── Solo → BEM
│ ├── 2–4 → BEM + SMACSS states
│ └── 5+ → SMACSS + BEM

└── YES → JS Framework / Modern Stack

├── Q2: Prototyping speed is priority?
│ └── YES → Utility-First (Tailwind)

├── Q2: Longevity + team scalability priority?
│ └── YES → ITCSS + BEM (or ITCSS + Tailwind layers)

└── Q2: Design system / component library?
└── YES → OOCSS + ITCSS

9. Scenario Reference Table

Project TypeSizeTeamBest ChoiceSecondary
WordPress landing pageSmallSoloBEM
WordPress business siteMedium1–2BEMSMACSS states
WordPress + WooCommerceMedium2–3BEM + SMACSS
WordPress FSE themeMedium1–3ITCSSBEM
Client site → Bricks BuilderSmall–MedSoloBEM
AI-generated prototypeAnySolo + AIUtility-First
React/Next.js appMedium2–5Utility-FirstBEM @apply
SaaS product (long term)Large5–15ITCSS + BEM
Component / design systemMed–Large2–8OOCSS + ITCSSBEM
Multi-brand white label siteMed–Large3+SMACSS + BEMITCSS
Enterprise / govt / media siteVery Large10+ITCSS + BEM
Personal portfolioSmallSoloAny (BEM simplest)
Rapid client pitch prototypeSmallSolo + AIUtility-First

For the AI-assisted "vibe coding" → WordPress workflow that this documentation is optimized for:

Phase 1: Prototype (Utility-First)

AI generates the initial component quickly using utility classes or inline styles. Speed > structure.

<!-- AI output: quick prototype -->
<div class="flex gap-4 p-6 bg-gray-900 rounded-2xl border border-gray-800">
<img class="w-16 h-16 rounded-full object-cover flex-shrink-0" src="avatar.jpg" alt="">
<div>
<h3 class="font-bold text-white">Name Here</h3>
<p class="text-gray-400 text-sm">Role / Title</p>
</div>
</div>

Phase 2: Refactor to BEM (Production CSS)

Once the design is confirmed, convert utility-class prototype to BEM for WordPress:

<!-- BEM production version -->
<div class="testimonial-card">
<img class="testimonial-card__avatar" src="avatar.jpg" alt="">
<div class="testimonial-card__body">
<h3 class="testimonial-card__name">Name Here</h3>
<p class="testimonial-card__role">Role / Title</p>
</div>
</div>
/* Clean, maintainable BEM CSS for WordPress */
.testimonial-card { display: flex; gap: 1rem; padding: 1.5rem; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); }
.testimonial-card__avatar { width: 64px; height: 64px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.testimonial-card__body { flex: 1; }
.testimonial-card__name { font-weight: 700; color: var(--color-text); }
.testimonial-card__role { font-size: var(--text-sm); color: var(--color-text-muted); }

Phase 3: Add SMACSS States (Dynamic Behavior)

WordPress and JavaScript toggle state classes:

/* State layer — toggled by JS or PHP body_class() */
.testimonial-card.is-featured { border-color: var(--color-primary); box-shadow: var(--shadow-primary); }
.testimonial-card.is-loading { opacity: 0.5; pointer-events: none; }

The AI Refactor Prompt

Convert these utility classes to BEM CSS for WordPress.

Original HTML with utilities:
[PASTE UTILITY-CLASS HTML HERE]

Requirements:
- BEM naming: .component-name, .component-name__element, .component-name--modifier
- Use these CSS variables: [paste :root block]
- Add .is-state classes for any interactive states
- Output: clean HTML (new class names) + separate CSS block
- Comment the CSS with: /* Component: [name] — [what it does] */

11. Anti-Pattern Warning Signs

Signs You're Using the Wrong Methodology

You See ThisProblemSwitch To
50+ utility classes on one elementToo much utility-firstBEM @apply extraction
!important everywhere to override component stylesSpecificity warITCSS layers
JS toggling arbitrary class namesNo state conventionSMACSS is- pattern
Copy-paste of structural CSS for each color variantNo skin separationOOCSS
Can't find which CSS file affects an elementNo organizationITCSS + file structure
Grandchild BEM: .block__elem__childBEM misuseFlatten to .block__child
.l-sidebar h2 overrides everywhereSMACSS layout contaminating modulesSMACSS separation fix
New developer needs 2 weeks to understand CSSNo methodology at allSMACSS or ITCSS + BEM

12. Summary: The Honest Recommendation

For 90% of web designers doing client work:
──────────────────────────────────────────
Default choice: BEM
With JS states: BEM + SMACSS (.is-*, .has-*)
For prototyping: Utility-First (Tailwind) → convert to BEM

For developers building products (SaaS, apps):
──────────────────────────────────────────────
Default choice: ITCSS + BEM
With React: Utility-First (Tailwind) or CSS Modules

For design system builders:
───────────────────────────
Best choice: ITCSS + OOCSS + BEM
Modern CSS: @layer (ITCSS) + CSS custom properties (OOCSS tokens) + BEM naming

The Golden Rule: Pick one. Document it. Stick to it.
Half-measures are worse than any single methodology applied consistently.