Skip to main content

Migration Guide

Background​

The previous token system had components reference primitive values directly, such as color.gray500 or spacing[3]. Because values were tied directly to their usage, changing a brand color or theme meant hunting down and editing every reference point.

This overhaul separates that connection into a semantic (role) layer. Components reference only "what is this color used for" — like vars.color.accent.default — while the actual value is decided in Figma and injected through the tokens.json → Style Dictionary → CSS variables → vars.* pipeline. As a result, cohort-specific brand color swaps or theme changes propagate from a single place without touching individual components.


The Three Token Layers​

tokens.json (Figma source)
↓ Style Dictionary build
primitive.css ← Layer 1: raw palette values
↓ semantic reference
semantic-dark.css ← Layer 2: role / intent mapping
↓ Vanilla Extract contract
vars.* ← Layer 3: type-safe variables consumed by components

Layer 1: Primitive​

The raw palette auto-generated into dist/css/primitive.css. It includes colors (--color-gray-500), spacing (--spacing-8), radius (--radius-4), and typography base values.

Components never reference these directly. The Layer 2 semantic tokens reference these values.

Layer 2: Semantic​

The role mapping auto-generated into dist/css/semantic-dark.css. It captures design decisions such as "what should the background color be?" or "what is the hover state of the accent color?".

--color-background-muted: var(--color-gray-800);  /* disabled background */
--color-accent-default: var(--color-brand-default); /* default brand accent */
--spacing-component-md: var(--spacing-12); /* input horizontal padding */

Layer 3: Component (vars.*)​

The vars object exported from @sipe-team/tokens. Generated via Vanilla Extract's createGlobalThemeContract, it lets you reference the Layer 2 CSS variables in a type-safe way.

import { vars } from '@sipe-team/tokens';

// Used in component styles
const button = style({
backgroundColor: vars.color.accent.default,
padding: `${vars.spacing.component.sm} ${vars.spacing.component.md}`,
borderRadius: vars.radius.component.md,
});

Migration Mapping Tables​

Before reading the tables
  • ✅ Value is unchanged, safe to swap / ⚠️ Value or hue changed, visual check needed
  • — means there is no matching token, so use a CSS literal directly
  • All color values shown are based on the dark theme.

Color​

Primitive / hardcoded → vars.color.*​

color.* primitive values are replaced with semantic tokens based on their purpose of use.

BeforeAfterActual value (dark)Purpose
color.black / '#000'vars.color.background.default#000000Default page / modal background
color.gray900 / '#18181b'vars.color.background.subtle#18181bCard / panel surface background
color.gray800 / '#27272a'vars.color.background.muted#27272aDisabled input field / skeleton background
color.white (text)vars.color.foreground.default#ffffffDefault body / heading text
color.gray400vars.color.foreground.subtle#a1a1aaSecondary description / hint text
color.gray500vars.color.foreground.muted#71717aPlaceholder / inactive icons
color.white (on accent background)vars.color.foreground.onAccent#ffffffCTA button label / badge text
color.gray700vars.color.border.default#3f3f46Default input / card border
color.gray500 (emphasized border)vars.color.border.strong#71717aHover input emphasized border
outline: 2px solid ${buttonOrange}vars.color.border.focus#f97316Keyboard focus ring

semanticColor.* → vars.color.status.*​

Status colors align all foregrounds to the 400 step for contrast on dark backgrounds (the old success/danger were at the 500 step). warning moved to the yellow family because the old orange could be confused with danger.

BeforeAfterBefore valueAfter valueNotes
semanticColor.successvars.color.status.success.foreground#22c55e (green500)#4ade80 (green400)⚠️ Value changed (500→400)
semanticColor.warningvars.color.status.warning.foreground#fb923c (orange400)#facc15 (yellow400)⚠️ Hue changed (orange→yellow)
semanticColor.dangervars.color.status.danger.foreground#ef4444 (red500)#f87171 (red400)⚠️ Value changed (500→400)
semanticColor.positivevars.color.status.info.foreground#60a5fa (blue400)#60a5fa (blue400)✅ Value unchanged

brandColor.* → vars.color.accent.*​

warning

Due to the brand color adjustment in #293, the actual color values have changed. Any place that hardcoded the old brandColor.default (#ffb24d) will see a visual change.

BeforeAfterBefore valueAfter valueNotes
brandColor.defaultvars.color.accent.default#ffb24d#f97316 (orange500)⚠️ Value changed
brandColor.hovervars.color.accent.hover#d9963f#fb923c (orange400)⚠️ Value changed
brandColor.subtlevars.color.accent.subtle#3b2005#3b1106 (orange950)⚠️ Value changed

themeColor.* → vars.color.accent.*​

