Propel

Color

The semantic color roles Propel ships, what each one means, and when to reach for it.

Propel’s colors are named for the job they do, never for the color they happen to be. You write bg-danger-subtle, not bg-red-100, so the same markup stays correct when the theme changes and a reader can tell what a line of markup means without opening a palette.

Most names follow one pattern:

bg-danger-subtle-hover
│  │      │      └─ state      hover · active · selected
│  │      └──────── emphasis   primary · secondary · subtle · strong · transparent
│  └─────────────── intent     accent · success · warning · danger · info
└────────────────── property   bg · text · text-icon · border · ring · outline

A slot is dropped when it has nothing to say. The surface stack and the content ladders skip intent and name a step in a scale instead — bg-surface-1, text-tertiary — and control states like disabled and placeholder take the emphasis slot for themselves. A token is added when a real component needs it, so not every combination exists.

The roles below are the ones you reach for by hand — a working subset of the 282 semantic color tokens Propel ships. The rest are the state variants the pattern generates, the ring, outline, stroke, and fill mirrors covered after the roles, and the product-specific sets for work-item states, priorities, plan tiers, and the scrollbar.

Surfaces

The stack a page is built from. Canvas sits furthest back, surfaces lift above it, and layers are the interactive fills that sit on top and respond to hover, selection, and press.

Light · DarkToken

bg-canvas

The page itself. Everything else sits on top of it.

oklch(0.9614 0.0013 286.38) · oklch(0.1689 0.0021 230.81)

bg-surface-1

A raised surface — cards, panels, table headers.

oklch(1 0 0) · oklch(0.1932 0.002 230.81)

bg-surface-2

One step above surface-1, for a selected or nested surface.

oklch(0.9911 0 0) · oklch(0.2158 0.0025 230.82)

bg-layer-1

The default interactive fill, for rows and controls that react to the pointer.

oklch(0.9821 0 0) · oklch(0.2158 0.0025 230.82)

bg-layer-2

A recessed fill — the inside of an input or a well.

oklch(1 0 0) · oklch(0.2378 0.0029 230.83)

bg-layer-3

The deepest fill, for a control nested inside a layer.

oklch(0.9614 0.0013 286.38) · oklch(0.2593 0.0033 230.84)

bg-layer-transparent

A fill that tints whatever it sits on instead of painting over it.

oklch(1 0 0 / 0%) · oklch(0.1482 0.0034 196.79 / 0%)

bg-backdrop

The scrim behind a modal.

oklch(0.1482 0.0034 196.79 / 30%) · oklch(0.1482 0.0034 196.79 / 80%)

bg-inverse

A surface that flips against the theme, for tooltips and toasts.

oklch(0.1689 0.0021 286.18) · oklch(0.9235 0.001733 230.6853)

Text

An emphasis ladder from primary down to tertiary, plus the colors for text on a filled or inverse surface. Pick by emphasis rather than by how light the color looks. Placeholder and disabled sit below the ladder and are state colors, not content colors.

Light · DarkToken

text-primary

Headings and the highest-emphasis text.

oklch(0.1689 0.0021 286.18) · oklch(0.9235 0.001733 230.6853)

text-secondary

Body copy and the default for most interface text.

oklch(0.3225 0.0045 219.62) · oklch(0.8455 0.0035 230.72)

text-tertiary

Supporting text — captions, metadata, section labels.

oklch(0.4176 0.0072 239.98) · oklch(0.7655 0.0054 230.76)

text-placeholder

Unfilled input text. Not a color for real content.

oklch(0.4928 0.0079 233.77) · oklch(0.6835 0.0074 230.81)

text-disabled

Text in a disabled control. Exempt from contrast rules, so never use it for content.

oklch(0.5651 0.0086 228.95) · oklch(0.5989 0.0096 230.88)

text-on-color

Text on a filled accent or status surface.

oklch(1 0 0) · oklch(0.9702 0 0)

text-inverse

Text on an inverse surface.

oklch(1 0 0) · oklch(0.1689 0.0021 230.81)

Border

Neutral edges step from subtle up to strong. Subtle is the default divider and container edge; the darker steps are for an edge that has to hold over a fill or read as deliberate.

Light · DarkToken

border-subtle

The default 1px divider and container edge.

oklch(0.9396 0.0017 247.84) · oklch(0.2593 0.0033 230.84)

border-subtle-1

A half-step darker — the edge tooltips, active tabs, and hovered controls hold on a raised fill.

oklch(0.9123 0.0017 247.84) · oklch(0.3011 0.0041 230.85)

border-strong

A deliberate edge — a control outline, or a divider over a fill.

oklch(0.8782 0.0035 247.86) · oklch(0.3415 0.0049 230.86)

border-disabled

The edge of a disabled control.

oklch(0.9614 0.0013 286.38) · oklch(0.2378 0.0029 230.83)

border-inverse

An edge on an inverse surface.

oklch(1 0 0) · oklch(0.1932 0.002 230.81)

Accent

Plane blue, and the only color that means 'act here'. It marks the primary action, the current selection, links, and focus. Used on more than a small share of a screen it stops reading as a signal.

Light · DarkToken

bg-accent-primary

The filled primary action.

oklch(0.4799 0.1158 242.91) · oklch(0.6311 0.126281 238.01)

bg-accent-subtle

A tinted accent surface — a selected row, an active nav item.

oklch(0.9847 0.0083 236.56) · oklch(0.2029 0.03 232.24)

text-accent-primary

Accent text and links.

oklch(0.4347 0.104093 242.4823) · oklch(0.6311 0.126281 238.01)

border-accent-strong

A focused control's border. The same name, as ring-accent-strong, paints the focus ring.

