CSS Architecture Patterns

CSS at scale is hard. Global namespace; specificity battles; the dreaded !important. Several methodologies attempt to make CSS manageable at scale. Each has trade-offs.

This page covers the major patterns and the modern consensus.

The problem

Without methodology, CSS at scale produces:

The methodologies try to prevent this.

BEM (Block-Element-Modifier)

A naming convention:

.card { }                    /* Block */
.card__title { }             /* Element */
.card__title--large { }      /* Modifier */

Names encode hierarchy. Global namespace; conflicts are visible in names.

<div class="card card--featured">
    <h2 class="card__title card__title--large">Title</h2>
    <div class="card__body">Body</div>
</div>

Pros:

Cons:

Was popular 2015-2020; less common now.

CSS Modules

Locally-scoped class names. The build tool transforms:

/* Card.module.css */
.title { color: blue; }
import styles from './Card.module.css';
<h2 className={styles.title}>Title</h2>

The build outputs unique class names per file; no global conflicts.

Pros:

Cons:

Common in React projects.

CSS-in-JS

Styles in JS:

// styled-components
const Button = styled.button`
    background: ${props => props.primary ? 'blue' : 'gray'};
    color: white;
    padding: 8px 16px;
`;

// emotion
const buttonStyle = css`
    background: blue;
`;

Pros:

Cons:

CSS-in-JS dominated 2018-2022 in React; some teams are moving away due to performance.

Atomic CSS / Tailwind

Single-purpose utility classes:

<button class="bg-blue-500 hover:bg-blue-700 text-white px-4 py-2 rounded">
    Click me
</button>

Each class does one thing. No custom CSS for this button.

Pros:

Cons:

Tailwind is the dominant atomic CSS implementation. Has become the modern default for many React/Vue projects.

Vanilla CSS / No methodology

Just write CSS. No conventions; rely on developer discipline.

Pros: simple; no tooling. Cons: doesn't scale; conflicts pile up.

For tiny projects, fine. Beyond a few hundred lines, methodology helps.

The modern consensus

For most new React/Vue/Svelte projects (2024+):

CSS-in-JS adoption has slowed for new projects. The performance issues and the complexity haven't justified the dynamic-styling benefits for many teams.

For design systems or component libraries, Web Components with shadow DOM provide their own isolation; can use plain CSS.

Specific guidance

Don't fight specificity

If you need !important, you're doing something wrong. Restructure or use a methodology that prevents specificity conflicts.

Reset / normalize

normalize.css or similar. Provides consistent baseline across browsers.

Modern features over old

CSS Grid, Flexbox, custom properties (CSS variables), container queries. Modern CSS is much more capable than 2010 CSS.

Performance

CSS is render-blocking. Smaller is better. Don't ship 500 KB of CSS.

Dark mode

CSS variables + media query is the modern approach:

:root {
    --bg: white;
    --text: black;
}

@media (prefers-color-scheme: dark) {
    :root {
        --bg: black;
        --text: white;
    }
}

body {
    background: var(--bg);
    color: var(--text);
}

Common failure patterns

Further Reading