tailwindlabs/tailwindcss
Debugging
DEBUG instrumentation
The Node packages all share an instrumentation helper at packages/@tailwindcss-node/src/instrumentation.ts. Every adapter respects a DEBUG env variable (packages/@tailwindcss-node/src/env.ts) that turns on timing output.
DEBUG=* pnpm --filter @tailwindcss/cli dev -- -i input.css -o out.cssYou will see lines like:
[@tailwindcss/cli] (initial build) … 14.7ms
Setup compiler … 5.2ms
Scan for candidates … 3.4ms
Build utilities … 4.1ms
Optimization … 1.2msThis is the same data path @tailwindcss/postcss, @tailwindcss/vite, @tailwindcss/webpack, the CLI, and the browser build all use. Each adapter calls using I = new Instrumentation() and wraps its work in I.start(name) / I.end(name) blocks. Search for Instrumentation in any adapter's index.ts to see what spans are reported.
The browser build's instrumentation (packages/@tailwindcss-browser/src/instrumentation.ts) reports to the browser performance timeline (performance.mark / performance.measure) instead of stderr.
Tracing the Rust scanner
The scanner uses the tracing crate. Enable it via the RUST_LOG env variable:
RUST_LOG=tailwindcss_oxide=debug cargo test -p tailwindcss-oxide -- --nocaptureSet up code lives in crates/oxide/src/scanner/init_tracing.rs. The first Scanner::new(...) call initializes the subscriber if RUST_LOG is set.
Common failure modes
"Scanner returned no candidates"
Usually means the scanner did not find your source files at all. Check:
- Is your file extension on the allow list in
crates/oxide/src/scanner/auto_source_detection.rs? - Are you adding it via
@sourcein CSS or via the host'ssources(Vite picks them up fromtransform/loadhooks; PostCSS picks them up fromresult.opts.from). - Is the file in
.gitignoreor.tailwindcssignore? The walker usescrates/ignore/, which respects both. - For programmatic use: are you passing the right
baseto theScanner?
"@apply doesn't pick up my custom utility"
The order of operations in parseCss (packages/tailwindcss/src/index.ts) matters: @utility registrations happen before @apply substitution. But if your custom utility lives in a JS plugin loaded by @plugin, the v3 compat hooks run after some passes. See packages/tailwindcss/src/compat/apply-compat-hooks.ts.
"My theme value isn't applied"
@theme blocks must be at the top level of the CSS file (no nesting). Inside @reference imports, @theme is treated as reference-only.
For programmatic checking: theme.get('--color-red-500') from Theme (packages/tailwindcss/src/theme.ts) tells you what value the design system actually resolved.
"Vite plugin keeps doing full rebuilds"
Vite's plugin records mtimes for every imported CSS or JS dependency. If a config file or plugin gets touched, the next change triggers a full rebuild (which clears the require-cache via clearRequireCache from @tailwindcss/node/require-cache). To debug, set DEBUG=*. The Register full rebuild paths span will show which files invalidated.
"PostCSS plugin: 'No such file' errors during HMR"
The PostCSS plugin caches per-(inputFile, base, optimize) triple. If your build system mutates base between runs, you'll get cache thrashing. Inspect the cache QuickLRU in packages/@tailwindcss-postcss/src/index.ts.
Running a specific candidate through the compiler
To debug a specific class compilation, write a unit test:
import { compileCss } from './test-utils/run'; // helper used throughout the suite
let result = await compileCss(`@import 'tailwindcss';`, [
'md:hover:bg-red-500',
]);
console.log(result);packages/tailwindcss/src/test-utils/ has small helpers that stand up a compiler instance with the standard imports. Most existing tests use them — pick one that's close and copy.
Inspecting the AST
packages/tailwindcss/src/ast.ts exports toCss(ast) and cssContext(node). To see the AST at any point:
import { walk } from './walk';
walk(ast, (node) => {
console.log(node.kind, 'name' in node ? node.name : '');
});The CSS parser also exports the typed AST node constructors (atRule, styleRule, decl, comment, context) so you can synthesize fragments by hand for tests.
When PostCSS gets in the way
@tailwindcss/postcss converts between the internal AST and PostCSS's AST in packages/@tailwindcss-postcss/src/ast.ts (postCssAstToCssAst / cssAstToPostCssAst). If a regression only reproduces under PostCSS, instrument the boundary: log the AST before and after the conversion. Source map information is preserved through the round-trip, so any divergence will show up in the dump.
When the upgrade tool refuses to run
@tailwindcss/upgrade requires a clean git worktree by default (see isRepoDirty() in packages/@tailwindcss-upgrade/src/utils/git.ts). Use --force to run anyway:
npx @tailwindcss/upgrade --forceIt also re-checks installedTailwindVersion vs expectedTailwindVersion from packages/@tailwindcss-upgrade/src/utils/version.ts. If you hit a version mismatch, run pnpm install (or your equivalent) and try again.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.