Functions and Directives

Purpose and Scope

Tailwind CSS exposes a small language on top of ordinary CSS. Directives are Tailwind-specific at-rules that tell the compiler where framework CSS, theme variables, custom utilities, sources, and variant behavior should enter the stylesheet. Functions are Tailwind-specific value helpers that are evaluated during compilation so authors can compute colors, spacing, and theme lookups without writing generated declarations by hand. This page explains that language from the perspective of someone maintaining project CSS, upgrading older stylesheets, or integrating Tailwind into a build pipeline. It also separates current author-facing features from compatibility behavior that exists to preserve older Tailwind projects.

The official documentation presents directives as custom at-rules for special Tailwind functionality, including @import for loading Tailwind, @theme for design tokens, @source for scan roots, @utility for custom utilities, @variant for applying variants inside CSS, and @custom-variant for registering project-specific variants. The source evidence here is strongest for two parts of that story: value functions evaluated by the compiler and migration codemods that rewrite legacy directive forms. Read this page as a reference for the visible behavior and the source modules that enforce or migrate that behavior, not as a complete parser walkthrough for every directive family.

Sources: packages/tailwindcss/src/css-functions.ts, packages/tailwindcss/src/css-functions.test.ts, packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.ts, packages/@tailwindcss-upgrade/src/codemods/css/migrate-variants-directive.ts

Relevant Source Files

  • packages/tailwindcss/src/css-functions.ts — Implements the compiler-side CSS value functions, including alpha composition, spacing multiplication, theme variable resolution, fallback handling, and legacy theme lookup entry points.
  • packages/tailwindcss/src/css-functions.test.ts — Exercises the public behavior of CSS functions, including successful output, inline theme resolution, optimization cases, and user-facing error messages.
  • packages/tailwindcss/src/compat/plugin-functions.ts — Implements the compatibility theme helper used by plugin and configuration APIs, merging CSS theme values with JavaScript configuration values while respecting default sources and opacity modifiers.
  • packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.ts — Rewrites legacy Tailwind directives and package imports into the current import-oriented CSS entry points, preserving layer order and optional prefixes.
  • packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.test.ts — Documents migration expectations for unchanged imports, prefix insertion, old tailwind.css imports, default directive collapse, and valid insertion locations.
  • packages/@tailwindcss-upgrade/src/codemods/css/migrate-variants-directive.ts — Converts the older variants directive into a utilities layer form so later migration steps can treat it as a custom utility registration path.

Directive Model

In current Tailwind CSS projects, the most important directive is usually @import. The installation guides show a minimal stylesheet that imports Tailwind, then the build step scans templates and emits generated CSS. The upgrade codemod reinforces that the v4 model prefers @import 'tailwindcss' over separate legacy @tailwind base, @tailwind components, and @tailwind utilities directives. When both base and utilities are present, the codemod inserts a single Tailwind import before the migrated directive block and removes the old at-rules. When only base or utilities are present, it uses more specific package imports and layer annotations so the resulting stylesheet remains semantically close to the original.

The migration code also handles older package import spellings. A stylesheet importing tailwindcss/tailwind.css is rewritten to import tailwindcss, and an optional prefix parameter can be appended to the new import. The tests show that an existing Tailwind import is not otherwise rewritten, but it can receive a prefix when the upgrade tool is configured with one. This distinction matters for real projects because an upgrade should avoid churn in already-current files while still applying a requested naming prefix consistently across the new entry point.

Sources: packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.ts, packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.test.ts

Layer order is another directive concern during migration. Tailwind has a conventional order for theme, base, components, and utilities. The migration codemod records the order of old base and utilities directives and can insert a top-level layer order declaration when the original stylesheet differed from the default sequence. That behavior is important because directives are not only syntactic markers; they influence cascade order. A migration that replaced syntax without preserving order could silently change which rules win in the final CSS, especially in projects that mixed framework layers with hand-authored rules.

The upgrade package removes legacy directives that no longer have an equivalent role, including components, screens, and variants in the old @tailwind form. It also unwraps an older responsive directive by replacing the at-rule with its child nodes when children exist, marking those children for formatting. Separately, the variants migration changes @variants into @layer utilities. The comment in the source explains the reason: old variants behavior effectively made nested selectors usable as utilities, so converting them into a utilities layer preserves the path toward later conversion into @utility rules.

Sources: packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.ts, packages/@tailwindcss-upgrade/src/codemods/css/migrate-variants-directive.ts

CSS Function Reference

The compiler registers four CSS function names in the supplied implementation: --alpha, --spacing, --theme, and theme. They are not arbitrary runtime CSS functions; the compiler walks CSS values, recognizes these names, and replaces them with ordinary CSS output or throws a build-time error. This means failures are discovered during compilation instead of in the browser. The tests intentionally assert exact error text for invalid calls, which is useful for users and integration authors because the same messages can be surfaced in CLI, PostCSS, Vite, or other build-tool environments.

