One Scale, Every Surface
Design system · Color foundations · Shipped Sep 2024

One color scale, used everywhere in the product.

Amplitude's colors used to live in several separate systems: monochrome plus/minus, primary plus/minus, negative/notice/positive, accent01–04, a table of descriptive hexes, and twelve chart colors with no dark values. Now every color in the product comes from one 50–950 scale. Each color works in both light and dark mode, and names are the same in code and Figma.

11 familiesgray, blue, indigo, purple, pink, red, orange, yellow, lime, mint, teal
11 steps each50 (lightest) to 950 (darkest), with the brand color at 500
6 legacy systemsretired and remapped onto the one scale
8+ PRsAug 26 – Sep 29, mostly with no visible change
The model

Three layers, from raw color to a finished button

Most of this update happens in the first two layers, which designers rarely touch directly. Getting those layers right is why the third one now behaves predictably.

Primitives

the paint

Raw colors with no meaning attached. A name like red-700 tells you the hue and the step, not what it's for.

Semantic tokens

the role

Aliases that point at a primitive and name its job. Each token picks one primitive for light mode and another for dark.

Components

the UI

Button, Alert, Badge and charts use the semantic tokens and never hard-code a hex. Changing a mapping updates every place that uses it.

A simple way to think about it: primitives are the paint cans on the shelf. Semantic tokens are the labels on the wall that say "doors are this can." Before, each team had its own shelf and its own labels, and some walls were painted straight from a hex code. Now there's one shelf, and the labels point at it.

Layer 1 · Primitives

The ramps

Every family has the same eleven steps. The outlined cell is 500, the brand anchor, which never moves. Each step has its own dark-mode value, so gray-950 means "strongest text" in both themes.

Light values
Dark values

Dark is tuned, not flipped

A dark ramp isn't the light ramp reversed. Gray was tuned for its role on each end: text is softened to #D7D8DA so it doesn't glare, and backgrounds are pushed close to black (#080809) so cards separate from the canvas.

Calmer light tints

Pale steps in teal and mint used to read as neon, and the dark ends of pink, indigo and red were oversaturated, because the old curve let saturation run too high toward both ends. The ramp generator now reduces saturation there. teal-100 went from #B7FFF9 to #DFF4F2 in light, and dark teal-700 from #66EDE4 to #96E5DE. Every 500 stayed exactly the same.

Brand gold stays gold

yellow-500 is #FAC400 in both modes. In dark mode it breaks the usual pattern on purpose, so the brand yellow never turns amber.

Layer 2 · Semantic tokens

Choose the role first, then the color

Eleven families with eleven steps each is a lot to choose from, and the ramps alone don't tell you which one to use. When you're designing something new, start by naming what each part is for. The semantic token for that role already points at the right step in both light and dark mode.

Ask 1

What am I coloring?

A surface, text or an icon, a line, an action, or a status. That narrows it to one group below.

Ask 2

How loud should it be?

foreground for primary text, muted-foreground for supporting text. card lifts a surface off background, and muted is a quiet fill.

Ask 3

What does it sit on?

A -foreground token is the text color for the surface with the same name. Use primary-foreground on primary and card-foreground on card, and contrast is already handled.

Building a settings card from scratch

Every color on this card comes from a semantic token. Nothing here uses a primitive or a hex value directly. Each yellow number matches a row in the list under the card.

What doesn't have a role token yet

For the full list with Figma variable names and the legacy migration tables, see What color should I use | Color token mapping in Confluence.

Success, warning and error tints. Only informative has a full set (accent, -foreground, -subtle). negative, notice and positive are planned. Until then, use the ramp pattern from the badges below: a 100 tint with 700 text from red, orange or mint. destructive is for destructive actions, not error states.

Chart colors. These have their own rules. See Chart colors below.

Data viz

Chart colors

Chart colors aren't semantic tokens. In a chart, a color stands for "series 3," not for a role like text or a border. Use these rules instead of choosing hues one at a time.

Top row is the solid color (the bar, line or dot). Bottom row is the paired tint for fills, highlights and chip backgrounds. Light and dark values are shown for each slot.

1Assign slots in order

Series 1 gets slot 1, series 2 gets slot 2, and so on. Don't skip slots to get colors you like better. The order is designed so neighboring series stay distinguishable, and it keeps colors the same from one chart to the next.

2One series uses slot 1

A single-series chart, sparkline or meter uses blue-500. Add more hues only when they encode different series.

3Solid for marks, tint for fills

Use the slot's base color for bars, lines and dots. Use its tint (mostly 100) behind text, such as legend chips, highlights or a bar's remaining portion. Put text on the card surface, not on a solid fill.

4Good/bad uses status colors

If the data means good versus bad, like NPS promoters versus detractors or pass versus fail, use red, orange and mint from the ramps, not categorical slots. Don't use those hues for neutral series that sit next to good/bad series.

5Group the long tail

More than 6 or so series gets hard to tell apart, even with twelve slots. Show the top series and group the rest as "Other" in gray-400, or switch to a table.

6Analytics charts color themselves

Charts in Lightning (segmentation, funnels, dashboards) get their colors from the org's chart color palette, and Dashboard v2 blocks use a fixed 6-color palette. Don't respec those per screen. These rules are for charts you design into product UI.

The migration

What each old name became

Each legacy system was mapped step by step onto the scale. Most of these PRs are renames with no visible change. They matter because once one name refers to one color, the tokens can be fixed and trusted.

BeforeNowWhat it meansPR
monochrome-plus5 … minus5gray-950 … gray-50All neutrals: text, borders and surfaces. Core UI went first, then the rest of the product in one remap.#144018 #145634
primary-minus4 · base · plus2blue-100 · 500 · 700The brand blue ramp, step for step (minus5→50 … plus5→950). Flat roles like bg-primary stay as they are.#146866
negative · notice · positivered · orange · mintStatus colors now come from real ramps, so tint, text and icon colors all come from the same family.#147706
accent01 · 02 · 03 · 04yellow · teal · pink · indigoNumbered accents, which told you nothing about the color, now use hue names.#147771
descriptiveColors hex tablesprimitivesHard-coded hex tables in Lightning and shared palettes were deleted.#149328 #149330
categorical-color-01 … 12blue-500, lime-500, purple-700…Chart slots now point at primitives. Slot 2 got a new lime ramp built on its old color. Slot 4 moved to teal-400.#151389
bodyBackground · primaryText · amplitudeBrandbackground · foreground · blue-500Leftover duplicates were merged into the standard ShadCN-style semantic names.#149881 #149783 #149643
Layer 3 · Real UI

What changed on screen

Three places where you can see the difference. Switch between Before and After. Each pair shows light and dark side by side, no matter which theme the page is in.

For your next file

How to pick a color now

1Start with the role

Reach for foreground, muted-foreground, border, background, primary or a status token first. You get dark mode for free and can't pick the wrong step.

2Use a step when no role fits

Illustrations, charts and one-off accents use primitives by name, like teal-600. Tints generally sit at 100, solid marks around 500, and text on a tint at 700.

3Never use a raw hex

A hex has no dark value and falls outside the system. If you need a color that isn't on the scale, raise it with UI Foundation. That's how lime was added.