# Behavior traces — tailwindlabs/tailwindcss

> Canonical page: https://codevetter.com/unpack/tailwindcss/behavior-traces

# Behavior traces — tailwindlabs/tailwindcss

Scanned commit 41d9cae8e53378d16087fcf359eb785c2fd42ce4 (2026-09-08). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Three flows matter most: a cold CLI build, a CLI watch-mode rebuild, and the Vite dev-server transform. All three share the same skeleton — create compiler, derive sources, construct `Scanner`, scan, build, write — but differ in how they decide between a full and an incremental rebuild. The browser CDN build is a fourth, MutationObserver-driven variant of the same loop. The distinguishing detail across all of them is that 'full rebuild' means *discarding and recreating the compiler*, while 'incremental' means only feeding new candidate strings into the existing one.
- Cold CLI build order: `run()` → `build.handle(flags)` → resolve `--cwd`/`--input`/`--output` to absolute paths and reject identical in/out → read input (or `drainStdin()`, or default `@import 'tailwindcss';`) → `createCompiler(input, I)` → `scanner.scan()` → `compiler.build(candidates)` → `write(output, map, args, I)` → print `Done in …`. — [packages/@tailwindcss-cli/src/index.ts#L30-L79](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/index.ts#L30-L79) [packages/@tailwindcss-cli/src/commands/build/index.ts#L118-L190](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L118-L190) [packages/@tailwindcss-cli/src/commands/build/index.ts#L494-L520](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L494-L520)
- `createCompiler` registers an `onDependency(path)` callback that pushes into `fullRebuildPaths`, and then deliberately appends a *negated* source for `path.basename(process.execPath)` so the standalone binary never scans itself — a rename-proof self-exclusion. — [packages/@tailwindcss-cli/src/commands/build/index.ts#L277-L320](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L277-L320)
- Watch mode calls `loadWatcher()` first (so a missing `@parcel/watcher` fails with a useful message before any watcher is registered), then `createWatchers(await watchDirectories(scanner), handle)`. — [packages/@tailwindcss-cli/src/commands/build/index.ts#L330-L340](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L330-L340) [packages/@tailwindcss-cli/src/commands/build/index.ts#L685-L700](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L685-L700)
- The watch handler short-circuits when the only changed file is `args['--output']`, preventing the write-triggers-rebuild infinite loop, then calls `getRebuildStrategy(files, fullRebuildPaths)` to choose a branch. — [packages/@tailwindcss-cli/src/commands/build/index.ts#L341-L352](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L341-L352) [packages/@tailwindcss-cli/src/commands/build/index.ts#L800-L840](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L800-L840)
- On a `full` rebuild the CLI does, in order: re-read input → `clearRequireCache(resolvedFullRebuildPaths)` → snapshot `backupRebuildPaths` → reset `fullRebuildPaths` → `createCompiler` → `scanner.scan()` → set up new watchers → tear down old watchers → `compiler.build(candidates)`. — [packages/@tailwindcss-cli/src/commands/build/index.ts#L355-L405](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L355-L405)
- On an `incremental` rebuild it calls `scanner.scanFiles(rebuildStrategy.changedFiles)` and returns early without writing if zero new candidates were found — so touching a file with no new classes costs no disk write. — [packages/@tailwindcss-cli/src/commands/build/index.ts#L408-L430](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L408-L430)
- The watch `catch` block restores `fullRebuildPaths = backupRebuildPaths` before logging, with an inline comment explaining the recovery case: deleting a transitive dependency of `tailwind.config.js` would otherwise leave the compiler permanently unable to re-register that dependency. — [packages/@tailwindcss-cli/src/commands/build/index.ts#L437-L475](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L437-L475)
- Polling mode is a separate code path from event-based watching: `--poll` is validated to be a positive number (default 250ms), and it drives a `fullRebuild(I)` closure plus cursor-hiding escape codes with `SIGINT`/`SIGTERM` handlers that exit 130/143. — [packages/@tailwindcss-cli/src/commands/build/index.ts#L166-L176](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L166-L176) [packages/@tailwindcss-cli/src/commands/build/index.ts#L522-L600](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-cli/src/commands/build/index.ts#L522-L600)
- Vite dev transform order: `isPotentialCssRootFile(id)` gate → look up a `Root` in `rootsByEnv` keyed by `this.environment?.name` → `root.generate(src, addWatchFile, I)` → return `{ code, map }`; returning `false` from `generate` deletes the root from the map so it can be garbage collected. — [packages/@tailwindcss-vite/src/index.ts#L194-L230](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-vite/src/index.ts#L194-L230) [packages/@tailwindcss-vite/src/index.ts#L358-L380](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-vite/src/index.ts#L358-L380)
- `Root.generate` decides to rebuild by calling `requiresBuild()`, which stats every entry in `buildDependencies` and returns true if any mtime advanced or any file is missing — an mtime-map strategy mirrored in the PostCSS and webpack plugins. — [packages/@tailwindcss-vite/src/index.ts#L505-L543](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-vite/src/index.ts#L505-L543) [packages/@tailwindcss-postcss/src/index.ts#L160-L200](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-postcss/src/index.ts#L160-L200)
- After compiling, `Root.generate` bails with `false` unless the compiler reported at least one of `Features.AtApply | JsPluginCompat | ThemeFunction | Utilities | Variants` — this is how ordinary non-Tailwind `.css` files pass through Vite untouched. — [packages/@tailwindcss-vite/src/index.ts#L436-L450](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-vite/src/index.ts#L436-L450) [packages/tailwindcss/src/index.ts#L313-L334](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/tailwindcss/src/index.ts#L313-L334)
- The PostCSS plugin has an even cheaper bail: before touching the cache it does a `root.walkAtRules` scan for any of `import|reference|theme|variant|config|plugin|apply|tailwind` and returns immediately if none are present. — [packages/@tailwindcss-postcss/src/index.ts#L96-L117](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-postcss/src/index.ts#L96-L117)
- Browser CDN flow: `rebuild('full')` → `createCompiler()` gathers all `style[type="text/tailwindcss"]` text, compares against `lastCss` to skip needless recompiles → `build(kind)` collects `element.classList` across `[class]` elements, diffs against a global `classes` Set, and assigns `sheet.textContent = compiler.build(newClasses)`; builds are serialized through a `buildQueue` promise chain. — [packages/@tailwindcss-browser/src/index.ts](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-browser/src/index.ts)
- Two MutationObservers drive the browser build: one watches each known `<style>` element for `characterData`/`childList` changes (→ full rebuild), the other watches `document.documentElement` for `class` attribute changes and added nodes (→ incremental), explicitly skipping the injected output `sheet` to avoid a feedback loop. — [packages/@tailwindcss-browser/src/index.ts](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-browser/src/index.ts)
- Upgrade flow order: dirty-git check → version match check → glob `**/*.css` → `Stylesheet.load` → `analyzeStylesheets` → `linkConfigsToStylesheets` (v3 only) → `migrateJsConfig` → `migrateStylesheet` → `splitStylesheets` (v3 only) → `postcss([sortBuckets(), formatNodes()])` → `writeFileSafely` → package-manager upgrade → template migration. — [packages/@tailwindcss-upgrade/src/index.ts#L46-L300](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/%40tailwindcss-upgrade/src/index.ts#L46-L300)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
