Migrating to 3.0.0-beta colours
Two generations of EDS colour are in use, and each needs a different route. This page starts with the one thing that decides how you approach either of them.
Not one of the 115 semantic colours in 2.0.0-beta resolves to the same value as any 3.0.0-beta token, and the 1.x palette was replaced outright. The scale is now contrast-driven rather than lightness-driven, so matching your old colours by eye, or by nearest hex, gives you a palette that fails contrast in one scheme or the other.
Map by role: what was this colouring, and what was it doing. The pairs below are matched that way, and the swatches show how large a change to expect.
Coming from EDS 1.x
1.x named colours by component role in a flat set of hand-picked hex values:
colors.interactive.primary__resting, colors.text.static_icons__default. There were 75 tokens in
five groups, and states were suffixes: __resting, __hover, __selected, __activated.
3.0.0-beta names colours by surface, behaviour, tone, level and state. That is five questions where 1.x asked one, which is why a single old token often has more than one right answer. Getting Started explains how to read a name, and it is worth reading before you start rather than after.
Text
static_icons__* covered both text and icons. 3.0.0-beta separates them, because a thin icon stroke
needs more contrast than a letterform of the same size. Everything below has an icon.* twin.
| EDS 1.x | 3.0.0-beta |
|---|---|
| text.static_icons__default | text.primary |
| text.static_icons__secondary | text.secondary |
| text.static_icons__tertiary | text.tertiary |
| text.static_icons__primary_white | text.invertedOn a solid accent fill use text.on-emphasis.accent instead. |
Backgrounds and surfaces
The ui group was a small set of planes plus three status tints. The planes map cleanly. The status tints
become non-interactive tone fills, which is the group for anything that carries meaning but cannot be
clicked.
| EDS 1.x | 3.0.0-beta |
|---|---|
| ui.background__default | background.surface |
| ui.background__light | background.canvas |
| ui.background__medium | no equivalentUsed for both fills and dividers. Pick background.non-interactive.neutral.muted or border.non-interactive.neutral.default by role. |
| ui.background__scrim | overlay.scrim |
| ui.background__overlay | overlay.scrim |
| ui.background__semitransparent | no equivalentColour tokens are opaque now, so translucency has no token. |
| ui.background__info | background.non-interactive.info.muted |
| ui.background__warning | background.non-interactive.warning.muted |
| ui.background__danger | background.non-interactive.danger.muted |
Interactive colours
The largest group, and the one that changes shape most. Three things to know before reading it:
primaryandsecondaryare not tones. They described a component's rank. 3.0.0-beta usesaccentfor the primary action andneutralfor the secondary one, and rank is expressed by choosingemphasisormuted.__restingis nowdefault, and__activatedis nowpressed.activewas read as "currently on" about as often as "being pressed".__highlightwas doing two different jobs, a muted tint and a selection state. Those are nowmutedandselected, and onlyaccentandneutralhaveselected.
| EDS 1.x | 3.0.0-beta |
|---|---|
| interactive.primary__resting | background.interactive.accent.emphasis.default |
| interactive.primary__hover | background.interactive.accent.emphasis.hover |
| interactive.primary__hover_alt | background.interactive.accent.muted.hover |
| interactive.primary__selected_highlight | background.interactive.accent.selected.default |
| interactive.primary__selected_hover | background.interactive.accent.selected.hover |
| interactive.secondary__resting | background.interactive.neutral.emphasis.default |
| interactive.secondary__highlight | background.interactive.neutral.muted.default |
| interactive.secondary__link_hover | text.interactive.link.hover |
| interactive.danger__resting | background.interactive.danger.emphasis.default |
| interactive.danger__hover | background.interactive.danger.emphasis.hover |
| interactive.danger__highlight | background.interactive.danger.muted.default |
| interactive.danger__text | text.on-muted.danger |
| interactive.warning__resting | background.interactive.warning.emphasis.default |
| interactive.warning__hover | background.interactive.warning.emphasis.hover |
| interactive.warning__highlight | background.interactive.warning.muted.default |
| interactive.warning__text | text.on-muted.warning |
| interactive.success__resting | background.interactive.success.emphasis.default |
| interactive.success__hover | background.interactive.success.emphasis.hover |
| interactive.success__highlight | background.interactive.success.muted.default |
| interactive.success__text | text.on-muted.success |
| interactive.disabled__fill | background.interactive.disabled |
| interactive.disabled__border | border.interactive.disabled |
| interactive.disabled__text | text.interactive.disabled |
| interactive.focus | border.interactive.focus |
| interactive.link_on_interactive_colors | text.on-emphasis.accent |
| interactive.icon_on_interactive_colors | icon.on-emphasis.accent |
| interactive.text_highlight | background.interactive.accent.selected.defaultSelection is a state, so it now sits under selected rather than under a tone tint. |
| interactive.link_in_snackbars | no equivalentThere is no token for this. A link on background.inverted takes text.inverted, so raise a request if you need a distinct value. |
| interactive.pressed_overlay_dark | no equivalentStates are named colours now, so there is no overlay. Use the pressed value of the token you started from. |
| interactive.pressed_overlay_light | no equivalentSame as the dark overlay above. |
Table colours
1.x shipped table fills as their own tokens. 3.0.0-beta has no component-specific colour: a table cell is a surface, and a hovered row is an interactive neutral fill. Use the same tokens you would use anywhere else.
| EDS 1.x | 3.0.0-beta |
|---|---|
| interactive.table__cell__fill_resting | background.surface |
| interactive.table__cell__fill_hover | background.interactive.neutral.muted.hover |
| interactive.table__cell__fill_activated | background.interactive.neutral.selected.default |
| interactive.table__header__fill_resting | background.canvas |
| interactive.table__header__fill_hover | background.interactive.neutral.muted.hover |
| interactive.table__header__fill_activated | background.interactive.neutral.selected.default |
Infographic and logo
infographic (24 tokens) becomes data-visualization.*, but this is a re-palette rather than a
rename. 3.0.0-beta has ten categorical hues of five steps each, plus a sequential and a diverging
ramp, and none of them corresponds to an old substitute__* or primary__* value. Re-pick your
series colours from the palette.
logo (2 tokens) has no equivalent. Brand marks are not part of the colour scale.
Coming from 2.0.0-beta
This route is closer to a translation. emphasis and muted already exist, tones already exist, and
fill, border and text are already separated. Four things change.
Dynamic colour is removed
The Appearance collection and appearance-switching at the semantic-colour level are gone. There is
one approach now: semantic colour, where the tone is part of the token name.
If you relied on a generic role token resolving to a different tone depending on context, name the
tone explicitly. If you have data-color-appearance in your markup, or appearance modes in your
Figma file, those come out.
Names are reordered, and fill is dropped
2.0.0-beta put the tone before the surface word and used fill. 3.0.0-beta puts the surface first,
drops fill, and adds the interactive split:
bg.accent.fill.emphasis.hover 2.0.0-beta
background.interactive.accent.emphasis.hover 3.0.0-beta
The interactive split is new
This is the part that needs a decision rather than a rename. In 2.0.0-beta, a tinted banner and a
tinted button could share --eds-color-bg-accent-fill-muted-default. They cannot now: the button is
background.interactive.* and carries hover and pressed values, the banner is
background.non-interactive.* and has none.
So when you meet a fill token, ask whether the thing it colours responds to input.
Usage covers the distinction.
active becomes pressed
Interactive active tokens become pressed states, not selected levels. Selection has its own
background level on accent and neutral, with default, hover and pressed states.
Surfaces and planes
| 2.0.0-beta | 3.0.0-beta |
|---|---|
| bg.canvas | background.canvas |
| bg.surface | background.surface |
| bg.floating | background.floating |
| bg.input | background.input |
| bg.backdrop | no equivalentUnder review together with overlay.scrim; the two describe the same job. |
| bg.disabled | background.interactive.disabled |
Fills
The pattern below repeats for all six tones. accent is shown; substitute neutral, info,
success, warning or danger for the rest.
| 2.0.0-beta | 3.0.0-beta |
|---|---|
| bg.accent.fill.emphasis.default | background.interactive.accent.emphasis.default |
| bg.accent.fill.emphasis.hover | background.interactive.accent.emphasis.hover |
| bg.accent.fill.emphasis.active | background.interactive.accent.emphasis.pressedactive became pressed. |
| bg.accent.fill.muted.default | background.interactive.accent.muted.default |
| bg.accent.canvas | background.non-interactive.accent.mutedA tinted plane that cannot be clicked is non-interactive now. |
| bg.accent.surface | background.non-interactive.accent.default |
Borders
The rows below map old border roles to non-interactive borders: subtle / medium / strong
becomes muted / default / emphasis. For a border that responds to input, decide its role
instead of translating by strength alone. Interactive borders have only
muted and emphasis levels, each with default, hover and pressed states.
| 2.0.0-beta | 3.0.0-beta |
|---|---|
| border.accent.subtle | border.non-interactive.accent.muted |
| border.accent.medium | border.non-interactive.accent.default |
| border.accent.strong | border.non-interactive.accent.emphasis |
| border.subtle | border.non-interactive.neutral.muted |
| border.focus | border.interactive.focus |
| border.disabled | border.interactive.disabled |
Text
Tone-coloured text is now named for what it sits on rather than for how strong it is. This
makes the pairing checkable: text.on-muted.accent is the foreground for an accent muted fill.
Check the actual fill, foreground, states, schemes and typography with APCA before shipping.
| 2.0.0-beta | 3.0.0-beta |
|---|---|
| text.strong | text.primary |
| text.subtle | text.secondary |
| text.accent.strong | text.on-muted.accentTone-coloured text sits on a tinted fill, so it is named for what it sits on. |
| text.accent.strong.on-emphasis | text.on-emphasis.accent |
| text.accent.subtle.on-emphasis | no equivalentOnly one foreground per tone on an emphasis fill now. Use text.on-emphasis.accent. |
| text.link | text.interactive.link.default |
| text.disabled | text.interactive.disabled |
Checking your work
Use APCA to assess text and controls against their actual backgrounds in both schemes and all applicable states, with the intended text size and weight. Do not treat a default-state pairing as proof that its hover and pressed states also pass. See Contrast and Foreground pairing.