Skip to content

Guide contents

Choosing the right token

When building application components with Eufemia design tokens, think semantically. Do not ask "which token has the same color value as the one I want?" Ask "what role does this color play in this component and state?"

If you still find yourself reaching for legacy --color-* variables, treat that as a migration signal. Those variables are deprecated; prefer semantic --token-* design tokens instead.

All Eufemia components already use design tokens. Their stylesheets are the best reference for how to apply tokens correctly. When you are unsure which token to use, inspect the Eufemia component that is closest to what you are building.

Token selection by role

Every color in a component has a semantic role. Identify the role first, then pick the matching token section.

RoleToken sectionExample
Readable texttext--token-color-text-neutral
Iconicon--token-color-icon-neutral
Background / fillbackground--token-color-background-neutral
Border / outlinestroke--token-color-stroke-neutral
Focus ringstroke--token-color-stroke-action-focus
Branded / decorativedecorative--token-color-decorative-first-bold-static

Token selection by state

After the role, identify the interaction or status state. Tokens encode states as suffixes.

Interaction states

StateSuffix exampleWhen to use
Default(no suffix)Resting state
Hover-hoverPointer over an interactive element
Pressed-pressedMouse down (active) or touch active
Focus-focusKeyboard focus
Disabled-disabledNon-interactive, visually muted
Selected-selectedCurrently chosen item in a group

Status states

StatusSuffix exampleWhen to use
Error-errorValidation failure
Destructive-destructiveDangerous or irreversible action
Positive-positiveSuccess or upward trend
Warning-warningCaution, needs attention
Information-infoNeutral informational notice

Modifiers

ModifierMeaning
-subtleLower-contrast variant, typically for backgrounds
-inverseFor use on a filled action surface
-ondarkFor use on a dark surface
-staticDoes not change with light/dark mode

Practical examples

Custom action card

.my-action-card {
background-color: var(--token-color-background-neutral);
color: var(--token-color-text-neutral);
box-shadow: inset 0 0 0 0.0625rem
var(--token-color-stroke-neutral-subtle);
&:hover {
box-shadow: 0 0 0 0.125rem var(--token-color-stroke-action-hover);
}
&:focus-visible {
box-shadow: 0 0 0 var(--focus-ring-width)
var(--token-color-stroke-action-focus);
outline: none;
}
&--selected {
background-color: var(--token-color-background-selected-subtle);
box-shadow: inset 0 0 0 0.0625rem var(--token-color-stroke-selected);
}
&--disabled {
color: var(--token-color-text-action-disabled);
box-shadow: inset 0 0 0 0.0625rem
var(--token-color-stroke-action-disabled);
}
}

Status banner

.my-banner {
&--error {
background-color: var(--token-color-background-error-subtle);
color: var(--token-color-text-neutral);
}
&--warning {
background-color: var(--token-color-background-warning-subtle);
color: var(--token-color-text-neutral);
}
&--success {
background-color: var(--token-color-background-positive-subtle);
color: var(--token-color-text-neutral);
}
}

Input-like field

.my-field {
--field-border-color: var(--token-color-stroke-action);
--field-border-width: 0.0625rem;
--field-border-inset: inset;
--field-background: var(--token-color-background-neutral);
--field-text-color: var(--token-color-text-neutral);
--field-placeholder-color: var(--token-color-text-neutral-alternative);
color: var(--field-text-color);
background-color: var(--field-background);
box-shadow: var(--field-border-inset) 0 0 0 var(--field-border-width)
var(--field-border-color);
&::placeholder {
color: var(--field-placeholder-color);
}
&:hover {
--field-border-color: var(--token-color-stroke-action-hover);
--field-border-width: 0.125rem;
--field-border-inset: ;
}
&:focus {
--field-border-color: var(--token-color-stroke-action-pressed);
--field-border-width: 0.125rem;
--field-border-inset: ;
}
&--error {
--field-border-color: var(--token-color-stroke-error);
}
&[disabled] {
--field-border-color: var(--token-color-stroke-action-disabled);
--field-background: var(--token-color-background-neutral-subtle);
--field-placeholder-color: var(--token-color-text-action-disabled);
}
}

Patterns learned from Eufemia components

Eufemia components already follow these patterns consistently. Use them as a reference when building your own.

Use local CSS variables to simplify state handling

When multiple child elements need to react to the same state, define a local CSS variable on the parent and point it at the right token for each state. This is cleaner than repeating tokens across many selectors.

Eufemia's Input component does this: it defines --input-border-color, --input-background-color, and --input-color-text at the component root and swaps token values for hover, focus, disabled, and error.

Eufemia's Button component does the same with --button-color-bg, --button-color-text, and --button-color-icon, then each variant (primary, secondary, tertiary) reassigns those variables to different tokens.

Use semantic tokens, not component tokens

The --token-color-component-* tokens are reserved for Eufemia's own components. Application code should use the semantic tokens from the background, text, icon, stroke, and decorative sections.

Semantic tokens express shared design-system meanings such as "action text" or "neutral background". They adapt automatically across themes.

Focus uses the shared action-focus tokens

See the Focus styling tokens section for details on which tokens to use for keyboard focus styling.

Use decorative tokens only for decoration

Decorative tokens are for branded or ornamental surfaces, not for UI states. If the color represents an error, a hover, or a selection, use the matching semantic token instead.

Dark surfaces use ondark tokens

The ondark suffix identifies token variants designed for use on dark backgrounds. Eufemia components use these tokens automatically when surface="dark" is active — you do not need to select them yourself.

When your component sits on a dark background, use the ondark suffixed tokens for text, icons, and strokes:

.my-component--dark {
color: var(--token-color-text-action-ondark);
border-color: var(--token-color-stroke-action-ondark);
.my-component__icon {
color: var(--token-color-icon-action-ondark);
}
}

To detect the surface in React, use the useTheme hook:

import { useTheme } from '@dnb/eufemia/shared'
function MyComponent() {
const theme = useTheme()
const isDark = theme?.surface === 'dark'
return (
<div className={clsx('my-component', isDark && 'my-component--dark')}>
...
</div>
)
}

Radius tokens

Radius tokens control border-radius values and follow the pattern --token-radius-{size}. Values differ between brands, so using radius tokens keeps your components consistent with the active theme.

.my-card {
border-radius: var(--token-radius-md);
}
.my-pill {
border-radius: var(--token-radius-full);
}

See the Radius tab for the full list with per-theme values.

Decision checklist

When styling a custom component:

  1. What is the role of this color? (text, icon, background, stroke, decorative)
  2. What state is it in? (default, hover, focus, pressed, disabled, selected, error, etc.)
  3. Does an Eufemia component with similar behavior already exist? If yes, check which tokens it uses.
  4. Use the semantic token that matches both role and state.
  5. If multiple children share a state, introduce a local CSS variable.
  6. Never pick a token by matching hex values. Pick by semantic meaning.

Common mistakes

  • Picking a token because its current color value matches an old hardcoded color.
  • Using --token-color-component-* tokens in application code — these are internal to Eufemia components.
  • Using decorative tokens for UI states like error, hover, or disabled.
  • Hardcoding ondark tokens without checking the surface context.
  • Repeating the same token in many selectors instead of using a local CSS variable.