oklch(0.4799 0.1158 242.91) · oklch(0.6311 0.126281 238.01)

Status

Four meanings — success, warning, danger, and info — each with a filled and a subtle surface, a text color, and a border. Reserve them for state. A danger color on something that isn't destructive spends the one signal the user reliably reads.

Light · DarkToken

bg-success-primary

A filled confirmation surface.

oklch(0.632 0.185972 147.3695) · oklch(0.7914 0.2091 151.66)

bg-success-subtle

A tinted success surface, for banners and badges.

oklch(0.9819 0.0181 155.83) · oklch(0.2721 0.067322 152.52)

bg-warning-primary

A filled caution surface.

oklch(0.7724 0.172798 65.367)

bg-warning-subtle

A tinted caution surface.

oklch(0.9869 0.0214 95.28) · oklch(0.3042 0.0853 45.16)

bg-danger-primary

A filled destructive surface.

oklch(0.583 0.238666 28.4765) · oklch(0.4446 0.1774 26.79)

bg-danger-subtle

A tinted danger surface, for error banners.

oklch(0.9705 0.0129 17.38) · oklch(0.2615 0.085 24.41)

bg-info-primary

A filled informational surface.

oklch(0.5465 0.2455 262.87) · oklch(0.8071 0.1007 250.45)

bg-info-subtle

A tinted informational surface.

oklch(0.9705 0.0142 254.6) · oklch(0.3796 0.1453 265.47)

Icon

Icons take their color from text, not from a parallel palette — the icon tokens mirror the text ladder and the status meanings so a glyph and its label always agree. SVGs follow currentColor, so setting the text color is usually enough.

Light · DarkToken

text-icon-primary

The default icon color, matching primary text.

oklch(0.1689 0.0021 286.18) · oklch(0.9235 0.001733 230.6853)

text-icon-secondary

An icon beside secondary text.

oklch(0.3225 0.0045 219.62) · oklch(0.8455 0.0035 230.72)

text-icon-tertiary

A decorative or low-emphasis glyph.

oklch(0.4928 0.0079 233.77) · oklch(0.7655 0.0054 230.76)

text-icon-disabled

An icon in a disabled control.

oklch(0.8466 0.0043 236.51) · oklch(0.5989 0.0096 230.88)

text-icon-on-color

An icon on a filled surface.

oklch(1 0 0) · oklch(0.9702 0 0)

Inline navigation. Links are the one place accent color appears in running text.

Light · DarkToken

text-link-primary

An inline link at rest.

oklch(0.4799 0.1158 242.91) · oklch(0.7408 0.100309 233.89)

text-link-primary-hover

The same link on hover.

oklch(0.4347 0.104093 242.4823) · oklch(0.6311 0.126281 238.01)

text-link-secondary

A quieter link, for dense or secondary navigation.

oklch(0.4928 0.0079 233.77) · oklch(0.8455 0.0035 230.72)

Labels

Labels are the one place Propel offers color without meaning attached: 8 hues for user-chosen tags, where the color is a handle rather than a signal. Every hue carries the same 7 properties — fills, hover and focus states, text, border, and icon — and yellow alone adds an extra icon-strong step.

Because labels live in the shared color namespace, the property appears twice in the utility — the indigo fill is bg-label-indigo-bg. Each cell below pairs the light value with the dark.

Huebgbg-stronghovericontextborderfocus
crimsoncrimson bgcrimson bg-strongcrimson hovercrimson iconcrimson textcrimson bordercrimson focus
emeraldemerald bgemerald bg-strongemerald hoveremerald iconemerald textemerald borderemerald focus
greygrey bggrey bg-stronggrey hovergrey icongrey textgrey bordergrey focus
indigoindigo bgindigo bg-strongindigo hoverindigo iconindigo textindigo borderindigo focus
orangeorange bgorange bg-strongorange hoverorange iconorange textorange borderorange focus
pinkpink bgpink bg-strongpink hoverpink iconpink textpink borderpink focus
purplepurple bgpurple bg-strongpurple hoverpurple iconpurple textpurple borderpurple focus
yellowyellow bgyellow bg-strongyellow hoveryellow iconyellow textyellow borderyellow focus

Reach for a label hue only when the user picked it. Anything the system decides belongs to a status or accent token, so a red the user chose can never be mistaken for an error the system raised.

Beyond the roles

The border vocabulary is mirrored into ring and outline, so a focus indicator can carry the same meaning as an edge. Focus lives here: controls pair ring-2 with ring-accent-strong, invalid fields swap it for ring-danger-strong, and toast and switch draw an offset outline-accent-strong instead. The text ladder reappears as stroke and fill for SVG that doesn’t follow currentColor.

The remaining tokens are product vocabulary rather than roles: colors for work-item states and priorities, plan tiers, and the scrollbar thumb, plus a raw blue set that info resolves through. Of these only the scrollbar colors appear in the library’s own source — the rest exist for Plane’s products, and none belong in new component work.

Guidelines

Author every color as a semantic utility. Reaching past it to a palette step is what breaks theming: the semantic layer is the only thing that re-resolves across light, dark, and the two contrast themes, so an element painted with a raw step keeps its color when everything around it changes.

Color is never the only carrier of meaning. A status that reads only as red fails for anyone who can’t separate it from green, so pair it with an icon, a label, or position. The same applies to required fields, chart series, and diff highlighting.

Keep the accent rare. It marks the primary action, the current selection, and links, and its scarcity is what makes it read as “act here” — a second filled accent button on the same view is one too many.

Don’t use text-disabled for content. It is exempt from contrast requirements because WCAG excuses inactive controls, so it is the one text token that can legitimately fail a contrast check. Text a reader has to parse belongs on the primary through tertiary ladder.