Utility-first basics

Install the toolchain, read a class list as the CSS it produces, and understand why the numbers in spacing utilities are a scale rather than pixels.

Installing and wiring it up

# v4 as a PostCSS plugin (Vite, webpack, Next.js)
npm install tailwindcss @tailwindcss/postcss

# v4 standalone CLI: compile and watch
npm install -D tailwindcss @tailwindcss/cli
npx @tailwindcss/cli -i ./src/input.css -o ./dist/output.css --watch
/* src/input.css (v4) */
@import "tailwindcss";

/* scan directories the automatic detection misses */
@source "../templates";
  • In v4 the single import replaces the three @tailwind directives of v3, and content detection is automatic: there is no content array to keep in sync.
  • In v3 the same file would start with @tailwind base;, @tailwind components; and @tailwind utilities;.
  • Utility classes are generated by scanning source files for complete class names, so the file list is what decides which CSS exists.
  • A build plugin shipped in v4 requires modern browsers: Safari 16.4, Chrome 111 and Firefox 128 are the practical floor.
💡
Because the scanner looks for complete class names, a name built at runtime such as 'text-' + size is never found and produces no CSS. Choose between full names with a lookup object, or list them in a safelist file that the scanner also reads.

Reading a class list

<button class="inline-flex items-center gap-2 rounded-lg bg-indigo-600 px-4 py-2
               text-sm font-medium text-white shadow-sm hover:bg-indigo-500
               focus-visible:outline-2 focus-visible:outline-offset-2">
  Save changes
  <svg class="size-4" aria-hidden="true">...</svg>
</button>
GroupExamplesCSS it produces
Layoutflex, grid, block, hiddendisplay and layout mode
Spacingp-4, px-2, mt-6, gap-3padding, margin, gap
Sizingw-full, size-8, max-w-prosewidth, height, max-width
Typographytext-sm, font-medium, leading-6font-size, weight, line-height
Colourbg-white, text-slate-700, border-slate-200colour values
Borders and radiusborder, rounded-lg, divide-yborder and radius properties
Effectsshadow-sm, opacity-50, blurshadows and filters
Positionrelative, inset-0, z-10positioning and stacking

The spacing scale and arbitrary values

<section class="mx-auto max-w-3xl px-4 py-12 sm:px-6">
  <h1 class="text-3xl/9 font-semibold tracking-tight">Title</h1>
  <p class="mt-3 text-base/7 text-slate-600">Body copy.</p>
  <a class="-mt-1 inline-block w-[37ch] truncate">Read more</a>
</section>
  • The number after the utility is a step on a scale, not a pixel count: p-4 is 1rem because one step is 0.25rem by default.
  • p-4 sets every side; px-4 py-2 overrides the inline and block axes only.
  • A leading dash negates: -mt-2 is a negative margin, and mt-2 is positive.
  • Arbitrary values go in square brackets, as in w-[37ch]. They bypass the design scale, so treat them as an exception and not a habit.
  • Fractional and keyword values exist for width and max-width: w-1/2, max-w-prose, min-h-screen.

FAQ

Does this not make the markup unreadable?
It makes the styling local and explicit, which is the trade being made. When the same list repeats, extract a component or a template partial rather than a custom CSS class.
How do I reuse a style that repeats everywhere?
In v4 define a custom utility with @utility for a new building block, or use @apply inside a genuinely semantic class such as .btn-primary when the markup is not yours to edit.

Responsive and state variants The box model

Last refreshed 2026-09-18.