Skip to Content
Config SystemDefining Design Tokens

Defining Design Tokens

Declare your project’s design tokens in foir.config.ts to keep them in source control alongside your models and operations. The designTokens block holds a W3C Design Tokens Format Module  document; foir push reconciles it to the platform.

See the Design Tokens feature page for what tokens are, how draft/published works, and the admin editor.

Quick start

import { defineConfig, defineDesignTokens } from '@eide/foir-cli/configs'; export default defineConfig({ key: 'my-storefront', name: 'My Storefront', designTokens: defineDesignTokens({ color: { $type: 'color', brand: { primary: { $value: '#2c4433', $description: 'Deep forest green' }, accent: { $value: '#d4553a' }, }, }, spacing: { $type: 'dimension', xs: { $value: '8px' }, sm: { $value: '12px' }, md: { $value: '16px' }, lg: { $value: '24px' }, }, }), });

Push it:

foir push --publish

--publish applies the tokens to the draft channel and promotes them to the published channel. Without it, tokens land on draft and your storefront keeps serving the last published snapshot.

The designTokens block

The block is a W3C document — top-level keys are token groups, leaves carry $value and optionally $description, $extensions. Group-level $type cascades to leaves so you don’t repeat the type on every entry.

designTokens: { color: { $type: 'color', brand: { primary: { $value: '#2c4433' }, }, }, font: { family: { $type: 'fontFamily', display: { $value: ['Inter Display', 'system-ui', 'sans-serif'] }, body: { $value: ['Inter', 'system-ui', 'sans-serif'] }, }, weight: { $type: 'fontWeight', regular: { $value: 400 }, bold: { $value: 700 }, }, size: { $type: 'dimension', body: { $value: '1rem' }, display1: { $value: 'clamp(2.5rem, 6vw, 5rem)' }, }, lineHeight: { $type: 'number', body: { $value: 1.6 }, display: { $value: 0.95 }, }, letterSpacing: { $type: 'dimension', normal: { $value: '0' }, tight: { $value: '-0.02em' }, }, }, typography: { $type: 'typography', body: { $value: { fontFamily: '{font.family.body}', fontSize: '{font.size.body}', fontWeight: '{font.weight.regular}', lineHeight: '{font.lineHeight.body}', letterSpacing: '{font.letterSpacing.normal}', }, $extensions: { 'foir.label': 'Body', 'foir.tag': 'p' }, }, }, spacing: { $type: 'dimension', xs: { $value: '8px' }, md: { $value: '16px' }, lg: { $value: '24px' }, }, radius: { $type: 'dimension', none: { $value: '0' }, md: { $value: '8px' }, full: { $value: '9999px' }, }, shadow: { $type: 'shadow', elevated: { $value: [ { offsetX: '0px', offsetY: '1px', blur: '2px', spread: '0px', color: '#0000001a' }, { offsetX: '0px', offsetY: '4px', blur: '8px', spread: '0px', color: '#00000033' }, ], }, }, border: { $type: 'border', hairline: { $value: { width: '1px', style: 'solid', color: '{color.brand.primary}' }, }, }, }

Group reference

Every group key is optional. Include only what your project needs.

Group$type$value shapeNotes
color.*colorstringAny CSS color: hex, rgb(), hsl(), named color.
font.family.*fontFamilystring[]Primary + fallbacks.
font.weight.*fontWeightnumber100–900.
font.size.*dimensionstringAny CSS length: 1rem, 16px, clamp(...).
font.lineHeight.*numbernumberUnitless multiplier.
font.letterSpacing.*dimensionstring0, 0.02em, -1px.
typography.*typographyobject{ fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, textTransform? }. Reference primitives with {path}.
spacing.*dimensionstringScale steps for padding, margin, gap.
radius.*dimensionstringCorner-radius scale.
shadow.*shadowobject or object[]Single layer or list of layers. Each layer: { offsetX, offsetY, blur, spread, color, inset? }.
border.*borderobject{ width, style, color }.

Top-level groups that don’t match one of the names above are accepted and stored verbatim — Foir surfaces them under source for build tooling but doesn’t expose them through a typed resolved array.

References

Any $value (or any field inside a composite $value) accepts the {path.to.token} reference syntax. References resolve recursively; cycles and unknown references are rejected at save time.

{ color: { $type: 'color', brand: { primary: { $value: '#2c4433' } }, surface: { accent: { $value: '{color.brand.primary}' } }, // → #2c4433 }, border: { $type: 'border', stamp: { $value: { width: '1px', style: 'solid', color: '{color.brand.primary}' }, }, }, }

Extensions

Use $extensions to attach editor metadata without affecting the resolved value.

ExtensionApplies toPurpose
foir.labelany tokenOverrides the editor display name.
foir.tagtypography tokensDefault semantic HTML tag (p, h1h4, blockquote).
foir.textTransformtypography tokensuppercase / lowercase / capitalize. Surfaces as textTransform on the resolved value.
foir.fontStyletypography tokensitalic / normal.
display1: { $value: { /* ... */ }, $extensions: { 'foir.label': 'Display 1', 'foir.tag': 'h1', 'foir.textTransform': 'uppercase', }, }

Helper functions

defineDesignTokens

Identity helper that gives you TypeScript IntelliSense on the designTokens shape.

import { defineDesignTokens } from '@eide/foir-cli/configs'; const tokens = defineDesignTokens({ color: { $type: 'color', brand: { primary: { $value: '#2c4433' } } }, });

The interface is permissive ([key: string]: unknown), so additional groups beyond color, font, and typography — like spacing, radius, shadow, border — are accepted at runtime.

Push behaviour

foir push reconciles designTokens alongside the rest of your config:

  • The document is validated at save time using the same rules as the admin editor — push fails if you introduce a cycle or an unknown reference.
  • Tokens are written to the draft channel. Pass --publish to also promote them to published in the same push.
  • Re-running foir push is idempotent — the platform stores the document verbatim, so pushing the same designTokens block twice is a no-op.
# Apply to draft only (storefront keeps serving the last published snapshot) foir push # Apply and publish in one step foir push --publish

See the CLI command reference for foir design-tokens subcommands that operate on tokens without going through a full foir push.

Last updated on