BeforeAfterNotes
theme5th.primary / '#FF7C27'vars.color.accent.defaultDefault brand color
'linear-gradient(225deg, #FF4500 0%, #FFB24D 100%)'vars.color.accent.hoverGradient → solid color replacement
theme5th.secondary / '#FE4E07'vars.color.accent.pressedAdded to the contract
'#000' (fill button text)vars.color.foreground.onAccent⚠️ onAccent is white (#fff). If black text is intended, confirm with design and handle separately
Entire theme1st ~ theme5th objectsRemovedUse vars.color.* directly

Spacing​

info

Type change: The old spacing[n] was a unitless number (12), whereas the new vars.spacing.* is a CSS variable string (var(--side-spacing-component-md) → 12px).

Before (spacing[n])After (vars.spacing.*)ValueNotes
spacing[0]00✅ Use directly
spacing[1]vars.spacing.component.xs4px✅ Value unchanged
spacing[2]vars.spacing.component.sm8px✅ Value unchanged
spacing[3] / '0 12px'vars.spacing.component.md12px✅ Value unchanged
spacing[4]vars.spacing.component.lg16px✅ Value unchanged
spacing[5]—20px⚠️ No semantic token → use '20px' directly
spacing[6]vars.spacing.component.xl24px✅ Value unchanged
spacing[8]vars.spacing.layout.sm32px✅ Value unchanged
spacing[10]vars.spacing.layout.md40px✅ Value unchanged
spacing[12]vars.spacing.layout.lg48px✅ Value unchanged
spacing[16]vars.spacing.layout.xl64px✅ Value unchanged
spacing[20]—80px⚠️ No semantic token → use '80px' directly
spacing[24]—96px⚠️ No semantic token → use '96px' directly

Typography​

info

Type change: The old fontSize[n], fontWeight.*, and lineHeight.* were numeric values, whereas the new vars.typography.* are CSS variable strings.

Font Size​

Before (fontSize[n])After (vars.typography.fontSize)Value
fontSize[12]vars.typography.fontSize['050']12px
fontSize[14]vars.typography.fontSize['100']14px
fontSize[16]vars.typography.fontSize['200']16px
fontSize[18]vars.typography.fontSize['300']18px
fontSize[20]vars.typography.fontSize['400']20px
fontSize[24]vars.typography.fontSize['500']24px
fontSize[28]vars.typography.fontSize['600']28px
fontSize[32]vars.typography.fontSize['700']32px
fontSize[36]vars.typography.fontSize['800']36px
fontSize[48]vars.typography.fontSize['900']48px

Font Weight​

Before (fontWeight.*)After (vars.typography.fontWeight.*)Value
fontWeight.regularvars.typography.fontWeight.regular400
fontWeight.mediumvars.typography.fontWeight.medium500
fontWeight.semiBoldvars.typography.fontWeight.semiBold600
fontWeight.boldvars.typography.fontWeight.bold700

Line Height / Font Family​

BeforeAfter (vars.typography.*)Value
lineHeight.regular / '150%'vars.typography.lineHeight.regular1.5
lineHeight.compactvars.typography.lineHeight.compact1.3
—vars.typography.fontFamily'Pretendard', sans-serif (new)

Radius​

Before (radius.* / hardcoded)After (vars.radius.*)ValueNotes
radius.none / '0'0—Not in contract, use directly
radius.sm / '2px'vars.radius.component.sm2px✅ Value unchanged
radius.md / '4px'vars.radius.component.md4px✅ Value unchanged
'6px' (hardcoded)——⚠️ Confirm with design, then snap to component.md (4px) or component.lg (8px)
radius.lg / '8px'vars.radius.component.lg8px✅ Value unchanged
radius.xl / '12px'vars.radius.component.xl12px✅ Value unchanged
radius.full / '9999px'vars.radius.component.full9999px✅ Value unchanged

Shadows​

shadows.* maps directly to vars.shadows.*.

Before (shadows.*)After (vars.shadows.*)
shadows.nonevars.shadows.none
shadows.smvars.shadows.sm
shadows.mdvars.shadows.md
shadows.lgvars.shadows.lg
shadows.xlvars.shadows.xl
shadows['2xl']vars.shadows['2xl']

Deprecated — No Direct Replacement​

danger

The tokens below are marked @deprecated and have no corresponding slot in the new contract. Use CSS literal values directly, or design a separate solution.

BeforeRecommended alternativeNotes
borderWidth.thin (1)'1px'
borderWidth.medium (2)'2px'
borderWidth.thick (4)'4px'
borderStyle.solid'solid'
opacity[50] (0.5)0.5
zIndex.modal (1400)1400⚠️ Consider adding to contract in the future
zIndex.tooltip (1700)1700
breakpoints.md (780)'@media (min-width: 780px)'
breakpointQuery.* / responsiveStyleWrite media queries directly
grid.columns / grid.gutter.* / grid.container.*—⚠️ No replacement