Skip to main content

OOCSS (Object-Oriented CSS): The Complete Guide

OOCSS (Object-Oriented CSS) was created by Nicole Sullivan at Yahoo in 2008 — one of the first formal CSS methodologies. It introduced the idea of treating CSS components as reusable "objects" with separate structure and skin concerns. While less popular as a standalone methodology today, its two core principles are the bedrock of every modern CSS system including Tailwind, BEM, and ITCSS.

1. The Two Core Principles

OOCSS is built on exactly two rules:

Principle 1: Separate Structure from Skin

Structure = layout, sizing, positioning (what it looks like geometrically) Skin = colored decorations (colors, gradients, borders, shadows, fonts)

/* ❌ Anti-pattern: structure and skin coupled together */
.btn-blue {
/* Structure */
display: inline-flex;
padding: 0.75rem 1.5rem;
border-radius: 9999px;
/* Skin */
background: #4299e1;
color: white;
border: none;
}
.btn-red {
/* Structure: COPY-PASTED! */
display: inline-flex;
padding: 0.75rem 1.5rem;
border-radius: 9999px;
/* Skin: different */
background: #e53e3e;
color: white;
border: none;
}
/* ✅ OOCSS: structure in one class, skin in another */

/* Structure class (the "object") */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem 1.5rem;
border: 2px solid transparent;
border-radius: 9999px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}

