Open-Source Wikis

/

Tailwind CSS

/

Packages

/

@tailwindcss/postcss

tailwindlabs/tailwindcss

@tailwindcss/postcss

Active contributors: Robin, Philipp, Adam

Purpose

The PostCSS plugin. Converts Tailwind input CSS to output CSS while staying a well-behaved member of a PostCSS pipeline. Implementation: packages/@tailwindcss-postcss/src/.

Directory layout

packages/@tailwindcss-postcss/
├── package.json
├── README.md
├── tsconfig.json
├── tsup.config.ts
└── src/
    ├── index.ts                     # Plugin entry point
    ├── ast.ts                       # postCssAstToCssAst + cssAstToPostCssAst
    ├── postcss-fix-relative-paths/  # Sub-plugin to handle `postcss-import` quirks
    └── *.test.ts                    # Tests + snapshots

Key abstractions

Symbol Description
tailwindcss(opts): AcceptedPlugin Default export, returns a PostCSS plugin descriptor.
CacheEntry Per-input-file cache holding the compiler, scanner, mtimes, ASTs.
getContextFromCache(...) LRU-keyed cache lookup.
cssAstToPostCssAst / postCssAstToCssAst Converters between Tailwind's internal AST and PostCSS's AST.
fixRelativePathsPlugin Sub-plugin that fixes paths when postcss-import ran first.

How it integrates with PostCSS

graph LR
    A[postcss-import<br/>maybe] --> B[fixRelativePathsPlugin]
    B --> C[Tailwind plugin]
    C --> D[postcss → out CSS]

The plugin signature is unusual because it nests two PostCSS plugins:

return {
  postcssPlugin: '@tailwindcss/postcss',
  plugins: [
    fixRelativePathsPlugin, // pre-transform fix-up
    {
      // The actual Tailwind plugin
      Once(root, { result }) {
        // ...
      },
    },
  ],
};

fixRelativePathsPlugin exists because postcss-import can run before Tailwind in a typical pipeline, which rewrites @import paths to be absolute. The fix-up walks the imported AST and re-relativizes paths so Tailwind's own @source, @plugin, and @config directives resolve against the original file location.

Caching

The plugin keeps an LRU<string, CacheEntry> (max size 50). Keys include input file path, base, and the optimize settings. Each entry caches:

  • mtimes — the file → mtime map for invalidation.
  • compiler — the result of compileAst(...).
  • scanner — the @tailwindcss/oxide Scanner.
  • tailwindCssAst — the static AST returned by the compiler (used to skip re-parsing).
  • cachedPostCssAst and optimizedPostCssAst — pre-converted outputs.
  • fullRebuildPaths — the list of file paths that should trigger a full re-compile when their mtime changes.

When the compiler emits new dependency paths (via onDependency), they go into fullRebuildPaths. On the next Once call, the plugin compares mtimes and either reuses the cached AST or rebuilds.

AST conversion

PostCSS uses its own AST. Tailwind's compiler uses an internal AST defined in packages/tailwindcss/src/ast.ts. packages/@tailwindcss-postcss/src/ast.ts provides the bridge:

  • postCssAstToCssAst(root) — read PostCSS into the internal AST. Used because the input CSS arrives from PostCSS.
  • cssAstToPostCssAst(ast, postcss) — write the internal AST back to PostCSS so downstream plugins can keep operating.

Source position information is preserved through both conversions, which is how source maps survive the round-trip.

Plugin options

type PluginOptions = {
  base?: string; // default: process.cwd()
  optimize?: boolean | { minify?: boolean }; // default: NODE_ENV === 'production'
  transformAssetUrls?: boolean; // default: true
};

base controls the project root the scanner walks and resolves @source patterns against. transformAssetUrls toggles URL rewriting for things like background-image: url(./logo.png).

Common pitfalls (and where they're handled)

  • postcss-import running first. Handled by fixRelativePathsPlugin. CHANGELOG.md mentions a related PR #19980 ("Resolve imports relative to base when result.opts.from is not provided").
  • @property polyfill in CSS modules. A flag inside index.ts disables the polyfill for *.module.css files because CSS modules' purity check would reject the global * rules the polyfill emits.
  • Source maps. The compiler's source map output is decoded by @tailwindcss/node's toSourceMap and attached to the PostCSS result.

Integration points

  • Reads from tailwindcss directly for toCss and the AST types.
  • Reads from @tailwindcss/node for compileAst, env, Features, Instrumentation, optimize, Polyfills.
  • Reads from @tailwindcss/oxide for the Scanner.
  • Returns a PostCSS plugin descriptor that any standard PostCSS host can consume.

Entry points for modification

  • Once(root, { result }) in index.ts is where the plugin runs per input file. Add timing or new cache keys here.
  • fixRelativePathsPlugin/ is its own subdirectory — modify if postcss-import interactions change.
  • postCssAstToCssAst / cssAstToPostCssAst are where format incompatibilities get bridged. New AST node kinds added to the compiler should map to a sensible PostCSS equivalent here.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

@tailwindcss/postcss – Tailwind CSS wiki | Factory