The difference is change scope, not specificity
Semantic tokens express reusable intent. Names such as color.action.primary, color.text.muted, or space.control.inline describe a decision that several consumers may share. A button, link, tab, and selected navigation item can all consume the same action role when they are meant to move together.
Component tokens express a contract owned by a component or component family. A token such as button.background.primary.hover says the button's primary hover background may need to change without redefining primary action behavior everywhere else. That independence is what matters. The extra alias implements it.
Primitive, core, semantic, decision, theme, contextual, and component are conventions. There is no universal standard behind them. Calcite documents a core-to-semantic-to-component hierarchy, while Figma teaches several possible relationships and acknowledges that a component layer may be unnecessary. The NYS Design System documents primitive, semantic, and theme layers without making component tokens a required tier. Copying one diagram into another organization doesn't settle the boundary.
Don't infer a contract from the token name
button-primary-background may still be a redundant restatement of a shared primary action role. Conversely, a broadly named semantic token may hide behavior that only one component needs. Inspect consumers and expected propagation before judging the abstraction.
| Semantic token | Component token | |
|---|---|---|
| Meaning | Reusable intent shared across suitable consumers | A property or state governed by a component contract |
| Reuse radius | Several components or surfaces with the same meaning | One component or a deliberately defined component family |
| Change authority | Foundation, theme, or system-level owner | Component owner or maintainers of its public customization contract |
| States | Shared state intent such as focus, danger, or disabled | A component's state-specific treatment when it must diverge |
| Modes | Usually remaps shared intent across light, dark, or theme modes | May reference mode-aware semantics or define a documented component exception |
| Override surface | Broad by design | Narrow by design |
| Expected propagation | Every named consumer of that intent should change | Only the named component consumers should change |
| Maintenance cost | Fewer aliases, but a poor abstraction can create excessive blast radius | More mappings and documentation, justified only by independent behavior |
Ask whether the component owns an independent decision
Start with one disputed visual decision, not a plan to create a complete component-token tier. Suppose a primary button currently uses the shared color.action.primary role for its default background. Someone proposes button.background.primary. Before adding it, write down what should change during the next meaningful design revision.
- Named consumers: which button variants and product surfaces use the proposed contract?
- Independent behavior: what plausible component change should not alter links, tabs, selected navigation, or other action surfaces?
- Modes: should the alias resolve through the same semantic role in light and dark, or is mode-specific divergence actually required?
- States: are default, hover, focus, disabled, and destructive treatments governed by shared intent or by the button contract?
- Protected surfaces: which unrelated consumers must remain unchanged when the component contract changes?
- Owner: who decides whether a mismatch is fixed in the semantic role, the component alias, the implementation, or an exception?
A future possibility isn't enough by itself. Most components could diverge someday. The useful threshold is a present, named need or a supported customization contract. Without one, every extra alias expands the graph that contributors must trace, review, migrate, and keep consistent across modes.
Use the smallest boundary that matches the authority
If several form controls share the same independently governed treatment, a family-level contract may be more accurate than separate input, select, checkbox, and radio aliases. The boundary should follow the decision owner and expected propagation, not the number of component files.
Use a decision matrix with five possible outcomes
The choice isn't limited to semantic or component. A disputed token may reveal a bad semantic abstraction, duplicated component aliases, or missing evidence. Choose the disposition that fixes the actual boundary.
| Disposition | Use when | Required evidence | Main risk | |
|---|---|---|---|---|
| Keep semantic | Keep semantic | All named consumers should continue to move together | Shared meaning, aligned modes and states, and no legitimate protected consumer | A later component exception may require a deliberate split |
| Split semantic | Split semantic | One semantic role combines meanings that only happen to share a value today | Two reusable intents with different expected consumers or future changes | Creating narrowly named semantics that merely disguise component scope |
| Introduce component | Introduce component | A component property has independent behavior or a supported override contract | Named consumers, protected surfaces, owner, modes, states, and a passing propagation check | Alias proliferation and hidden mode divergence |
| Merge component aliases | Merge component aliases | Existing component tokens always move together and have no separate owners or contracts | Dependency map plus observed shared propagation | Removing a distinction that a supported consumer still relies on |
| Block pending evidence | Block pending evidence | Expected consumers, protected surfaces, ownership, or mode behavior are unknown | An unresolved field and a named person responsible for resolving it | Treating delay as a silent permanent decision |
An overloaded semantic role is easy to miss. Imagine color.action.primary drives both primary controls and selected navigation, but navigation now needs a quieter color across several navigation components. The answer may be a reusable color.navigation.selected role, rather than a button token or a one-off override. The new boundary remains semantic because the intent is still shared beyond one component.
Inspect a real semantic source before designing the component layer
Browse published kits to see which upstream roles and modes a consuming project can map. Then create component aliases only where your application has evidence for an independent contract.
Trace the alias path before adding another hop
An alias path makes the proposed contract reviewable. The following names are illustrative project conventions, not Identity Forge output or a universal DTCG structure.
{
"color": {
"blue": {
"600": {
"$type": "color",
"$value": "#2457D6"
}
},
"action": {
"primary": {
"$type": "color",
"$value": "{color.blue.600}"
}
}
},
"button": {
"background": {
"primary": {
"$type": "color",
"$value": "{color.action.primary}"
}
}
}
}If button.background.primary always equals color.action.primary, has no independent owner, exposes no supported override, and should change whenever the semantic role changes, it adds another name but no separate decision. Keep the button mapped directly to the semantic token.
The component alias earns its place when the component contract can change independently. For example, a product may decide that primary buttons need a distinct hover treatment while text links and selected navigation continue using the shared action role. The component token can still reference a semantic value by default. Its purpose is to provide a narrow override point with documented ownership, even when there is no visual difference on day one.
A component token doesn't need a unique value
Two aliases may resolve to the same value today and still represent different contracts. Keep them separate only if independent ownership or expected future changes are concrete and documented. Coincidental equality proves neither that they should merge nor that they should remain separate.
Run a controlled two-change propagation test
A diagram predicts reach. A controlled change shows where the current implementation actually reaches. Record the expectation before editing, so a surprising result can't be reinterpreted as success afterward. Run the test in both relevant modes and representative states. Use reversible test values that make propagation easy to see, then restore the accepted values through your normal workflow.
- 1
Freeze the boundary under review
Name the source decision, current alias path, modes, states, intended consumers, protected surfaces, owner, and proposed disposition. Mark unknown fields as unresolved rather than guessing.
- 2
Write the expected result for the semantic change
Choose one shared semantic role. List every representative consumer that should change and every surface outside that intent that should not. Do this before changing a value.
- 3
Apply one temporary semantic change
Change the semantic role at its authoritative source, regenerate or remap through the project's normal path, and inspect the named consumers in each relevant mode and state. Record observations separately from expectations.
- 4
Write the expected result for the component change
If a component boundary is still justified, state which component consumers should change and which semantic peers are protected. Include any supported downstream override surface.
- 5
Apply one temporary component change
Change only the proposed component contract. Verify that its named consumers move and that links, navigation, other controls, and other protected surfaces do not. Record missing, extra, and mode-specific changes.
- 6
Choose a disposition from the evidence
Keep semantic, split semantic, introduce component, merge redundant component aliases, or block pending evidence. Restore accepted values and retain the observations with the decision record.
A semantic test fails when an intended consumer doesn't move, an unrelated consumer changes, or light and dark modes follow different undocumented paths. A component test fails when the component doesn't respond, the change leaks into protected surfaces, or a local override masks the alias. These failures don't automatically mean another token is needed. First, locate the layer that owns the mismatch.
Don't promote an expectation into evidence
If the test hasn't been run, write expected, illustrative, or unresolved. A proposed alias graph is a design decision under review, not proof that a consuming application maps or renders it correctly.
Keep Identity Forge on the upstream side of the boundary
Identity Forge supplies semantic light and dark roles through its design-kit exports, including DTCG, CSS, Tailwind, and shadcn formats. Those roles are upstream inputs. The consuming application owns any component aliases, mappings, exceptions, component APIs, and evidence that a change reached the correct surfaces.
Token specimen · real values
Ambient Sage
Live renderAmbient Sage's actual tokens — the same values its exports use.
For an Ambient Sage consumer, primary, primary-foreground, border, or ring can enter the project as semantic inputs. A proposed alias such as button.background.primary remains hypothetical until the application defines it. Whether that alias should exist depends on the application's button contract, not on the source kit having a primary role.
Copy this token-boundary decision record
Keep one compact record beside the token source or contribution proposal. Its storage format is an organizational choice. What matters is keeping expectations, observations, authority, and unresolved evidence distinct.
decision: "Should primary button background get a component token?"
status: "candidate"
source_authority: "design-system semantic token source"
source_decision: "color.action.primary"
current_alias_path:
- "color.blue.600"
- "color.action.primary"
proposed_alias: "button.background.primary"
modes:
light: "mapped; observation pending"
dark: "mapped; observation pending"
states:
default: "in scope"
hover: "in scope"
focus: "ring governed separately"
disabled: "out of scope; uses disabled intent"
named_consumers:
- "PrimaryButton"
- "ConfirmButton"
protected_surfaces:
- "text links"
- "selected navigation"
- "primary tabs"
expected_semantic_change:
- "all named action-primary consumers change"
expected_component_change:
- "only named primary button consumers change"
observations:
semantic_change: "not run"
component_change: "not run"
protected_surfaces: "not inspected"
exceptions: []
evidence_status: "pending_evidence"
owner: "component-system maintainer"
unresolved:
- "Does dark-mode hover require independent behavior?"
- "Is the alias a supported consumer override?"
disposition: "block pending evidence"The record makes the choice reversible. Another maintainer can see why the boundary was proposed, what would falsify it, and whether the decision is settled. A candidate or pending_evidence status must remain open. It isn't a weaker spelling of approved.
Diagnose the common boundary failures
- Component aliases merely rename shared intent: several component tokens point to one semantic role, always move with it, and have no separate owners or override contracts. Consider mapping components directly to the semantic role or merging redundant aliases.
- A semantic token encodes one component: a supposedly reusable name describes button placement, input borders, or another narrow consumer. Move it to component scope if the behavior and authority are genuinely local.
- One semantic name hides two reusable meanings: consumers share a value today but should have different propagation. Split the semantic intent before reaching for per-component tokens.
- Modes diverge without a declared rule: light mode follows the alias graph while dark mode contains a local value. Repair the mapping or document a real mode exception.
- Local overrides mask the contract: selectors, utility classes, inline values, or detached component copies prevent the token change from reaching a consumer. Fix the implementation before redesigning the token hierarchy.
- Aliases form a cycle or an untraceable chain: no layer can be identified as authoritative. Break the cycle and give each decision one direction of dependency.
- No one owns correction: foundation, theme, and component maintainers each expect another layer to fix a mismatch. Assign the owner before adding another layer.
The costliest failure is token proliferation without correction authority. A large graph can look mature while making every change harder to explain. Review the distinct contracts rather than the number of aliases. Each layer should answer a separate question and have someone responsible for that answer.
Make the decision on one disputed token
Choose one token whose scope is disputed. Name its consumers and protected surfaces, run the semantic change, then run the proposed component change if the first result supports it. Record what actually moved in each mode and state. Finish with one explicit disposition: keep, introduce, merge, split, or block.
Component and semantic token questions
Should every component have component tokens?
No. A component can consume semantic tokens directly when its styling expresses shared intent. Add component tokens only for independently governed behavior, a supported customization surface, or another present contract that justifies the extra mapping.
Can a component token reference a semantic token?
Yes. That is a common and useful alias path. The component token can inherit the semantic value by default while preserving a narrow point for an authorized component-level change.
Does a different value prove that a component token is needed?
No. A different value may reveal an overloaded semantic role, a legitimate component exception, or an implementation bug. Decide based on meaning, consumers, ownership, and expected propagation rather than value inequality alone.
What if the team cannot identify protected surfaces?
Block the decision pending evidence. Map representative consumers before changing the hierarchy. Without protected surfaces, the team cannot test whether the proposed component boundary isolates anything.
Are component tokens generated by Identity Forge?
No supported product fact establishes that Identity Forge generates component tokens. Its semantic light and dark exports are upstream inputs. The consuming application owns component aliases, mappings, exceptions, and downstream verification.
Sources
- Design tokens explained (and how to build a design token system) | Contentful: Contentful presents primitive, semantic, and component tokens as layers in a broader token-system architecture, with component tokens generally referring to semantic tokens.
- Design token usage | Calcite Design System - Esri Developer: Calcite groups tokens into core, semantic, and component tiers and uses component tokens for component-scoped overrides and customization.
- Update 1: Tokens, variables, and styles - Figma Learn - Help Center: Figma explains primitive, semantic, and component-specific tokens while allowing token organization and ordering to follow the needs of the system.
- Component-tokens first? Hear me out... | by Dan Donald - Medium: Dan Donald argues that change difficulty and blast radius can justify reconsidering the conventional sequence in which token layers are introduced.
- When "semantic tokens" are no longer semantic. | by Nate Baldwin | Design Systems Collective: Nate Baldwin examines how semantic tokens lose usefulness when their intent is either too generic or too specific to support sound reuse.
- Design Tokens - NYS Design System: The NYS Design System documents primitive, semantic, and theme layers, showing that a working token architecture does not require a universal component-token tier.
- Ambient Sage Design Kit: The public Ambient Sage kit provides 28 semantic tokens in light and dark modes and offers developer-oriented exports as an upstream design-kit artifact.