/* Skin classes (the "themes") */
.btn-blue { background: #4299e1; color: white; }
.btn-red { background: #e53e3e; color: white; }
.btn-green { background: #48bb78; color: white; }
.btn-outline { background: transparent; border-color: currentColor; }
.btn-ghost { background: transparent; color: var(--color-text-muted); }
<!-- Compose in HTML -->
<button class="btn btn-blue">Primary</button>
<button class="btn btn-red">Danger</button>
<button class="btn btn-outline btn-blue">Outline Blue</button>

Principle 2: Separate Container from Content

Elements should not depend on the container they're placed in. A heading looks the same regardless of whether it's in a sidebar, main content, or card.

/* ❌ Anti-pattern: content tied to container */
.sidebar h2 { font-size: 1.25rem; color: var(--color-primary); }
.main h2 { font-size: 2rem; color: var(--color-text); }
/* Now you can't reuse the same heading in both places */
/* ✅ OOCSS: container-independent classes */
.heading-lg { font-size: 2rem; font-weight: 700; line-height: 1.2; }
.heading-md { font-size: 1.5rem; font-weight: 700; line-height: 1.3; }
.heading-sm { font-size: 1.25rem; font-weight: 600; line-height: 1.4; }
.text-primary { color: var(--color-primary); }
.text-muted { color: var(--color-text-muted); }
<aside class="sidebar">
<h2 class="heading-sm text-primary">Sidebar Heading</h2>
</aside>
<main>
<h2 class="heading-lg">Main Content Heading</h2>
</main>

2. The Media Object — OOCSS's Most Famous Pattern

Nicole Sullivan introduced the media object as OOCSS's archetypal reusable object. It's the pattern of an image (or media) alongside a text block — used in comments, tweets, user cards, notifications, and more.

<!-- The media object -->
<div class="media">
<div class="media__figure">
<img src="avatar.jpg" alt="User" class="media__image">
</div>
<div class="media__body">
<h4 class="media__title">Nicole Sullivan</h4>
<p class="media__text">This is a comment or notification text.</p>
</div>
</div>

<!-- Reversed: media on the right -->
<div class="media media--reverse">
<div class="media__figure">
<img src="avatar.jpg" alt="User" class="media__image">
</div>
<div class="media__body">
<p>Text on left, image on right.</p>
</div>
</div>
/* The Media Object — the original OOCSS component */
.media {
display: flex;
align-items: flex-start;
gap: 1rem;
}

.media--reverse {
flex-direction: row-reverse;
}

.media--center {
align-items: center;
}

.media__figure { flex-shrink: 0; }

.media__image {
display: block;
border-radius: var(--radius-full); /* Circular avatar by default */
}

.media__body {
flex: 1;
min-width: 0; /* Prevents flex overflow */
}

.media__title {
font-weight: 700;
margin-bottom: 0.25rem;
color: var(--color-text);
}

.media__text {
color: var(--color-text-muted);
line-height: 1.6;
}

/* Skin layers — applied alongside .media */
.media--sm .media__image { width: 32px; height: 32px; }
.media--md .media__image { width: 48px; height: 48px; }
.media--lg .media__image { width: 64px; height: 64px; }

3. Building Reusable OOCSS Objects

The Box Object

/* Structure: the box object */
.box {
display: block;
padding: var(--space-6);
overflow: hidden;
}

/* Skins: visual treatments */
.box-card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
}

.box-glass {
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.08);
border-radius: var(--radius-lg);
backdrop-filter: blur(20px);
}

.box-highlight {
background: rgba(108, 99, 255, 0.08);
border: 1px solid rgba(108, 99, 255, 0.2);
border-left: 4px solid var(--color-primary);
border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

.box-dark {
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
}

The Stack Object

A vertical layout object for spacing children consistently:

/* The stack object: consistent vertical spacing */
.stack { display: flex; flex-direction: column; }

/* Skins: gap sizes */
.stack-sm { gap: 0.5rem; }
.stack-md { gap: 1rem; }
.stack-lg { gap: 2rem; }
.stack-xl { gap: 4rem; }

The Cluster Object

Groups of inline items that wrap:

/* The cluster: wrapping inline group */
.cluster {
display: flex;
flex-wrap: wrap;
align-items: center;
}

/* Skins */
.cluster-sm { gap: 0.5rem; }
.cluster-md { gap: 1rem; }
.cluster-lg { gap: 1.5rem; }
.cluster-left { justify-content: flex-start; }
.cluster-center { justify-content: center; }
.cluster-right { justify-content: flex-end; }

The Grid Object

/* The grid object */
.grid-layout { display: grid; }

/* Skins */
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-auto-fill { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.grid-gap-sm { gap: 1rem; }
.grid-gap-md { gap: 1.5rem; }
.grid-gap-lg { gap: 2rem; }

4. OOCSS Typography Objects

/* Structure: typographic scale */
.text-hero { font-size: clamp(2.5rem, 5vw + 1rem, 5rem); line-height: 1.1; }
.text-display { font-size: clamp(2rem, 4vw, 3.5rem); line-height: 1.15; }
.text-h1 { font-size: clamp(1.75rem, 3vw, 2.5rem); line-height: 1.2; }
.text-h2 { font-size: clamp(1.5rem, 2.5vw, 2rem); line-height: 1.25; }
.text-h3 { font-size: clamp(1.25rem, 2vw, 1.5rem); line-height: 1.3; }
.text-xl { font-size: 1.25rem; line-height: 1.4; }
.text-base { font-size: 1rem; line-height: 1.6; }
.text-sm { font-size: 0.875rem; line-height: 1.5; }
.text-xs { font-size: 0.75rem; line-height: 1.4; }

/* Skin: weights */
.weight-normal { font-weight: 400; }
.weight-medium { font-weight: 500; }
.weight-semibold { font-weight: 600; }
.weight-bold { font-weight: 700; }
.weight-black { font-weight: 900; }

/* Skin: colors */
.color-default { color: var(--color-text); }
.color-muted { color: var(--color-text-muted); }
.color-primary { color: var(--color-primary); }
.color-danger { color: var(--color-danger); }
.color-success { color: var(--color-success); }

/* Skin: families */
.font-heading { font-family: var(--font-heading); }
.font-body { font-family: var(--font-body); }
.font-mono { font-family: var(--font-mono); }

5. OOCSS Alert / Notification Object

<div class="alert alert-success">
<span class="alert__icon"></span>
<p class="alert__message">Your changes have been saved successfully.</p>
<button class="alert__close" aria-label="Close">×</button>
</div>

<div class="alert alert-danger">
<span class="alert__icon"></span>
<p class="alert__message">Please fix the errors before submitting.</p>
</div>

<div class="alert alert-info">
<span class="alert__icon"></span>
<p class="alert__message">Your account is pending verification.</p>
</div>
/* Structure: alert object */
.alert {
display: flex;
align-items: flex-start;
gap: 0.75rem;
padding: 1rem 1.25rem;
border-radius: var(--radius-md);
border-left: 4px solid transparent;
font-size: var(--text-sm);
line-height: 1.5;
}

.alert__icon {
flex-shrink: 0;
font-size: 1rem;
margin-top: 0.1rem;
}

.alert__message {
flex: 1;
margin: 0;
}

.alert__close {
flex-shrink: 0;
background: none;
border: none;
cursor: pointer;
font-size: 1.25rem;
line-height: 1;
opacity: 0.6;
transition: opacity 0.2s;
}
.alert__close:hover { opacity: 1; }

/* Skin: color variants */
.alert-success {
background: rgba(56, 161, 105, 0.1);
border-left-color: var(--color-success, #38a169);
color: #68d391;
}
.alert-success .alert__icon { color: var(--color-success); }

.alert-danger {
background: rgba(229, 62, 62, 0.1);
border-left-color: var(--color-danger, #e53e3e);
color: #fc8181;
}
.alert-danger .alert__icon { color: var(--color-danger); }

.alert-warning {
background: rgba(237, 137, 54, 0.1);
border-left-color: var(--color-warning, #ed8936);
color: #f6ad55;
}
.alert-warning .alert__icon { color: var(--color-warning); }

.alert-info {
background: rgba(108, 99, 255, 0.1);
border-left-color: var(--color-primary, #6c63ff);
color: #a78bfa;
}
.alert-info .alert__icon { color: var(--color-primary); }

6. OOCSS Badge / Tag Object

<span class="badge">Default</span>
<span class="badge badge-primary">Primary</span>
<span class="badge badge-success">Active</span>
<span class="badge badge-danger">Error</span>
<span class="badge badge-outline badge-primary">Outline</span>
<span class="badge badge-sm badge-success">Small</span>
<span class="badge badge-lg badge-primary">Large</span>
/* Structure: badge object */
.badge {
display: inline-flex;
align-items: center;
gap: 0.3rem;
padding: 0.25rem 0.625rem;
border-radius: var(--radius-full);
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
border: 1px solid transparent;
line-height: 1;
white-space: nowrap;
}

/* Size skins */
.badge-sm { font-size: 0.65rem; padding: 0.15rem 0.5rem; }
.badge-lg { font-size: 0.875rem; padding: 0.375rem 0.875rem; }

/* Color skins */
.badge-primary { background: rgba(108,99,255,0.15); color: var(--color-primary); border-color: rgba(108,99,255,0.3); }
.badge-success { background: rgba(56,161,105,0.15); color: var(--color-success); border-color: rgba(56,161,105,0.3); }
.badge-danger { background: rgba(229,62,62,0.15); color: var(--color-danger); border-color: rgba(229,62,62,0.3); }
.badge-warning { background: rgba(237,137,54,0.15); color: var(--color-warning); border-color: rgba(237,137,54,0.3); }
.badge-neutral { background: rgba(255,255,255,0.08); color: var(--color-text-muted); border-color: var(--color-border); }

/* Outline skin modifier */
.badge-outline { background: transparent; }

7. OOCSS in WordPress

Applying OOCSS principles in WordPress child theme development:

/* ── Structural object: container ── */
.container {
width: 100%;
margin-left: auto;
margin-right: auto;
padding-left: clamp(1rem, 4vw, 2rem);
padding-right: clamp(1rem, 4vw, 2rem);
}
/* Skin: max widths */
.container-sm { max-width: 640px; }
.container-md { max-width: 960px; }
.container-lg { max-width: 1200px; }
.container-xl { max-width: 1440px; }

/* ── Structural object: section ── */
.section { display: block; }
/* Skin: vertical spacing */
.section-sm { padding-block: 3rem; }
.section-md { padding-block: 5rem; }
.section-lg { padding-block: 8rem; }
/* Skin: backgrounds */
.section-dark { background: var(--color-bg); }
.section-surface { background: var(--color-surface); }
.section-primary { background: var(--color-primary); color: white; }

/* ── Override GeneratePress with OOCSS objects ── */
.site-main .entry-content > * {
max-width: 72ch; /* Container skin on GP content */
}

8. OOCSS vs BEM vs Utility-First

AspectOOCSSBEMUtility-First
Core unitObject (reusable)Block (component)Single declaration
HTML verbosityMediumLowHigh
CSS file sizeSmall (reuse objects)MediumVery small (no custom)
Learning curveLowMediumLow
Team scalabilityGoodExcellentGood with framework
Specificity riskLow (flat)Low (flat)Very low
WordPress fitGoodExcellentRequires build step
Prototyping speedMediumMediumFast
MaintainabilityGoodExcellentGood

OOCSS is best for:

  • Projects with many visual variations of the same component type
  • Teams that want the simplicity of utilities but with more semantic grouping
  • Libraries and design systems where objects are the currency
  • Working alongside Sass @extend or @mixin patterns

9. OOCSS File Organization

css/
├── objects/
│ ├── _media.css ← Media object
│ ├── _box.css ← Box object
│ ├── _stack.css ← Stack object
│ ├── _cluster.css ← Cluster object
│ ├── _grid.css ← Grid object
│ └── _container.css ← Container object
├── skins/
│ ├── _colors.css ← Color skin utilities
│ ├── _typography.css ← Text skin utilities
│ ├── _spacing.css ← Spacing skin utilities
│ └── _surfaces.css ← Background/border skins
├── base/
│ ├── _reset.css
│ └── _variables.css
└── main.css

10. OOCSS Quick Reference

Two Principles:
1. Structure ≠ Skin — Separate layout from decoration
2. Container ≠ Content — Elements don't depend on where they live

Object Structure classes: define box model, layout, positioning
Skin classes: define colors, gradients, borders, shadows

Composition: <div class="[OBJECT] [SKIN] [SKIN]">
Example: <div class="btn btn-primary btn-lg">Text</div>
<div class="box box-card box-spacious">Content</div>
<div class="badge badge-success badge-sm">Active</div>

DO: Keep structure classes size/color-neutral
DO: Skin classes ONLY add decoration — never change size
DON'T: Use descendant selectors: .card h2 { } (ties content to container)
DON'T: Name classes after visual appearance: .red-box (use .box + .box-danger)