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
@tailwinddirectives of v3, and content detection is automatic: there is nocontentarray 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>| Group | Examples | CSS it produces |
|---|---|---|
| Layout | flex, grid, block, hidden | display and layout mode |
| Spacing | p-4, px-2, mt-6, gap-3 | padding, margin, gap |
| Sizing | w-full, size-8, max-w-prose | width, height, max-width |
| Typography | text-sm, font-medium, leading-6 | font-size, weight, line-height |
| Colour | bg-white, text-slate-700, border-slate-200 | colour values |
| Borders and radius | border, rounded-lg, divide-y | border and radius properties |
| Effects | shadow-sm, opacity-50, blur | shadows and filters |
| Position | relative, inset-0, z-10 | positioning 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-4is 1rem because one step is 0.25rem by default. p-4sets every side;px-4 py-2overrides the inline and block axes only.- A leading dash negates:
-mt-2is a negative margin, andmt-2is 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.Related
Responsive and state variants The box model
Last refreshed 2026-09-18.