Why I'm moving off Tailwind after eight years
In 2018 I wrote about discovering Tailwind as someone who had no idea how to structure CSS. Given the choice between chaos and Tailwind, I happily chose Tailwind, and it helped me build a lot of small sites. But after spending a week migrating a couple of sites toward semantic HTML and vanilla CSS, I've learned some things worth sharing.
To be clear: I'm not a full-time frontend developer. My CSS education has come in fits and starts over many years. But the time I spent in Tailwind wasn't wasted — it turns out the tool taught me more than I expected.
What Tailwind taught me about CSS structure
When I started thinking about structuring CSS on my own, I was intimidated. Then I read posts like "A whole cascade of layers" and "How I write CSS in 2024" and realized a few things:
- Every CSS codebase has multiple concerns: layouts, fonts, colors, common components
- Systems or guidelines for each concern are essential, or things descend into chaos
- Tailwind already gave me systems for many of these — and I already know them
Tailwind provides a reset stylesheet, a color palette, and a font scale. My approach to moving away was to keep the useful parts of those systems and reimplement them with variables and conventions.
The building blocks of my new CSS setup
My new approach organizes CSS into several distinct concerns: reset, components, colors, font sizes, utilities, base styles, spacing, responsive design, and a build system.
Reset
I copied Tailwind's preflight styles directly — the first 200 lines of tailwind.css. Over the years I've developed a relationship with this reset. For example, Tailwind sets box-sizing: border-box on every element, meaning an element's width includes its padding:
* { box-sizing: border-box; }
It would be a real adjustment to write CSS without these defaults. There are surely things like html { line-height: 1.5; } that I'm subconsciously used to without realizing they come from the reset.
Components
The bulk of my CSS is organized by components, in a way spiritually related to Vue or React components — though the site might not have any JavaScript at all. The convention is simple:
- Each component has a unique class
- CSS for one component never overrides CSS for another
- Each component gets its own CSS file
This means editing one component's class won't mysteriously break something elsewhere. When I edit a 100-line component file, I only have to think about those 100 lines. For example, this HTML is the .zine component:
<figure class="zine horizontal">
<img src="whatever.jpg">
</figure>
Its CSS uses nested selectors:
.zine {
...
&.horizontal {
...
}
&.vertical {
...
}
&:hover {
...
}
}
I haven't done anything programmatic — no web components or @scope — to enforce isolation. Just having the convention and trying my best is already a big improvement.
Colors
colours.css stores a set of variables I use as needed. Color is hard, and I didn't want to revisit my palette during this refactor, so I left it alone. The only guideline: all colors used on the site must be listed in this file.
:root {
--pink: #fea0c2;
--pink-light: #F9B9B9;
--red: #f91a55;
--orange: rgb(222, 117, 31);
...
}
Font sizes
One thing I appreciated about Tailwind: to make text big, I just wrote text-lg, and if that wasn't big enough, xl or 2xl. No thinking about em vs px vs rem. So I defined variables taken from Tailwind's scale:
--size-xs: 0.75rem;
--line-height-xs: 1rem;
--size-sm: 0.875rem;
--line-height-sm: 1.25rem;
Now setting a font size looks like this — a bit more verbose, but workable:
h3 {
font-size: var(--size-lg);
line-height: var(--line-height-lg);
}
Utilities and base styles
Some things, like buttons, appear in many components. I call these utilities and copied a few from Tailwind, such as .sr-only for screenreader-only content. This section is deliberately small, and I'm cautious about making changes there.
Base styles apply across the whole site and must stay small — I'm not confident enough to enforce many global styles. Right now only two rules feel safe (and the <section> one might change):
/* put a 950px column in the middle of each <section> */
section {
--inner-width: 950px;
padding: 3rem max(1rem, (100% - var(--inner-width))/2);
}
a {
color: var(--orange);
}
For base styles, I'm working bottom-up: start with almost nothing, then migrate shared patterns from components as they emerge.
Spacing
I haven't fully solved padding and margin management. In Tailwind I haphazardly added spacing until things looked right, and I want to be more principled. My current approach: let outer layout components own spacing. For a <section> with children that need space between them:
section > *+* {
margin-top: 1rem;
}
Inspiring reads on this topic include the owl selector and "no outer margin".
Responsive design: more grid, fewer breakpoints
In Tailwind I handled responsive design with lots of media queries using the md:text-xl syntax. Now I'm trying something different: flexible CSS grid layouts that need fewer breakpoints. It's harder but teaches me what's possible with grid — things I don't think Tailwind can express.
For example, auto-fit can automatically use two columns on wide screens and one column on narrow ones:
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), max-content));
justify-content: center;
I've also relied heavily on grid-template-areas, another feature I don't believe you can use with Tailwind. A useful reference: "A responsive grid layout with no media queries" from CSS Tricks.
The build system: esbuild
In development, no build system is needed — CSS has built-in import statements:
@import "reset.css";
@import "typography.css";
@import "colors.css";
It also supports native nested selectors:
.page {
h2 { ...}
}
For production, I can bundle the CSS file with esbuild:
esbuild style.css --bundle --loader:.svg=dataurl --loader:.woff2=file --outfile=/tmp/out.css
I usually avoid CSS and JS build systems, but esbuild is different — it's based on web standards and ships as a static Go binary.
Why migrate at all?
Several factors drove this change:
- Tailwind has become far more dependent on a build system since 2018 — newer versions seem impossible to use without one. I've been stuck on Tailwind v2 for years (though litewind now exists)
- The docs always said to use a build system, but I never did, leaving me with 2.8MB
tailwind.min.cssfiles (270K gzipped) in many projects - I'm a better CSS developer now than when I started
- Tailwind is limiting when you want to do unusual things in CSS — those limits can be useful (much of this post is reimplementing them), but I want to pick and choose now
- Some projects ended up mixing vanilla CSS and Tailwind, which wasn't fun to maintain
- I got curious about what writing more semantic HTML would feel like
Why CSS itself is worth taking seriously
All of what I've said about Tailwind's useful systems is true. But there's another reason I moved away, inspired by a post I read three years ago: "Tailwind and the Femininity of CSS". It describes an attitude I recognize in myself:
They've heard it's simple, so they assume it's easy. But then when they try to use it, it doesn't work. It must be the fault of the language, because they know that they are smart, and this is supposed to be easy.
Over the last ten years I've learned to love and respect CSS as a technology. I decided years ago that my response to "CSS is hard" would be getting better at CSS, not devaluing it. That changed everything. Many frustrations — like "centering is impossible" — had been solved long ago. And "centering" isn't always straightforward; it makes sense there are many ways to do it. CSS is hard because it's solving a hard problem.
The new CSS features of the last decade-plus are impressive, and learning them has been genuinely rewarding. That post made me feel like Tailwind contributes to devaluing CSS expertise — not something I want to be part of, even though Tailwind has served me well. Especially now, with LLMs everywhere, valuing human expertise matters more than ever.
Another critical post that influenced me: "Classic rock, Mario Kart, and why we can't agree on Tailwind".
Thanks to Melody Starling, who originally designed and wrote the CSS for wizardzines.com — everything cool about that site is thanks to Melody. And thanks to the CSS community (CSS Tricks, Smashing Magazine, and others) for sharing practices so openly.



