Design tokens

Shared design values for web following the DSCG v1 spec.

Colors

Background surface

text

button

border

semantic

neutral

orange

red

yellow

green

blue

purple

pink

Shadows

Shadow tokens provide consistent elevation across components. Each shadow is tuned for a specific UI context. Click any swatch to copy its Tailwind class.

Focus rings

Typography

The type system uses Instrument Sans with custom OpenType feature settings. All sizes bundle font-size, line-height, and letter-spacing into a single Tailwind class.

Type scale

ClassSizeLine heightLetter spacingUse
text-display-lg52px56px-0.02emHero headlines
text-display36px40px-0.02emPage titles
text-display-sm32px32px-0.02emSection titles
text-headline-lg30px36px-0.01emLarge headings
text-headline24px28px-0.01emStandard headings
text-headline-sm16px20px0Small headings
text-body-lg-long16px24px0Large body, multi-line
text-body-lg16px20px0Large body, single-line
text-body-long14px20px0Body text, multi-line
text-body14px17px0Default body text
text-body-sm12px15px0Captions, fine print
3 lines
<h1 className="text-display font-medium">Page title</h1>
<p className="text-body-long text-secondary">Description text with comfortable reading line height.</p>
<span className="text-body-sm text-tertiary">Caption</span>

Font weights

Two weights are defined as custom values to align with Instrument Sans optical weight:

ClassValue
font-normal450
font-medium550

OpenType features

The default font-feature-settings are applied globally: 'ss01' (rounded dots), 'ss03' (alternate y), 'ss12' (alternate currency), 'ss09' (alternate Q), 'kern'.

Link tokens and utilities handle external and internal link styling with built-in hover states.

Styles an element as an external link using color-link (blue-500). On hover, the color shifts to color-link-hover and an underline appears.

3 lines
<a href="https://wander.com" className="text-link">
  Visit Wander
</a>

Styles an element as an internal link using the primary text color. On hover, an underline appears. Use this for in-app navigation where blue link styling is not appropriate.

3 lines
<a href="/docs/components/button" className="text-link-internal">
  Button component
</a>

Easing

18 easing curves across three families. Use with the ease-* utility class for transitions and animations.

FamilyCurves
Ease inease-in-quad, ease-in-cubic, ease-in-quart, ease-in-quint, ease-in-expo, ease-in-circ
Ease outease-out-quad, ease-out-cubic, ease-out-quart, ease-out-quint, ease-out-expo, ease-out-circ
Ease in-outease-in-out-quad, ease-in-out-cubic, ease-in-out-quart, ease-in-out-quint, ease-in-out-expo, ease-in-out-circ
7 lines
<div className="transition-transform duration-300 ease-out-cubic hover:scale-105">
  Smooth scale on hover
</div>

<div className="transition-opacity duration-500 ease-in-out-quart">
  Fade with quartic easing
</div>

Spacing

A t-shirt scale for layout rhythm — section padding, vertical stacks, content gaps. Namespaced under layout- so it doesn't shadow Tailwind's container scale (max-w-2xl etc. continue to work as expected). Use with any spacing utility: p-layout-md, gap-layout-2xl, my-layout-7xl.

ClassValue
layout-4xs2px
layout-3xs4px
layout-2xs8px
layout-xs12px
layout-sm16px
layout-md24px
layout-lg32px
layout-xl40px
layout-2xl48px
layout-3xl56px
layout-4xl64px
layout-5xl72px
layout-6xl80px
layout-7xl96px
layout-8xl128px

For finer-grained spacing, Tailwind's numeric scale (p-1, p-2, …) remains available.

Breakpoints

Responsive breakpoints for use with Tailwind responsive prefixes (sm:, md:, etc.).

PrefixWidthPixels
sm40rem640px
md46.5rem744px
lg65rem1040px
xl80.5rem1288px
2xl90rem1440px
3xl104.75rem1676px
4xl120.5rem1928px

The md breakpoint (744px) remains the viewport threshold used by useIsDesktop and useIsMobile in the shared utils. Core components that opt into container-driven desktop/mobile presentation use the lg breakpoint (65rem / 1040px) so embedded previews switch from their available width.

WanderOS tokens

Required for the WanderOS website, layering a product-specific set of spacing and radius tokens on top of the core stylesheet. Distributed as a separate import so consumers outside of WanderOS websites are unaffected.

2 lines
import '@wandercom/design-system-tokens/tailwind.css';
import '@wandercom/design-system-tokens/websites.css';

Spacing

Intent-named spacing for section padding, content gaps, and card padding. Each aliases the layout scale, so changes to a base step propagate everywhere. Available as p-*, m-*, gap-*, etc.

