Persona

Color

How the core palette and the alias layer divide the work, and where the link between them gets lost.

Updated Sep 22, 2026

PDS ships color in two layers. 119 core steps are the palette. 153 alias tokens are the decisions already made with it — color.Text.Primary, color.Background.Button.Primary Hover — and each one resolves to a core step. Nothing prevents you from typing a core step directly, so the only thing separating the palette from the system is which layer you reach for.

119 core tokens across 8 families. Each step is labelled in whichever of black or white meets WCAG AA against it, which today is every step.

Purple

color.Core.Purple.*
  • 50
    #F4F5FF
  • 100
    #E9EAFF
  • 150
    #DEE0FF
  • 200
    #D3D5FF
  • 250
    #C8CBFE
  • 300
    #BDC0FE
  • 350
    #B2B6FE
  • 400
    #A7ABFE
  • 450
    #7A7FFD
  • 500
    #7379FD
  • 600
    #3F48FD
  • 700
    #0B16FC
  • 800
    #020CD0
  • 900
    #02099C
  • 1000
    #010668
  • 1100
    #010334

Red

color.Core.Red.*
  • 50
    #FEF3F2
  • 100
    #FDE7E4
  • 150
    #FCDBD7
  • 200
    #FBCFCA
  • 250
    #FAC2BC
  • 300
    #F9B6AF
  • 350
    #F8AAA1
  • 400
    #F79E94
  • 500
    #F47466
  • 600
    #F04B38
  • 700
    #E62711
  • 800
    #B81F0E
  • 900
    #8A170A
  • 1000
    #5C0F07
  • 1100
    #2E0803

Orange

color.Core.Orange.*
  • 50
    #FFF9F1
  • 100
    #FFF4E3
  • 150
    #FFEED4
  • 200
    #FFE9C6
  • 250
    #FEE3B8
  • 300
    #FEDDAA
  • 350
    #FED89B
  • 400
    #FED28D
  • 500
    #FEBF5C
  • 600
    #FDAB2B
  • 700
    #F59602
  • 800
    #C47802
  • 900
    #935A01
  • 1000
    #623C01
  • 1100
    #311E00

Yellow

color.Core.Yellow.*
  • 50
    #FDFFF1
  • 100
    #FBFFE4
  • 150
    #F9FFD6
  • 200
    #F7FFC9
  • 250
    #F4FEBB
  • 300
    #F2FEAD
  • 350
    #F0FEA0
  • 400
    #EEFE92
  • 500
    #E6FE60
  • 600
    #DFFD2F
  • 700
    #D3F802
  • 800
    #A9C602
  • 900
    #7F9501
  • 1000
    #556301
  • 1100
    #2A3200

Green

color.Core.Green.*
  • 50
    #F1FCF8
  • 100
    #E2F9F0
  • 150
    #D4F5E9
  • 200
    #C6F2E2
  • 250
    #B7EFDA
  • 300
    #A9ECD3
  • 350
    #9AE8CB
  • 400
    #8CE5C4
  • 500
    #66DCB1
  • 600
    #41D49D
  • 700
    #2BBC86
  • 800
    #22966B
  • 900
    #1A7150
  • 1000
    #114B36
  • 1100
    #09261B

Teal

color.Core.Teal.*
  • 50
    #F3FEFF
  • 100
    #E6FDFF
  • 150
    #DAFBFF
  • 200
    #CDFAFF
  • 250
    #C1F9FE
  • 300
    #B4F8FE
  • 350
    #A8F6FE
  • 400
    #9BF5FE
  • 500
    #68F0FD
  • 600
    #36EBFD
  • 700
    #03E6FC
  • 800
    #02B8CA
  • 900
    #028A98
  • 1000
    #015C65
  • 1100
    #012E33

Blue

color.Core.Blue.*
  • 50
    #F1F6FF
  • 100
    #E3EEFF
  • 150
    #D4E5FF
  • 200
    #C6DDFF
  • 250
    #B8D4FE
  • 300
    #AACBFE
  • 350
    #9BC3FE
  • 400
    #8DBAFE
  • 500
    #5C9CFE
  • 600
    #2B7FFD
  • 700
    #0263F5
  • 800
    #024FC4
  • 900
    #013B93
  • 1000
    #012862
  • 1100
    #001431

Slate

color.Core.Slate.*
  • 25
    #F9F9FA
  • 50
    #F5F5F6
  • 100
    #F1F1F2
  • 200
    #E2E2E3
  • 300
    #DBDBDB
  • 400
    #CCCCCC
  • 500
    #BDBDBD
  • 600
    #A0A0A0
  • 700
    #848484
  • 800
    #707070
  • 900
    #565656
  • 1000
    #434343
  • 1100
    #313131

69 of the 119 core steps are referenced by an alias. The other 50 have no decision attached, and they are not scattered. 30 of them are the half-steps — 50, 150, 250, 350, and purple’s 450 — and another seven are step 1100, the darkest, in every hue. Slate’s 1100 is the exception, because it is color.Text.Primary.

So the alias layer was built on the round steps and left the ones between them alone. The half-steps still render in the matrix above and have never been the answer to a question. Reach for one and you are making the decision rather than applying it, and the package has nowhere to record what you decided.

Background holds 85 of the 153 alias names

Section titled “Background holds 85 of the 153 alias names”

The alias layer is lopsided: Background has 85 tokens, Border 45, Text 14, and Primary 9. The reason is how it grew. Background names a surface per component per state, so color.Background.Label.Purple Disabled and color.Background.Button.Outline Hover are separate tokens rather than one rule applied twice. A background you need probably already has a token. A text color for a state nobody has built probably does not — fourteen names cover every piece of text in the product.

Section titled “The CSS build keeps the alias-to-core link and the JavaScript build resolves it”

In dist/css, 147 of the 153 alias properties are a var() pointing at another property, and the chain is intact: --color-background-button-primary is var(--color-primary-purple), which is var(--color-core-purple-600). Change one step and everything downstream of it moves.

In dist/js, the same token is a flat string. Color.BackgroundButtonPrimary is #3F48FD, and nothing in the value records that it came from purple 600. So a search for every consumer of a core step finds them in stylesheets and misses them in JavaScript, and the only complete map of alias to core is the 189 references in the Figma token file.

Six alias tokens have no core step behind them: color.Primary.Black and color.Primary.White are literal hex, and the four transparent button backgrounds are transparent — spelled rgba(0, 0, 0, 0) in CSS and transparent in JavaScript.

The tokens define one color per name. color.Background.Primary is white, in every theme, because the package has no concept of a theme.

Dark mode in the React library is therefore a second map, written by hand in @persona/design-system, and it reaches past the alias layer to core steps: its dark background is Color.CoreSlate1000 and its dark secondary text is Color.CoreSlate500. Neither is an alias, so neither appears in the alias layer as a dark value, and the two maps can disagree without anything detecting it.

The swatch labels are computed contrast against black and white

Section titled “The swatch labels are computed contrast against black and white”

Every step above is labeled in whichever of black or white meets WCAG AA against it, computed from the token at build time, and where neither reaches 4.5:1 the swatch says so. That is contrast for the swatch against black or white, and nothing else. It does not tell you the contrast of two tokens you are about to put together, and it is not a conformance claim — see Accessibility for what PDS does and does not verify.