# Canvas Tokens v4.4 Reference

Current-state reference for `@workday/canvas-tokens-web@4.4.0-beta.11` and Canvas Kit v16.

Use this document when choosing tokens, migrating deprecated paths, or enabling the Sana Canvas theme. For historical migrations (v2→v3, v13→v14), see the older guides listed at the end.

> **Your priors may be out of date.** In v4.4, ~64% of `system.color.*` is deprecated, including all of `text.*` and `icon.*` and nearly all of `bg.*`. Trust this reference and the installed package JSDoc over older MCP migration docs.

## Setup

### Install

```bash
npm install @workday/canvas-tokens-web@4.4.0-beta.11
```

### CSS imports

Import once at the application root, in this order:

```css
@import '@workday/canvas-tokens-web/css/base/_variables.css';
@import '@workday/canvas-tokens-web/css/brand/_variables.css';
@import '@workday/canvas-tokens-web/css/component/_variables.css';
@import '@workday/canvas-tokens-web/css/system/_variables.css';
/* Import Sana last so equal-specificity [data-theme] rules win over :root */
@import '@workday/canvas-tokens-web/css/sana/_variables.css';
```

### Sana Canvas theme

Canvas Kit v16 targets the Sana Canvas visual language. Enable it on your root element:

```html
<html data-theme="sana-canvas">
```

The Sana stylesheet reassigns palette values, shape sizes, depth shadows, typography, and 88+ semantic color tokens under `[data-theme="sana-canvas"]`. Consumers write plain `system.*` paths — values change automatically when the theme attribute is set.

Load Sana Sans via `@workday/canvas-kit-react/fonts` (`injectGlobal(...fonts)`).

### JavaScript import

```tsx
import {system, base, brand, component} from '@workday/canvas-tokens-web';
```

Each token is a CSS variable **name** (e.g. `--cnvs-sys-color-fg-default`), not a resolved value. Use `createStyles` from `@workday/canvas-kit-styling` so tokens resolve to `var(--cnvs-…)` automatically. Inside template literals, wrap manually with `cssVar()` or `` `var(${token})` ``:

```tsx
import {createStyles, cssVar} from '@workday/canvas-kit-styling';

const styles = createStyles({
  color: system.color.fg.default,                            // auto-wrapped
  border: `1px solid ${cssVar(system.color.border.default)}`, // manual wrap in template literal
});
```

## Namespaces

| Namespace | Contains | Use for |
|---|---|---|
| `base` | Raw palette and scales (`neutral500`, `size300`) | Decorative work, or escape hatch when no `system` token fits |
| `brand` | Tenant-themeable ramps (`primary600`, `gradient`) | Brand color outside `system.color.brand.*` |
| `system` | Semantic tokens | **Default for everything** |
| `component` | Per-component hooks (`systemIcon`, `expressiveIcon`) | Overriding one component's internals |

### Off-limits (internal only)

Do **not** use these in application code:

- `system.legacy.*`, `base.legacy`, `base.sana`, `brand.legacy`, `component.legacy`
- `system.sana.*`
- `@workday/canvas-kit-react/tokens` (`canvas`, `colors`, `space`, `type`, `depth`, `borderRadius`)

Canvas Kit uses `.legacy` and `.sana` namespaces internally so components keep working when consumers are on older token versions. The `legacy` paths carry `var()` fallbacks to deprecated CSS variables. Write plain `system.*` and let the theme CSS resolve the correct value.

## Color

Five families, split by the **role** the color plays:

| Family | Role | Examples |
|---|---|---|
| `surface.*` | Container and page backgrounds | `surface.default`, `surface.raised`, `surface.alt.default`, `surface.modal` |
| `fg.*` | Text, icons, any foreground mark | `fg.default`, `fg.muted.default`, `fg.inverse`, `fg.disabled` |
| `border.*` | Strokes, dividers, outlines | `border.default`, `border.strong`, `border.input.default` |
| `accent.*` | Solid non-brand emphasis fills | `accent.ai`, `accent.info`, `accent.success` |
| `brand.*` | Tenant-brandable semantics | `brand.accent.primary`, `brand.fg.primary.default`, `brand.border.primary` |

