Get started

Tailwind CSS v4 design tokens: map external semantic roles to @theme

Treat a Tailwind CSS v4 token handoff as a traceable chain. The upstream system owns semantic intent and mode values; application CSS loads the runtime properties. Tailwind maps selected roles into namespaces that generate utilities, and components consume those utilities. Accept the handoff only after the built output and representative consumers match the recorded expectations.

Updated September 9, 2026

Keep five responsibilities separate

An external token export, a runtime custom property, a Tailwind theme mapping, a generated utility, and a consuming component form a connected chain. They aren't interchangeable sources of truth. When a button looks wrong, changing the last declaration visible in DevTools may hide the mismatch while leaving the ownership error intact.

ResponsibilityOwned artifactAcceptance question
Upstream systemSemantic intent and mode-specific valuesVersioned token source or exportIs the intended role current, approved, and present in every required mode?
Application loadingImport order, selector strategy, and runtime availabilityServed external custom propertiesDid the intended source version and selector-scoped values reach the inspected artifact?
Tailwind mappingNamespace aliases and utility vocabulary@theme declarations and generated rulesDoes each mapping produce its expected declaration and utility?
ConsumerUse of shared roles and bounded exceptionsClasses, component CSS, and computed stylesDoes each named surface resolve the expected value without an undeclared override?
Acceptance ownerEvidence review and dispositionCompleted acceptance recordDoes the evidence support accept, revise, or block?
The handoff chain and the question owned by each layer

Do not promote an alias into the source

A Tailwind name can give the application a stable vocabulary without owning the design decision. When the external source changes, regenerate or update the mapping. Don't copy the same raw value into several @theme declarations and treat those copies as authoritative.

Use only the Tailwind v4 mechanics the handoff needs

Tailwind v4 uses CSS-first configuration. Theme-variable namespaces connect declarations to utility families. A color variable in the appropriate namespace can supply background, text, border, and related color utilities. Tailwind also exposes theme variables as CSS custom properties, depending on how they are declared and emitted.

When a Tailwind theme variable points to an external custom property, Tailwind documents the inline option for resolving that reference in the generated utility. The generated rule then refers directly to the external property. That property remains a runtime dependency, so it must be present in served CSS wherever the utility can apply.

@import "tailwindcss";

:root {
  --source-action-background: oklch(0.62 0.17 252);
}

@theme inline {
  --color-action: var(--source-action-background);
}
Illustrative Tailwind CSS v4 mapping. The external and Tailwind identifiers are deliberately different.
<button class="bg-action">Save changes</button>
A separate HTML consumer keeps both examples valid and copyable.

For this fixture, expect a generated declaration equivalent to .bg-action { background-color: var(--source-action-background); }. The raw color stays in one external property. The Tailwind mapping supplies the vocabulary, while the generated rule preserves the runtime dependency.

Static controls emission, not authority

Use @theme static when the project contract requires every declared theme variable in generated CSS, including variables without detected utility usage. Static emission doesn't make Tailwind the owner of an upstream design decision.

@theme static {
  --radius-control: 0.5rem;
}
An illustrative Tailwind-owned token that the project requires in every generated artifact.

Separate contracts, expectations, observations, and decisions

The phrase "Tailwind supports this" can mean four different things. Keep the official framework contract, your project expectation, an environment-specific observation, and the acceptance decision in separate fields. A failure in one toolchain isn't a universal framework rule, and a clean build doesn't prove that the application served the intended CSS.

Official contractProject expectationEnvironment observationAcceptance decision
CSS importsTailwind v4 advertises built-in import supportThe application should load the selected upstream token CSSClearly Design reports a missing served import in particular Nuxt and Next.js setupsInspect this project's served output and revise loading if required properties are absent
Inline mapping@theme inline can place an external-variable reference in a generated utilitybg-action should reference --source-action-backgroundRecord the generated declaration and the runtime property's selector-scoped presenceBlock if the rule or its runtime dependency is absent
Runtime valuesTheme variables and referenced custom properties participate in runtime CSS resolutionThe action role should resolve to the recorded mode valueRecord the computed background color on every named consumer in both modesAccept only when observations match expectations
Static emission@theme static requests generation of all declared theme variablesA named non-class consumer may require an otherwise unused variableRecord whether the required variable exists in the inspected artifactRevise if the contract requires the variable and the artifact omits it
Diagnostic evidence matrix

