Short answer: what is Tailwind CSS? It is a CSS framework that gives you small, single-purpose classes, such as p-4 or text-center, and you build a design by putting many of them on an element. It does not ship ready-made buttons or cards. It ships the building pieces, and a build step creates a CSS file that contains only the classes you used.

This guide is for people who build WordPress themes and keep hearing about Tailwind. It explains what Tailwind is, how it works, how version 4 changed the setup, and what to watch for when you use it next to WordPress. Every fact about Tailwind comes from tailwindcss.com. When I give an opinion or an inference, I say so.

What is Tailwind CSS, in plain words

Most CSS frameworks give you components. You add a class like btn or card, and you get a finished look. Tailwind takes the other route. The docs describe the idea this way: “You style things with Tailwind by combining many single-purpose presentational classes (utility classes) directly in your markup.”

So instead of writing this CSS:

.notice {
  padding: 1rem;
  border-radius: 0.5rem;
  background-color: #f1f5f9;
}

and this HTML:

<div class="notice">Saved.</div>

you write only the HTML:

<div class="rounded-lg bg-slate-100 p-4">Saved.</div>

There is no separate stylesheet to write for that notice. Each class does one job. p-4 sets padding. rounded-lg sets the corner radius. bg-slate-100 sets the background color.

The Tailwind docs admit that this “contradicts a lot of traditional best practices.” Years of CSS teaching said to keep style out of your markup. The docs answer that with a list of benefits: faster development, safer changes, easier maintenance, portable code, and CSS that does not keep growing. I will look at the real tradeoffs later, because they matter for a theme.

How is that different from inline styles?

It looks like inline styles, but the docs point out three differences. Utilities draw from a predefined design system, so you pick p-4 from a scale instead of typing any number you like. They can express hover, focus and media query styles, which inline styles cannot. And they live in one generated stylesheet, not scattered across the page.

A first look at how it works

Tailwind has two parts you need to know about.

  1. A set of utility classes. There are hundreds, for spacing, color, type, layout, borders, shadows and more.
  2. A build step. It reads your files, finds the class names you used, and writes a CSS file with only those.

The second part is why Tailwind files stay small. The docs describe how detection works: Tailwind v4 “scans your files as plain text, extracts tokens that look like class names, and generates CSS only for the tokens that match a utility it knows.” It does not parse your PHP or your JavaScript. It just reads text and looks for known class names.

That has one big consequence, and the docs call it out. Tailwind “has no way of understanding string concatenation or interpolation in the programming language you’re using.” This is wrong:

<div class="text-<?php echo $error ? 'red' : 'green'; ?>-600">

The full strings text-red-600 and text-green-600 never appear in the file, so Tailwind never generates them. The docs show the fix, which is to write complete class names:

<div class="<?php echo $error ? 'text-red-600' : 'text-green-600'; ?>">

I changed the docs’ template example into PHP because this is a WordPress audience, but the rule is exactly theirs: always write the whole class name in your source.

States and screen sizes with variants

Plain CSS handles hover and screen size with extra selectors and media queries. Tailwind handles them with prefixes, which it calls variants.

The docs show hover:bg-sky-700 as a class that does nothing unless the element is hovered. You can stack variants too, as in disabled:hover:bg-sky-500.

Screen sizes work the same way. In the docs’ example, grid grid-cols-2 sm:grid-cols-3 gives two columns by default and three columns from the sm breakpoint up. The default sm breakpoint is 40rem. The design is mobile first: an unprefixed class applies everywhere, and a prefixed one applies from that size up.

Here is a small card, close to the example in the docs, with dark mode variants:

<div class="mx-auto flex max-w-sm items-center gap-x-4 rounded-xl bg-white p-6 shadow-lg dark:bg-slate-800">
  <img class="size-12 shrink-0" src="/img/logo.svg" alt="" />
  <div>
    <div class="text-xl font-medium text-black dark:text-white">ChitChat</div>
    <p class="text-gray-500 dark:text-gray-400">You have a new message!</p>
  </div>
</div>

Read it left to right and it starts to make sense. flex items-center gap-x-4 is the layout. rounded-xl bg-white p-6 shadow-lg is the card look. dark:bg-slate-800 swaps the background in dark mode. You can see the whole design in the markup, with no CSS file open.

What the current version is

The docs navigation showed version v4.3 when I checked on 11 November 2026. I am quoting that from the docs page itself, and it may have moved since you read this. The upgrade guide says the major version 4 was a bigger change than most updates, so the version number matters when you read tutorials. Many older tutorials were written for v3 and use setup steps that no longer apply.

What changed in version 4

The upgrade guide lists the changes. These are the ones that matter most for a theme builder.

