← Back to DevBytes

Mastering CSS Architecture: BEM, SMACSS, OOCSS: Tips and Best Practices

What is CSS Architecture and Why It Matters

CSS architecture refers to a structured approach for writing and organizing stylesheets so they remain predictable, reusable, and maintainable as a project grows. Without a deliberate architecture, CSS inevitably becomes a tangled mess of overlapping selectors, mysterious side effects, and "quick-fix" overrides — a state commonly described as specificity spaghetti. A solid architecture gives you a shared vocabulary, clear rules for selector naming, and a blueprint for how styles should be composed. This tutorial covers three foundational methodologies — BEM, OOCSS, and SMACSS — that together form the bedrock of modern CSS thinking.

BEM — Block Element Modifier

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

What is BEM?

BEM is a naming convention and mental model invented by Yandex that breaks every interface component into three distinct parts:

The convention uses double underscores to connect block to element, and double hyphens to connect block/element to modifier. This flat, descriptive naming avoids deeply nested selectors and makes the relationship between parts visually obvious in both HTML and CSS.

Why BEM Matters

How to Use BEM

Start by identifying blocks — the highest-level abstractions in your interface. Then list their elements, and finally define the modifiers that represent different states or visual variants.

<!-- Example: a card component in BEM -->
<div class="card card--featured">
  <div class="card__header">
    <h2 class="card__title">Article Title</h2>
    <span class="card__tag card__tag--new">New</span>
  </div>
  <div class="card__body">
    <p class="card__text">Lorem ipsum dolor sit amet...</p>
    <button class="card__button card__button--primary">Read More</button>
  </div>
  <div class="card__footer">
    <span class="card__author">Jane Doe</span>
  </div>
</div>

The corresponding CSS stays clean and shallow:

/* Block: card */
.card {
  border-radius: 8px;
  background: #fff;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  padding: 1.5rem;
}

/* Block modifier: featured */
.card--featured {
  border-left: 4px solid #ff6b35;
  box-shadow: 0 4px 16px rgba(0,0,0,0.15);
}

/* Elements */
.card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
}

.card__title {
  font-size: 1.25rem;
  margin: 0;
}

.card__tag {
  font-size: 0.75rem;
  padding: 0.25rem 0.75rem;
  border-radius: 20px;
  background: #eee;
}

/* Element modifier */
.card__tag--new {
  background: #e3fceb;
  color: #0d6e2e;
}

.card__body {
  line-height: 1.6;
}

.card__text {
  margin-bottom: 1rem;
}

.card__button {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  background: #ddd;
}

.card__button--primary {
  background: #0066cc;
  color: #fff;
}

.card__footer {
  margin-top: 1.5rem;
  color: #666;
  font-size: 0.875rem;
}

BEM Best Practices

OOCSS — Object-Oriented CSS

What is OOCSS?

OOCSS, pioneered by Nicole Sullivan, treats CSS patterns as reusable "objects" — visual abstractions that can be mixed and matched across a project. The methodology rests on two core principles:

In practice, OOCSS encourages you to build a library of small, composable classes (often called "abstracts" or "primitives") that handle common visual patterns, then layer them onto HTML elements as needed.

Why OOCSS Matters

How to Use OOCSS

The classic OOCSS example is the Media Object — a pattern so ubiquitous it's now built into frameworks like Bootstrap. You extract the reusable structure (an image beside a block of text) into an object, then apply skin variations separately.

<!-- The Media Object: a reusable structure -->
<div class="media">
  <img class="media__img" src="avatar.jpg" alt="Avatar">
  <div class="media__body">
    <p>This is the body content that sits next to the image.</p>
  </div>
</div>

The CSS separates structure from skin:

/* ==========================================
   Structure: layout and positioning rules
   ========================================== */

.media {
  display: flex;
  gap: 1rem;
  align-items: flex-start;
}

.media__img {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  object-fit: cover;
}

.media__body {
  flex: 1;
  min-width: 0; /* prevents overflow in flex layouts */
}

/* ==========================================
   Skin: purely visual styling
   ========================================== */

/* Neutral skin (default) */
.media--neutral {
  background: #f8f8f8;
  padding: 1rem;
  border-radius: 8px;
}

/* Highlight skin */
.media--highlight {
  background: #fff3cd;
  border: 1px solid #ffc107;
  padding: 1rem;
  border-radius: 8px;
}

/* Subtle skin */
.media--subtle {
  background: transparent;
  padding: 0.5rem 0;
  border-bottom: 1px solid #e0e0e0;
}

Now you can compose these objects anywhere. A comment thread, a notification list, and a sidebar widget all reuse the same .media structure with different skins:

<!-- Notification with highlight skin -->
<div class="media media--highlight">
  <img class="media__img" src="alert-icon.png" alt="">
  <div class="media__body">Your order has been shipped!</div>
