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 testsKey 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 stringThe "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
sourcesdeclared via@source. - The
rootsetting ('none',null, or{ base, pattern }) declared viasource(...). - The
featuresbit-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.ts—createSourceMap(...)builds aDecodedSourceMapfrom 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) — implementsaddUtilities/matchUtilities/addVariant/matchVariant/addBase/addComponentsso v3 plugins keep working.apply-config-to-theme.ts— reads a v3tailwind.config.jstheme and pours it into the v4Theme.apply-keyframes-to-theme.ts— same but for keyframes.colors.ts/colors.cts— exports the default color palette (re-exported astailwindcss/colors).default-theme.ts/default-theme.cts— exports the default theme (re-exported astailwindcss/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'sscreensto v4 breakpoint variants.dark-mode.ts— translates v3'sdarkMode: 'class'/'media'/'selector'config.container.ts— the v3containercomponent.
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 inutilities.test.ts. - Adding a new variant: edit
packages/tailwindcss/src/variants.ts, add tests invariants.test.ts. - Changing how a candidate is parsed: edit
packages/tailwindcss/src/candidate.ts, add tests incandidate.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
parseCssinsidepackages/tailwindcss/src/index.ts. Add the new at-rule's branch there. - Tweaking property sort order: edit
packages/tailwindcss/src/property-order.ts. TheGLOBAL_PROPERTY_ORDERarray drives the comparator incompileCandidates.
For deeper details on individual subsystems:
- Variants
- Utilities (the
UtilityandDesignSystemtypes) - Source maps
- Scanning and extraction — covers how candidates arrive at
build() - v3 compatibility
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.