You import Tailwind with plain CSS. In v3 you wrote three @tailwind lines. In v4 it is one:

/* v3 */
@tailwind base;
@tailwind components;
@tailwind utilities;

/* v4 */
@import "tailwindcss";

Configuration moved into CSS. In v3 your design choices lived in tailwind.config.js. In v4 you put them in your CSS file with a @theme block. The upgrade guide shows this example:

@theme {
  --font-display: "Satoshi", "sans-serif";
  --breakpoint-3xl: 120rem;
  --color-avocado-100: oklch(0.99 0 0);
}

The old JavaScript config is no longer found on its own. The guide says JavaScript config files “are still supported for backward compatibility, but they are no longer detected automatically in v4.” If you still have one, you load it with @config "./tailwind.config.js";. Some old options, such as corePlugins, safelist and separator, are not supported in v4.0.

There is a browser floor. The guide says “Tailwind CSS v4.0 is designed for Safari 16.4+, Chrome 111+, and Firefox 128+,” and that if you need older browsers you should “stick with v3.4.” Check your site’s analytics before you pick v4. A WordPress site with many visitors on old phones is a real case.

There is an upgrade tool. It runs with npx @tailwindcss/upgrade, needs Node.js 20 or higher, and the guide recommends running it on a new branch.

Setting up Tailwind v4 with Vite

WordPress themes with a build step often use Vite. The Tailwind docs have a page for that setup. It has these steps.

Install the packages:

npm install tailwindcss @tailwindcss/vite

Add the plugin to your Vite config:

import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [
    tailwindcss(),
  ],
})

Add one line to your CSS file:

@import "tailwindcss";

Run your dev command, and make sure the compiled CSS reaches the page. The docs say to include the compiled CSS in the <head>, and that your framework may handle this for you.

That last part is the WordPress part. The docs cannot say how your theme loads CSS, because they do not know about WordPress. In a theme you would normally load the built file with wp_enqueue_style() in a wp_enqueue_scripts callback. The Tailwind docs do not cover that, so check how your Vite setup writes its output files before you wire it up.

There are also other ways in. The upgrade guide names @tailwindcss/postcss for PostCSS setups and @tailwindcss/cli for the command line tool. Pick the one that matches the build you already have.

The @theme block: where Tailwind keeps your design decisions

If you take one idea from this guide for theme work, take this one. In v4, the @theme block is where you define your tokens, which are the named values your whole design uses.

The docs say @theme defines theme variables: “CSS variables that also tell Tailwind to generate utility classes.” A normal :root variable only sets a value. A theme variable also creates classes.

@import "tailwindcss";

@theme {
  --color-mint-500: oklch(0.72 0.11 178);
}

That one line gives you bg-mint-500, text-mint-500 and fill-mint-500. The name decides what you get. Tailwind groups variables into namespaces:

NamespaceWhat it creates
--color-*Color classes such as bg-red-500
--font-*Font family classes such as font-sans
--text-*Font size classes such as text-xl
--breakpoint-*Responsive variants such as sm:*
--spacing-*Spacing and size classes such as px-4

The docs list more namespaces, including --font-weight-, --radius-, --shadow- and --container-.

Every theme variable becomes a CSS variable

The docs say each theme variable is compiled into a regular CSS variable on :root. That means you can use them outside Tailwind classes. Your own CSS can read them:

.typography p {
  font-size: var(--text-base);
  color: var(--color-gray-700);
}

And so can inline styles, or JavaScript. The docs show getComputedStyle(document.documentElement).getPropertyValue("--shadow-xl") for the JavaScript case.

This matters for WordPress, because a block theme also turns its design settings into CSS variables. The handbook says theme.json presets follow the --wp--preset--{type}--{slug} pattern. Two systems that both produce CSS variables can share values. I wrote the long version of that idea in From a Tailwind Theme to theme.json: One Palette for Both, so I will not repeat it here.

Changing the defaults

The docs show three levels of change.

Add something new by defining a variable that does not exist yet:

@theme {
  --font-script: Great Vibes, cursive;
}

Override a default by redefining it. This changes the sm breakpoint from 40rem to 30rem:

@theme {
  --breakpoint-sm: 30rem;
}

Replace a whole group by setting its namespace to initial. This removes every default color and keeps only yours:

@theme {
  --color-*: initial;
  --color-white: #fff;
  --color-midnight: #121063;
  --color-tahiti: #3ab7bf;
}

After that, bg-red-500 no longer exists and bg-midnight does. For a brand theme, this is often what you want, because it stops anyone from using a color that is not in the brand.

There is one more option worth knowing. When a theme variable points at another variable, use @theme inline. The docs warn that without it, the inner variable may resolve in the wrong place and fall back to a default font. And by default only the variables you actually use are written out. @theme static forces all of them to be written.