</div>

<!-- Comment with neutral skin -->
<div class="media media--neutral">
  <img class="media__img" src="user-avatar.jpg" alt="User">
  <div class="media__body">Great article, thanks for sharing!</div>
</div>

<!-- Sidebar item with subtle skin -->
<div class="media media--subtle">
  <img class="media__img" src="doc-icon.png" alt="">
  <div class="media__body">Recent document update</div>
</div>

You can also create utility-style "abstracts" for common skins:

/* Abstract skin classes — apply to any element */

.skin-box {
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 6px;
  padding: 1rem;
}

.skin-shadow {
  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}

.skin-primary {
  background: #0066cc;
  color: #fff;
}

.skin-success {
  background: #e6f9e8;
  border-left: 3px solid #2e7d32;
}

OOCSS Best Practices

SMACSS — Scalable and Modular Architecture for CSS

What is SMACSS?

SMACSS, created by Jonathan Snook, is a style guide for organizing CSS into five distinct categories based on their scope and purpose. Rather than a strict naming convention, it's a system of categorization that tells you where to put rules and how to think about them. The five categories are:

Why SMACSS Matters

How to Use SMACSS

Start by structuring your files or sections according to the five categories. A typical SMACSS-inspired directory might look like:

styles/
  ├── base/
  │   ├── reset.css
  │   ├── typography.css
  │   └── forms.css        (default input/select styles)
  ├── layout/
  │   ├── grid.css
  │   ├── header.css
  │   ├── footer.css
  │   └── sidebar.css
  ├── modules/
  │   ├── button.css
  │   ├── card.css
  │   ├── modal.css
  │   ├── navigation.css
  │   └── search.css
  ├── state/
  │   └── states.css        (global state rules)
  ├── theme/
  │   ├── dark.css
  │   └── holiday.css
  └── main.css              (imports and cascading order)

Here's how each category translates into actual CSS with clear conventions:

/* ==========================================
   BASE — element defaults, no classes needed
   ========================================== */

html {
  box-sizing: border-box;
  font-size: 16px;
}

*, *::before, *::after {
  box-sizing: inherit;
}

body {
  font-family: 'Inter', sans-serif;
  line-height: 1.6;
  color: #333;
  background: #fff;
  margin: 0;
}

a {
  color: #0066cc;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

/* ==========================================
   LAYOUT — prefixed with l- to distinguish
   from modules
   ========================================== */

.l-page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.l-header {
  background: #1a1a2e;
  color: #fff;
  padding: 1rem 2rem;
  position: sticky;
  top: 0;
  z-index: 100;
}

.l-main {
  display: flex;
  flex: 1;
}

.l-sidebar {
  width: 260px;
  background: #f4f4f4;
  padding: 1.5rem;
  border-right: 1px solid #ddd;
}

.l-content {
  flex: 1;
  padding: 2rem;
}

.l-footer {
  background: #1a1a2e;
  color: #ccc;
  padding: 1.5rem 2rem;
  text-align: center;
}

.l-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}

/* ==========================================
   MODULES — self-contained components
   (can use BEM naming inside modules)
   ========================================== */

/* Module: Search */
.mod-search {
  display: flex;
  gap: 0.5rem;
}

.mod-search__input {
  flex: 1;
  padding: 0.5rem 0.75rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  font-size: 0.9rem;
}

