@source & content detection

[check: …]
📖 Pair this live demo with the companion guide (.md) — this page is the rendered ground truth. ↗ Builds on build tooling (the build pipeline that performs the scan) and pairs with plugins & ecosystem (@plugin / @reference, the other CSS-first directives of Phase 7).

v3 content:[] → v4 @source

Tailwind ships only the CSS for classes it can see in your source. v4 finds those files automatically (plain-text scan from the stylesheet's base path). @source is the CSS-first override — it lives in your app.css, not in tailwind.config.js. The Play CDN mirrors this idea at runtime: it scans the live DOM via a MutationObserver and compiles whatever class tokens it finds.

conceptv3 (config file)v4 (CSS-first)
where it lives tailwind.config.jscontent: [...] your CSS → @source "...";
default behavior you MUST list every path auto-detect: scans everything from the base path, skips .gitignore/node_modules/binaries/CSS/lockfiles
add a path content: ["./src/**/*.{tsx,jsx}"] @source "../src/components"; (relative to the stylesheet)
exclude a path glob negation in the array @source not "../legacy";
safelist (force-generate) safelist: [...] @source inline("..."); with brace expansion
kill auto-detection no auto-detection to kill @import "tailwindcss" source(none);
set the base path content: paths are the base @import "tailwindcss" source("../src");
@import "tailwindcss"; @source "../node_modules/@acmecorp/ui-lib"; /* external lib in node_modules (gitignored) */ @source "../src/components"; /* explicit add */ @source not "../src/components/legacy"; /* exclude legacy dir */ @source inline("{hover:,focus:,}bg-red-{50,{100..900..100},950}"); /* safelist + brace expansion */

1 · simulate the build-time scan

This is a build-time concept — nothing ships to the browser. Toggle the files below to model @source (include) vs @source not (exclude). The readout shows which class tokens Tailwind would extract by scanning those files as plain text, and the unique utilities it would try to generate. Flip source(none) to model disabling auto-detection (then nothing is auto-included).

mode: auto-detect
scanned files: 0 · candidate tokens: 0 · unique utilities: 0
Tokens that don't map to a real utility (e.g. clearfix, float-left) are extracted as candidates but thrown away — Tailwind never parses your code as JS/HTML, it only hunts for class-like strings.

2 · @source inline() proven live (safelist analog)

At build time, @source inline("bg-fuchsia-500") forces Tailwind to emit a class even if it never appears literally in any file. The Play CDN can't read @source — instead it watches the DOM with a MutationObserver. So we prove the same promise live: click inject to add bg-fuchsia-500 to the box below after load. The CDN spots the new token and compiles it — exactly what a safelist guarantees.

auto-detected (present at load)

bg-cyan-500

safelist-injected (added post-load)

(no class yet)

static: — · dynamic: —

intent → @source pattern

intentpatternwhy
scan a gitignored dep @source "../node_modules/@acme/ui"; node_modules is skipped by default — a Tailwind-built lib lives there
monorepo: scan a sibling package @source "../../packages/shared"; auto-detection stays inside the base path; reach across with a relative path
monorepo: set the base path @import "tailwindcss" source("../src"); build runs from repo root; point detection at this app's src
exclude legacy / non-Tailwind dir @source not "../legacy"; large dirs with zero Tailwind classes just slow the scan
turn auto-detection off @import "tailwindcss" source(none); multi-stylesheet apps: each CSS file gets only its own explicit @sources
safelist one class @source inline("underline"); force-generate a class that no file contains literally (CMS, dynamic)
safelist a grid of classes @source inline("{hover:,}bg-red-{100,500,900}"); brace expansion (comma = set, {a..z..step} = range)
ban a class outright @source not inline("container"); detected in files but you never want it emitted