Get started

Shadcn design tokens: map CSS variables, presets, and component ownership

Shadcn gives you configurable CSS variables and component code that your project can edit. But that freedom doesn't tell you which layer owns a design decision or whether a preset changed only what you intended. A durable token workflow adds an authority map, named consumers, protected surfaces, and evidence from the rendered interface.

Updated August 3, 2026

Start with an authority chain

A token value can appear in several places, but that doesn't make every location authoritative. A design brief may approve the role. Project configuration may encode it. A preset or registry item may deliver files, while a component consumes the resulting variable and a local selector may override it. Finally, the browser renders an outcome. Each layer answers a different question.

Pick one correction owner for each layer. When the rendered button is wrong, this prevents the familiar loop of patching the button, then the theme, then the button again. Work from the approved decision toward the rendered surface, and stop at the first layer that disagrees with its input.

What it establishesCorrection owner
Approved design decisionThe intended semantic role, mode behavior, and permitted exceptionsDesign-system owner
Project token configurationHow the project represents the approved roleProject theme owner
Preset or registry deliveryWhich files and settings arrived from a particular source versionDelivery or integration owner
Editable component codeWhich role a component and its variants actually consumeComponent owner
Local exceptionA named, deliberate departure with limited scopeFeature or surface owner
Rendered evidenceWhat representative consumers visibly did in the tested buildReviewer or acceptance owner
The authority map separates what each layer can establish from where its correction belongs.

The source of truth isn't one universal file

The authoritative location depends on the decision. Project configuration can own a token mapping without owning the product rationale. Component code can own variant behavior without owning the semantic color choice. Rendered output is evidence of behavior, not the source of the decision.

Map one semantic role before changing the system

Start with one role whose propagation you can observe. A primary action role is useful because it usually reaches several states and often has different expectations in light and dark modes. Don't invent a namespace or configuration key from a generic tutorial. Inspect the project's configuration and component source, then record the identifier it actually uses.

The record should separate the semantic job from its current value. Values can change between modes or releases while the job stays stable. Name consumers narrowly enough to inspect them. "Buttons" is too broad when a change should reach the primary variant but leave secondary, destructive, or disabled treatments alone.

Token change record

Source version: [kit, preset, or decision version]
Semantic role: [role name used by the project]
Purpose: [the job this role performs]
Light expectation: [approved value or visual expectation]
Dark expectation: [approved value or visual expectation]
Project mapping: [actual project variable or theme destination]
Delivered artifact: [file or configuration surface]
Named consumers:
  - [component, variant, state]
  - [component, variant, state]
Allowed local exceptions:
  - [surface, reason, owner]
Protected surfaces:
  - [surface that must remain unchanged]
Evidence status: expected | observed | unresolved
Correction owner: [decision, mapping, delivery, component, exception, review]
Disposition: accept | revise | block
Notes: [observations and unresolved limits]
Copyable source-to-consumer record. Replace every bracketed field with evidence from the consuming project.

Component specimen · Button

Ambient Sage

Live render

The button primitive in Ambient Sage, across 4 states.

Default

Hover

Focus

Disabled

A component specimen makes the consumer states concrete. It doesn't prove that another project maps or renders those states identically.

Map the dark expectation separately. Don't assume automatic inversion. Then list any exceptions. An exception isn't simply whatever looks different today; it needs a reason, a defined scope, and an owner. Without those, it's an unresolved conflict.

Treat preset commands as consequential changes

The shadcn package reference gives each command a different job. Create starts a new project through a custom design-system flow. Init installs dependencies, adds the utility used by components, and configures Tailwind CSS and CSS variables. Apply changes an existing project by overwriting preset configuration, reinstalling detected UI components, and updating fonts and CSS variables. Add brings in a selected component and its required dependencies.

Apply therefore has the widest documented replacement surface. You don't need to avoid it automatically, but you do need to bound the change first. A successful command means the files were processed. It says nothing about preserved customizations, correct token consumption, or rendered acceptance.

Protect local work before applying a preset

Before the operation, record the current configuration, CSS-variable definitions, font setup, installed components, and known component edits. Mark every local change that must survive. If you can't identify the protected surfaces, you can't distinguish replacement from an intended update.

