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.
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 paintRaw 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 roleAliases that point at a primitive and name its job. Each token picks one primitive for light mode and another for dark.
Components
the UIButton, 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.
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.
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.
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.
What am I coloring?
A surface, text or an icon, a line, an action, or a status. That narrows it to one group below.
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.
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.
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.
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.
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.
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.
| Before | Now | What it means | PR |
|---|---|---|---|
monochrome-plus5 … minus5 | gray-950 … gray-50 | All neutrals: text, borders and surfaces. Core UI went first, then the rest of the product in one remap. | #144018 #145634 |
primary-minus4 · base · plus2 | blue-100 · 500 · 700 | The brand blue ramp, step for step (minus5→50 … plus5→950). Flat roles like bg-primary stay as they are. | #146866 |
negative · notice · positive | red · orange · mint | Status colors now come from real ramps, so tint, text and icon colors all come from the same family. | #147706 |
accent01 · 02 · 03 · 04 | yellow · teal · pink · indigo | Numbered accents, which told you nothing about the color, now use hue names. | #147771 |
descriptiveColors hex tables | primitives | Hard-coded hex tables in Lightning and shared palettes were deleted. | #149328 #149330 |
categorical-color-01 … 12 | blue-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 · amplitudeBrand | background · foreground · blue-500 | Leftover duplicates were merged into the standard ShadCN-style semantic names. | #149881 #149783 #149643 |
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.
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.