What Preflight does, and why WordPress people should care

Tailwind includes a reset called Preflight. The docs describe it as “an opinionated set of base styles,” built on modern-normalize. When you import tailwindcss, it is added for you.

Here is what it does, in the docs’ own words where they are short:

  • It removes all default margins, “including headings, blockquotes, paragraphs, etc.”
  • Headings are “completely unstyled by default, and have the same font size and weight as normal text.”
  • Lists have “no bullets or numbers.”
  • Images and other replaced elements, such as svg and video, are display: block, and images are limited to the parent width with max-width: 100%; height: auto;.
  • Every element gets border: 0 solid, so adding the border class always gives a solid 1px border.

If you build a theme from scratch and write every element with classes, this is helpful, because you start from a clean page.

WordPress is different, because a lot of the content is not yours. Posts are written in the block editor, and the output uses core block markup with core block classes. A reset that removes list bullets and heading sizes will change how that content looks. This is my inference from how Preflight is described, not a statement from the Tailwind docs: expect to restyle post content, or add a typography layer, when you turn Preflight on for a theme.

You have two options.

Keep Preflight and style the content. The docs say you can add your own base styles in the base layer:

@layer base {
  h1 { font-size: var(--text-2xl); }
  a {
    color: var(--color-blue-600);
    text-decoration-line: underline;
  }
}

Leave Preflight out. The docs say “to disable Preflight, simply omit its import while keeping everything else,” and show how to import the pieces one at a time:

@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/utilities.css" layer(utilities);

The second option can make sense when you add Tailwind to an existing theme and cannot afford to change every page. I have not tested either option across a range of themes, so try it on a staging copy first.

How Tailwind finds your classes in a theme

Because Tailwind reads files as plain text, it needs to know which files to read. The docs say it scans “every file in your project” except a short list: files in your .gitignore, files in node_modules, binary files, CSS files and package manager lock files.

For a WordPress theme, this has two practical effects.

First, your PHP templates are scanned automatically if they are inside the project and not ignored. Second, anything outside the project is not scanned. If a plugin or a parent theme outputs Tailwind classes, you need to add the path with @source:

@import "tailwindcss";
@source "./node_modules/@acmecorp/ui-lib";

The docs show more forms of @source. You can change the base path, ignore a folder with @source not, or turn off automatic detection with source(none) and name each path yourself.

Classes that never appear in your files

Sometimes a class comes from the database, such as a class a site owner types into the block editor’s “Additional CSS class” field. Tailwind cannot see that, because it is not in your files. The old fix was a safelist in the config. The upgrade guide says safelisting is now done with @source inline():

@import "tailwindcss";
@source inline("underline");

You can add variants and ranges with brace patterns, such as @source inline("{hover:,}bg-red-{50,{100..900..100},950}");. Use this sparingly. Every class you force adds to the CSS file.

What about the Play CDN?

You may have seen a one-line script version of Tailwind. It is called the Play CDN, and the docs say to add this to the <head>:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

The docs are direct about the limit: “The Play CDN is designed for development purposes only, and is not intended for production.” It is good for a quick test or a demo page, and not for a live theme. For custom CSS with the Play CDN, the docs say to use type="text/tailwindcss" on a style tag.

I covered when the CDN is fine and when you need a build step in Tailwind CSS CDN on a WordPress Site, so start there if that is your question.

The honest tradeoffs for a WordPress theme

Tailwind is popular, and it is not the right answer for every theme. Here is a fair look.

The long class list. A busy element can carry fifteen classes. The docs address it directly: “Don’t panic! In practice this isn’t the problem you might be worried it is.” Their advice is to use loops and components. If a card is rendered in a loop, “the actual class list is only written once.” For reuse across files, they suggest components or template partials. In WordPress terms, that means a template part, a pattern or a block render function, so the class list lives in one file.

Conflicts. The docs say when two utilities target the same property, the one that appears later in the stylesheet wins, not the one later in the class attribute. Do not add conflicting classes to the same element.

Editor content. Authors of posts in the block editor do not write Tailwind classes. They use the block controls. That means Tailwind helps most in the parts of the theme you control, such as templates, patterns and custom blocks, and less in post content. This is a fact about how WordPress works rather than something in the Tailwind docs.

A build step. You need Node and a build tool, and your team has to run it. If your theme is a few hundred lines of CSS and one developer maintains it, plain CSS or theme.json may be simpler. My comparison of CSS frameworks puts Tailwind next to the alternatives, including plain modern CSS.

Browser support. See the v4 floor above. A site that must support older browsers has to stay on v3.4 or use something else.

When Tailwind is a good fit

