What Utility Classes Actually Compose
Utility-first CSS libraries like Tailwind have pushed composition to the forefront of frontend conversations. Yet the version of composition they promote has always felt a bit simplistic to me. Applying p-4, text-red-500, and rounded to an element is, functionally, not far removed from dumping the same declarations into a custom class:
<div class="p-4 border-2 border-blue-500"> ... </div>
Honestly, how is that fundamentally different from writing:
/* This is composition too! */
.card {
padding: 1rem;
border: 2px solid var(--color-blue-500)
}
That said, the popularity of Tailwind has made me reconsider composition more seriously. Here are the notes I've collected on what it really means to compose styles in CSS.
Composition Is Built Into the Language
CSS doesn't need a framework to be composable — it has been since the cascade was introduced. A base button style can be defined with just a handful of declarations:
.button {
display: inline-flex;
padding: 0.75em 1.5em;
/* other styles... */
}
You can extend it by adding more classes in the markup:
<button class="button primary"> ... </button>
<button class="button secondary"> ... </button>
.primary { background: orange; }
.secondary { background: pink; }
You can even make something that looks like a button out of an entirely different element:
<a href="#" class="button"> ... </a>
In both cases, the cascade does the composing for you — one by layering .red onto .button, and the other by layering .button onto an anchor element. Nobody talks about this as a deliberate architectural strategy because it's simply how CSS works.
Composition Happens in CSS Files Too
When developers discuss composition, they almost always mean composing class names in the HTML:
<div class="one two"> ... </div>
But there's a second, equally valid place to compose: inside the CSS itself. Using Sass mixins or advanced Tailwind utilities are both ways to build styles from smaller parts without ever touching the HTML:
@mixin button () {
display: inline-flex;
padding: 0.75em 1.5em;
/* other styles ... */
}
.button {
@include button;
}
Composition Doesn't Solve Bloat
There's a common assumption that class-based composition reduces CSS bloat. That is only true if you're using utility classes — in which case, you move the bloat over to your HTML. On the selector side of things:
<div class="utility composition">...</div>
<div class="one utility at a time">...</div>
<div class="may create html bloat">...</div>
Selector composition may keep CSS heavier, but it does add fewer bytes to your markup:
<div class="class composition">...</div>
<div class="card primary">...</div>
<div class="may override properties">...</div>
<div class="less html bloat"> ... </div>
So which wins? There isn't a clear answer. But it's a moot point, anyway. A few kilobytes of CSS rarely matters in a world where a single image usually weighs 150kb or more. Obsessing over the utility-versus-selector tradeoff for performance reasons is a distraction. Your time is better spent improving code structure and recognition for the humans who maintain it, not shaving off a couple of milliseconds of load time.
The Four Categories of Styling
Stepping back, all styles you write fall into one of four distinct buckets:
- Layout: Positioning things on the page
- Typography: Everything related to fonts
- Theming: Everything related to color
- Effects: Gradients, shadows, and other visual decoration
Properties rarely cross these boundaries. font-weight belongs exclusively to typography; color belongs only to theming. This makes the buckets a natural fit for composable classes that let you mix and match concerns the way you would with Lego bricks.
Composing your HTML with separate classes for each concern looks like this:
<!-- These are all pseudo classes. Use your imagination for now! -->
<div class="layout-1 layout-2 effects-1">
<h2 class="typography-1 theming-1"> ... </div>
<div class="typography-2"> ... </div>
</div>
A concrete example using the classes from Splendid Styles and Splendid Layouts shows the pattern in practice:
<div class="card vertical elevation-3">
<h2 class="inter-title"> ... </h2>
<div class="prose"> ... </div>
</div>
What I've Taken Away
- CSS is compositional by nature, a property built into the cascade.
- Most developers frame composition too narrowly, limiting it to class names in HTML.
- Composition works just as well inside your stylesheet, through mixins or utilities.
- Separating styles into layout, typography, theming, and effects provides a solid mental model for building composable classes.



