Overview

Tailwind v4 (the 4.x line, current as of 2026) configures through CSS, scans source files automatically, and requires Safari 16.4+, Chrome 111+, or Firefox 128+. This page is the hub; tokens, dark mode, variants, plugins, responsive design, and migration have their own pages.

Install with the Vite plugin or PostCSS

Use @tailwindcss/vite on Vite-based stacks and @tailwindcss/postcss elsewhere; the CLI also works.

npm install tailwindcss @tailwindcss/vite
@import "tailwindcss";

Configure in CSS, not JavaScript

Define tokens with @theme; each token becomes a CSS variable and a utility. A tailwind.config.js is not detected; load one only through @config, which ignores corePlugins, safelist, and separator.

@import "tailwindcss";
 
@theme {
  --color-brand: oklch(0.72 0.18 264);
  --font-display: "Schibsted Grotesk", system-ui, sans-serif;
}

This yields bg-brand and font-display. See tailwind-theme. Tailwind emits real cascade layers (theme, base, components, utilities), so unlayered CSS beats utilities; see css-cascade-layers.

Compose with utilities; extract at the third use

Keep utilities inline. Extract a component when the same combination appears in three or more places and should change as a unit. Avoid @apply for your own components, because it hides the contract inside a stylesheet. Use it for markup you cannot annotate, such as a CMS body or a third-party widget. In CSS modules and Vue or Svelte <style> blocks, start the file with @reference "../app.css"; so @apply can see the theme. For the comparison with scoped stylesheets, see tailwind-vs-css-modules.

Treat arbitrary values as a missing token

Use bracket syntax for one-off values and add a token when the same value appears a third time.

<div class="w-[412px] grid-cols-[1fr_500px_2fr] bg-(--brand) [mask-type:luminance] [&:nth-child(3)]:font-bold">
  • Underscores stand in for spaces inside brackets.
  • [property:value] sets any CSS property without a named utility, such as [content-visibility:auto].
  • Reference CSS variables with parentheses: bg-(--brand). The v3 form bg-[--brand] no longer works.
  • Keep arbitrary values out of shared components; they should read tokens.
  • Prefer var() inside calc() so the value keeps its link to a token: mt-[calc(var(--header-h)+1rem)].

Write complete class names

Tailwind scans files as plain text, so a concatenated class such as `bg-${color}-600` is never generated. Map props to complete strings instead:

const colorVariants = { blue: "bg-blue-600 text-white", red: "bg-red-500 text-white" };
<button className={colorVariants[color]} />;

Register sources the scanner skips (it ignores .gitignored paths, node_modules, binary files, and CSS files) with @source "../node_modules/@acme/ui";. Safelist generated classes with @source inline("{hover:,}bg-red-{50,{100..900..100},950}");; the safelist config key is gone.

Set up tooling once

  • Prettier: prettier-plugin-tailwindcss sorts classes. On v4 you must set "tailwindStylesheet": "./src/app.css" to point at your CSS entry, and the plugin must be last in plugins.
  • ESLint: eslint-plugin-tailwindcss 4.x supports Tailwind v4 and requires settings.tailwindcss.cssConfigPath pointing at the same CSS entry. no-contradicting-classname catches p-2 p-3 on one element, and no-unnecessary-arbitrary-value flags brackets that match a built-in token.
  • Editor: install bradlc.vscode-tailwindcss for autocomplete, hover CSS, and lint. Add helper names to tailwindCSS.classFunctions (for example cn, cva) and custom props to tailwindCSS.classAttributes so strings outside class and className get completions.
  • DevTools: a struck-through property means another rule wins, usually unlayered CSS. A missing property means the class was never generated.

Avoid common pitfalls

  • Unlayered resets beat utilities. A global * { margin: 0 } outside any layer overrides every utility. Put it in @layer base.
  • Custom layer order. Declare it before the import, for example @layer theme, base, vendor, components, utilities;. A list placed after @import "tailwindcss" appends new layers after utilities, so they win.
  • Tokens missing. A @theme block only takes effect in the CSS file Tailwind processes (the one with @import "tailwindcss") or a file it imports; a separately bundled stylesheet generates no utilities.
  • Preprocessors. Sass, Less, and Stylus are not supported; Tailwind handles imports, prefixes, and nesting.
  • Plugins. Container queries are built in; typography and forms remain official plugins; see tailwind-plugins.