Preset preflight

  • Identify the exact operation and source version you intend to use.
  • Inventory project configuration and the locations that define CSS variables.
  • Record the current font families, loading setup, and role mappings.
  • List detected or installed components that the operation may revisit.
  • Name component-local edits, selectors, and variants that must survive.
  • Write the expected changes by file, role, component, state, and mode.
  • Keep a separate protected-surface list for everything expected to remain unchanged.
  • Choose representative consumers and unrelated surfaces for the post-operation check.
  • Set the decision rule before running the operation: accept, revise, or block.

Inspect a complete kit before mapping it

Use a public kit to see the boundary between an upstream design artifact and the project-owned evidence still required after delivery.

Keep a source-to-consumer register

A preflight protects the operation. A source-to-consumer register keeps the system maintainable afterward. Give each semantic role and consumer relationship its own row. If one role reaches a primary button, link, focus ring, and selected navigation item, record them as separate consumers because each can fail independently.

RecordWhy it matters
Source versionThe approved kit, preset, or decision versionSeparates current evidence from stale artifacts
Role and modesSemantic job plus light and dark expectationsPrevents a single-mode pass from hiding a mismatch
Delivered artifactThe file or configuration surface received by the projectLocates delivery failures
Consumers and statesNamed components, variants, and interaction statesMakes propagation falsifiable
Local overridesSelector or component, reason, scope, and ownerDistinguishes exceptions from drift
Protected surfacesUnrelated behavior or styling that must not changeDetects collateral replacement
EvidenceExpected, observed, or unresolved, with notesStops assumptions from being promoted to results
DispositionAccept, revise, or blockRecords the actual decision
A compact register for reviewing propagation without making the rendered page the source of truth.

Avoid vague evidence such as "looks good." Write an observation a second reviewer can challenge: the primary button changed in both modes; its focus treatment was present; the secondary variant kept its prior treatment; the unrelated input error state didn't change. Screenshots or automated checks can support those observations, but the register should explain what each artifact demonstrates.

Run one controlled token change

A controlled change answers one narrow question: does an approved role reach its intended consumers without changing protected surfaces? It isn't a complete accessibility review, regression suite, or proof that every component uses the system. Keep that broader evidence separate.

  1. 1

    Freeze the prior state

    Record the source version, project mapping, current light and dark results, component-local overrides, and protected surfaces. Keep enough evidence to compare behavior after the operation.

  2. 2

    State the expected propagation

    Name the intended consumers and states. Also name what must remain unchanged. Put those expectations in the record before observing the result.

  3. 3

    Perform the bounded project operation

    Use the operation selected during preflight. Record the files and configuration surfaces it reports changing. Don't treat command completion as acceptance.

  4. 4

    Inspect the implementation path

    Confirm that the expected project mapping exists and that each named component consumes it. Look for component-local values or selectors that intercept the role.

  5. 5

    Observe representative states and modes

    Render the named consumers in light and dark modes. Include states that expose different paths, such as default, hover, focus, disabled, and error where supported.

  6. 6

    Check protected and unrelated surfaces

    Inspect the local customizations and unrelated consumers listed during preflight. Record whether they remained unchanged instead of relying on memory.

  7. 7

    Choose a disposition

    Accept only if intended consumers changed, protected surfaces did not, observations are recorded, and any unresolved evidence is non-blocking. Otherwise, revise the owning layer or block the change.

Keep expectations and observations in separate fields

When one sentence says both what should happen and what did happen, reviewers can mistake an assumption for evidence. Record the expectation first. Then add a dated observation from the consuming project.

Route failures to the layer that owns them

When the result is wrong, start at the top of the authority chain. The first mismatch owns the correction. A local patch may hide the symptom while every other consumer remains wrong.

  • Wrong upstream decision: the approved role or mode expectation itself is unsuitable. Return it to the design-system owner.
  • Stale project mapping: the approved role is correct, but the project's variable or theme destination still carries an older value. Correct the project token layer.
  • Preset replacement: the operation replaced configuration, fonts, CSS variables, or component code that the preflight marked as protected. Restore them through the project's normal reviewed change process and revise the integration plan.
  • Component-local override: the shared mapping is correct, but a selector, literal, variant, or local rule intercepts it. Correct the component layer or document a deliberate exception.
  • Missing consumer: the component never references the intended role. Add or correct the source-to-consumer mapping.
  • Absent observation: the code path appears correct, but no representative render was inspected. Leave the evidence unresolved instead of accepting by inference.

This order matters because rendered similarity can mislead. A component may happen to match the approved color through a local literal while bypassing the semantic role entirely. It looks correct until the next mode or preset change. The register catches that structural defect before it becomes drift.

