Reconsidering Tailwind: How I Reclaimed My CSS Structure
After years of relying on Tailwind CSS for rapid prototyping and small sites, I recently migrated several projects back to vanilla CSS. It turned out to be a delightful, eye-opening experience that revealed how much Tailwind had secretly taught me about structured CSS. In this Q&A, I share the systems and guidelines I carried over from Tailwind and those I developed on my own.
1. What did using Tailwind teach you about structuring CSS?
Believe it or not, Tailwind inadvertently gave me a solid foundation in CSS organization. Every codebase needs systems for managing different aspects — layouts, typography, colors, common components — and Tailwind already provided many of those. I discovered that by mimicking its built-in patterns, I could structure my vanilla CSS without chaos.
- Reset: Tailwind’s Preflight (a CSS reset) sets consistent defaults like
box-sizing: border-boxandline-height: 1.5. I simply copied it into my new project. - Color palette: Tailwind’s predefined colors taught me to define a limited, systematic palette using CSS custom properties.
- Font scale: The framework’s type scale made me realize the importance of a consistent font-size hierarchy.
I also learned to separate concerns: reset, base, components, utility classes, spacing, responsive design, and the build system. Tailwind forced me to think about these as distinct layers, and now I apply the same mental model to vanilla CSS.
2. Why did you keep Tailwind’s reset (Preflight) in your new project?
After years of using Tailwind, I had grown accustomed to its default Preflight reset. It’s the first 200 lines of tailwind.css, and it normalizes browser inconsistencies. For example, it sets box-sizing: border-box on every element, which makes width calculations intuitive. It also sets html { line-height: 1.5; }, a subtle but pervasive default that I didn’t realize I relied on until I tried to write CSS without it. Re-creating that reset from scratch would have been tedious, so I copied it directly. This decision let me focus on the “fun” parts of CSS (components, colors) while keeping a stable foundation. It’s a small piece of Tailwind’s opinionated defaults that I now consider essential for my workflow.
3. How do you organize CSS into components without a framework?
I adopted a component-based approach inspired by Vue and React, even for static HTML sites. Each component gets a unique class name, its own CSS file, and strict rules: no component’s CSS ever overrides another’s. This isolation prevents mysterious breakage when editing one piece of code. For instance, a .card component has its own card.css file with styles for its layout, typography, and variants. About 80% of my CSS lives in these component files. The remaining 20% covers base styles, utility classes, and responsive tweaks. This system makes my codebase scalable and predictable — I can confidently change a button’s appearance without worrying about site-wide side effects.
4. How did you design your color palette and font scale?
I migrated Tailwind’s system of color tokens (like blue-500, slate-200) into CSS custom properties. For example: --color-primary: #3b82f6; --color-primary-light: #93c5fd;. This gives me the same intuitive naming without Tailwind’s class syntax. For fonts, I set up a type scale using the same ratio Tailwind uses (1.25 or 1.333). I define --font-size-sm: 0.875rem; --font-size-base: 1rem; --font-size-lg: 1.125rem; and so on. This ensures visual harmony. The key lesson: systematize your design tokens. They become the single source of truth for colors and typography, just like in Tailwind.
5. What role do utility classes play in your new vanilla CSS approach?
I still use utility classes, but sparingly. Tailwind encouraged hundreds of one-off utilities; now I only create utilities for genuinely repetitive patterns, like .text-center, .mt-4, or .flex. But I favor component-level or contextual classes over utilities for large parts of the interface. For instance, instead of class="flex items-center space-x-2" on every list item, I write class="list-item" and style it in a component file. This reduces HTML bloat and makes semantic elements more readable. Utilities remain handy for quick spacing or alignment tweaks, but they are no longer my primary tool. I’ve learned to ask: “Does this style truly need to be atomic, or does it belong to a component?”
6. How do you handle spacing and responsive design?
For spacing, I defined a set of spacing tokens based on Tailwind’s 4-point scale (0.25rem, 0.5rem, 1rem, etc.) as CSS variables: --space-1: 0.25rem; --space-2: 0.5rem; --space-4: 1rem;. I use these in margin/padding rules, ensuring consistency across components. For responsive design, I kept the concept of breakpoints (sm, md, lg, xl) defined in media queries. But instead of applying them directly in HTML, I write component-specific responsive styles in the same CSS file. For example, a card grid might have @media (min-width: 768px) { .card-grid { display: grid; } }. This approach removes the need for Tailwind’s md:grid-cols-3 classes and keeps styling centralized. The result: more semantic markup and less repetition.
7. Did you change your build system after dropping Tailwind?
Yes. Tailwind relies on a PostCSS plugin that generates classes on the fly. Without it, I needed a different build process. I now use PostCSS with Autoprefixer and CSSnano for basic processing and minification. I also implement a simple CSS custom properties approach for theming, like :root { --color-bg: white; } and [data-theme="dark"] { --color-bg: #1e293b; }. This gives me the dynamic theming Tailwind provided, but with vanilla CSS. The build step is simpler and faster — no need for a JIT engine. For small to medium sites, this setup is more than sufficient. It also gave me a deeper understanding of how CSS works under the hood.