.mod-search__button {
  padding: 0.5rem 1rem;
  background: #0066cc;
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

/* Module: Modal */
.mod-modal {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.mod-modal__dialog {
  background: #fff;
  border-radius: 8px;
  padding: 2rem;
  max-width: 500px;
  width: 90%;
  box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}

.mod-modal__title {
  margin-top: 0;
  font-size: 1.25rem;
}

/* ==========================================
   STATE — prefixed with is- or has-
   ========================================== */

.is-hidden {
  display: none !important;
}

.is-expanded {
  width: 100%;
}

.is-active {
  background: #0066cc;
  color: #fff;
}

.is-loading {
  opacity: 0.6;
  pointer-events: none;
  position: relative;
}

.is-loading::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 24px;
  height: 24px;
  border: 3px solid #ccc;
  border-top-color: #333;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

.has-dropdown {
  position: relative;
}

.has-error {
  border-color: #d32f2f;
  background: #fce4e4;
}

/* ==========================================
   THEME — alternative visual skins
   ========================================== */

.theme-dark body {
  background: #121212;
  color: #e0e0e0;
}

.theme-dark .l-header,
.theme-dark .l-footer {
  background: #1e1e1e;
}

.theme-dark .mod-modal__dialog {
  background: #2c2c2c;
  color: #e0e0e0;
}

.theme-dark a {
  color: #80b3ff;
}

In your HTML, you compose these categories together. A typical page structure looks like:

<div class="l-page theme-dark">
  <header class="l-header">
    <nav class="mod-navigation">
      <!-- navigation module -->
    </nav>
  </header>

  <div class="l-main">
    <aside class="l-sidebar">
      <div class="mod-search">
        <input class="mod-search__input" type="text" placeholder="Search...">
        <button class="mod-search__button">Go</button>
      </div>
    </aside>

    <main class="l-content">
      <div class="l-grid">
        <div class="mod-card is-loading">
          <!-- card module content -->
        </div>
      </div>
    </main>
  </div>

  <footer class="l-footer">
    <p>&copy; 2025 Company Name</p>
  </footer>
</div>

SMACSS Best Practices

Combining the Three Methodologies

The real power emerges when you blend BEM, OOCSS, and SMACSS into a cohesive system. A practical integration might look like this:

Here is a concrete example of a product card that uses all three:

<!-- SMACSS: l- prefix for layout wrapper -->
<div class="l-grid">

  <!-- BEM inside a module: mod- prefix + BEM naming -->
  <div class="mod-card mod-card--featured is-loading">
    
    <div class="mod-card__image-wrapper">
      <img class="mod-card__image" src="product.jpg" alt="Product">
      <span class="mod-card__badge mod-card__badge--sale">Sale</span>
    </div>

    <div class="mod-card__body">
      <h3 class="mod-card__title">Wireless Headphones</h3>
      <p class="mod-card__price">$89.99</p>
    </div>

    <!-- OOCSS: abstract skin class layered on -->
    <button class="mod-card__button skin-primary skin-shadow">
      Add to Cart
    </button>

  </div>

  <!-- Another card with a different skin -->
  <div class="mod-card">
    <div class="mod-card__body">
      <h3 class="mod-card__title">USB-C Hub</h3>
      <p class="mod-card__price">$34.99</p>
    </div>
    <button class="mod-card__button skin-success skin-shadow">
      Add to Cart
    </button>
  </div>

</div>

The accompanying CSS demonstrates the clean separation:

/* ==========================================
   SMACSS Layout (l-)
   ========================================== */
.l-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.5rem;
  padding: 2rem;
}

/* ==========================================
   SMACSS Module (mod-) with BEM naming
   ========================================== */

/* Block */
.mod-card {
  background: #fff;
  border-radius: 8px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Block modifier */
.mod-card--featured {
  border: 2px solid #ff6b35;
  box-shadow: 0 4px 20px rgba(255,107,53,0.2);
}

/* Elements */
.mod-card__image-wrapper {
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 3;
}

.mod-card__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.3s ease;
}

.mod-card:hover .mod-card__image {
  transform: scale(1.05);
}

.mod-card__badge {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  padding: 0.25rem 0.75rem;
  border-radius: 4px;
  font-size: 0.75rem;
  font-weight: 600;
  background: #333;
  color: #fff;
}

/* Element modifier */
.mod-card__badge--sale {
  background: #d32f2f;
}

.mod-card__body {
  padding: 1.25rem;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.mod-card__title {
  font-size: 1.1rem;
  margin: 0 0 0.5rem;
}

.mod-card__price {
  font-size: 1.25rem;
  font-weight: 700;
  color: #0066cc;
  margin: 0 0 1rem;
}

.mod-card__button {
  margin-top: auto;
  padding: 0.75rem 1rem;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 600;
  width: 100%;
}

/* ==========================================
   OOCSS Skin abstracts
   ========================================== */
.skin-primary {
  background: #0066cc;
  color: #fff;
}

.skin-primary:hover {
  background: #004c99;
}

.skin-success {
  background: #2e7d32;
  color: #fff;
}

.skin-success:hover {
  background: #1e5c22;
}

.skin-shadow {
  box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}

/* ==========================================
   SMACSS State (is-)
   ========================================== */
.is-loading {
  opacity: 0.5;
  pointer-events: none;
  position: relative;
}

.is-loading::before {
  content: 'Loading...';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,0.8);
  font-size: 0.9rem;
  color: #666;
  z-index: 10;
}

Conclusion

Mastering CSS architecture is not about memorizing a single methodology — it's about understanding the complementary strengths of BEM, OOCSS, and SMACSS, then combining them into a system that fits your team and project. BEM gives you a naming convention that makes relationships explicit and specificity flat. OOCSS pushes you to separate structure from skin, yielding a reusable library of visual abstractions. SMACSS provides the organizational backbone, ensuring every rule has a clear home in one of five categories. Together, they transform CSS from a source of anxiety into a predictable, scalable, and genuinely enjoyable part of your development workflow. Start small — adopt one practice at a time — and let the architecture grow organically as your project demands it.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles