As applications grow, CSS can become unmaintainable. A solid architecture prevents style conflicts and ensures consistency at scale.
Core Principles
Predictability
Class names should be predictable. Same name, same meaning everywhere.
Reusability
Create reusable utilities and components. Avoid one-off styles.
Maintainability
CSS should be easy to modify without breaking other parts.
Scalability
Architecture should support growth without requiring major refactors.
Organizational Patterns
ITCSS (Inverted Triangle CSS)
Layer CSS from generic to specific.
layers/
├── settings/ # Variables, config
├── tools/ # Mixins, functions
├── generic/ # Reset, normalize
├── base/ # HTML element styles
├── objects/ # Layout classes
├── components/ # UI components
├── utilities/ # Single-purpose classes
└── shame/ # Quick fixes (temporary)BEM Methodology
Block, Element, Modifier naming convention.
.card {} /* Block */
.card__title {} /* Element */
.card__image {} /* Element */
.card--featured {} /* Modifier */
.card--dark {} /* Modifier */Benefits:
- Clear relationship between selectors
- Low specificity
- Self-documenting code
- Easy to understand hierarchy
Utility-First (Tailwind Approach)
Predefined utility classes for rapid development.
<div class="flex items-center gap-4 p-4 bg-white rounded-lg shadow-md">
<h2 class="text-xl font-bold text-gray-900">Title</h2>
</div>Benefits:
- No custom CSS needed
- Consistent design system
- Small bundle after purging
- Fast development
Component Architecture
Atomic Design
Break UI into atoms, molecules, and organisms.
components/
├── atoms/ # Buttons, inputs, labels
├── molecules/ # Search bar, card header
├── organisms/ # Navigation, cards, forms
├── templates/ # Page layouts
└── pages/ # Complete pagesComponent Isolation
Each component should be self-contained.
/* Component scope */
.component-name {
/* Container queries for self-containment */
container-type: inline-size;
/* CSS custom properties for theming */
--component-bg: #fff;
--component-text: #000;
background: var(--component-bg);
color: var(--component-text);
}
/* Nested elements */
.component-name__element {
/* Styles scoped to component */
}State Management
State Classes
Use data attributes for component states.
.card[data-state="loading"] {
opacity: 0.6;
pointer-events: none;
}
.card[data-state="error"] {
border-color: red;
}
.card[data-state="success"] {
border-color: green;
}Theme Management
CSS custom properties for theming.
:root {
--color-primary: #3b82f6;
--color-secondary: #8b5cf6;
--spacing-unit: 0.25rem;
--font-size-base: 1rem;
}
[data-theme="dark"] {
--color-primary: #60a5fa;
--color-secondary: #a78bfa;
}Naming Conventions
Structured Naming
/* [namespace]-[component][-modifier][-state] */
.navbar {}
.navbar__link {}
.navbar__link--active {}
.modal__overlay[data-open="true"] {}Namespace Prefixes
Prevent conflicts with scoped prefixes.
.admin-panel {}
.admin-panel__sidebar {}
.public-site {}
.public-site__header {}Layer Management
Cascade Layers (@layer)
Control specificity conflicts.
@layer reset, base, components, utilities;
@layer components {
.card {
/* Lower priority than utilities */
}
}
@layer utilities {
.bg-blue-500 {
/* Always applies, overrides components */
}
}Documentation
Style Guide
Document your design system.
/**
* @component Card
* @description A container for content with elevation
* @modifier --featured Adds shadow and border
* @example
* <div class="card card--featured">
* <div class="card__body">Content</div>
* </div>
*/
.card {}CSS Comments
Document complex logic.
/* Use min() instead of min-width for responsive images */
/* Why: Works with container queries */
.image-grid {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
}File Organization
Feature-Based Structure
Organize by feature, not file type.
src/
├── features/
│ ├── auth/
│ │ ├── auth.module.css
│ │ ├── login-form.module.css
│ │ └── register-form.module.css
│ └── dashboard/
│ ├── dashboard.module.css
│ └── sidebar.module.css
└── shared/
├── button.module.css
└── input.module.cssPerformance Optimization
Critical CSS
Extract above-the-fold styles.
/* Inline critical CSS directly in HTML */
<style>
/* Hero section styles */
.hero { /* ... */ }
.nav { /* ... */ }
</style>Reduce Specificity
Keep selectors simple.
/* Bad - overly specific */
.header .nav .nav-list .nav-item .nav-link {}
/* Good - flat specificity */
.nav-link {}Avoid !important
Use cascade layers instead.
/* Bad */
.button {
background: blue !important;
}
/* Good - use layers */
@layer overrides {
.button {
background: blue;
}
}Testing CSS
Visual Regression Testing
Catch unintended style changes.
// Storybook + Chromatic
test('Button visual snapshot', () => {
const button = render(<Button>Click</Button>);
expect(button).toMatchSnapshot();
});Linting
Enforce architecture rules.
{
"rules": {
"selector-class-pattern": "^(BEM|utility)-",
"selector-max-id": 0,
"no-descending-specificity": true
}
}Migration Strategies
Incremental Adoption
Don't rewrite everything at once.
- 1Add new styles in new architecture
- 2Gradually migrate old components
- 3Delete old styles when safe
Feature Flags
Toggle between old and new styles.
@feature-flag new-design-system {
.button {
/* New styles */
}
}Conclusion
CSS architecture is about maintaining order as complexity grows. Choose patterns that fit your team and project. BEM for components, utilities for spacing, layers for specificity, and clear organization for maintainability. The best architecture is one your team can follow consistently.