
This is part of a series on the modern web stack, written from a WordPress developer’s seat for people who’ve built on the web for years and still hit stack terms they can’t define. The goal is literacy, enough that you can read any stack list and know what each piece does.
Each post has a 10-15 minute audio companion, generated with NotebookLM, for the gym or the commute.
For most of my WordPress career, styling meant a style.css in the theme directory, maybe split into partials, possibly compiled from SCSS, with Genesis or Underscores underneath. Classes had semantic names like .site-header, .entry-content and .widget-area. You named elements by role, styled each one in a stylesheet and hoped you remembered which class belonged where.
Then I opened a modern Next.js codebase and every HTML element looked like this:
<div className="flex items-center justify-between rounded-lg border bg-card px-4 py-3 text-sm shadow-sm">
No semantic class names, no external stylesheet, just a wall of short utility classes inside the markup. My first reaction was somewhere between confusion and revulsion. I assumed this was what people meant when they said modern frontend was a mess.
I was wrong. Tailwind has won, for reasons that take a while to see from the outside. On top of it sits shadcn/ui, which does something stranger and more effective still. This post explains both.
Tailwind CSS
Tailwind is a utility-first CSS framework. Instead of class names that describe what an element is (button-primary, nav-item), you write class names that describe what it does (px-4 py-2 bg-blue-500 rounded text-white). The advantages take a minute to surface.
You stop naming things
Every WordPress theme developer has burned hours choosing between .cta, .action, .primary-button, .button-primary and .btn-cta. With Tailwind the question never comes up, because you compose the styles directly.
The stylesheet stops growing
Traditional CSS files balloon as features are added, with old rules left in case something still uses them. Tailwind’s stylesheet stays about the same size at ten components or ten thousand, since every class gets reused. The build also strips unused classes, so production CSS is tiny.
The design tokens enforce consistency
You define the color palette, spacing scale, type scale and breakpoints once, and every utility class draws from those tokens. Designers and developers can’t drift from the system because the framework won’t let them.
Components stay self-contained
A Tailwind component is fully described by its own JSX. You can read its appearance from the markup, without opening a stylesheet to find out what .entry-meta does in this particular theme.
The cost is busy markup. That’s an acceptable price in component-based development, where you read one component at a time instead of the whole site.
shadcn/ui
shadcn/ui builds on Tailwind to solve the component primitives problem.
Most design systems shipped as packages you installed: Material UI, Chakra, Ant Design. You imported their components, customized them through props and lived with their API indefinitely. Customization always hit a ceiling, and the bundle always grew.
shadcn/ui isn’t a package. It’s a CLI that copies component source code into your repo. Run npx shadcn add button and a Button component appears in src/components/ui/button.tsx. Edit it however you like. There’s no library to update, because the code is yours.
The components sit on Radix UI, an accessible primitive library, and are styled with Tailwind. They look good by default, follow accessibility practice and become customizable by editing the code. That combination has taken over the React component-library market faster than almost anything in the last three years.
The WordPress Analogue
Tailwind is to CSS what Gutenberg’s block library is to page-builder shortcodes: opinionated primitives that compose into anything. shadcn/ui is to React components what a starter theme is to a custom WordPress theme: a working template you customize instead of a library you call.
WordPress had this debate fifteen years ago. Write semantic CSS classes and let the stylesheet describe the design, or use utility classes the way Bootstrap did and compose inline? WordPress culture chose semantic and React culture chose utility. The React choice happened to fit component-based development in a way that Bootstrap on top of WP themes never did.
The Styling Approaches You’ll Meet
These are the main approaches in modern React projects:
| Approach | Style location | Strengths | Trade-offs |
|---|---|---|---|
| Tailwind CSS | Inline className utilities | Tiny CSS bundle, consistency, no naming | Verbose markup, learning curve |
| CSS Modules | Co-located CSS files scoped to the component | Familiar CSS syntax, automatic scoping | More files, harder to share design tokens |
| CSS-in-JS (styled-components, Emotion) | Inside JS files | Dynamic styling, theme support | Runtime overhead, increasingly unpopular |
| Vanilla CSS | Old-school stylesheets | No build complexity, simplest model | Drift, hard to scale on teams |
| Tailwind + shadcn/ui | Tailwind utilities + copy-in components | The default modern setup, ships fast | Tailwind verbosity carries through |
| Component libraries (Material UI, Chakra) | Library-provided components, theme system | A complete design system fast | Customization hits walls, larger bundles |
In 2026, Tailwind plus shadcn/ui is the default for almost any new React or Next.js project. A starter kit usually means Next.js, Tailwind, shadcn/ui and maybe Drizzle and Supabase.
Tailwind 4, which shipped in early 2025, dropped the JavaScript config file and reads design tokens from CSS itself. Older guides reference tailwind.config.js, while the current setup is more CSS-native.
What This Changes for WordPress People
Custom Gutenberg blocks get easier
The block editor is built on React, and Tailwind works inside Gutenberg blocks. If you’ve shipped a custom block with bespoke CSS, switching to Tailwind cuts the styling work a lot, and the blocks look more at home on the modern web.
Design hand-offs improve
When a designer hands you a Figma file with spacing tokens (4, 8, 12, 16, 24) and a color palette, you map them straight into Tailwind’s config and the system stays intact through the build. WordPress themes tend to drift from designs because the developer invents its own classes.
AI agents write better styles
Agents are good at Tailwind because the class names are short, the patterns are well documented and the constraints stop them from inventing styles. Give an agent a Figma file and ask for Tailwind plus shadcn/ui, and the output is usually about 90% right. The same agent writing WordPress theme CSS produces inconsistent, hard-to-review results.
Where Tailwind Gets Messy
Verbosity
A complex component can have classes that wrap to three lines. Use the @apply directive for patterns you reuse across the site, such as a button style, but don’t abstract every component into a custom class. The verbosity is the price of the consistency and the small bundle.
Dark mode needs setup
Tailwind has a dark: variant you can apply to any class. For dark mode to work end to end you also need a palette designed for both modes and a toggle that adds and removes the dark class on the root element. The mechanism is simple and the design work isn’t.
shadcn/ui components are your code
Updates don’t arrive through pnpm update. To get a newer version of a component, you re-run the CLI command and accept the new copy. That’s the cost of having the code in your repo, so track which components you’ve copied and when.
Mixed styling approaches
A codebase that’s half Tailwind, half CSS Modules and half styled-components turns incoherent fast. Pick one approach per project.
The first two weeks
New developers see a Tailwind component and recoil. After two weeks of writing it, the markup reads normally and the trade-off makes sense. Give it those two weeks before deciding.
From Recognizing Tailwind to Using It
A few resources if you want to go from recognizing Tailwind to writing it productively.
Video
- “Tailwind CSS in 100 Seconds” by Fireship, then “Why I use Tailwind for everything” by Theo (about 20 minutes).
- The shadcn/ui tutorial on the shadcn channel (about 30 minutes). The CLI workflow is unusual enough that watching it once beats reading about it.
Docs worth bookmarking
- The Tailwind CSS docs. The utility-first introduction is the best argument for the approach, so read it before deciding you hate Tailwind.
- The shadcn/ui docs. Browse the component gallery and you’ll recognize the design language from half the modern web.
- The Radix UI primitives that shadcn/ui is built on, useful if you ever build custom interactive components.
For which copy-paste component libraries and AI-coder tooling feed shadcn into your build, see the companion piece Why AI-Generated UI Looks Generic (and How to Fix It).
The next post ties WordPress back into the modern stack with headless WordPress: what it is, when it makes sense and how to think about hybrid CMS-plus-modern-frontend projects.

Leave a Reply