ClassMaps toValue
sectionlayout-7xl96px
section-smlayout-2xl48px
content-gaplayout-xl40px
content-gap-smlayout-sm16px
content-gap-lglayout-2xl48px
card-paddinglayout-md24px
card-padding-lglayout-lg32px
bleedlayout-md24px

Radius scale

Primitive stops for use with rounded-* utilities.

ClassValue
rounded-xs2px
rounded-sm6px
rounded-md8px
rounded-lg10px
rounded-xl12px

Radius aliases

Semantic aliases that map to the primitive scale per UI context. Prefer these over the primitives when the use is intent-driven.

ClassMaps toValue
rounded-sectionxl12px
rounded-section-smlg10px
rounded-cardxl12px
rounded-mediaxl12px
rounded-media-lg2xl (Tailwind default)16px
rounded-inputlg10px
rounded-dropdownxl12px
rounded-popover3xl (Tailwind default)24px
rounded-modal3xl (Tailwind default)24px
rounded-tooltip2xl (Tailwind default)16px

Tokens are sourced from packages/tokens/src/websites.tokens.json and compiled into dist/websites.css during build:design-system.

Custom utilities

Beyond standard Tailwind, the token stylesheet provides compound utilities that wire up interactive states automatically.

bg-button-*

Applies background, text color, hover, and focus-visible states for a button variant in one class.

2 lines
<button className="bg-button-primary rounded-lg px-4 py-2">Primary</button>
<button className="bg-button-checkout rounded-lg px-4 py-2">Book now</button>

Available variants: primary, secondary, outline, ghost, destructive, checkout, disabled, slider-thumb.

border-badge-*

Semantic border colors for badge variants. Each token encodes the correct color and alpha for light and dark modes, removing the need for manual opacity modifiers.

ClassLightDark
border-badge-defaultneutral-900 / 10%neutral-50 / 16%
border-badge-successsuccess / 10%success / 20%
border-badge-errorerror / 10%error / 20%
border-badge-warningwarning / 10%warning / 20%
border-badge-infoinfo / 10%info / 24%
5 lines
<Badge variant="neutral">Default</Badge>
<Badge variant="success">Active</Badge>
<Badge variant="error">Failed</Badge>
<Badge variant="warning">Pending</Badge>
<Badge variant="info">Verified</Badge>

shadow-focus / shadow-focus-destructive

Focus ring tokens using a double box-shadow (inner surface ring + outer accent ring).

2 lines
<button className="focus-visible:shadow-focus rounded-lg">Focusable</button>
<button className="focus-visible:shadow-focus-destructive rounded-lg">Destructive focus</button>

touch-hitbox

Expands the tap target to a minimum 44x44px hit area using a pseudo-element overlay, without changing visual size.

3 lines
<button className="touch-hitbox">
  <SmallIcon />
</button>

scrollbar / scrollbar-thin / scrollbar-none

Control scrollbar appearance using native CSS scrollbar properties.

2 lines
<div className="scrollbar-thin overflow-y-auto">Thin scrollbar</div>
<div className="scrollbar-none overflow-y-auto">Hidden scrollbar</div>

Installation

1 lines
pnpm add @wandercom/design-system-tokens

See the installation guide for authentication setup.

Usage

Web

Import tokens in your application:

1 lines
import '@wandercom/design-system-tokens/tailwind.css';

For WanderOS product projects, also import the WanderOS tokens to pick up its spacing and radius tokens:

1 lines
import '@wandercom/design-system-tokens/websites.css';

Tokens are available as Tailwind classes:

21 lines
<h1 className="text-display font-medium">Welcome to Wander</h1>

<div className="bg-surface-primary text-primary">
  Content with design tokens
</div>

<a href="/properties" className="text-link-internal">
  Browse properties
</a>

<button className="bg-button-checkout focus-visible:shadow-focus rounded-lg px-4 py-2">
  Book now
</button>

<div className="shadow-dropdown rounded-xl border-overlay-primary bg-surface-dropdown p-2">
  Dropdown menu content
</div>

<div className="transition-transform duration-300 ease-out-cubic hover:scale-105">
  Animated element
</div>

Color engine

A self-contained color system built on OKLCh perceptual color space with WCAG contrast compliance. Given three seed colors, the engine generates full scales, resolves ~55 semantic tokens per background context, and exports to CSS or JSON.

1 lines
pnpm add @wandercom/design-system-tokens
6 lines
import {
  generateScales,
  resolveRoleTokens,
  generateExportCSS,
  type SeedColors,
} from '@wandercom/design-system-tokens/color-engine';

Seed colors

The entire palette derives from three hex seed colors:

7 lines
import type { SeedColors } from '@wandercom/design-system-tokens/color-engine';

