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 Type | Size | Team | Best Choice | Secondary |
|---|---|---|---|---|
| WordPress landing page | Small | Solo | BEM | — |
| WordPress business site | Medium | 1–2 | BEM | SMACSS states |
| WordPress + WooCommerce | Medium | 2–3 | BEM + SMACSS | — |
| WordPress FSE theme | Medium | 1–3 | ITCSS | BEM |
| Client site → Bricks Builder | Small–Med | Solo | BEM | — |
| AI-generated prototype | Any | Solo + AI | Utility-First | — |
| React/Next.js app | Medium | 2–5 | Utility-First | BEM @apply |
| SaaS product (long term) | Large | 5–15 | ITCSS + BEM | — |
| Component / design system | Med–Large | 2–8 | OOCSS + ITCSS | BEM |
| Multi-brand white label site | Med–Large | 3+ | SMACSS + BEM | ITCSS |
| Enterprise / govt / media site | Very Large | 10+ | ITCSS + BEM | — |
| Personal portfolio | Small | Solo | Any (BEM simplest) | — |
| Rapid client pitch prototype | Small | Solo + AI | Utility-First | — |
10. The Vibe Coding Workflow: Recommended Pattern
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 This | Problem | Switch To |
|---|---|---|
| 50+ utility classes on one element | Too much utility-first | BEM @apply extraction |
!important everywhere to override component styles | Specificity war | ITCSS layers |
| JS toggling arbitrary class names | No state convention | SMACSS is- pattern |
| Copy-paste of structural CSS for each color variant | No skin separation | OOCSS |
| Can't find which CSS file affects an element | No organization | ITCSS + file structure |
Grandchild BEM: .block__elem__child | BEM misuse | Flatten to .block__child |
.l-sidebar h2 overrides everywhere | SMACSS layout contaminating modules | SMACSS separation fix |
| New developer needs 2 weeks to understand CSS | No methodology at all | SMACSS 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.