Utility-first CSS framework we use to build this site—fast, flexible, and polarizing
The best CSS framework for building modern UIs fast—once you accept the utility-first philosophy.
Quick take
Tailwind CSS is a utility-first CSS framework that has fundamentally changed how many developers write styles. Instead of writing custom CSS or using component libraries like Bootstrap, you compose designs using small, single-purpose utility classes directly in your HTML. This approach is either revolutionary or horrifying depending on who you ask—it's one of the most polarizing tools in modern web development. We use Tailwind to build this site and multiple other projects, and after thousands of hours with it, we have strong opinions.
The core concept is utility classes for every CSS property. Instead of writing `.card { padding: 1rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }`, you write `<div class="p-4 rounded-lg shadow">` in your HTML. Every spacing value, color, font size, border, shadow, transition—everything has a corresponding utility class. Tailwind ships with hundreds of these utilities out of the box. The value proposition is speed and consistency: no context switching between HTML and CSS files, no naming classes, no specificity battles.
We've built this entire site with Tailwind, and the development speed is genuinely impressive. Once you learn the utility class naming conventions (which takes a few days), you can build UI components rapidly without leaving your markup. Need padding? `p-4`. Need margin top? `mt-4`. Need responsive breakpoints? `md:flex lg:grid`. The IntelliSense plugins for VSCode autocomplete class names, making the workflow even faster. We can build and iterate on layouts in a fraction of the time it would take writing custom CSS or fighting with Bootstrap overrides.
The consistency is a massive benefit. Tailwind's default design system (spacing scale, color palette, typography, shadows) is well-considered and promotes visual coherence. You're working within constraints—spacing increments of 0.25rem, a limited color palette, standardized border radii. This prevents the design drift that happens when every developer invents slightly different spacing or colors. Our site maintains visual consistency across pages because we're all using the same utility classes.
Customization is powerful and well-designed. The `tailwind.config.js` file lets you override defaults, add custom colors, extend the spacing scale, define custom breakpoints, and add plugins. We've customized our color palette, added brand-specific spacing values, and configured typography scales. The configuration is JavaScript, which means it's programmatic—you can generate values, import design tokens, or integrate with design systems. This flexibility is far superior to Bootstrap's SASS variable overrides.
The build process is mandatory. Tailwind requires a build step (PostCSS) to scan your templates, extract used classes, and generate a minimized CSS file containing only the utilities you actually use. This purging is critical—Tailwind's complete utility set would be multiple megabytes. With purging, our production CSS is typically 10-30KB. The build setup is straightforward with modern tools (Vite, Next.js, webpack), but it's an additional complexity compared to dropping in a Bootstrap CDN link.
The responsive utilities are excellent. Every utility can be prefixed with breakpoint modifiers: `sm:`, `md:`, `lg:`, `xl:`, `2xl:`. This makes responsive design declarative and fast. You can see all responsive behavior in your markup: `<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">` creates a responsive grid without touching CSS. The dark mode support works similarly with `dark:` prefix: `<div class="bg-white dark:bg-gray-900">`. This inline responsiveness and theming is significantly faster than traditional media queries and CSS variables.
The component extraction problem is Tailwind's main practical challenge. Complex components end up with dozens of utility classes, creating verbose HTML. A button might be `<button class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg shadow-sm transition-colors duration-150">`. This is readable for simple components but becomes unwieldy for complex ones. Tailwind recommends extracting repeated patterns into components (React, Vue) or using `@apply` in CSS to group utilities. We extract common components (buttons, cards, inputs) into React components, which solves the verbosity but reintroduces some abstraction.
The learning curve is real. The utility class names are intuitive once you understand the system (e.g., `p-4` is padding, `mt-2` is margin-top, `text-lg` is font size), but newcomers face memorization and unfamiliarity. The documentation is excellent, but you'll reference it frequently in the first weeks. Developers accustomed to semantic CSS and separation of concerns often resist Tailwind's inline approach on principle. The philosophical shift—from "separation of concerns" to "colocation of concerns"—is harder to accept than the technical learning.
The "ugly HTML" criticism is valid. Tailwind markup is more verbose and less readable than semantic HTML with separate CSS. A simple card component that might be `<div class="card">` in traditional CSS becomes `<div class="bg-white rounded-lg shadow-md p-6 max-w-sm">` in Tailwind. For people who value clean, semantic HTML, this is aesthetically unpleasant. The counterargument is that colocation (seeing styles where they're applied) is faster and easier to maintain than jumping between HTML and CSS files. Both perspectives are valid—it's a preference trade-off.
Tailwind UI (the official component library, sold separately) is valuable but not cheap. It's a collection of professionally designed components (headers, footers, forms, dashboards, marketing sections) built with Tailwind. The components are beautiful and production-ready, saving significant design and development time. The pricing ($299 one-time for all-access) is reasonable for professionals but inaccessible for hobbyists. The free Headless UI library provides unstyled, accessible components (dropdowns, modals, tabs) that pair well with Tailwind. Combined, these resources accelerate development significantly.
The ecosystem is strong. Popular UI component libraries like shadcn/ui, DaisyUI, and Flowbite provide pre-built components. Third-party plugins add functionality (forms, typography, aspect ratios, animations). The community shares component libraries, design systems, and configuration patterns. Tailwind has become the de facto standard for utility-first CSS, which means abundant resources, tutorials, and community support.
Performance is excellent when configured correctly. The purged CSS is tiny (often < 20KB). The utility classes are optimized for reuse—the same classes apply across components, maximizing caching and minimizing file size. Compared to component libraries that ship large CSS bundles (Bootstrap is 150KB+), Tailwind is significantly lighter. The trade-off is build complexity, but the performance outcome justifies it.
The biggest philosophical question: is utility-first CSS the right abstraction? Traditional CSS advocates argue Tailwind conflates styling and structure, making HTML unreadable and difficult to maintain. Tailwind advocates argue colocation is faster, easier to reason about, and produces more maintainable systems because styles are explicit and scoped. After using both approaches extensively, we believe Tailwind is superior for teams building application UIs and interactive websites. For content-heavy sites where semantic HTML and accessibility are paramount, traditional CSS or a hybrid approach may be better.