Treat the Clearly Design report as diagnostic evidence tied to a specific approach, date, and environment: its build appeared healthy while the served output lacked the expected token CSS. Tailwind's official release material separately documents built-in import support. Neither fact determines what your framework served. Inspect the exact artifact used by your application.

Choose the narrowest layer that owns the decision

Put it here whenDo not put it here whenEvidence to record
Upstream sourceThe value expresses portable design intent or a shared semantic roleIt exists only to satisfy one framework or local componentSource version, semantic role, modes, and decision owner
Global runtime CSSA custom property is referenced at runtime by an inline-generated utility, non-Tailwind CSS, inline styles, or another consumerA verified build step has materialized the value and the served output contains no runtime reference to the external propertyLoad path, required selector scope, import order, runtime dependency name, and served-artifact presence
@theme mappingA Tailwind namespace should expose the role through a stable utility vocabularyThe behavior is contextual and has no stable utility meaningExternal identifier, namespace, expected generated declaration, expected utility, and mapping owner
Project custom CSSA documented project behavior does not fit ordinary theme or utility constraintsA standard theme variable or utility already expresses it clearlySelector scope, consumers, reason, protected surfaces, and owner
Component or layoutA named consumer owns independent behavior or a bounded exceptionSeveral unrelated consumers should change togetherComponent contract, states, scope, owner, and exit condition
Placement criteria for values and behavior

Let the required change scope determine placement. If an action background must change across buttons, selected navigation, and other action surfaces together, keep the meaning semantic. If only one promotional card may differ, record a card-level exception instead of redefining the global action role.

Copy the source-to-consumer worksheet

Create one record for every disputed or release-critical mapping. The record below includes the fields needed to trace both build output and runtime resolution. Its identifiers, values, consumers, and owners are illustrative project fixtures.

record_id: action-background-001
source_version: demo-kit@2026-09-09
semantic_role: action background
mode_values:
  light:
    upstream_identifier: --source-action-background
    expected_value: oklch(0.62 0.17 252)
  dark:
    upstream_identifier: --source-action-background
    expected_value: oklch(0.74 0.13 252)
tailwind_namespace: --color-*
theme_mapping: --color-action -> var(--source-action-background)
mapping_directive: "@theme inline"
mapping_owner: frontend platform
runtime_dependency: --source-action-background
runtime_scope_expectation:
  light: ':root and [data-theme="light"]'
  dark: '[data-theme="dark"]'
expected_utility: bg-action
expected_generated_declaration: 'background-color: var(--source-action-background)'
utility_evidence:
  expected_present: true
  observed_present: pending inspection
named_consumers:
  - primary save button
  - selected navigation item
consumer_observations:
  light:
    expected: recorded light value
    observed: pending
  dark:
    expected: recorded dark value
    observed: pending
evidence_status: pending inspection
protected_surfaces:
  destructive_button:
    light: { expected: recorded baseline, observed: pending }
    dark: { expected: recorded baseline, observed: pending }
  informational_badge:
    light: { expected: recorded baseline, observed: pending }
    dark: { expected: recorded baseline, observed: pending }
declared_exceptions: []
correction_owner: design systems engineer
final_disposition: pending
Replace every fixture name, value, version, and owner with facts from the consuming project.

Use expected, observed, contradicted, unresolved, or accepted for evidence status. "The build passed" isn't a mapping status. It doesn't identify the source version, prove the generated rule exists, establish that the runtime dependency loaded, or show what a consumer computed.

Map light and dark values behind one stable utility

The consuming application owns its mode selectors. In this bounded fixture, a data attribute changes the value behind one stable external identifier. Tailwind maps that identifier once, so components keep using bg-action in either mode.

:root,
[data-theme="light"] {
  --source-action-background: oklch(0.62 0.17 252);
}

[data-theme="dark"] {
  --source-action-background: oklch(0.74 0.13 252);
}

