Open-Source Wikis

/

Tailwind CSS

/

Packages

/

tailwindcss (the compiler)

tailwindlabs/tailwindcss

tailwindcss (the compiler)

Active contributors: Robin, Philipp, Jordan

Purpose

packages/tailwindcss/ is the CSS compiler. It parses an input stylesheet, builds a design system from @theme/@plugin/@config, accepts a list of class candidates, and emits the final CSS. Every adapter (CLI, Vite, PostCSS, Webpack, browser) calls into this package through a single entry point: compile() (or compileAst() for callers that already have an AST).

Directory layout

packages/tailwindcss/
├── index.css           # Top-level @import "tailwindcss" entry
├── preflight.css       # Browser reset
├── theme.css           # Default theme tokens (colors, spacing, fonts, etc.)
├── utilities.css       # @layer utilities target
├── package.json
├── playwright.config.ts
├── src/
│   ├── index.ts                  # compile() and compileAst()
│   ├── css-parser.ts             # CSS → AST
│   ├── ast.ts                    # AST node types + toCss + optimizeAst
│   ├── walk.ts                   # AST traversal helpers
│   ├── apply.ts                  # @apply implementation
│   ├── at-import.ts              # @import resolution
│   ├── candidate.ts              # parseCandidate (string → Candidate)
│   ├── compile.ts                # compileCandidates / compileAstNodes
│   ├── css-functions.ts          # theme(...), --spacing(), etc.
│   ├── design-system.ts          # buildDesignSystem
│   ├── theme.ts                  # Theme class
│   ├── utilities.ts              # All built-in utilities (~7,800 lines)
│   ├── variants.ts               # Built-in variants + applyVariant infra
│   ├── canonicalize-candidates.ts # Canonical-form normalization
│   ├── canonicalize-calc-expressions.ts
│   ├── selector-parser.ts        # Mini parser for selector strings
│   ├── attribute-selector-parser.ts
│   ├── value-parser.ts           # Mini parser for declaration values
│   ├── source-maps/              # Source map generation
│   ├── compat/                   # v3 plugin/config bridge
│   ├── utils/                    # segment, escape, compare, brace-expansion, …
│   └── *.test.ts                 # Co-located tests
└── tests/
    └── ui.spec.ts                # Playwright UI tests

Key abstractions

Type / function File Description
compile(css, opts) packages/tailwindcss/src/index.ts Parse CSS, build design system, return { build, sources, root, features }.
compileAst(ast, opts) packages/tailwindcss/src/index.ts Same pipeline, takes an AST directly.
parseCandidate(str, ds) packages/tailwindcss/src/candidate.ts Parse md:hover:bg-red-500/50 into a typed Candidate (or null).
compileCandidates(cs, ds) packages/tailwindcss/src/compile.ts Turn candidates into AST nodes, sort by variant + property order.
buildDesignSystem(theme) packages/tailwindcss/src/design-system.ts Construct the runtime context every utility/variant reads from.
Theme packages/tailwindcss/src/theme.ts Keyed theme tokens with namespace + option flags.
walk(ast, fn) packages/tailwindcss/src/walk.ts Visit every AST node; supports replace/skip/stop actions.
optimizeAst(ast, ...) packages/tailwindcss/src/ast.ts Final cleanup pass: dedupe, merge, polyfill emit.
Features (bit-set) packages/tailwindcss/src/index.ts Reports which Tailwind features the input CSS used.
Polyfills (bit-set) packages/tailwindcss/src/index.ts Toggles @property / color-mix fallbacks.
createCssUtility(node) packages/tailwindcss/src/utilities.ts Convert a @utility at-rule into a registered utility.

How a build runs

sequenceDiagram
    participant Adapter
    participant Compile as compile()
    participant Parser as css-parser
    participant Walker as parseCss walk
    participant Compat as compat hooks
    participant DS as DesignSystem
    participant Build as compiler.build()

    Adapter->>Compile: compile(input, options)
    Compile->>Parser: CSS.parse(input)
    Parser-->>Compile: AST
    Compile->>Walker: walk(ast)
    Walker->>Walker: substituteAtImports
    Walker->>Walker: collect @theme / @utility / @variant / @plugin / @config / @source
    Compile->>Compat: applyCompatibilityHooks(theme, plugins, configs)
    Compile->>DS: buildDesignSystem(theme)
    Compile->>Walker: substituteAtVariant + substituteAtApply + substituteFunctions
    Compile-->>Adapter: { build, sources, features, root }
    Adapter->>Build: build(candidates)
    Build->>Build: parseCandidate per candidate
    Build->>Build: compileAstNodes per parsed candidate
    Build->>Build: sort + clone static AST + insert
    Build->>Build: optimizeAst (dedupe, polyfills)
    Build-->>Adapter: CSS string