const seeds: SeedColors = {
  brand: '#1a1a2e',           // dominant brand hue (placed at scale stop 500)
  accentPrimary: '#e94560',   // action/CTA color
  accentSecondary: '#0f3460', // supporting variety color
};

Generating scales

generateScales produces a 9-stop scale (50, 100, 200, 300, 500, 700, 800, 900, 950) for each seed, preserving hue and scaling chroma proportionally.

6 lines
import { generateScales } from '@wandercom/design-system-tokens/color-engine';

const scales = generateScales(seeds);

scales.brand[500];          // seed color
scales.accentPrimary[50];   // lightest accent stop

For a single color, use generateScale:

3 lines
import { generateScale } from '@wandercom/design-system-tokens/color-engine';

const brandScale = generateScale('#1a1a2e');

Presets

11 named palettes are available as starting points:

4 lines
import { PRESETS } from '@wandercom/design-system-tokens/color-engine';

const seeds = PRESETS['Ocean Calm'];
const scales = generateScales(seeds);

Available presets: Classic Persian, Pink Navy, Mauve Garden, Wander, Electric Clash, Ocean Calm, Sunset Warmth, Forest Dew, Coral Reef, Signal Pop, Heritage.

Resolving semantic tokens

Given a background context, the engine resolves a full set of semantic tokens (surfaces, text, buttons, borders, semantic colors) with WCAG-compliant contrast ratios.

9 lines
import { resolveRoleTokens, type BackgroundRole } from '@wandercom/design-system-tokens/color-engine';

const { background, foreground, tokens } = resolveRoleTokens('theme-base', scales);

tokens.text.primary;           // WCAG AA against background
tokens.surface.secondary;      // slightly offset surface
tokens.button.background;      // CTA button fill
tokens.border.primary;         // default border
tokens.semantic.error;         // error color for the mode

BackgroundRole is a union of all supported contexts:

GroupRoles
Neutralwhite, black
Themetheme-light, theme-base, theme-dark
Accent Primaryaccent-primary-light, accent-primary-base
Accent Secondaryaccent-secondary-light, accent-secondary-base, accent-secondary-dark

Section and nav tokens

For page layout sections (optionally with background images) and navigation bars:

18 lines
import {
  resolveSectionTokens,
  resolveNavTokens,
  type SectionConfig,
} from '@wandercom/design-system-tokens/color-engine';

const hero: SectionConfig = { role: 'theme-dark', img: true };

const section = resolveSectionTokens(hero, scales);
section.background;            // hex background
section.effectiveBackground;   // after image overlay blending
section.tokens.text.primary;   // collapsed to single level when img is present

const nav = resolveNavTokens(hero, scales);
nav.background;                // "transparent" for hero overlap
nav.backgroundBlur;            // frosted-glass tint
nav.ctaBackground;             // CTA button fill
nav.isTransparent;             // true when nav overlays hero

Color space utilities

Low-level color functions for conversion, contrast checking, and manipulation:

17 lines
import {
  hexToOklch,
  oklchToHex,
  contrastRatio,
  ensureContrast,
  mixColors,
  alphaBlend,
  getLuminance,
  WCAG_AA,
} from '@wandercom/design-system-tokens/color-engine';

hexToOklch('#e94560');                          // { L, C, h } in OKLCh
oklchToHex(0.7, 0.15, 25);                     // hex with gamut clamping
contrastRatio('#ffffff', '#1a1a2e');             // WCAG 2.x ratio [1, 21]
ensureContrast('#888888', '#ffffff', WCAG_AA);  // adjusted hex meeting 4.5:1
mixColors('#ff0000', '#0000ff', 50);            // linear sRGB interpolation
alphaBlend('#000000', '#ffffff', 0.5);          // composite at alpha

Exporting tokens

Generate CSS custom properties or structured JSON for external consumers:

7 lines
import { generateExportCSS, generateExportJSON } from '@wandercom/design-system-tokens/color-engine';

// CSS with [data-theme="..."] attribute selectors per background role
const css = generateExportCSS(scales);

// Structured JSON with seeds, primitives (scale stops), and modes (semantic tokens)
const json = generateExportJSON(seeds, scales);

The CSS output uses data-theme attribute selectors, mapping semantic tokens to CSS custom properties (e.g., --color-primary, --color-surface-secondary).

Constants

WCAG contrast thresholds are exported for use in custom validation:

7 lines
import {
  WCAG_AA,              // 4.5 — normal-size text
  WCAG_AA_LARGE,        // 3   — large text / UI elements
  WCAG_AAA,             // 7   — enhanced contrast
  WCAG_TEXT_SECONDARY,  // 5.5 — secondary text
  WCAG_TEXT_MUTED,      // 3.5 — muted/placeholder text
} from '@wandercom/design-system-tokens/color-engine';