These cases tend to work well:

  • You are building a custom theme with templates you control, and you already use a build tool.
  • Your team likes keeping style next to markup and wants to avoid a growing stylesheet.
  • You want your tokens in one place, in plain CSS variables.
  • You are building a headless front end on top of WordPress, where WordPress does not output the markup.

And these cases tend to work less well:

  • You are styling a lot of block editor content that you do not control.
  • You need to support old browsers.
  • Nobody on the team wants a build step.

A small WordPress example

Here is a template part for a call to action, written with Tailwind classes. It is a plain PHP file, as you would use in a classic or hybrid theme, and every class name is written in full so Tailwind can find it.

<?php
$tone = $args['tone'] ?? 'light';
$classes = [
	'light' => 'bg-white text-slate-900',
	'dark'  => 'bg-slate-900 text-white',
];
?>
<section class="<?php echo esc_attr( $classes[ $tone ] ); ?> rounded-xl p-8">
	<h2 class="text-2xl font-semibold"><?php echo esc_html( $args['title'] ?? '' ); ?></h2>
	<a class="mt-4 inline-block rounded-lg bg-mint-500 px-5 py-3 font-medium hover:opacity-90"
		href="<?php echo esc_url( $args['url'] ?? '#' ); ?>">
		<?php echo esc_html( $args['label'] ?? '' ); ?>
	</a>
</section>

Two details matter here. The $classes array holds complete class names, which follows the docs’ rule about dynamic classes, the same pattern as their colorVariants example. And bg-mint-500 works because the theme defined --color-mint-500 in an @theme block. The escaping functions are standard WordPress practice and have nothing to do with Tailwind. The $args array is how get_template_part() passes data to a part in WordPress.

Keeping all your variant classes in one array like this is also how you handle the “long class list” worry in WordPress. The list lives in one file.

The best next step is the official docs, because Tailwind changes and any article can age. In order, I would read these pages:

  1. The page on styling with utility classes, to get the mental model.
  2. The theme variables page, to learn @theme and namespaces.
  3. The page on detecting classes in source files, so you never hit the dynamic class bug.
  4. The Preflight page, so you know what the reset does to your content.
  5. The upgrade guide, if you meet v3 tutorials or old code.

If you are deciding between Tailwind and the block theme’s own system, I would start with theme.json for your tokens and add Tailwind only where a build step pays for itself. That is my opinion, and it is not a rule.

Frequently asked questions

What is Tailwind CSS used for?

It is used to style web pages by adding small utility classes to HTML elements. A build step reads your files and creates a CSS file with only the classes you used.

Is Tailwind CSS free?

The framework itself is open source. For license details, read the project page and the license file rather than trusting an article, because terms can change. I did not verify the license for this post.

What version of Tailwind CSS is current?

The docs navigation showed v4.3 on 11 November 2026. Check the top of tailwindcss.com for the version at the time you read this.

Do I need Node to use Tailwind?

For a production site, yes, you need a build step, and the docs offer Vite, PostCSS and CLI options. The Play CDN needs no build, but the docs say it is for development only.

Does Tailwind work with WordPress block themes?

It can. The main points to plan for are loading the built CSS file, Preflight’s effect on block content, and classes that come from the database. A block theme’s theme.json and Tailwind’s @theme both produce CSS variables, so they can share values.

What is the difference between Tailwind and Bootstrap?

Bootstrap gives you ready-made components such as buttons and cards. Tailwind gives you low-level utilities and you build the components. My CSS frameworks comparison goes through the differences.

Where is the Tailwind documentation?

At tailwindcss.com/docs. The pages I used for this guide are on installing with Vite, theme variables, detecting classes, Preflight, the Play CDN and the upgrade guide.

Is Tailwind v4 different from v3?

Yes. In v4 you import with @import "tailwindcss", you configure in CSS with @theme, JavaScript config files are not found automatically, and the browser floor is higher. The upgrade guide lists every change.

The short version

Tailwind CSS is a set of small utility classes plus a build step that keeps only the ones you use. In version 4 you set it up with one CSS import, you keep your tokens in a @theme block, and every token becomes a CSS variable. For a WordPress theme, the points to plan for are the build step, the Preflight reset, and class names that come from the database instead of your files.

Try it on a staging copy of one template first. If the class lists feel natural and the build step does not slow your team down, it can be a good fit. If not, theme.json and plain CSS are still good tools.

Source note: all Tailwind facts and quotes come from tailwindcss.com (installation with Vite, theme variables, detecting classes in source files, Preflight, Play CDN, utility classes and upgrade guide pages), read on 11 November 2026. WordPress behavior is described from general theme practice and the theme handbook. File paths in the @source and @config examples are shortened. Statements marked as inference are mine and were not tested across themes. The license was not checked.