```tsx
import {createStyles, cssVar} from '@workday/canvas-kit-styling';

const styles = createStyles({
  backgroundColor: system.color.surface.default,
  color: system.color.fg.muted.default,
  borderBlockEnd: `1px solid ${cssVar(system.color.border.default)}`,
});
```

### Picking within a family

- Intensity is short: `default` and `strong` (plus `stronger` on `fg`). There is **no** `soft`/`softer`/`softest` ladder on `surface.*` — that belonged to the removed `bg.*` family.
- **Status meaning** uses neutral words: `fg.danger|warning|success|info` and `surface.danger|warning|success|info`.
- **Tenant branding** uses `system.color.brand.*`. `brand.accent` has `primary`, `critical`, `caution`, `positive`, `action` — no `secondary`/`tertiary`.
- **AI surfaces**: `accent.ai` for fills (pair with `fg.inverse`), `fg.ai` for AI-tinted text/icons, `surface.ai.default|hover|pressed` for AI panels.
- **Elevation** is `system.depth[1]`–`[6]`, used whole: `boxShadow: system.depth[1]`. Shadow *colors* are `shadow.base` / `shadow.ambient`.

### Translucent surfaces

22 of 29 `surface.*` tokens resolve to alpha palette values. Only these 7 are opaque: `default`, `modal`, `popover`, `navigation`, `inverse`, `ai.pressed`, `overlay.mixin`.

- Do not stack alpha surfaces — nesting compounds the wash.
- A translucent surface inherits whatever is behind it; check in situ.
- When migrating from `bg.*`, intensity names invert (`bg.positive.softer` → `surface.success.strong`) because the new family uses alpha steps, not opaque palette steps.

### Deprecated color families

| Don't write | Write |
|---|---|
| `system.color.bg.*` | `system.color.surface.*` |
| `system.color.text.*` | `system.color.fg.*` (brand text → `brand.fg.*`) |
| `system.color.icon.*` | `system.color.fg.*` |
| `system.color.border.divider` | `system.color.border.default` |
| `system.color.border.container` | `system.color.border.strong` |
| `system.color.border.primary.default` | `system.color.brand.border.primary` |
| `system.color.fg.primary.*` | `system.color.brand.fg.primary.*` |
| `system.color.shadow.1` / `.2` | `system.color.shadow.base` / `.ambient` |
| `system.color.static.*` | A `base` palette ramp |

Only `bg.default` and `bg.alt.default` survive; prefer `surface.default` / `surface.alt.default`.

> **JSDoc caveat:** `@deprecated` replacement strings are written for humans, not as valid expressions. They say `sys.color.…` but the import is `system`. `brand.primary.600` is really the flat `brand.primary600`. Resolve against installed exports before writing.

### Current `system.color.*` inventory (107 tokens)

#### `system.color.surface` (29)

- `system.color.surface.default`
- `system.color.surface.navigation`
- `system.color.surface.popover`
- `system.color.surface.modal`
- `system.color.surface.raised`
- `system.color.surface.alt.default`
- `system.color.surface.alt.strong`
- `system.color.surface.loading`
- `system.color.surface.info.default`
- `system.color.surface.info.strong`
- `system.color.surface.danger.default`
- `system.color.surface.danger.strong`
- `system.color.surface.warning.default`
- `system.color.surface.warning.strong`
- `system.color.surface.success.default`
- `system.color.surface.success.strong`
- `system.color.surface.ai.default`
- `system.color.surface.ai.hover`
- `system.color.surface.ai.pressed`
- `system.color.surface.transparent`
- `system.color.surface.inverse`
- `system.color.surface.contrast.default`
- `system.color.surface.contrast.strong`
- `system.color.surface.overlay.hover.default`
- `system.color.surface.overlay.hover.inverse`
- `system.color.surface.overlay.mixin`
- `system.color.surface.overlay.pressed.default`
- `system.color.surface.overlay.pressed.inverse`
- `system.color.surface.overlay.scrim`