The --alpha function accepts a color and an opacity segment separated by a slash. The implementation splits the argument around the slash, trims both sides, requires both a color and an alpha value, rejects additional arguments, and returns the result of Tailwind’s alpha-composition helper. Tests show a simple red color and fifty percent opacity compiling into an oklab color with an alpha channel. They also show the user-facing failures for no arguments, a missing alpha segment, and too many arguments. In practice, this function is best used when author CSS needs the same color opacity behavior as generated utilities.

The --spacing function multiplies a project spacing token by a caller-provided value. It requires exactly one argument and requires the --spacing theme variable to exist. When the multiplier is present as a normal theme variable, output can remain a calculation involving the variable; when the theme value is inline, the result can be fully resolved. The implementation also optimizes common zero and one cases. Zero returns plain zero, and one returns the multiplier directly, avoiding unnecessary calculations. Those optimizations are covered in tests for both regular and inline theme declarations, which makes the output behavior predictable for generated snapshots and downstream tooling.

Sources: packages/tailwindcss/src/css-functions.ts, packages/tailwindcss/src/css-functions.test.ts

The --theme function resolves CSS variables from the Tailwind theme. It only accepts paths that start with a CSS custom property name, and it supports an inline suffix to force direct value substitution. The implementation also automatically inlines values when the function appears inside an at-rule, because CSS variables cannot be used in every at-rule position, such as certain media query expressions. If resolution fails, fallback arguments can be joined and returned; without a fallback, the compiler throws an error that asks the user to check the variable name or provide a fallback value. This creates a clear contract for authoring token-dependent CSS.

Fallback handling for --theme is more nuanced than a simple default value. If there is no fallback, the resolved value is returned as-is. If the fallback is initial, the resolved value still wins. If the resolved value is initial, the joined fallback wins instead. The implementation also includes logic for injecting fallback values into referenced theme or variable expressions, so nested references can still degrade cleanly. This behavior is designed for theme variables, not for arbitrary configuration paths, which is why the function enforces the leading custom-property syntax before it attempts resolution.

Sources: packages/tailwindcss/src/css-functions.ts

Compatibility Theme Helper

The plain theme function appears in the CSS function registry as a legacy entry point, but plugin compatibility has a richer helper in the compatibility package. The createThemeFn implementation accepts a design system, a lazily supplied configuration theme, and a resolver. It parses a path, extracts a trailing opacity modifier after the final slash, converts the path into a key path, and reads both CSS theme values and JavaScript configuration values. The result is a merged view that lets older plugin and configuration code continue to ask for theme values while v4 projects move design tokens into CSS.

The precedence rules are deliberate. CSS theme values generally win over configuration values, but default-marked sources are treated carefully so user configuration can override built-in defaults. Object values are copied with a merge helper to avoid mutating the original configuration, then CSS values are overlaid. Tuple-like values receive special handling so base and extra metadata can each respect default flags. After resolution, the helper can apply an opacity modifier with the same alpha-composition utility used elsewhere. That makes the compatibility helper part of the bridge between older JavaScript configuration APIs and the CSS-first theme model.

Sources: packages/tailwindcss/src/compat/plugin-functions.ts

Examples and Migration Flow

A typical current input file can be very small:

@import 'tailwindcss';

From there, project authors add @theme blocks for custom tokens, @source entries for scan paths that automatic detection misses, @utility blocks for local utilities, and variant directives when they want Tailwind variant behavior inside authored CSS. When upgrading an older project, the safer flow is to run the upgrade tool rather than manually deleting directives. The codemods know where imports can be inserted after charset declarations, layer declarations, and license headers, and the tests verify that the new import is placed in a valid location while leaving unrelated CSS rules in place.

A legacy input that used the old layer directives is normalized toward the import form:

@tailwind base;
@tailwind components;
@tailwind utilities;

The expected modern result is:

@import 'tailwindcss';

If a project used only part of Tailwind, the migration remains more specific. A utilities-only directive becomes an import of the utilities entry point with a utilities layer annotation. A base-only directive becomes theme and preflight imports with theme and base layer annotations. This preserves intent for projects that intentionally opted into only a subset of framework CSS. For maintainers, that means directive migration should be reviewed as a cascade-preservation change, not just a syntactic conversion.

Sources: packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.ts, packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.test.ts

Testing Signals and Next Steps

The tests provide strong behavioral signals for both author-facing functions and migration outcomes. Function tests use compile helpers and snapshots so the asserted contract is the final CSS or exact thrown error, not an internal data structure. Migration tests process input through PostCSS plugins, sorting, and formatting helpers before comparing output. That test style is useful because users experience these features through compiled CSS files, CLI output, and codemod results. When changing a directive or function, update tests around the produced CSS and the diagnostic text, because integrations depend on those stable surfaces.

For deeper reading, continue with the Theme page for @theme variables, Adding Custom Styles for @utility and custom variants, Detecting Classes in Source Files for @source, and Upgrade Guide for codemod sequencing. If you are integrating Tailwind into a build tool, pair this page with the tailwindcss Package API and Node API pages, because functions are evaluated during compilation and directive migration is only one part of the broader build pipeline.