Use Ambient Sage as a bounded intake example

Ambient Sage is a public Identity Forge kit. Its published page documents a warm sage surface system, a vivid yellow accent used sparingly, Plus Jakarta Sans for product typography, JetBrains Mono for technical strings, and component-scale treatments. Identity Forge also publishes a shadcn delivery route for the kit.

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

Type scaleHeading, body, and mono in the kit's fonts

Typography

Ambient Sage

Scale: compact-product

Density: balanced

Heading · Plus Jakarta Sans · 1.875rem

Ship beautiful product faster

Subheading · Plus Jakarta Sans · 1.375rem

A warm-sage neutral-surface mobile kit with a single vivid yellow accent, flat tonal cards, and oversized display numerals.

Body · Plus Jakarta Sans · 1rem

Ambient Sage uses a near-white warm-sage canvas (#f3f4ef) with card panels distinguished only by a tonal shift to #e5e6e0, never by shadows or borders. A single vivid yellow (#fee951) is the only saturated color and appears sparingly at component scale as orbs, button fills, and focus rings. Primary data values render as oversized bold hero numerals with a small superscript unit. Typography is a friendly rounded geometric (Plus Jakarta Sans) with no uppercase and no tight tracking, while JetBrains Mono is reserved for hex codes and technical strings. Generous rounding and luminance-only contrast give the whole system a calm, minimal feel.

Mono · JetBrains Mono · 0.8125rem

npx shadcn add ambientsage.json

Aa

Plus Jakarta Sans · Heading

400500600700

Aa

Plus Jakarta Sans · Body

400500600700

ABCDEFGHIJKLM NOPQRSTUVWXYZ

abcdefghijklmnopqrstuvwxyz

0123456789 & @ # % →

Radius & spacingCorner radius, elevation, and spacing steps

Tokens

Ambient Sage primitives

density: balanced

Radius scale

sm · 0.375rem
md · 0.75rem
lg · 1.25rem
xl · 1.75rem

Component radius

button
card
input

Elevation

level 1
level 2
level 3
level 4

Spacing · base 4px

1x
2x
3x
4x
6x
8x
Ambient Sage supplies a concrete upstream token and typography artifact. Mapping and acceptance in the consuming project remain separate work.

That evidence supports an intake record, not a compatibility verdict. You can record that the public kit and a shadcn artifact exist. This dossier doesn't establish that a particular project accepted the artifact, preserved its customizations, mapped every token correctly, or achieved rendered parity.

Ambient Sage intake record

Upstream artifact: Ambient Sage public design kit
Artifact availability: observed
Shadcn delivery route: observed
Upstream colors and typography: documented
Project token mapping: unresolved
Installed component behavior: unresolved
Local overrides: unresolved
Light-mode acceptance: unresolved
Dark-mode acceptance: unresolved
Protected-surface check: unresolved
Disposition: block acceptance until project-owned evidence is recorded
A source-side intake record that doesn't promote artifact availability into implementation proof.

Accept only what the evidence shows

Accept the change when the intended consumers changed in every required mode and state, protected surfaces stayed intact, local exceptions remain deliberate and owned, and the observations are recorded. Revise when the intended decision is sound but the mapping, delivery, or component layer is wrong. Block when a protected surface changed, a required consumer failed, or the evidence needed for the decision is still missing.

For your next token change, choose one semantic role and complete the register before touching the preset. If you can't name its consumers, exceptions, protected surfaces, and correction owner, the implementation isn't ready for a consequential operation.

Sources

  • Introduction - shadcn/ui: Shadcn describes itself as open code and a code distribution platform, leaving the installed component source under the consuming project's control.
  • The Foundation for your Design System - shadcn/ui: The official site presents shadcn as a customizable foundation whose components can be extended and made project-specific.
  • shadcn - npm: The package reference says init configures Tailwind CSS and CSS variables, apply overwrites preset configuration and updates detected components, fonts, and CSS variables, and add installs a component with its dependencies.
  • GitHub - shadcn-ui/ui: The public repository is the authoritative upstream source surface for shadcn code, history, issues, releases, and contributions.
  • Ambient Sage Design Kit: Ambient Sage is a public kit with documented colors, typography, component-scale treatments, and a shadcn installation route.
  • Visual kits for agents that need to design well: Identity Forge's public kit gallery describes its kits as systems containing fonts, colors, tokens, and component rules.