#### `system.color.fg` (20)

- `system.color.fg.muted.default`
- `system.color.fg.muted.strong`
- `system.color.fg.info.default`
- `system.color.fg.info.strong`
- `system.color.fg.default`
- `system.color.fg.strong`
- `system.color.fg.stronger`
- `system.color.fg.disabled`
- `system.color.fg.inverse`
- `system.color.fg.danger.default`
- `system.color.fg.danger.strong`
- `system.color.fg.warning.default`
- `system.color.fg.warning.strong`
- `system.color.fg.success.default`
- `system.color.fg.success.strong`
- `system.color.fg.link.default`
- `system.color.fg.link.hover`
- `system.color.fg.ai`
- `system.color.fg.contrast.default`
- `system.color.fg.contrast.strong`

#### `system.color.border` (11)

- `system.color.border.input.default`
- `system.color.border.input.hover`
- `system.color.border.contrast.default`
- `system.color.border.default`
- `system.color.border.strong`
- `system.color.border.info.default`
- `system.color.border.danger`
- `system.color.border.warning`
- `system.color.border.inverse.default`
- `system.color.border.inverse.strong`
- `system.color.border.transparent`

#### `system.color.accent` (11)

- `system.color.accent.ai`
- `system.color.accent.info`
- `system.color.accent.danger`
- `system.color.accent.warning`
- `system.color.accent.success`
- `system.color.accent.contrast`
- `system.color.accent.muted.default`
- `system.color.accent.muted.soft`
- `system.color.accent.overlay.hover`
- `system.color.accent.overlay.pressed`
- `system.color.accent.overlay.mixin`

#### `system.color.brand` (30)

- `system.color.brand.focus.primary`
- `system.color.brand.focus.critical`
- `system.color.brand.focus.caution.outer`
- `system.color.brand.focus.caution.inner`
- `system.color.brand.surface.primary.default`
- `system.color.brand.surface.primary.strong`
- `system.color.brand.surface.critical.default`
- `system.color.brand.surface.critical.strong`
- `system.color.brand.surface.caution.default`
- `system.color.brand.surface.caution.strong`
- `system.color.brand.surface.positive.default`
- `system.color.brand.surface.positive.strong`
- `system.color.brand.surface.selected`
- `system.color.brand.accent.primary`
- `system.color.brand.accent.critical`
- `system.color.brand.accent.caution`
- `system.color.brand.accent.positive`
- `system.color.brand.accent.action`
- `system.color.brand.fg.primary.default`
- `system.color.brand.fg.primary.strong`
- `system.color.brand.fg.critical.default`
- `system.color.brand.fg.critical.strong`
- `system.color.brand.fg.caution.default`
- `system.color.brand.fg.caution.strong`
- `system.color.brand.fg.positive.default`
- `system.color.brand.fg.positive.strong`
- `system.color.brand.fg.selected`
- `system.color.brand.border.primary`
- `system.color.brand.border.critical`
- `system.color.brand.border.caution`

#### `system.color.shadow` (2)

- `system.color.shadow.base`
- `system.color.shadow.ambient`

#### `system.color.focus` (2)

- `system.color.focus.inverse`
- `system.color.focus.contrast`

#### `system.color.bg` (2, legacy — prefer `surface.*`)

- `system.color.bg.default`
- `system.color.bg.alt.default`

## Spacing — three families, matched to the property

Using the wrong family is a semantic error even when the pixel value matches.

| CSS property | Family |
|---|---|
| `gap`, `rowGap`, `columnGap`, `margin*` | `system.gap.*` |
| `padding*` | `system.padding.*` |
| `width`, `height`, `min/max`, `inlineSize`, `blockSize` | `system.size.*` |