@theme inline {
  --color-action: var(--source-action-background);
}
Illustrative application CSS. Both selector scopes provide the runtime property referenced by the generated utility.
<button class="bg-action">Save changes</button>
The component keeps one stable semantic utility across both modes.

First, inspect the generated bg-action rule. It should contain the recorded reference to --source-action-background. Next, activate the explicit light selector and record the button's computed background-color. Repeat under the explicit dark selector. Compare both observations with the worksheet instead of deciding that the colors merely look plausible.

The selector does not define theme policy

This fixture covers only explicit selector resolution. The application still owns operating-system preference, default selection, persistence, initialization flash prevention, nested theme behavior, selector precedence, import order, and overrides.

Token specimen · real values

Ambient Sage

Live render

Ambient Sage's actual tokens — the same values its exports use.

Color tokensSemantic roles with HEX / HSL / CMYK

Color tokens

Ambient Sage
light · HEX · HSL · CMYK

Core

#F3F4EF

background

H 72 · C0, 0, 2, 4

#1A1C17

foreground

H 84 · C7, 0, 18, 89

#E5E6E0

card

H 70 · C0, 0, 3, 10

#ECEEE8

muted

H 80 · C1, 0, 3, 7

#D8D9D2

border

H 68.57 · C0, 0, 3, 15

Brand

#FEE951

primary

H 52.72 · C0, 8, 68, 0

#1A1C17

primary-fg

H 84 · C7, 0, 18, 89

#E5E6E0

secondary

H 70 · C0, 0, 3, 10

#F7E464

accent

H 52.24 · C0, 8, 60, 3

#FEE951

ring

H 52.72 · C0, 8, 68, 0

Semantic

#C0392B

destructive

H 5.64 · C0, 70, 78, 25

#FFFFFF

destructive-fg

H 0 · C0, 0, 0, 0

#2D7238

success

H 129.57 · C61, 0, 51, 55

#C97D12

warning

H 35.08 · C0, 38, 91, 21

#545651

muted-fg

H 84 · C2, 0, 6, 66

Charts

#FEE951

chart-1

H 52.72 · C0, 8, 68, 0

#4A8FD4

chart-2

H 210 · C65, 33, 0, 17

#6BBF8A

chart-3

H 142.14 · C44, 0, 28, 25

#E07498

chart-4

H 340 · C0, 48, 32, 12

#E8A24B

chart-5

H 33.25 · C0, 30, 68, 9

Ambient Sage is a bounded upstream example with published semantic light and dark roles and a Tailwind v4 export. The consuming-project mappings remain illustrative.

The public Ambient Sage page establishes a real upstream artifact with semantic light and dark roles and a Tailwind CSS v4 export. It doesn't establish your application's selector strategy, file order, aliases, consumers, overrides, or observed results. Record those in the consuming project's worksheet.

Inspect a real semantic kit before mapping it

Use Ambient Sage to examine a published token system, then record the exact roles and runtime dependencies your Tailwind application will consume.

Classify responsive variation before implementing it

Responsive values don't have one universal home. The unresolved Tailwind discussion about responsive design tokens captures the tension: repeated breakpoint classes burden consumers, while a global responsive property can hide context and change more surfaces than intended. Before choosing syntax, classify the decision by meaning, scope, consumers, and owner.

Placement signalRequired scope recordFailure condition
Semantic intentThe meaning should change coherently for every legitimate consumer under the same conditionCondition, all consumers, source owner, and protected rolesOne consumer needs independent behavior, showing that the global meaning is too broad
Layout policyA page region or layout system owns the adaptationContainer or viewport condition, affected regions, layout owner, and exceptionsThe rule leaks into a component used outside that layout
Breakpoint utilityConsumers may choose the variation independently and should show it at each useBreakpoint, named consumers, permitted combinations, and code ownerRequired consumers repeat inconsistent variants or omit a required state
Component contractOne component owns responsive behavior across supported placementsComponent, variants, conditions, states, and component ownerDetached instances or local overrides contradict the shared contract
Responsive placement record

