Tailwind CSS review: utility-first, honestly evaluated

Filed under Review

By Gerald · 27 June 2026

Colour swatches and paint samples arranged on a design table

I used to write CSS the traditional way. Separate files, class names like .card-header, media queries at the bottom, and a growing sense that I was fighting my own codebase. When I tried Tailwind CSS for the first time, the HTML looked wrong. Every element had a dozen class names. The stylesheet was gone. It felt like giving up on everything I had learned.

Two weeks later, I stopped writing traditional CSS entirely. Tailwind is not perfect, but it solves real problems that conventional CSS creates. Here is an honest review of where it helps, where it hurts, and when you should skip it.

Tailwind CSS makes styling fast and consistent once you learn its patterns. The tradeoff is cluttered HTML and a learning curve that can feel steep if you are used to writing CSS by hand.

What Tailwind does well

Speed of development

With Tailwind, you style elements by adding utility classes directly in your markup. Need padding? Add p-4. Need a flex container? Add flex items-center justify-between. The class names are predictable and limited to a documented scale.

This removes the constant context switching between HTML and CSS files. You see the styling logic where the element lives. You do not invent class names, write selectors, or worry about specificity wars. The time from idea to styled component is shorter than with any CSS approach I have used.

On Flow, I can build a new UI component in minutes. The button styles, spacing, and responsive behavior are all expressed in the JSX. There is no separate CSS file to open, no naming convention to debate, and no unexpected cascade breaking my layout.

Consistency through constraints

Tailwind provides a fixed scale for spacing, colors, typography, and shadows. p-4 is always 1rem. text-lg is always 1.125rem. bg-slate-100 is always the same color. You cannot accidentally use 1.1rem in one place and 1.125rem in another.

This constraint is powerful. It enforces a design system without requiring you to build one from scratch. Your application naturally stays consistent because the available values are limited and named.

For Flow, this means the spacing between cards, the border radius on buttons, and the shadow depth on modals all follow the same scale. I did not write a style guide. Tailwind's defaults became the style guide.

Small production bundles

Tailwind uses Just-in-Time compilation to generate only the CSS you actually use. If your markup contains flex, p-4, and text-slate-900, Tailwind outputs exactly those rules. It does not ship a full framework stylesheet.

The result is extremely small CSS bundles. Most Tailwind projects ship less than 10 kilobytes of CSS after compression. For Flow, the entire stylesheet is under 8 kilobytes. That is smaller than most single CSS files I wrote by hand in older projects.

Responsive design without media queries

Tailwind's responsive prefixes let you write conditional styles inline. md:flex-row applies flex-row only at medium breakpoints. lg:p-8 increases padding on large screens. You read the responsive behavior where the element is defined.

This is faster than writing separate media queries in a CSS file. It is also less error-prone because the responsive logic sits next to the base styles. You do not forget to update a media query when you change a component.

Where Tailwind becomes a mess

CSS utility classes in a code editor
Tailwind removes the gap between design and implementation, but it can clutter your markup.

HTML clutter

The biggest visible downside is long class strings. A moderately complex component can accumulate twenty or thirty class names. Reading the HTML requires parsing a dense line of utilities.

Here is a real example from Flow's note editor toolbar:

<div className="flex items-center gap-2 px-3 py-2 bg-white border border-slate-200 rounded-lg shadow-sm">

That is readable. But a more complex component can stretch across multiple lines:

<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 p-4 md:p-6 bg-white border border-slate-200 rounded-xl shadow-sm hover:shadow-md transition-shadow">

This is harder to scan. You can extract components or use the classnames utility to organize classes, but the clutter does not disappear. It moves around.

The learning curve is real

Tailwind has hundreds of utility classes. You must memorize or look up the naming conventions for flexbox, grid, spacing, colors, typography, and more. The documentation is excellent, but the first few weeks involve constant searching.

If you work with designers or developers who do not know Tailwind, they will struggle to read or modify your markup. The knowledge investment is significant, and it is not transferable to other CSS frameworks.

Over-customization tempts you

Tailwind's configuration file lets you extend every default. You can add custom colors, spacing scales, breakpoints, and animations. This is useful for branding, but it is easy to overdo.

I have seen projects where the Tailwind config is larger than the CSS it replaced. Custom values for every spacing increment, brand-specific color names, and bespoke breakpoints remove the consistency benefits. If you customize everything, you are just writing CSS with extra steps.

Complex animations and pseudo-elements are awkward

Tailwind handles simple hover states and transitions well. Complex keyframe animations, arbitrary pseudo-element styling, and advanced selectors are possible but verbose. You either add custom CSS in a separate file or bloat your config with one-off utilities.

For Flow, I use Tailwind for 95% of styling and a small CSS file for the remaining 5%. That hybrid works well, but it means Tailwind is not a complete replacement for CSS.

When to skip Tailwind

You should skip Tailwind if your team already has a mature CSS architecture that works. Switching costs time and introduces inconsistency during the transition.

Skip it if you build many one-off marketing pages with unique designs. Tailwind shines when components repeat. A page that never reuses a style is faster to write in plain CSS or a CSS-in-JS solution.

Skip it if you work with designers who write CSS but do not write JSX. Tailwind lives in your markup. If your workflow separates design from markup, the model breaks down.

How Flow uses Tailwind

Flow's entire UI is styled with Tailwind CSS. Buttons, cards, modals, forms, and the kanban board all use utility classes. The design stays consistent because we stick to the default scale with only minor color customizations for the brand palette.

The combination of Tailwind and Vite is particularly pleasant. Vite handles Tailwind compilation through PostCSS, and HMR updates styles instantly. Changing a bg-slate-100 to bg-slate-50 reflects in the browser immediately.

For complex repeated patterns, like the note card component, I extract the class string into a variable or a separate component. This keeps the JSX readable without abandoning the utility approach.

Frequently asked questions

Is Tailwind CSS worth learning? Yes, if you build modern web applications with component-based frameworks like React or Vue. The speed and consistency benefits are significant once you know the class names. If you only maintain legacy sites or work in a team with strong existing CSS conventions, the switching cost may not be worth it.

Does Tailwind increase bundle size? No. Tailwind's Just-in-Time compiler generates only the CSS you use. Production bundles are typically smaller than hand-written CSS because there is no unused code. The development build includes more classes for the browser inspector, but production is optimized.

When should I not use Tailwind? Avoid Tailwind if your team has a working CSS architecture, if you build mostly unique one-off pages, or if your design workflow keeps CSS separate from markup. Tailwind is also less ideal for projects that need complex custom animations and extensive pseudo-element styling.

Can I use Tailwind with CSS modules? Yes. Tailwind works alongside any CSS approach. You can use Tailwind for layout and spacing while keeping component-specific styles in CSS modules. Many projects use this hybrid approach for the best of both worlds.

Does Tailwind replace CSS entirely? No. Tailwind handles most styling needs, but complex animations, custom fonts loading strategies, and global resets often still need plain CSS. A typical Tailwind project includes a small CSS file for these cases.

Related reading

My verdict

Tailwind CSS is the right default for modern React and Vue projects. It trades HTML clarity for development speed, and that trade is usually worth it. Learn the core utilities, resist the urge to customize everything, and keep a small CSS file for the edge cases. Your styles will be consistent, your bundle will be small, and you will build faster than with any other CSS approach I have tried.

Own your notes and tasks

Flow puts notes, tasks, and a capture inbox in one place you pay for once, connected to your AI tools.

Create your account

Read this on flowproductivity.space · More from The Flow Journal · Try the Flow demo