`system.size.*` tokens (`xxxs` through `xxl`) are **valid** for component dimensions. `system.space.x1`–`x20` are **deprecated** — there is no `system.space` in new code.

### `system.gap` (exterior space)

| Token | px |
|---|---|
| `gap.none` | 0 |
| `gap.xs` | 4 |
| `gap.sm` | 8 |
| `gap.md` | 16 |
| `gap.lg` | 24 |
| `gap.xl` | 32 |
| `gap.xxl` | 64 |

No 12px or 20px `gap` token — use `base.size150` / `base.size250`.

### `system.padding` (interior space)

| Token | px |
|---|---|
| `padding.none` | 0 |
| `padding.xxs` | 4 |
| `padding.xs` | 8 |
| `padding.sm` | 12 |
| `padding.md` | 16 |
| `padding.lg` | 20 |
| `padding.xl` | 24 |
| `padding.xxl` | 32 |

No 40px `padding` token — use `base.size500`.

### `system.size` (element dimensions)

| Token | px | Canvas Kit components |
|---|---|---|
| `size.xxxs` | 16 | Switch thumb, checkbox, pill icon button, loading dots, radio inner circle |
| `size.xxs` | 20 | Count badge min width, preview switch container, color swatches |
| `size.xs` | 24 | Extra-small buttons, status indicator, switch track, pills, radio, segmented control small, avatar extra-small |
| `size.sm` | 32 | Small buttons, switch track width, segmented control, avatar small, system icon circle medium |
| `size.md` | 40 | Medium buttons, multi-select input, avatar medium, system icon circle large |
| `size.lg` | 48 | Large buttons, tab items, avatar large |
| `size.xl` | 56 | Table cells and toasts (token package intent; rarely used directly in stencils) |
| `size.xxl` | 64 | Text area min height, table header min height |

See [size.md](./size.md) for component file references.

### Escape hatches

When no `system.size.*` token fits, use a `base.size*` step or `px2rem`. Do not compose `system.size` with `base.size` via `calc.add`.

```tsx
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
import {base} from '@workday/canvas-tokens-web';

// Nearest base size step (12px)
const compactStyles = createStyles({height: base.size150});

// Genuinely arbitrary dimension
const fixedStyles = createStyles({width: px2rem(236)});
```

`base.size*` px: `0`=0 · `25`=2 · `50`=4 · `75`=6 · `100`=8 · `150`=12 · `200`=16 · `250`=20 · `300`=24 · `400`=32 · `500`=40 · `600`=48 · `700`=56 · `800`=64.

## Shape — t-shirt sizes only

| Token | Default | Sana | Use |
|---|---|---|---|
| `shape.none` | 0 | same | Full-width containers, headers, side panels |
| `shape.xs` | — | 4px | Sana-only — compact rounding |
| `shape.sm` | 4px | 6px | Pills, status indicators, checkboxes |
| `shape.md` | 8px | same | Inputs, toasts, tooltips, snackbars |
| `shape.lg` | 12px | same | Rich text editors |
| `shape.xl` | 16px | same | Compact cards |
| `shape.xxl` | 24px | 20px | Cards, list items |
| `shape.xxxl` | 32px | 28px | Modals, dialogs, bottom sheets |
| `shape.full` | 999px | same | Buttons, badges, avatars — fully rounded |

The scalar scale (`x1`, `x1Half`, `x2`, `x4`, `x6`) plus `zero`, `half`, and `round` are deprecated. Use `none` (not `zero`) and `full` (not `round`). Pick by component type, not by matching a pixel value.

## Typography

Prefer Canvas text components (`Title`, `Heading`, `BodyText`, `Subtext`) with `size` props. In CSS:

```tsx
const labelStyles = createStyles({
  ...system.type.subtext.md,
  color: system.color.fg.muted.default,
});
```

