CSS Architecture — BEM / SMACSS
Without architecture, CSS degrades into specificity wars, unused styles, and cascading chaos. As applications grow, a methodology ensures your stylesheets remain maintainable, predictable, and scalable across teams and feature sets.
CSS architecture addresses five core problems: global namespace pollution, specificity conflicts, dead code accumulation, unclear dependencies, and inconsistent naming. Methodologies like BEM, SMACSS, ITCSS, and OOCSS each provide a different lens for solving these issues.
| 1 | /* Without architecture — specificity mess */ |
| 2 | .content .sidebar .widget .title { font-size: 18px; } |
| 3 | div.widget .content > .title { font-size: 16px; } |
| 4 | |
| 5 | /* With BEM architecture — flat, predictable */ |
| 6 | .widget__title { font-size: 18px; } |
| 7 | .widget__title--large { font-size: 24px; } |
BEM is the most widely adopted CSS naming convention. It stands for Block, Element, Modifier — three distinct parts of a component's structure. BEM provides a clear, strict naming convention that maps directly to component architecture.
| 1 | .card { } /* Block — standalone component */ |
| 2 | .card__title { } /* Element — part of a block */ |
| 3 | .card__image { } /* Element — another part */ |
| 4 | .card__title--featured { } /* Modifier — variation of an element */ |
| 5 | .card--dark { } /* Modifier — variation of a block */ |
BEM Rules
BEM in Practice
| 1 | .card { |
| 2 | background: #1A1A2E; |
| 3 | border-radius: 8px; |
| 4 | overflow: hidden; |
| 5 | } |
| 6 | |
| 7 | .card__image { |
| 8 | width: 100%; |
| 9 | aspect-ratio: 16 / 9; |
| 10 | object-fit: cover; |
| 11 | } |
| 12 | |
| 13 | .card__body { |
| 14 | padding: 16px; |
| 15 | } |
| 16 | |
| 17 | .card__title { |
| 18 | font-size: 1.25rem; |
| 19 | font-weight: 600; |
| 20 | color: #E0E0E0; |
| 21 | margin-bottom: 8px; |
| 22 | } |
| 23 | |
| 24 | .card__text { |
| 25 | font-size: 0.875rem; |
| 26 | color: #808080; |
| 27 | line-height: 1.6; |
| 28 | } |
| 29 | |
| 30 | .card--featured { |
| 31 | border: 2px solid #00FF41; |
| 32 | } |
| 33 | |
| 34 | .card__title--large { |
| 35 | font-size: 1.5rem; |
| 36 | } |
best practice
SMACSS, created by Jonathan Snook, organizes CSS into five categories based on role and purpose. Unlike BEM which focuses on naming, SMACSS focuses on categorization and rules for each category type.
| Category | Prefix | Example | Purpose |
|---|---|---|---|
| Base | element selectors | body, a, h1 | Default element styles, resets |
| Layout | .l- | .l-grid, .l-sidebar | Page-level layout containers |
| Module | .module- | .card, .nav, .widget | Reusable components |
| State | .is-, .has- | .is-active, .is-hidden | Temporary states and toggles |
| Theme | .theme- | .theme-dark | Visual theming overrides |
| 1 | /* Base — reset and defaults */ |
| 2 | body { font-family: 'JetBrains Mono', monospace; background: #0D0D0D; color: #E0E0E0; } |
| 3 | |
| 4 | /* Layout — page structure */ |
| 5 | .l-grid { display: grid; grid-template-columns: 240px 1fr; gap: 24px; } |
| 6 | .l-sidebar { width: 240px; } |
| 7 | |
| 8 | /* Module — components */ |
| 9 | .card { background: #1A1A2E; border-radius: 8px; padding: 16px; } |
| 10 | |
| 11 | /* State — dynamic conditions */ |
| 12 | .is-active { border-color: #00FF41; } |
| 13 | .is-hidden { display: none; } |
| 14 | |
| 15 | /* Theme — visual variations */ |
| 16 | .theme-dark { --bg: #0D0D0D; --text: #E0E0E0; } |
ITCSS, created by Harry Roberts, organizes stylesheets by specificity and reach — from far-reaching generic styles to narrow explicit overrides. The inverted triangle represents decreasing scope and increasing specificity as you go down.
| 1 | /* Settings */ |
| 2 | $color-primary: #00FF41; |
| 3 | $font-mono: 'JetBrains Mono', monospace; |
| 4 | |
| 5 | /* Tools */ |
| 6 | @mixin truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } |
| 7 | |
| 8 | /* Generic */ |
| 9 | *, *::before, *::after { box-sizing: border-box; } |
| 10 | |
| 11 | /* Elements */ |
| 12 | h1 { font-size: 2rem; color: #E0E0E0; } |
| 13 | a { color: #00FF41; } |
| 14 | |
| 15 | /* Objects */ |
| 16 | .o-grid { display: grid; gap: 24px; } |
| 17 | .o-wrapper { max-width: 1200px; margin: 0 auto; } |
| 18 | |
| 19 | /* Components */ |
| 20 | .c-card { background: #1A1A2E; border-radius: 8px; } |
| 21 | .c-button { background: #00FF41; color: #0D0D0D; } |
| 22 | |
| 23 | /* Utilities */ |
| 24 | .u-hidden { display: none !important; } |
| 25 | .u-text-center { text-align: center !important; } |
info
OOCSS, pioneered by Nicole Sullivan, treats CSS objects as reusable visual patterns. It follows two core principles: separate structure from skin, and separate container from content.
| 1 | /* Principle 1: Separate structure from skin */ |
| 2 | /* Structure (one class) */ |
| 3 | .media { display: flex; gap: 16px; align-items: flex-start; } |
| 4 | |
| 5 | /* Skin (separate class) */ |
| 6 | .theme-dark { background: #0D0D0D; color: #E0E0E0; } |
| 7 | .theme-card { background: #1A1A2E; border-radius: 8px; } |
| 8 | |
| 9 | /* Combined */ |
| 10 | <div class="media theme-card theme-dark">...</div> |
| 11 | |
| 12 | /* Principle 2: Separate container from content */ |
| 13 | /* Instead of: */ |
| 14 | .sidebar .media { flex-direction: column; } |
| 15 | |
| 16 | /* Use: */ |
| 17 | .media-stacked { flex-direction: column; } |
| 18 | <div class="media media-stacked">...</div> |
CSS Modules solve the same problems as methodologies but through tooling rather than naming conventions. Understanding the relationship helps you choose the right approach for your project.
| Aspect | BEM / SMACSS | CSS Modules |
|---|---|---|
| Scoping | Convention-based naming | Automatic via build tool |
| Learning curve | Low — just naming rules | Medium — requires build setup |
| Output | Human-readable classes | Scoped hashed classes |
| Debugging | Easy — meaningful names | Harder — generated names |
| Combination | Works with any approach | Often paired with BEM naming |
note
Beyond BEM, several naming patterns help organize CSS and communicate intent through class names alone.
| 1 | /* SUIT CSS — BEM variant with camelCase */ |
| 2 | .ComponentName { } /* PascalCase for components */ |
| 3 | .ComponentName-elementName { } /* camelCase for elements */ |
| 4 | .ComponentName-elementName--modifier { } /* modifier */ |
| 5 | |
| 6 | /* Atomic / Functional CSS */ |
| 7 | .flex { display: flex; } |
| 8 | .text-center { text-align: center; } |
| 9 | .mt-4 { margin-top: 16px; } |
| 10 | .p-2 { padding: 8px; } |
| 11 | |
| 12 | /* Namespace prefixes */ |
| 13 | .c-card { } /* Component */ |
| 14 | .l-grid { } /* Layout */ |
| 15 | .u-hidden { } /* Utility */ |
| 16 | .js-submit { } /* JavaScript hook (never styled directly) */ |
| 17 | .t-dark { } /* Theme */ |
A well-organized stylesheet structure makes large codebases navigable. Here is a recommended file organization that combines multiple methodologies:
| 1 | styles/ |
| 2 | ├── abstracts/ |
| 3 | │ ├── _variables.css # colors, fonts, spacing tokens |
| 4 | │ ├── _mixins.css # reusable mixins |
| 5 | │ └── _functions.css # custom CSS functions |
| 6 | ├── base/ |
| 7 | │ ├── _reset.css # CSS reset / normalize |
| 8 | │ ├── _typography.css # base heading, text styles |
| 9 | │ └── _animations.css # @keyframes definitions |
| 10 | ├── layout/ |
| 11 | │ ├── _grid.css # grid system |
| 12 | │ ├── _sidebar.css # sidebar layout |
| 13 | │ └── _header.css # header layout |
| 14 | ├── components/ |
| 15 | │ ├── _card.css # card component |
| 16 | │ ├── _button.css # button component |
| 17 | │ └── _nav.css # navigation component |
| 18 | ├── pages/ |
| 19 | │ ├── _home.css # home-specific styles |
| 20 | │ └── _dashboard.css # dashboard-specific styles |
| 21 | ├── themes/ |
| 22 | │ ├── _dark.css # dark theme overrides |
| 23 | │ └── _light.css # light theme overrides |
| 24 | ├── vendors/ |
| 25 | │ └── _prism.css # third-party style overrides |
| 26 | └── main.css # imports all partials in order |
Each methodology addresses different pain points. The best choice depends on your team size, project scale, and existing tooling.