Skip to content
Design System
Contact

Colors

Semantic color tokens read live from the active theme, with WCAG contrast checks.

FoundationsCSS variablesTailwind v4 @theme

Semantic tokens

All 48 color tokens from globals.css, read live from the preview theme. Switch Light and Dark in the header and the values and ratios update.

Clicking a swatch copies its
Contrast against the sheet
7:1+AAA4.5:1+AA3:1+AA LargeUnder 3:1Fail

Surfaces 8

Layers from the desk to the sheet. Most screens use only canvas, background, subtle and muted.

Text and ink 10

Four steps of ink. Primary is the ink button, so it matches foreground.

Brand 5

Iris violet marks selection, focus, the first chart series and AI. It is never the default button.

Status 8

Kept apart from brand so a selected row never reads as a success.

Lines 3

Hairlines do the work shadows usually do. Sheet edge is translucent so it reads on any surface.

Chart palette 6

Six series colors in a fixed order. Rated as graphics against the sheet (3:1).

Sidebar 8

The shell sidebar sits on the canvas, so it has its own set.

Contrast

The pairs the product actually ships, measured in the preview theme. Translucent soft fills are composited over the sheet first, the same way the browser paints them.

Measuring…

Show
Contrast pairs
WCAG contrast ratios of common foreground and background token pairs
SamplePair and useRatioTarget
foreground on backgroundBody text…4.5:1
ink-2 on backgroundSecondary text…4.5:1
muted-foreground on backgroundDescriptions…4.5:1
muted-foreground on subtleToolbar labels…4.5:1
muted-foreground on mutedSegmented control…4.5:1
ink-3 on backgroundIcons, decorative meta…3:1
foreground on canvasText on the desk…4.5:1
sidebar-foreground on sidebarNavigation…4.5:1
primary-foreground on primaryInk button…4.5:1
brand-foreground on brandBrand button…4.5:1
brand-ink on brand-softSelected, AI badge…4.5:1
brand on backgroundLinks, active icons…4.5:1
success on success-softStatus badge…4.5:1
warning on warning-softStatus badge…4.5:1
info on info-softStatus badge…4.5:1
destructive on danger-softStatus badge…4.5:1
destructive on backgroundField errors…4.5:1
ring on backgroundFocus indicator…3:1

Ratios use the WCAG 2.x relative luminance formula and are truncated, never rounded up. Text needs 4.5:1 (AA) or 7:1 (AAA); 3:1 passes only for text at 18px, or 14px bold, and for icons, focus rings and chart marks (WCAG 1.4.11).

Status colors

Four status hues, each with a soft fill. StatusDot carries state in lists; soft badges carry it in tables and headers.

Status dots
LivePaidPendingIn reviewOverdueSelectedDraft
Soft badges
  • success
    Paid
    Paid
  • warning
    Pending
    Pending
  • info
    In review
    In review
  • destructive
    Overdue
    Overdue
  • Never color alone. Pair every status hue with a word, an icon or a dot label so the meaning survives color blindness and grayscale.
  • Small labels use ink. Where a tinted label measures below 4.5:1 (see the ratios above), use the ink label with a colored dot, or keep tinted text to icons and 14px semibold.

Chart palette

Hover a series to isolate it in the bar; click to copy its variable for Recharts.

Series order
  • Assign in order. Series 1 is always the subject of the chart, so it gets brand iris. Keep a series on the same color across every chart on a page.
  • Six at most. Group the tail into “Other” on chart-6. More categories than that belong in a table or a ranked bar list.
  • Label what is light. Series under 3:1 on the sheet (see the badges) need direct labels or a legend with values, never color alone.

Tag palette

Ten Notion-style colors for user-defined labels. They sit outside the semantic tokens so a green tag never means success. Click a color to copy its Tag element.

Notion tag palette
tags.tsxtsx
1import { Tag } from "@/components/common/tag"
2import { tagHex } from "@/lib/tags"
3
4<Tag>AI</Tag> // known labels keep their color
5<Tag label="Northwind" /> // anything else hashes to a stable color
6<Tag color="teal" dot>SAP</Tag>
7
8// Charts and SVG, where classes can't reach
9<circle fill={tagHex.teal} />

Light and dark

Key tokens in both themes at once, read from explicit .theme-light and .theme-dark scopes. Dark soft fills are translucent so they tint whatever surface they sit on.

Light.theme-light
Invoice 1042 AI
  • --canvas…
  • --background…
  • --foreground…
  • --ink-2…
  • --muted-foreground…
  • --border…
  • --brand…
  • --brand-soft…
  • --brand-ink…
  • --success…
  • --warning…
  • --destructive…
Dark.theme-dark
Invoice 1042 AI
  • --canvas…
  • --background…
  • --foreground…
  • --ink-2…
  • --muted-foreground…
  • --border…
  • --brand…
  • --brand-soft…
  • --brand-ink…
  • --success…
  • --warning…
  • --destructive…

Usage

Reach for the semantic class first. Raw hex values never appear in components.

usage.tsxtsx
1// 1. Tailwind classes (preferred): tokens are mapped in @theme inline
2<div className="rounded-xl border bg-background text-foreground">
3 <p className="text-ink-2">Secondary text</p>
4 <span className="bg-brand-soft text-brand-ink">Selected</span>
5</div>
6
7// 2. Arbitrary values when a utility doesn't exist
8<div className="shadow-[0_0_0_1px_var(--sheet-edge)]" />
9
10// 3. Inline styles, SVG and Recharts
11<Bar dataKey="revenue" fill="var(--chart-1)" />
12<CartesianGrid vertical={false} stroke="var(--border)" />
13
14// 4. Force a theme inside a preview
15<div className="theme-dark bg-background text-foreground">…</div>