- Levels: `system.type.{subtext|body|heading|title}.{sm|md|lg}`.
- **`.small` / `.medium` / `.large` are deprecated** — use `.sm` / `.md` / `.lg`.
- Pieces: `system.fontSize.body.md`, `system.lineHeight.body.md`, `system.fontFamily.{default|mono|global}`, `system.fontWeight.{light|normal|medium|bold}`.
- There is **no `system.font.*` namespace**.

Under Sana, typography also changes via the theme CSS (Sana Sans LCG 05 VF, adjusted body sizes, letter-spacing).

## Sana theme overrides

When `data-theme="sana-canvas"` is set, the Sana stylesheet reassigns tokens without changing JS paths. Key differences from the default theme:

| Category | What changes |
|---|---|
| Shape | `sm` 4→6px, `xxl` 24→20px, `xxxl` 32→28px; adds `xs` at 4px |
| Color | 88 semantic overrides (surfaces, borders, fg, brand accents, chart palettes) |
| Depth | All six `depth[1..6]` shadow values |
| Opacity | `accent.hover` (4.5%) and `accent.pressed` (11.8%) |
| Typography | Sana Sans, adjusted body/heading sizes, letter-spacing |
| Breakpoints | Adds `breakpoints.xxl` (2180px) |
| New in beta.11 | `surface.elevated`, `border.elevated` (Sana-only) |

Consumers do not import `system.sana.*` — the CSS cascade applies these overrides to the same `--cnvs-sys-*` variables that `system.*` references.

## Other non-color families

### `system.depth`

`[1]` · `[2]` · `[3]` · `[4]` · `[5]` · `[6]` — use whole: `boxShadow: system.depth[3]`.

### `system.opacity`

`zero` · `disabled` · `overlay` · `contrast` · `full` · `shadow.first` · `shadow.second` · `surface.hover` · `surface.pressed` · `accent.hover` · `accent.pressed`

### `system.breakpoints`

`zero` · `sm` · `md` · `lg` · `xl` (Sana adds `xxl`)

### `component.*`

- `component.systemIcon`: `color.*`, `size.xxs`–`size.xl`
- `component.expressiveIcon`: `color.*`, `size.xs`–`size.xl`

## v16 utilities

### `cornerShapeStencil`

Applies CSS `corner-shape` as progressive enhancement alongside `border-radius`. Used by Card, Menu, and MultiSelect in v16.

```tsx
import {cornerShapeStencil} from '@workday/canvas-kit-react/common';
import {createStencil} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

export const myStencil = createStencil({
  extends: cornerShapeStencil,
  base: {
    [cornerShapeStencil.vars.shape]: system.shape.xxl,
  },
});
```

### `colorSpace`

Theme-aware color mixing from `@workday/canvas-kit-styling`:

```tsx
import {colorSpace} from '@workday/canvas-kit-styling';

backgroundColor: colorSpace.hover({color: system.color.surface.alt.default}),
```

`hover()` and `pressed()` lighten in Sana Canvas and darken in the default theme, using overlay mixin tokens that the Sana CSS reassigns.

## Verification

```bash
node -p "require('@workday/canvas-tokens-web/package.json').version"
rg "system\.(space|shape\.(x\d|x1Half|half|zero|round))" src/
rg "system\.color\.(text|icon|bg)\." src/
rg "system\.legacy\." src/
rg "canvas-kit-react/tokens" src/
```

## Regenerating this inventory

After a token package bump:

```bash
node modules/mcp/build/generate-token-reference-data.mjs
rg "@deprecated" node_modules/@workday/canvas-tokens-web/dist/es6/system/index.d.ts -A3
```

## Related documentation

| Document | Scope |
|---|---|
| This file | Current v4.4 token surface |
| `tokens/v4/v4-token-migration.md` | v3 → v4 migration tables |
| `llm-token-migration-14.txt` | Historical v13→v14 migration |
| `tokens/token-migration.md` | Historical v2→v3 fruity palette |
| `upgrade-guides/16.0-UPGRADE-GUIDE.md` | Canvas Kit v16 component changes |
| `theming.md` | CSS variable theming patterns |
