02 — Design Tokens Extraction from Figma
Dev Guide — Savoy Signature Hotels
PRD refs:05_Design_System_and_Theming.md,A03_Design_Tokens.md
1. Purpose
Section titled “1. Purpose”This guide defines how to extract design tokens from Figma files and translate them into the CSS variable system used by the Savoy platform. Design tokens are the foundation of the multi-site theming system — every visual property flows from tokens.
2. Token Architecture
Section titled “2. Token Architecture”Figma Styles & Variables | vpackages/themes/src/ _base.css <-- Shared tokens (spacing, breakpoints, typography scale, z-index) savoy-palace.css <-- Theme-specific tokens (colors, fonts, shadows) royal-savoy.css saccharum.css ... (8 theme files) index.css <-- Imports _base + all themesKey principle: Base tokens are shared across all sites. Theme tokens vary per site and are scoped under [data-theme="site-key"].
3. Extraction Process
Section titled “3. Extraction Process”3.1 Identify Figma Sources
Section titled “3.1 Identify Figma Sources”In the Figma file, tokens are typically found in:
| Figma Location | Token Category |
|---|---|
| Local Styles > Colors | --color-* tokens |
| Local Styles > Text | --font-*, --text-* tokens |
| Local Styles > Effects | --shadow-* tokens |
| Figma Variables (if used) | Spacing, breakpoints, radii |
| Component padding/margin inspector | --space-* references |
3.2 Color Tokens
Section titled “3.2 Color Tokens”For each hotel theme in Figma, extract the color palette:
Figma inspection:
- Select a primary-colored element in the Figma hotel design
- In the right panel, note the fill color hex value
- Map it to the correct token name
Token naming convention:
| Role | Token Name | Example Value (Savoy Palace) |
|---|---|---|
| Primary brand color | --color-primary | #1a365d |
| Primary lighter | --color-primary-light | #2a5a8e |
| Primary darker | --color-primary-dark | #0f2340 |
| Secondary/accent | --color-secondary | #c9a96e |
| Secondary lighter | --color-secondary-light | #d4bc8e |
| Secondary darker | --color-secondary-dark | #b08f4f |
| Highlight accent | --color-accent | #e8d5b7 |
| Page background | --color-bg | #faf9f7 |
| Alt background | --color-bg-alt | #f2efe9 |
| Dark background | --color-bg-dark | #1a1a1a |
| Surface (cards) | --color-surface | #ffffff |
| Body text | --color-text | #1a1a1a |
| Muted text | --color-text-muted | #6b7280 |
| Inverse text (on dark bg) | --color-text-inverse | #ffffff |
| Text on primary | --color-text-on-primary | #ffffff |
| Text on secondary | --color-text-on-secondary | #1a1a1a |
| Borders | --color-border | #e5e2dc |
| Strong borders | --color-border-strong | #c9c4bb |
| Success | --color-success | #059669 |
| Warning | --color-warning | #d97706 |
| Error | --color-error | #dc2626 |
3.3 Typography Tokens
Section titled “3.3 Typography Tokens”From Figma:
- Select a heading text element
- Note: font family, weight, size
- Map to tokens
Per-theme tokens (fonts vary by hotel):
| Token | Description | Example |
|---|---|---|
--font-heading | Heading font stack | 'Playfair Display', serif |
--font-body | Body text font | 'Inter', sans-serif |
--font-accent | Decorative/accent font | 'Cormorant Garamond', serif |
--font-weight-normal | Regular weight | 400 |
--font-weight-medium | Medium weight | 500 |
--font-weight-semibold | Semibold weight | 600 |
--font-weight-bold | Bold weight | 700 |
Shared tokens (same across all themes) — in _base.css:
| Token | Value | Figma Size |
|---|---|---|
--text-xs | 0.75rem | 12px |
--text-sm | 0.875rem | 14px |
--text-base | 1rem | 16px |
--text-lg | 1.125rem | 18px |
--text-xl | 1.25rem | 20px |
--text-2xl | 1.5rem | 24px |
--text-3xl | 1.875rem | 30px |
--text-4xl | 2.25rem | 36px |
--text-5xl | 3rem | 48px |
--text-6xl | 3.75rem | 60px |
3.4 Spacing Tokens
Section titled “3.4 Spacing Tokens”Spacing is shared (not theme-specific). Extract from Figma’s padding/margin values:
| Token | Value | Usage |
|---|---|---|
--space-1 | 0.25rem (4px) | Tight inner spacing |
--space-2 | 0.5rem (8px) | Small gaps |
--space-3 | 0.75rem (12px) | Icon-text spacing |
--space-4 | 1rem (16px) | Standard padding |
--space-6 | 1.5rem (24px) | Section inner padding |
--space-8 | 2rem (32px) | Module padding (mobile) |
--space-12 | 3rem (48px) | Module padding (tablet) |
--space-16 | 4rem (64px) | Module padding (desktop) |
--space-20 | 5rem (80px) | Large section gaps |
--space-24 | 6rem (96px) | Hero padding |
Rule: If a Figma spacing value doesn’t match the scale, round to the nearest token. Never introduce one-off spacing values.
3.5 Shadow Tokens
Section titled “3.5 Shadow Tokens”Per-theme (shadow colors may use the primary color with opacity):
| Token | Example (Savoy Palace) |
|---|---|
--shadow-sm | 0 1px 2px rgba(26, 54, 93, 0.05) |
--shadow-md | 0 4px 12px rgba(26, 54, 93, 0.08) |
--shadow-lg | 0 8px 24px rgba(26, 54, 93, 0.12) |
4. Implementation
Section titled “4. Implementation”4.1 Adding a New Theme
Section titled “4.1 Adding a New Theme”When Figma delivers a new hotel’s design system:
[data-theme="hotel-next"] { /* -- Colors -- */ --color-primary: #...; --color-primary-light: #...; --color-primary-dark: #...; --color-secondary: #...; /* ... complete palette */
/* -- Typography -- */ --font-heading: '...', serif; --font-body: '...', sans-serif; /* ... weights */
/* -- Shadows -- */ --shadow-sm: ...; --shadow-md: ...; --shadow-lg: ...;}Then import in index.css:
@import './_base.css';@import './savoy-signature.css';@import './savoy-palace.css';/* ... */@import './hotel-next.css';4.2 Adding Fonts
Section titled “4.2 Adding Fonts”Register the font in apps/web/src/lib/fonts.ts:
import { NewFont } from 'next/font/google';
const newFont = NewFont({ subsets: ['latin'], variable: '--font-heading', display: 'swap',});
const THEME_FONTS: Record<string, string> = { 'hotel-next': `${newFont.variable} ${inter.variable}`, // ...};5. Validation Checklist
Section titled “5. Validation Checklist”After extracting and implementing tokens for a theme:
- All color tokens have sufficient contrast (4.5:1 for text, 3:1 for large text)
- Font families load correctly (check Network tab — no 404s)
- No layout shift from font loading (
font-display: swap) - Theme file < 5KB gzipped
- Storybook theme switcher shows the new theme
- All existing components render correctly with the new theme
- Tokens match Figma specifications exactly (spot-check 5 colors, 3 font sizes)
6. Figma-to-CSS Quick Mapping
Section titled “6. Figma-to-CSS Quick Mapping”When inspecting a Figma element, translate properties:
| Figma Inspector | CSS Implementation |
|---|---|
Fill: #1a365d | background-color: var(--color-primary) (match to nearest token) |
Text: Inter, 16px, Regular | font-family: var(--font-body); font-size: var(--text-base); font-weight: var(--font-weight-normal) |
Padding: 32px 24px | padding: var(--space-8) var(--space-6) |
Gap: 16px | gap: var(--space-4) |
Corner radius: 8px | border-radius: var(--radius-md) |
Shadow: 0 4px 12px rgba(...) | box-shadow: var(--shadow-md) |
| Opacity text: 60% | color: var(--color-text-muted) (use semantic token, not opacity) |