Tailwind's output is small because it contains only the utilities you use, which depends on the compiler finding every class in your source. v4 does this automatically, but automatic is not magical. In this lesson you will learn how content detection works, how to add or exclude sources, how to safelist classes the scanner cannot see, and how to produce a minified production build.
Tailwind v4 scans every file in your project for strings that look like utility classes and generates CSS for exactly those. There is no content array to configure. A few sensible rules keep the scan fast:
.gitignore are skipped, so node_modules, build output and lock files are never scanned.The compiler is written in Rust (the Oxide engine) and uses Lightning CSS for parsing, prefixing and minification, so full builds are several times faster than v3 and incremental rebuilds take microseconds.
Scanning is text-based and never executes code: bg-${color}-500 in a template literal produces no CSS, and a class stored in a database is invisible. Those cases need safelisting.
@source registers extra paths relative to the CSS file. The common case is a component library inside node_modules, which the default rules ignore:
@import "tailwindcss";
@source "../node_modules/@acme/ui"; /* scan a package for its utilities */
@source not "../src/fixtures"; /* exclude a folder (v4.1+) */Two options on the import itself change the starting point:
@import "tailwindcss" source("../src"); /* scan only src instead of the whole project */
@import "tailwindcss" source(none); /* disable automatic detection entirely */source(none) combined with explicit @source directives is the right setup for monorepos, where scanning every package would be slow and would pull in classes from unrelated apps.
When a class must exist even though it never appears in a scanned file, declare it inline. Brace expansion generates whole families in one line:
@source inline("underline");
@source inline("{hover:,focus:,}bg-red-{50,{100..900..100},950}");
@source inline("{sm:,md:,lg:,}grid-cols-{1..4}");The second line produces bg-red-50 through bg-red-950 with their hover: and focus: forms, and the third produces responsive grid columns. Keep safelists small: every entry is CSS shipped to every visitor whether used or not. The v3 safelist config key no longer exists.
Each integration has its own way to minify:
| Setup | Production command | Minification |
|---|---|---|
| CLI | npx @tailwindcss/cli -i src/app.css -o dist/app.css --minify | The --minify flag |
| Vite | vite build | Automatic |
| Next.js / PostCSS | next build | Automatic via the framework |
Lightning CSS minifies and adds vendor prefixes, so autoprefixer and cssnano are unnecessary. A typical production stylesheet is well under 50 KB before compression; if yours is much larger, look for an over-broad @source, a large safelist or @theme static. The Play CDN has no production mode and must be replaced by a build step before launch.
@apply in large component classes; @apply duplicates declarations into every class that uses it.@source "../node_modules" wholesale, which scans thousands of files and safelists nothing useful.--minify in a CLI build script while Vite users get it for free.Which files does Tailwind v4 skip when scanning for classes by default?
content configuration.@source "path" adds a directory, @source not "path" excludes one, and source(none) on the import disables auto-detection.@source inline("…") safelists classes with brace expansion; keep it minimal.--minify in the CLI; Vite and Next.js do it automatically through Lightning CSS.@theme static when the CSS grows.Next lesson: Migrating from Tailwind v3 to v4 — run the upgrade tool and handle every renamed utility and behaviour change.