CSS at scale requires organization. Without structure, stylesheets become unmaintainable. Here are patterns that keep CSS manageable as projects grow.
The Specificity Problem#
1/* Specificity wars */
2.button { color: blue; }
3.header .button { color: red; }
4.header .nav .button { color: green; }
5#main .header .nav .button { color: purple; }
6.button { color: orange !important; } /* Desperation */BEM (Block Element Modifier)#
1/* Block: Standalone component */
2.card { }
3
4/* Element: Part of block (double underscore) */
5.card__header { }
6.card__body { }
7.card__footer { }
8
9/* Modifier: Variation (double hyphen) */
10.card--featured { }
11.card--compact { }
12.card__header--large { }1<div class="card card--featured">
2 <div class="card__header card__header--large">
3 <h2 class="card__title">Title</h2>
4 </div>
5 <div class="card__body">Content</div>
6 <div class="card__footer">
7 <button class="card__button">Action</button>
8 </div>
9</div>BEM Benefits#
1/* Flat specificity - all selectors are equal */
2.card { }
3.card--featured { }
4.card__header { }
5
6/* Easy to understand scope */
7/* .card__header belongs to .card */
8
9/* Safe to modify - no cascade surprises */CSS Modules#
1/* Button.module.css */
2.button {
3 padding: 8px 16px;
4 border-radius: 4px;
5}
6
7.primary {
8 background: blue;
9 color: white;
10}
11
12.secondary {
13 background: gray;
14 color: black;
15}1// Button.tsx
2import styles from './Button.module.css';
3
4function Button({ variant = 'primary', children }) {
5 return (
6 <button className={`${styles.button} ${styles[variant]}`}>
7 {children}
8 </button>
9 );
10}
11
12// Compiled output:
13// <button class="Button_button_x7d2s Button_primary_a3f1k">CSS Modules Benefits#
/* Automatic unique class names */
/* No naming collisions */
/* Local scope by default */
/* Dead code elimination possible */Utility-First (Tailwind CSS)#
1<!-- Traditional CSS -->
2<button class="btn btn-primary btn-large">Submit</button>
3
4<!-- Utility-first -->
5<button class="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
6 Submit
7</button>Component Extraction#
1// When utilities repeat, extract components
2function Button({ variant = 'primary', size = 'md', children }) {
3 const baseStyles = 'font-medium rounded-lg transition-colors';
4
5 const variants = {
6 primary: 'bg-blue-600 text-white hover:bg-blue-700',
7 secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
8 };
9
10 const sizes = {
11 sm: 'px-3 py-1.5 text-sm',
12 md: 'px-4 py-2',
13 lg: 'px-6 py-3 text-lg',
14 };
15
16 return (
17 <button className={`${baseStyles} ${variants[variant]} ${sizes[size]}`}>
18 {children}
19 </button>
20 );
21}@apply for Reuse#
1/* styles.css */
2@layer components {
3 .btn {
4 @apply font-medium rounded-lg transition-colors;
5 }
6
7 .btn-primary {
8 @apply bg-blue-600 text-white hover:bg-blue-700;
9 }
10
11 .btn-secondary {
12 @apply bg-gray-200 text-gray-800 hover:bg-gray-300;
13 }
14}CSS-in-JS#
1// Styled Components
2import styled from 'styled-components';
3
4const Button = styled.button<{ $primary?: boolean }>`
5 padding: 8px 16px;
6 border-radius: 4px;
7 background: ${props => props.$primary ? 'blue' : 'gray'};
8 color: ${props => props.$primary ? 'white' : 'black'};
9
10 &:hover {
11 opacity: 0.9;
12 }
13`;
14
15// Usage
16<Button $primary>Submit</Button>1// Emotion
2import { css } from '@emotion/react';
3
4const buttonStyle = css`
5 padding: 8px 16px;
6 border-radius: 4px;
7`;
8
9function Button({ children }) {
10 return <button css={buttonStyle}>{children}</button>;
11}File Organization#
Feature-Based#
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.module.css
│ │ └── Button.test.tsx
│ └── Card/
│ ├── Card.tsx
│ └── Card.module.css
├── styles/
│ ├── globals.css
│ ├── variables.css
│ └── reset.css
Layer-Based (ITCSS)#
styles/
├── 01-settings/ # Variables, config
│ └── _variables.css
├── 02-tools/ # Mixins, functions
│ └── _mixins.css
├── 03-generic/ # Reset, normalize
│ └── _reset.css
├── 04-elements/ # Bare HTML elements
│ └── _typography.css
├── 05-objects/ # Layout patterns
│ └── _grid.css
├── 06-components/ # UI components
│ └── _button.css
└── 07-utilities/ # Helper classes
└── _utilities.css
CSS Custom Properties#
1:root {
2 /* Colors */
3 --color-primary: #3b82f6;
4 --color-primary-dark: #2563eb;
5
6 /* Spacing */
7 --spacing-sm: 0.5rem;
8 --spacing-md: 1rem;
9 --spacing-lg: 2rem;
10
11 /* Typography */
12 --font-sans: 'Inter', sans-serif;
13 --font-size-base: 1rem;
14
15 /* Shadows */
16 --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
17 --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
18}
19
20.button {
21 padding: var(--spacing-sm) var(--spacing-md);
22 background: var(--color-primary);
23 font-family: var(--font-sans);
24}
25
26.button:hover {
27 background: var(--color-primary-dark);
28}
29
30/* Theme switching */
31[data-theme="dark"] {
32 --color-primary: #60a5fa;
33 --color-primary-dark: #3b82f6;
34}Best Practices#
1/* 1. Avoid deep nesting */
2/* ❌ Bad */
3.page .content .sidebar .widget .button { }
4
5/* ✅ Good */
6.sidebar-widget__button { }
7
8/* 2. Avoid !important */
9/* If needed, it indicates a specificity problem */
10
11/* 3. Keep selectors simple */
12/* ❌ Bad */
13div.container > ul.nav li a.active { }
14
15/* ✅ Good */
16.nav__link--active { }
17
18/* 4. Use logical properties */
19/* Instead of */
20margin-left: 1rem;
21/* Use */
22margin-inline-start: 1rem;Conclusion#
Choose an architecture that fits your team and project. BEM works great for traditional CSS, CSS Modules provide scoping, utility-first excels for rapid development, and CSS-in-JS offers full JavaScript integration.
The best CSS architecture is one your team follows consistently.