A responsive spacing role applied identically to every page section may belong upstream or in layout policy. A card that changes composition inside a narrow container may own that behavior. A one-off marketing arrangement may be clearest as explicit breakpoint utilities. Recording the scope makes the decision reviewable; the category name alone doesn't settle the architecture.

Run a reproducible acceptance record

Freeze expectations before inspecting anything. Use content that exposes ordinary and edge behavior: a primary save button, the same action with a long label, a selected navigation item, a destructive action, and an informational badge. Inspect both explicit modes in the same immutable built artifact.

  1. 1

    Freeze the source and mapping

    Record the exact upstream version, mode values, external identifiers, runtime selector scopes, @theme declarations, expected generated rules, expected utilities, consumers, owners, and exceptions.

  2. 2

    Inspect the built and served artifact

    Confirm that the intended source version, required runtime properties, expected generated declarations, and required static variables are present in the asset the application actually serves.

  3. 3

    Record utility evidence

    For each required utility, record whether the expected declaration is present and whether it references the expected runtime dependency.

  4. 4

    Inspect representative consumers

    In each mode, record the computed value for the primary save button and selected navigation item. Compare each observation with its recorded expectation.

  5. 5

    Inspect protected surfaces

    Record expected and observed values for the destructive button and informational badge in both modes. Treat an unexplained difference as a failure.

  6. 6

    Route every mismatch

    Send a wrong source value upstream, a missing runtime property to application loading, a missing generated rule to the Tailwind mapping, and a local conflict to the component or exception owner.

  7. 7

    Choose the disposition

    Accept only when all required evidence matches. Revise a bounded mismatch with a known correction owner. Block when a required artifact, mode, observation, exception, or owner remains unresolved.

acceptance_id: tailwind-token-handoff-001
execution_context:
  tailwind_version: record exact installed v4 version
  application_build: record immutable build identifier
  source_version: record exact upstream artifact version
built_artifact:
  location: record inspected served CSS asset
  runtime_dependency: --source-action-background
  runtime_dependency_presence:
    light_scope: { expected: present, observed: pending }
    dark_scope: { expected: present, observed: pending }
utility_evidence:
  bg-action:
    expected_present: true
    observed_present: pending
    expected_declaration: 'background-color: var(--source-action-background)'
    observed_declaration: pending
mode_matrix:
  light:
    selector: '[data-theme="light"]'
    primary_save_button:
      expected: recorded light action value
      observed: pending computed background-color
    selected_navigation_item:
      expected: recorded light action value
      observed: pending computed background-color
  dark:
    selector: '[data-theme="dark"]'
    primary_save_button:
      expected: recorded dark action value
      observed: pending computed background-color
    selected_navigation_item:
      expected: recorded dark action value
      observed: pending computed background-color
protected_surfaces:
  destructive_button:
    light: { expected: recorded light baseline, observed: pending }
    dark: { expected: recorded dark baseline, observed: pending }
  informational_badge:
    light: { expected: recorded light baseline, observed: pending }
    dark: { expected: recorded dark baseline, observed: pending }
representative_content:
  - Save changes
  - A deliberately long primary-action label
declared_exceptions:
  - record selector, reason, scope, owner, and removal condition
unresolved_behavior:
  - record responsive classification or selector-precedence questions
correction_owners:
  upstream_value: design system owner
  application_loading: frontend platform owner
  tailwind_mapping: frontend platform owner
  component_override: component owner
failure_conditions:
  - wrong or unknown source version
  - runtime dependency absent from a required selector scope
  - expected utility or generated declaration absent
  - generated declaration references the wrong dependency
  - either mode resolves to the wrong recorded value
  - a protected surface differs from its expected baseline without an approved exception
  - an exception or unresolved mismatch has no owner
disposition: block until every required observation is recorded
This template defines pending evidence and failure conditions. It doesn't claim that a project test or compatibility benchmark has already run.

Let the record determine the final decision. Accept when the exact source version reaches the served artifact, every runtime dependency exists in its required scope, the expected utilities and declarations are present, both modes produce the recorded consumer values, and protected surfaces match their baselines. Revise a mismatch when its layer and owner are known. Block when the evidence or responsibility remains unclear.

Sources