The "static AST" is cached inside the compile() closure: after parseCss finishes, the compiler has an AST with every @apply, @variant, @theme, and theme(...) call resolved. Each call to build(candidates) clones this static AST and splices in the per-candidate rules at the position where @tailwind utilities originally appeared.

The static AST cache

The closure returned by compile() keeps:

  • The fully-resolved static AST (no candidates inserted yet).
  • The DesignSystem.
  • The list of sources declared via @source.
  • The root setting ('none', null, or { base, pattern }) declared via source(...).
  • The features bit-set the adapter uses to skip work.
  • A cache of "previously seen candidates" so repeated builds in dev mode skip parsing.

The cache invalidates only when the adapter calls compile() again with new input CSS — typically because a watched dependency (a @plugin JS file, a @config, an @imported CSS file) changed mtime. The actual invalidation logic lives in each adapter, not in the compiler.

@theme, @utility, @variant, @apply — the four directives

These are the four CSS at-rules that drive the configuration model.

Directive Handler What it does
@theme parseCss walk in packages/tailwindcss/src/index.ts Adds tokens to the Theme and emits :root declarations (unless reference).
@utility createCssUtility in packages/tailwindcss/src/utilities.ts Registers a static or functional utility.
@variant parseCss walk + substituteAtVariant Either applies a variant or registers one (when used like @custom-variant).
@apply substituteAtApply in packages/tailwindcss/src/apply.ts Pulls another utility's rules into the current rule body.

Other recognized directives: @import (resolved through at-import.ts), @plugin, @config, @source, @reference, @custom-variant, @layer, and the legacy @tailwind base|components|utilities form.

Source maps

packages/tailwindcss/src/source-maps/ is its own subsystem with three files worth knowing:

  • source-map.tscreateSourceMap(...) builds a DecodedSourceMap from the CSS AST.
  • line-table.ts — fast line/column lookup over the original input string.
  • translation-map.ts — maps generated CSS positions back to source positions.

There is also visualize-source-map.ts (~600 lines), a developer tool that prints a side-by-side ANSI dump for verifying mappings during testing. It's used by tests/visualize-source-map.test.ts and the recently-added "Add source map visualization for tests" PR (commit 1ca0aacd, 2026-04-30).

See Source maps for end-to-end behavior.

v3 compat layer

packages/tailwindcss/src/compat/ translates the v3 JavaScript ecosystem into v4 concepts. Major files:

  • apply-compat-hooks.ts (~520 lines) — orchestrator. Calls into all the other compat steps.
  • plugin-api.ts (~750 lines) — implements addUtilities/matchUtilities/addVariant/matchVariant/addBase/addComponents so v3 plugins keep working.
  • apply-config-to-theme.ts — reads a v3 tailwind.config.js theme and pours it into the v4 Theme.
  • apply-keyframes-to-theme.ts — same but for keyframes.
  • colors.ts / colors.cts — exports the default color palette (re-exported as tailwindcss/colors).
  • default-theme.ts / default-theme.cts — exports the default theme (re-exported as tailwindcss/defaultTheme).
  • flatten-color-palette.ts — re-export of the v3 helper that some plugins still call.
  • legacy-utilities.ts — utilities that exist only for v3 backwards compat.
  • screens-config.ts — translates v3's screens to v4 breakpoint variants.
  • dark-mode.ts — translates v3's darkMode: 'class' / 'media' / 'selector' config.
  • container.ts — the v3 container component.

See v3 compatibility.

Entry points for modification

  • Adding a new built-in utility: edit packages/tailwindcss/src/utilities.ts. The file is divided into sections per CSS property family; pick the right section and follow the surrounding style. Add tests in utilities.test.ts.
  • Adding a new variant: edit packages/tailwindcss/src/variants.ts, add tests in variants.test.ts.
  • Changing how a candidate is parsed: edit packages/tailwindcss/src/candidate.ts, add tests in candidate.test.ts. Be aware the Rust extractor also has rules about candidate shapes — see crates/oxide.
  • Changing CSS at-rule semantics: the dispatch loop is in parseCss inside packages/tailwindcss/src/index.ts. Add the new at-rule's branch there.
  • Tweaking property sort order: edit packages/tailwindcss/src/property-order.ts. The GLOBAL_PROPERTY_ORDER array drives the comparator in compileCandidates.

For deeper details on individual subsystems:

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

tailwindcss (the compiler) – Tailwind CSS wiki | Factory