Writing Reusable CSS with OOCSS: 10 Code-Along Examples

Learn OOCSS by building it. Ten copy-and-run examples covering structure versus skin, container versus content, the media object, composing a profile card, avoiding location-dependent styles, and OOCSS compared with BEM and atomic.

The OOCSS guide explains two principles: separate structure from skin, and separate container from content. The payoff is reuse, you build interfaces by combining small objects rather than writing a new rule for every component. This workbook puts that into practice. You will create a handful of structure objects and skin classes, build the media object, compose a profile card from existing pieces, and finish by seeing the same button written three ways across OOCSS, BEM and atomic CSS. By the end the core idea, compose rather than duplicate, will be muscle memory.

1. Build the object and skin library

Everything below composes these classes. Notice how they are grouped by job: structure objects only handle layout, skin classes only handle cosmetics, and content classes only style text. That separation is the whole philosophy in one file. Save this as styles.css.

/* Base */
* { box-sizing: border-box; }
body { margin: 0; font-family: sans-serif; color: #11243a; }
/* STRUCTURE objects: layout only, no colours */
.btn {
display: inline-block;
padding: 0.5rem 1rem;
border: 0;
border-radius: 6px;
text-decoration: none;
cursor: pointer;
font: inherit;
}
.media { display: flex; align-items: flex-start; gap: 1rem; }
.media__figure { flex: 0 0 auto; }
.media__body { flex: 1; }
.grid { display: flex; flex-wrap: wrap; gap: 1rem; }
.grid__item { flex: 1 1 240px; }
.box { padding: 1rem; }
.avatar { width: 48px; height: 48px; border-radius: 50%; }
/* SKIN classes: cosmetics only, no layout */
.card-skin {
background: #ffffff;
border: 1px solid #d9e2ec;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(17, 36, 58, 0.1);
}
.skin-primary { background: #1f4e78; color: #ffffff; }
.skin-ghost { background: transparent; color: #1f4e78; border: 1px solid #1f4e78; }
.skin-muted { background: #f0f4f8; color: #11243a; }
/* CONTENT classes: text styling, container-agnostic */
.content-title { margin: 0 0 0.25rem; font-size: 1.1rem; font-weight: 700; }
.content-text { margin: 0; color: #5b6b7d; font-size: 0.9rem; line-height: 1.4; }

Read the comments and the structure is the lesson: no skin class mentions layout, and no structure object mentions colour. Keeping those concerns in separate classes is what lets you recombine them freely in the examples that follow.

2. Structure versus skin: the button

The first principle says layout and cosmetics should live in different classes. The .btn object provides the shape and spacing; a skin class provides the colours. Put both on the element and you have a finished button.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
</head>
<body style="padding:1rem">
<a href="#" class="btn skin-primary">Primary</a>
<a href="#" class="btn skin-ghost">Ghost</a>
</body>
</html>

Both buttons share the exact same .btn structure and differ only by their skin. To add a third style you write one new skin class, not a whole new button rule. That is the duplication the article is trying to eliminate.

3. One skin, many structures

The separation cuts the other way too. Because a skin knows nothing about layout, the same skin class works on completely different objects. Here .skin-primary styles both a button and a badge.

/* Add to styles.css */
.badge {
display: inline-block;
padding: 0.15rem 0.6rem;
border-radius: 999px;
font-size: 0.75rem;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
</head>
<body style="padding:1rem">
<a href="#" class="btn skin-primary">A primary button</a>
<span class="badge skin-primary">A primary badge</span>
</body>
</html>

The button and the badge have different shapes but identical colours, drawn from one skin class. This is reuse in both directions: structures share skins, and skins are shared across structures.

4. The media object

The media object is OOCSS’s signature pattern: a fixed figure beside a flexible body. It appears in comments, list rows, notifications and more. The object only arranges layout; it makes no assumptions about what goes inside.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
</head>
<body style="padding:1rem">
<div class="media" style="max-width:420px">
<div class="media__figure">
<div class="avatar skin-primary"></div>
</div>
<div class="media__body">
<p class="content-title">Ada Lovelace</p>
<p class="content-text">The media object keeps the figure fixed and lets the body fill the rest.</p>
</div>
</div>
</body>
</html>

media__figure never grows and media__body takes the remaining width. Crucially, the object does not care that the figure is an avatar or that the body holds a title and text. Swap the figure for an icon or the body for a form and the object still works.

5. Container versus content

The second principle says a container should not style its content. The content classes from the library carry their own look, so they appear identical whether they sit inside a media object or a plain box.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
</head>
<body style="padding:1rem">
<div class="media" style="max-width:420px;margin-bottom:1rem">
<div class="media__figure"><div class="avatar skin-muted"></div></div>
<div class="media__body">
<p class="content-title">Inside a media object</p>
<p class="content-text">Same content classes, one container.</p>
</div>
</div>
<div class="box card-skin" style="max-width:420px">
<p class="content-title">Inside a plain box</p>
<p class="content-text">Same content classes, a different container.</p>
</div>
</body>
</html>

The title and text look the same in both places because their styling lives in .content-title and .content-text, not in a rule like .media .title. Content that styles itself can be moved anywhere without breaking, which is exactly what makes it reusable.

6. A panel from a structure object plus a skin

Combining .box for structural padding with .card-skin for cosmetics gives you a panel, again with no new CSS. This is the structure-versus-skin idea applied to a container instead of a button.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
</head>
<body style="padding:1rem;background:#f0f4f8">
<div class="box card-skin" style="max-width:360px">
<p class="content-title">A simple panel</p>
<p class="content-text">Box gives the padding, card-skin gives the surface.</p>
</div>
</body>
</html>

.box could pad any element and .card-skin could decorate any element; together they make a card. If you wanted a flat card you would drop .card-skin and keep .box. The pieces stay independent.

7. Compose a profile card

Now the article’s headline example: a user profile card built entirely by composing objects and skins that already exist. Read the class lists and you can see every reused piece.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
</head>
<body style="padding:1rem;background:#f0f4f8">
<div class="grid" style="max-width:760px">
<div class="grid__item">
<div class="box card-skin">
<div class="media">
<div class="media__figure"><div class="avatar skin-primary"></div></div>
<div class="media__body">
<p class="content-title">Grace Hopper</p>
<p class="content-text">Compiler pioneer.</p>
<a href="#" class="btn skin-ghost" style="margin-top:.5rem">View profile</a>
</div>
</div>
</div>
</div>
<div class="grid__item">
<div class="box card-skin">
<div class="media">
<div class="media__figure"><div class="avatar skin-muted"></div></div>
<div class="media__body">
<p class="content-title">Alan Turing</p>
<p class="content-text">Foundations of computing.</p>
<a href="#" class="btn skin-ghost" style="margin-top:.5rem">View profile</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

Count how many distinct objects appear: grid, grid item, box, card skin, media, avatar, content classes, button, button skin. Not one of them was written for this card; the whole component is assembled from the library. That is composition over duplication in a single screenshot.

8. Reuse the same objects for a different component

To prove the objects really are general, build something else entirely, a notification row, from the same media object and content classes, just with a different skin.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
</head>
<body style="padding:1rem">
<div class="box skin-muted" style="max-width:480px;border-radius:8px">
<div class="media">
<div class="media__figure"><span class="badge skin-primary">New</span></div>
<div class="media__body">
<p class="content-title">A new article is live</p>
<p class="content-text">Reusing the media object and content classes for a totally different component.</p>
</div>
</div>
</div>
</body>
</html>

A profile card and a notification look nothing alike, yet they share the media object, the content classes and the badge. Each new component you build this way makes the next one cheaper, because the library keeps growing while the CSS does not.

9. Avoid location-dependent styles

The most common way to break reuse is to style by location, like .sidebar .title { ... }. That rule only works inside the sidebar, so the moment you need the same title elsewhere you copy and tweak it. OOCSS forbids this; style the object, not where it happens to sit.

/* Anti-pattern, do NOT do this:
.sidebar h2 { font-size: 1.1rem; font-weight: 700; color: #11243a; }
.footer h2 { font-size: 1.1rem; font-weight: 700; color: #11243a; }
The same look, duplicated, tied to two locations.
OOCSS way: one object class, reusable anywhere.
Already defined in styles.css as .content-title */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
</head>
<body style="padding:1rem">
<aside style="margin-bottom:1rem">
<h2 class="content-title">Sidebar heading</h2>
</aside>
<footer>
<h2 class="content-title">Footer heading</h2>
</footer>
</body>
</html>

Both headings use the same .content-title object and look identical without any location-specific rule. Generic, shallow selectors like this are what the article means by keeping objects reusable: a class describes what something is, not where it lives.

10. OOCSS versus BEM versus atomic

OOCSS is one of three popular reuse philosophies, and the guide places it alongside BEM and atomic CSS. Here is the same button in all three so you can feel the difference. Add the BEM and atomic helpers, then compare.

/* Add to styles.css */
/* BEM: one self-contained class carrying structure AND skin */
.button--primary {
display: inline-block;
padding: 0.5rem 1rem;
border-radius: 6px;
text-decoration: none;
background: #1f4e78;
color: #ffffff;
}
/* Atomic: many single-purpose utilities */
.inline-block { display: inline-block; }
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
.rounded { border-radius: 6px; }
.no-underline { text-decoration: none; }
.bg-blue { background: #1f4e78; }
.text-white { color: #ffffff; }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
</head>
<body style="padding:1rem">
<!-- OOCSS: structure object + skin object -->
<a href="#" class="btn skin-primary">OOCSS</a>
<!-- BEM: one descriptive component class -->
<a href="#" class="button--primary">BEM</a>
<!-- Atomic: a list of single-purpose utilities -->
<a href="#" class="inline-block px-4 py-2 rounded no-underline bg-blue text-white">Atomic</a>
</body>
</html>

All three buttons look the same; they differ in where the decisions live. OOCSS splits a component into a structure object and a skin object, so both are reusable on their own. BEM puts everything in one named component class, which is the most self-documenting but the least granular. Atomic spreads the styling across many tiny utilities, which is the most reusable but the most verbose in the markup. None is strictly best; OOCSS sits in the middle, favouring a small set of recombinable objects.

Work through these and you will have built an object-and-skin library and composed several unrelated components from it without writing a new rule each time. The guidelines from the guide are the habits to keep: make small single-purpose modules, use generic and shallow selectors, name them consistently, document them so the team reuses them, and always reach for an existing object before writing a new one. Do that and your stylesheet grows far more slowly than your interface, which is the entire point of OOCSS.

See you soon.

View Comments (1)

Leave a Reply

Subscribe to My Newsletter

Subscribe to my email newsletter to get the latest posts delivered right to your email. Pure inspiration, zero spam.

Discover more from Discuss Data Science, Machine Learning and Analytics

Subscribe now to keep reading and get access to the full archive.

Continue reading