diff --git a/.changeset/token-map-initial.md b/.changeset/token-map-initial.md new file mode 100644 index 0000000000..70b9053fe9 --- /dev/null +++ b/.changeset/token-map-initial.md @@ -0,0 +1,5 @@ +--- +"@justeattakeaway/pie-token-map": minor +--- + +[Added] - New `pie-token-map` package providing programmatic lookup of PIE design tokens by name, returning CSS variable references (`var(--dt-*)`). Supports color, typography, radius, spacing, elevation, gradient, motion, breakpoint, and z-index token categories. diff --git a/packages/tools/pie-token-map/.eslintignore b/packages/tools/pie-token-map/.eslintignore new file mode 100644 index 0000000000..44de66f72e --- /dev/null +++ b/packages/tools/pie-token-map/.eslintignore @@ -0,0 +1,3 @@ +.turbo +dist +node_modules diff --git a/packages/tools/pie-token-map/API-DOCS.md b/packages/tools/pie-token-map/API-DOCS.md new file mode 100644 index 0000000000..58130977d6 --- /dev/null +++ b/packages/tools/pie-token-map/API-DOCS.md @@ -0,0 +1,426 @@ +# API Documentation — @justeattakeaway/pie-token-map + +Everything exported from the package entry point (`src/index.ts`). + +--- + +## Types + +### `CssVarRef` + +A template literal type representing a CSS custom property reference. + +```ts +type CssVarRef = `var(--dt-${string})`; +``` + +All getter functions (except `getTypography`) return this type. In practice the values look like `'var(--dt-color-background-default)'` or `'var(--dt-spacing-b)'`. + +--- + +### `TokenCategory` + +A union of the nine supported token category strings. + +```ts +type TokenCategory = + | 'color' + | 'typography' + | 'radius' + | 'spacing' + | 'elevation' + | 'gradient' + | 'motion' + | 'breakpoint' + | 'zIndex'; +``` + +Used as the first argument to `hasToken()`. + +--- + +### `TypographyTokenValue` + +An object describing the full set of CSS variable references for a single typography token. Headings and subheadings are "responsive" (they have separate `sizeWide`/`sizeNarrow` values). Body and caption tokens are "non-responsive" (they have a single `size`). + +```ts +interface TypographyTokenValue { + family: CssVarRef; // Always present + weight: CssVarRef; // Always present + size?: CssVarRef; // Non-responsive tokens only + lineHeight?: CssVarRef; // Non-responsive tokens only + sizeWide?: CssVarRef; // Responsive tokens only + lineHeightWide?: CssVarRef; // Responsive tokens only + sizeNarrow?: CssVarRef; // Responsive tokens only + lineHeightNarrow?: CssVarRef; // Responsive tokens only + fontStyle?: CssVarRef; // Italic tokens only + textDecoration?: CssVarRef; // Link tokens only + paragraph?: CssVarRef; // Body and caption tokens only +} +``` + +--- + +### Token Name Types + +Each category exports a string literal union type for its valid token names. These are generated from the design tokens source. + +| Type | Category | Example values | +|---|---|---| +| `ColorTokenName` | Color | `'background-default'`, `'interactive-brand'` | +| `TypographyTokenName` | Typography | `'heading-m'`, `'body-s'`, `'caption'` | +| `RadiusTokenName` | Radius | `'rounded-a'`, `'rounded-none'` | +| `SpacingTokenName` | Spacing | `'a'`, `'b'`, `'c'`, `'none'` | +| `ElevationTokenName` | Elevation | `'01'`, `'card'`, `'below-10'` | +| `GradientTokenName` | Gradient | `'jetplus-brand-01'`, `'ai-container-default'` | +| `MotionTokenName` | Motion | `'easing-in'`, `'timing-200'` | +| `BreakpointTokenName` | Breakpoint | `'xs'`, `'sm'`, `'md'`, `'lg'`, `'xl'`, `'xxl'` | +| `ZIndexTokenName` | Z-Index | `'base'`, `'modal'`, `'toast'`, `'tooltip'` | + +--- + +## Functions + +### `getColor(name)` + +Look up a color design token by name. + +| Detail | Value | +|---|---| +| **Parameter** | `name: string` — the color token name | +| **Returns** | `CssVarRef \| undefined` — the CSS variable reference, or `undefined` if the name is not found | + +```ts +import { getColor } from '@justeattakeaway/pie-token-map'; + +getColor('background-default'); // 'var(--dt-color-background-default)' +getColor('interactive-brand'); // 'var(--dt-color-interactive-brand)' +getColor('nonexistent'); // undefined +``` + +Platform-agnostic aliases are included. For example, `getColor('container-base')` returns `'var(--dt-color-web-container-base)'` — the alias maps to the web-prefixed CSS variable automatically. + +--- + +### `getTypography(name)` + +Look up a typography design token by name. Returns a composite object instead of a single CSS variable string. + +| Detail | Value | +|---|---| +| **Parameter** | `name: string` — the typography token name | +| **Returns** | `TypographyTokenValue \| undefined` — an object of CSS variable references, or `undefined` if not found | + +```ts +import { getTypography } from '@justeattakeaway/pie-token-map'; + +const heading = getTypography('heading-xs'); +// { +// family: 'var(--dt-font-heading-xs-family)', +// weight: 'var(--dt-font-heading-xs-weight)', +// sizeWide: 'var(--dt-font-heading-xs-size--wide)', +// lineHeightWide: 'var(--dt-font-heading-xs-line-height--wide)', +// sizeNarrow: 'var(--dt-font-heading-xs-size--narrow)', +// lineHeightNarrow: 'var(--dt-font-heading-xs-line-height--narrow)', +// } + +const body = getTypography('body-s'); +// { +// family: 'var(--dt-font-body-s-family)', +// weight: 'var(--dt-font-body-s-weight)', +// size: 'var(--dt-font-body-s-size)', +// lineHeight: 'var(--dt-font-body-s-line-height)', +// paragraph: 'var(--dt-font-body-s-paragraph)', +// } + +getTypography('nonexistent'); // undefined +``` + +**Responsive vs. non-responsive:** heading and subheading tokens have `sizeWide`/`sizeNarrow`/`lineHeightWide`/`lineHeightNarrow` (and no `size`/`lineHeight`). Body, caption, and interactive tokens have `size`/`lineHeight` (and no wide/narrow variants). + +**Optional fields:** `fontStyle` appears only on italic tokens (e.g., `heading-m-italic`). `textDecoration` appears only on link tokens (e.g., `body-s-link`). `paragraph` appears on body and caption tokens but not on heading or interactive tokens. + +--- + +### `getRadius(name)` + +Look up a border-radius design token by name. + +| Detail | Value | +|---|---| +| **Parameter** | `name: string` — the radius token name | +| **Returns** | `CssVarRef \| undefined` | + +```ts +import { getRadius } from '@justeattakeaway/pie-token-map'; + +getRadius('rounded-a'); // 'var(--dt-radius-rounded-a)' +getRadius('rounded-none'); // 'var(--dt-radius-rounded-none)' +getRadius('nonexistent'); // undefined +``` + +Available names: `rounded-a`, `rounded-b`, `rounded-c`, `rounded-d`, `rounded-e`, `rounded-none`. + +--- + +### `getSpacing(name)` + +Look up a spacing design token by name. + +| Detail | Value | +|---|---| +| **Parameter** | `name: string` — the spacing token name | +| **Returns** | `CssVarRef \| undefined` | + +```ts +import { getSpacing } from '@justeattakeaway/pie-token-map'; + +getSpacing('b'); // 'var(--dt-spacing-b)' +getSpacing('none'); // 'var(--dt-spacing-none)' +getSpacing('zzz'); // undefined +``` + +Available names: `a`, `a-small`, `b`, `c`, `d`, `e`, `f`, `g`, `h`, `i`, `j`, `none`. + +--- + +### `getElevation(name)` + +Look up an elevation (box-shadow) design token by name. + +| Detail | Value | +|---|---| +| **Parameter** | `name: string` — the elevation token name | +| **Returns** | `CssVarRef \| undefined` | + +```ts +import { getElevation } from '@justeattakeaway/pie-token-map'; + +getElevation('below-10'); // 'var(--dt-elevation-below-10)' +getElevation('card'); // 'var(--dt-elevation-card)' +getElevation('nope'); // undefined +``` + +Available names: `01`, `02`, `03`, `04`, `05`, `above-20`, `below-10`, `below-20`, `card`, `inverse-01`, `inverse-02`, `inverse-03`, `inverse-04`, `inverse-05`, `inverse-above-20`, `inverse-below-10`, `inverse-below-20`, `inverse-card`. + +--- + +### `getGradient(name)` + +Look up a gradient design token by name. + +| Detail | Value | +|---|---| +| **Parameter** | `name: string` — the gradient token name | +| **Returns** | `CssVarRef \| undefined` | + +```ts +import { getGradient } from '@justeattakeaway/pie-token-map'; + +getGradient('jetplus-brand-01'); // 'var(--dt-gradient-jetplus-brand-01)' +getGradient('ai-container-default'); // 'var(--dt-gradient-ai-container-default)' +getGradient('nonexistent'); // undefined +``` + +Available names: `ai-border-default`, `ai-container-default`, `jetplus-brand-01`, `jetplus-brand-02`, `jetplus-brand-03`, `jetplus-support-01`, `jetplus-support-02`. + +--- + +### `getMotion(name)` + +Look up a motion (animation easing or timing) design token by name. + +| Detail | Value | +|---|---| +| **Parameter** | `name: string` — the motion token name | +| **Returns** | `CssVarRef \| undefined` | + +```ts +import { getMotion } from '@justeattakeaway/pie-token-map'; + +getMotion('easing-in'); // 'var(--dt-motion-easing-in)' +getMotion('timing-200'); // 'var(--dt-motion-timing-200)' +getMotion('nonexistent'); // undefined +``` + +Available names: `easing-in`, `easing-out`, `easing-persistent-expressive`, `easing-persistent-functional`, `timing-100`, `timing-150`, `timing-200`, `timing-250`, `timing-300`, `timing-350`. + +--- + +### `getBreakpoint(name)` + +Look up a responsive breakpoint design token by name. + +| Detail | Value | +|---|---| +| **Parameter** | `name: string` — the breakpoint token name | +| **Returns** | `CssVarRef \| undefined` | + +```ts +import { getBreakpoint } from '@justeattakeaway/pie-token-map'; + +getBreakpoint('sm'); // 'var(--dt-breakpoint-sm)' +getBreakpoint('xxl'); // 'var(--dt-breakpoint-xxl)' +getBreakpoint('nonexistent'); // undefined +``` + +Available names: `xs`, `sm`, `md`, `lg`, `xl`, `xxl`. + +--- + +### `getZIndex(name)` + +Look up a z-index design token by name. These values come from `pie-css` rather than `tokens.json`. + +| Detail | Value | +|---|---| +| **Parameter** | `name: string` — the z-index token name | +| **Returns** | `CssVarRef \| undefined` | + +```ts +import { getZIndex } from '@justeattakeaway/pie-token-map'; + +getZIndex('modal'); // 'var(--dt-z-index-modal)' +getZIndex('toast'); // 'var(--dt-z-index-toast)' +getZIndex('nonexistent'); // undefined +``` + +Available names: `base`, `bottom-sheet`, `cookie-banner`, `dropdown`, `fab`, `modal`, `popover`, `side-sheet`, `toast`, `tooltip`. + +--- + +### `hasToken(category, name)` + +Check whether a token name exists in a given category. Useful for validation before calling a getter. + +| Detail | Value | +|---|---| +| **Parameters** | `category: TokenCategory` — one of the nine category strings; `name: string` — the token name to check | +| **Returns** | `boolean` — `true` if the token exists, `false` otherwise | + +```ts +import { hasToken } from '@justeattakeaway/pie-token-map'; + +hasToken('color', 'background-default'); // true +hasToken('color', 'nonexistent'); // false +hasToken('spacing', 'b'); // true +hasToken('zIndex', 'modal'); // true +``` + +--- + +## Objects + +### `tokenMap` + +A convenience object that bundles all getter functions and raw maps into a single import. Useful when you want one import to access everything. + +| Property | Type | Description | +|---|---|---| +| `getColor` | `(name: string) => CssVarRef \| undefined` | Same as the standalone `getColor` function | +| `getTypography` | `(name: string) => TypographyTokenValue \| undefined` | Same as the standalone `getTypography` function | +| `getRadius` | `(name: string) => CssVarRef \| undefined` | Same as the standalone `getRadius` function | +| `getSpacing` | `(name: string) => CssVarRef \| undefined` | Same as the standalone `getSpacing` function | +| `getElevation` | `(name: string) => CssVarRef \| undefined` | Same as the standalone `getElevation` function | +| `getGradient` | `(name: string) => CssVarRef \| undefined` | Same as the standalone `getGradient` function | +| `getMotion` | `(name: string) => CssVarRef \| undefined` | Same as the standalone `getMotion` function | +| `getBreakpoint` | `(name: string) => CssVarRef \| undefined` | Same as the standalone `getBreakpoint` function | +| `getZIndex` | `(name: string) => CssVarRef \| undefined` | Same as the standalone `getZIndex` function | +| `hasToken` | `(category: TokenCategory, name: string) => boolean` | Same as the standalone `hasToken` function | +| `version` | `string` | The version of `@justeat/pie-design-tokens` used to generate the maps (e.g., `'7.11.1'`) | +| `colors` | `Record` | The raw color map object | +| `typography` | `Record` | The raw typography map object | +| `radii` | `Record` | The raw radius map object | +| `spacing` | `Record` | The raw spacing map object | +| `elevation` | `Record` | The raw elevation map object | +| `gradients` | `Record` | The raw gradient map object | +| `motion` | `Record` | The raw motion map object | +| `breakpoints` | `Record` | The raw breakpoint map object | +| `zIndices` | `Record` | The raw z-index map object | + +```ts +import { tokenMap } from '@justeattakeaway/pie-token-map'; + +tokenMap.getColor('interactive-brand'); // 'var(--dt-color-interactive-brand)' +tokenMap.hasToken('elevation', 'card'); // true +tokenMap.version; // '7.11.1' +Object.keys(tokenMap.colors); // all color token names +``` + +--- + +## Constants + +### `tokenCategories` + +A readonly array of all nine category strings. Useful for iteration. + +```ts +import { tokenCategories } from '@justeattakeaway/pie-token-map'; + +// ['color', 'typography', 'radius', 'spacing', 'elevation', +// 'gradient', 'motion', 'breakpoint', 'zIndex'] +``` + +### `tokensVersion` + +The version string of the `@justeat/pie-design-tokens` package that was used to generate the current maps. + +```ts +import { tokensVersion } from '@justeattakeaway/pie-token-map'; + +console.log(tokensVersion); // '7.11.1' +``` + +### Token Name Arrays + +Each category exports a readonly array of its valid token names. These are the same values used to define the token name union types. + +| Export | Example | +|---|---| +| `colorTokenNames` | `['active-01', 'background-default', ...]` | +| `typographyTokenNames` | `['body-l', 'body-s', 'heading-m', ...]` | +| `radiusTokenNames` | `['rounded-a', 'rounded-b', ...]` | +| `spacingTokenNames` | `['a', 'a-small', 'b', ...]` | +| `elevationTokenNames` | `['01', '02', 'card', ...]` | +| `gradientTokenNames` | `['ai-border-default', 'jetplus-brand-01', ...]` | +| `motionTokenNames` | `['easing-in', 'timing-100', ...]` | +| `breakpointTokenNames` | `['lg', 'md', 'sm', 'xl', 'xs', 'xxl']` | +| `zIndexTokenNames` | `['base', 'modal', 'toast', ...]` | + +### Raw Map Objects + +Each category also exports its full map object directly, for cases where you want to iterate or spread all tokens at once. + +| Export | Type | +|---|---| +| `colorMap` | `Record` | +| `typographyMap` | `Record` | +| `radiusMap` | `Record` | +| `spacingMap` | `Record` | +| `elevationMap` | `Record` | +| `gradientMap` | `Record` | +| `motionMap` | `Record` | +| `breakpointMap` | `Record` | +| `zIndexMap` | `Record` | + +--- + +## Code Generation Script + +The file `scripts/generate-token-map.ts` is not part of the public API, but it is responsible for creating all the files in `src/generated/`. It reads `@justeat/pie-design-tokens/dist/tokens.json` and writes one TypeScript file per category. + +### Key internal functions (not exported) + +| Function | What it does | +|---|---| +| `generateSimpleMap(names, prefix, typeName, mapName, namesArrayName)` | Creates a `.ts` file mapping token names to `var(--dt-{prefix}-{name})` strings. Used for radius, spacing, elevation, gradient, motion, breakpoint, and z-index. | +| `generateColorMap()` | Generates the color map, filtering out platform-specific tokens (`android-*`, `ios-*`) and adding platform-agnostic aliases (`container-base` → `web-container-base`). | +| `generateTypographyMap()` | Generates the typography map with composite `TypographyTokenValue` objects, handling responsive vs. non-responsive tokens, italic styles, link decorations, and paragraph spacing. | +| `generateVersion()` | Writes a file exporting the `@justeat/pie-design-tokens` package version string. | + +Run the generator with: + +```bash +yarn generate +``` diff --git a/packages/tools/pie-token-map/ONBOARDING.md b/packages/tools/pie-token-map/ONBOARDING.md new file mode 100644 index 0000000000..379392b84d --- /dev/null +++ b/packages/tools/pie-token-map/ONBOARDING.md @@ -0,0 +1,169 @@ +# Onboarding Guide — pie-token-map + +Get up and running with `@justeattakeaway/pie-token-map` in under 10 minutes. + +--- + +## Prerequisites + +Before you start, make sure you have: + +- **Node.js** 18 or later +- **Yarn** (the PIE monorepo uses Yarn workspaces) +- **Git** access to the PIE monorepo at `https://github.com/justeattakeaway/pie.git` + +--- + +## Step 1 — Clone the monorepo + +```bash +git clone https://github.com/justeattakeaway/pie.git +cd pie +``` + +--- + +## Step 2 — Install dependencies + +From the repo root, install all workspace dependencies: + +```bash +yarn +``` + +This installs everything for every package in the monorepo, including `@justeat/pie-design-tokens` which `pie-token-map` depends on. + +--- + +## Step 3 — Navigate to the package + +```bash +cd packages/tools/pie-token-map +``` + +--- + +## Step 4 — Generate the token maps + +The token maps in `src/generated/` are created from the design tokens JSON file. Regenerate them to make sure they are up to date: + +```bash +yarn generate +``` + +You should see output like: + +``` +Generating token maps... + Generated .../src/generated/color-map.ts + Generated .../src/generated/typography-map.ts + Generated .../src/generated/radius-map.ts + ... +Done. +``` + +--- + +## Step 5 — Build the package + +```bash +yarn build +``` + +This runs the generator and then bundles the library into `dist/` as an ES module with TypeScript declarations. + +--- + +## Step 6 — Run the tests + +```bash +yarn test +``` + +All tests should pass. The test suite covers every getter function, completeness against the source `tokens.json`, and the structure of typography tokens. + +--- + +## Step 7 — Try it out + +Open a Node REPL or create a quick test file: + +```ts +// test-it.ts +import { getColor, getSpacing, getTypography, hasToken, tokenMap } from './src/index.js'; + +console.log(getColor('background-default')); +// → var(--dt-color-background-default) + +console.log(getSpacing('d')); +// → var(--dt-spacing-d) + +console.log(getTypography('heading-m')); +// → { family: 'var(--dt-font-heading-m-family)', weight: '...', ... } + +console.log(hasToken('zIndex', 'modal')); +// → true + +console.log(tokenMap.version); +// → 7.11.1 +``` + +--- + +## Step 8 — Understand the file structure + +``` +pie-token-map/ +├── scripts/ +│ └── generate-token-map.ts # Reads tokens.json, writes src/generated/ +├── src/ +│ ├── generated/ # Auto-generated files (do not edit by hand) +│ │ ├── color-map.ts +│ │ ├── typography-map.ts +│ │ ├── radius-map.ts +│ │ ├── spacing-map.ts +│ │ ├── elevation-map.ts +│ │ ├── gradient-map.ts +│ │ ├── motion-map.ts +│ │ ├── breakpoint-map.ts +│ │ ├── z-index-map.ts +│ │ └── version.ts +│ ├── types.ts # Shared types (CssVarRef, TokenCategory, etc.) +│ └── index.ts # Public API — getter functions and exports +├── test/ +│ └── unit/ +│ └── token-map.spec.ts # Unit tests (Vitest) +├── dist/ # Built output (ES module + .d.ts) +├── package.json +├── tsconfig.json +└── vite.config.ts +``` + +**Key rule:** never edit files inside `src/generated/` directly. If you need to change what gets generated, edit `scripts/generate-token-map.ts` and run `yarn generate`. + +--- + +## Step 9 — Make a change + +Here is a simple exercise to confirm your setup works end to end: + +1. Open `scripts/generate-token-map.ts`. +2. Find the `generateZIndexMap` function. The z-index token names are hard-coded there. +3. Add a temporary name to the array (e.g., `'test-layer'`). +4. Run `yarn generate` — check that `src/generated/z-index-map.ts` now includes `'test-layer'`. +5. Run `yarn test` — some completeness tests will fail because the test expects exactly 10 z-index tokens. This confirms the tests are catching real issues. +6. Revert your change and run `yarn test` again to see everything pass. + +--- + +## Step 10 — Next steps + +- Read `API-DOCS.md` for the full API reference. +- Read `README.md` for integration examples with `pie-css` and web components. +- Run `yarn lint:scripts` to check your code against the project's ESLint rules. +- Run `yarn watch` during development for automatic rebuilds on file changes. +- When bumping the `@justeat/pie-design-tokens` version in `package.json`, always run `yarn generate` and commit the updated generated files. + +--- + +You are now ready to work on `pie-token-map`. diff --git a/packages/tools/pie-token-map/README.md b/packages/tools/pie-token-map/README.md new file mode 100644 index 0000000000..7d6a94d34a --- /dev/null +++ b/packages/tools/pie-token-map/README.md @@ -0,0 +1,207 @@ +# @justeattakeaway/pie-token-map + +Programmatic lookup of PIE design tokens by name. Instead of hard-coding CSS custom property strings throughout your codebase, this package gives you typed functions and maps that return the correct `var(--dt-...)` references at runtime. It works alongside `pie-css` (which defines the actual custom properties) and PIE web components. + +## Status + +**Alpha** — the API is functional and tested but may change before 1.0. + +## Tech Stack + +| Tool | Purpose | +|---|---| +| TypeScript | Source language | +| Vite | Library bundler (ES module output) | +| Vitest | Unit testing | +| `vite-plugin-dts` | Generates rolled-up `.d.ts` type declarations | +| `rollup-plugin-visualizer` | Bundle size analysis (`stats.html`) | +| `@justeat/pie-design-tokens` | Source of truth for token definitions (`tokens.json`) | +| Turborepo | Monorepo task orchestration (the `run -T` commands) | + +## Installation + +```bash +# With yarn (recommended inside the PIE monorepo) +yarn add @justeattakeaway/pie-token-map + +# With npm +npm install @justeattakeaway/pie-token-map +``` + +### Peer Requirement + +Your page must load `pie-css` so that the CSS custom properties (`--dt-color-*`, `--dt-font-*`, etc.) are actually defined. Without `pie-css`, the `var(--dt-...)` strings this package returns will resolve to empty values in the browser. + +## Usage Examples + +### Basic token lookup + +```ts +import { getColor, getSpacing, getRadius } from '@justeattakeaway/pie-token-map'; + +// Returns 'var(--dt-color-background-default)' or undefined +const bg = getColor('background-default'); + +// Returns 'var(--dt-spacing-b)' or undefined +const gap = getSpacing('b'); + +// Returns 'var(--dt-radius-rounded-a)' or undefined +const corners = getRadius('rounded-a'); +``` + +### Typography tokens + +Typography tokens are composite objects rather than single CSS var strings, because a single font style involves multiple properties. + +```ts +import { getTypography } from '@justeattakeaway/pie-token-map'; + +const heading = getTypography('heading-m'); +// heading.family → 'var(--dt-font-heading-m-family)' +// heading.weight → 'var(--dt-font-heading-m-weight)' +// heading.sizeWide → 'var(--dt-font-heading-m-size--wide)' +// heading.sizeNarrow → 'var(--dt-font-heading-m-size--narrow)' + +const body = getTypography('body-s'); +// body.size → 'var(--dt-font-body-s-size)' +// body.lineHeight → 'var(--dt-font-body-s-line-height)' +// body.paragraph → 'var(--dt-font-body-s-paragraph)' +``` + +### Check if a token exists + +```ts +import { hasToken } from '@justeattakeaway/pie-token-map'; + +hasToken('color', 'background-default'); // true +hasToken('color', 'made-up-name'); // false +hasToken('zIndex', 'modal'); // true +``` + +### Use the convenience object + +```ts +import { tokenMap } from '@justeattakeaway/pie-token-map'; + +tokenMap.getColor('interactive-brand'); +tokenMap.getSpacing('d'); +tokenMap.hasToken('radius', 'rounded-a'); +tokenMap.version; // e.g. '7.11.1' +``` + +### Access raw maps directly + +```ts +import { colorMap, spacingMap } from '@justeattakeaway/pie-token-map'; + +// Iterate all color tokens +Object.entries(colorMap).forEach(([name, cssVar]) => { + console.log(name, cssVar); +}); +``` + +## Integration with pie-css and Web Components + +### With pie-css + +`pie-css` outputs the CSS custom properties that this package references. A typical setup: + +```html + + + + +``` + +### With PIE Web Components + +Use token-map inside a web component to apply design tokens programmatically: + +```ts +import { LitElement, html, css, unsafeCSS } from 'lit'; +import { getColor, getSpacing, getRadius } from '@justeattakeaway/pie-token-map'; + +class MyCard extends LitElement { + static styles = css` + :host { + display: block; + background-color: ${unsafeCSS(getColor('container-default'))}; + padding: ${unsafeCSS(getSpacing('d'))}; + border-radius: ${unsafeCSS(getRadius('rounded-b'))}; + } + `; + + render() { + return html``; + } +} +customElements.define('my-card', MyCard); +``` + +### Dynamic styling based on tokens + +```ts +import { tokenMap } from '@justeattakeaway/pie-token-map'; + +function applyElevation(element: HTMLElement, level: string) { + const elevation = tokenMap.getElevation(level); + if (elevation) { + element.style.boxShadow = elevation; + } +} + +function applyMotion(element: HTMLElement) { + const easing = tokenMap.getMotion('easing-persistent-functional'); + const duration = tokenMap.getMotion('timing-200'); + if (easing && duration) { + element.style.transition = `all ${duration} ${easing}`; + } +} +``` + +## Token Categories + +This package covers nine categories of design tokens: + +| Category | Getter function | Example token names | +|---|---|---| +| Color | `getColor()` | `background-default`, `interactive-brand`, `content-link` | +| Typography | `getTypography()` | `heading-m`, `body-s`, `caption`, `interactive-l` | +| Radius | `getRadius()` | `rounded-a` through `rounded-e`, `rounded-none` | +| Spacing | `getSpacing()` | `a`, `b`, `c`, `d`, `e`, `f`, `g`, `h`, `i`, `j`, `none` | +| Elevation | `getElevation()` | `01` through `05`, `card`, `below-10`, `above-20` | +| Gradient | `getGradient()` | `jetplus-brand-01`, `ai-container-default` | +| Motion | `getMotion()` | `easing-in`, `easing-out`, `timing-200` | +| Breakpoint | `getBreakpoint()` | `xs`, `sm`, `md`, `lg`, `xl`, `xxl` | +| Z-Index | `getZIndex()` | `base`, `modal`, `toast`, `tooltip`, `dropdown` | + +## Scripts + +```bash +yarn generate # Regenerate token maps from @justeat/pie-design-tokens +yarn build # Generate + bundle with Vite +yarn test # Run unit tests with Vitest +yarn lint:scripts # Lint with ESLint +yarn watch # Build in watch mode +``` + +## How Code Generation Works + +The `scripts/generate-token-map.ts` script reads `@justeat/pie-design-tokens/dist/tokens.json`, then writes typed TypeScript map files into `src/generated/`. These generated files are committed to the repo so consumers do not need the design tokens package installed at runtime. When the upstream tokens package is updated, run `yarn generate` to refresh the maps. + +## Contribution Guidelines + +1. **Clone the PIE monorepo** — this package lives at `packages/tools/pie-token-map`. +2. **Install dependencies** — run `yarn` from the repo root. +3. **Make changes** — edit files in `src/` or `scripts/`. Do not edit files in `src/generated/` by hand; run `yarn generate` instead. +4. **Run tests** — `yarn test` must pass before you open a PR. +5. **Run the linter** — `yarn lint:scripts` (or `yarn lint:scripts:fix` to auto-fix). +6. **Keep generated files in sync** — if you update the generator script or bump the design tokens version, run `yarn generate` and commit the updated generated files. +7. **Follow the existing code style** — the project uses ESLint with the shared `@justeattakeaway/pie-components-config` configuration. +8. **Open a PR** against the main PIE repo with a clear description of what changed and why. diff --git a/packages/tools/pie-token-map/package.json b/packages/tools/pie-token-map/package.json new file mode 100644 index 0000000000..344a587329 --- /dev/null +++ b/packages/tools/pie-token-map/package.json @@ -0,0 +1,39 @@ +{ + "name": "@justeattakeaway/pie-token-map", + "version": "0.1.0", + "description": "Programmatic lookup of PIE design tokens by name, returning CSS variable references.", + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "src", + "dist" + ], + "publishConfig": { + "access": "public" + }, + "license": "Apache-2.0", + "sideEffects": false, + "repository": { + "type": "git", + "url": "https://github.com/justeattakeaway/pie.git", + "directory": "packages/tools/pie-token-map" + }, + "pieMetadata": { + "componentStatus": "alpha" + }, + "scripts": { + "generate": "run -T ts-node --esm ./scripts/generate-token-map.ts", + "build": "yarn generate && run -T vite build", + "generate:verify": "yarn generate && git diff --exit-code src/generated/", + "test": "run -T vitest run", + "lint:scripts": "run -T eslint .", + "lint:scripts:fix": "yarn lint:scripts --fix", + "watch": "run -T vite build --watch" + }, + "devDependencies": { + "@justeat/pie-design-tokens": "7.11.1", + "@justeattakeaway/pie-components-config": "*" + } +} diff --git a/packages/tools/pie-token-map/scripts/generate-token-map.ts b/packages/tools/pie-token-map/scripts/generate-token-map.ts new file mode 100644 index 0000000000..c83d04a3fa --- /dev/null +++ b/packages/tools/pie-token-map/scripts/generate-token-map.ts @@ -0,0 +1,298 @@ +/** + * Reads @justeat/pie-design-tokens/dist/tokens.json and generates + * typed token maps in src/generated/. + * + * Run: yarn generate + */ + +import { createRequire } from 'node:module'; +import { writeFileSync, mkdirSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const require = createRequire(import.meta.url); + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const GENERATED_DIR = resolve(__dirname, '../src/generated'); + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const tokens = require('@justeat/pie-design-tokens/dist/tokens.json'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const tokensPkg = require('@justeat/pie-design-tokens/package.json'); + +const HEADER = '// Auto-generated by scripts/generate-token-map.ts — do not edit manually.\n'; + +/** + * Returns true if a property key needs quoting (contains hyphens, starts with a digit, etc.). + */ +function needsQuotes (key: string): boolean { + return !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key); +} + +/** + * Format a property key, quoting only when necessary. + */ +function formatKey (key: string): string { + return needsQuotes(key) ? `'${key}'` : key; +} + +function writeFile (filePath: string, content: string): void { + writeFileSync(filePath, content, 'utf-8'); + // eslint-disable-next-line no-console + console.info(` Generated ${filePath}`); +} + +/** + * Generate a simple map where each token name maps to a single CSS var ref. + */ +function generateSimpleMap ( + names: string[], + prefix: string, + typeName: string, + mapName: string, + namesArrayName: string, +): string { + const sorted = [...names].sort(); + const namesArr = sorted.map((n) => ` '${n}',`).join('\n'); + const entries = sorted.map((n) => ` ${formatKey(n)}: 'var(--dt-${prefix}-${n})',`).join('\n'); + + return `${HEADER}import type { CssVarRef } from '../types.js'; + +export const ${namesArrayName} = [ +${namesArr} +] as const; + +export type ${typeName} = typeof ${namesArrayName}[number]; + +export const ${mapName}: Record<${typeName}, CssVarRef> = { +${entries} +}; +`; +} + +// --------------------------------------------------------------------------- +// Color map +// --------------------------------------------------------------------------- + +/** + * Platform-agnostic aliases for Android parity. + * tokens.json uses `web-container-*` but the server sends `container-*`. + */ +const COLOR_ALIASES: Record = { + 'container-base': 'web-container-base', + 'container-base-dark': 'web-container-base-dark', + 'container-neutral': 'web-container-neutral', + 'container-prominent': 'web-container-prominent', +}; + +/** + * Platform-specific token prefixes that should be excluded from the web map. + * These CSS custom properties are not output by pie-css, so they would silently + * resolve to empty values if used on the web. + */ +const PLATFORM_SPECIFIC_PREFIXES = ['android-', 'ios-']; + +function generateColorMap (): void { + const colorNames: string[] = Object.keys(tokens.theme.jet.color.alias.default) + .filter((name) => !PLATFORM_SPECIFIC_PREFIXES.some((prefix) => name.startsWith(prefix))); + + // Add platform-agnostic alias names + const aliasNames = Object.keys(COLOR_ALIASES).filter((a) => !colorNames.includes(a)); + const allNames = [...colorNames, ...aliasNames]; + + const sorted = [...allNames].sort(); + const namesArr = sorted.map((n) => ` '${n}',`).join('\n'); + const entries = sorted.map((n) => { + // Aliases point to the web-prefixed CSS var + const cssName = COLOR_ALIASES[n] || n; + return ` ${formatKey(n)}: 'var(--dt-color-${cssName})',`; + }).join('\n'); + + const content = `${HEADER}import type { CssVarRef } from '../types.js'; + +export const colorTokenNames = [ +${namesArr} +] as const; + +export type ColorTokenName = typeof colorTokenNames[number]; + +export const colorMap: Record = { +${entries} +}; +`; + writeFile(resolve(GENERATED_DIR, 'color-map.ts'), content); +} + +// --------------------------------------------------------------------------- +// Typography map +// --------------------------------------------------------------------------- +function generateTypographyMap (): void { + const fontTokens = tokens.theme.jet.font.alias as Record>; + const names = Object.keys(fontTokens).sort(); + + const namesArr = names.map((n) => ` '${n}',`).join('\n'); + + const entries = names.map((name) => { + const token = fontTokens[name]; + const isResponsive = 'size--wide' in token; + const hasStyle = 'font-style' in token; + const hasDecoration = 'text-decoration' in token; + const hasParagraph = 'paragraph' in token; + + const prefix = `--dt-font-${name}`; + const props: string[] = [ + ` family: 'var(${prefix}-family)',`, + ` weight: 'var(${prefix}-weight)',`, + ]; + + if (isResponsive) { + props.push(` sizeWide: 'var(${prefix}-size--wide)',`); + props.push(` lineHeightWide: 'var(${prefix}-line-height--wide)',`); + props.push(` sizeNarrow: 'var(${prefix}-size--narrow)',`); + props.push(` lineHeightNarrow: 'var(${prefix}-line-height--narrow)',`); + } else { + props.push(` size: 'var(${prefix}-size)',`); + props.push(` lineHeight: 'var(${prefix}-line-height)',`); + } + + if (hasStyle) { + props.push(` fontStyle: 'var(${prefix}-font-style)',`); + } + if (hasDecoration) { + props.push(` textDecoration: 'var(${prefix}-text-decoration)',`); + } + if (hasParagraph) { + props.push(` paragraph: 'var(${prefix}-paragraph)',`); + } + + return ` ${formatKey(name)}: {\n${props.join('\n')}\n },`; + }).join('\n'); + + const content = `${HEADER}import type { TypographyTokenValue } from '../types.js'; + +export const typographyTokenNames = [ +${namesArr} +] as const; + +export type TypographyTokenName = typeof typographyTokenNames[number]; + +export const typographyMap: Record = { +${entries} +}; +`; + + writeFile(resolve(GENERATED_DIR, 'typography-map.ts'), content); +} + +// --------------------------------------------------------------------------- +// Radius map +// --------------------------------------------------------------------------- +function generateRadiusMap (): void { + const names: string[] = Object.keys(tokens.theme.jet.radius.alias); + const content = generateSimpleMap(names, 'radius', 'RadiusTokenName', 'radiusMap', 'radiusTokenNames'); + writeFile(resolve(GENERATED_DIR, 'radius-map.ts'), content); +} + +// --------------------------------------------------------------------------- +// Spacing map +// --------------------------------------------------------------------------- +function generateSpacingMap (): void { + const names: string[] = Object.keys(tokens.theme.jet.spacing.alias); + const content = generateSimpleMap(names, 'spacing', 'SpacingTokenName', 'spacingMap', 'spacingTokenNames'); + writeFile(resolve(GENERATED_DIR, 'spacing-map.ts'), content); +} + +// --------------------------------------------------------------------------- +// Elevation map +// --------------------------------------------------------------------------- +function generateElevationMap (): void { + const names: string[] = Object.keys(tokens.theme.jet.elevation.alias.default); + const content = generateSimpleMap(names, 'elevation', 'ElevationTokenName', 'elevationMap', 'elevationTokenNames'); + writeFile(resolve(GENERATED_DIR, 'elevation-map.ts'), content); +} + +// --------------------------------------------------------------------------- +// Gradient map +// --------------------------------------------------------------------------- +function generateGradientMap (): void { + const names: string[] = Object.keys(tokens.theme.jet.gradient.alias.default); + const content = generateSimpleMap(names, 'gradient', 'GradientTokenName', 'gradientMap', 'gradientTokenNames'); + writeFile(resolve(GENERATED_DIR, 'gradient-map.ts'), content); +} + +// --------------------------------------------------------------------------- +// Motion map +// Note: Motion tokens only exist under `global` in tokens.json (there is no +// `alias` key for motion). Using `global` here is intentional and is the only +// available source for these tokens. +// --------------------------------------------------------------------------- +function generateMotionMap (): void { + const names: string[] = Object.keys(tokens.theme.jet.motion.global); + const content = generateSimpleMap(names, 'motion', 'MotionTokenName', 'motionMap', 'motionTokenNames'); + writeFile(resolve(GENERATED_DIR, 'motion-map.ts'), content); +} + +// --------------------------------------------------------------------------- +// Breakpoint map +// --------------------------------------------------------------------------- +function generateBreakpointMap (): void { + const names: string[] = Object.keys(tokens.theme.jet.breakpoint.alias); + const content = generateSimpleMap(names, 'breakpoint', 'BreakpointTokenName', 'breakpointMap', 'breakpointTokenNames'); + writeFile(resolve(GENERATED_DIR, 'breakpoint-map.ts'), content); +} + +// --------------------------------------------------------------------------- +// Z-index map (hand-written — values from pie-css/css/input.css, not tokens.json) +// --------------------------------------------------------------------------- +function generateZIndexMap (): void { + const names = [ + 'base', + 'bottom-sheet', + 'cookie-banner', + 'dropdown', + 'fab', + 'modal', + 'popover', + 'side-sheet', + 'toast', + 'tooltip', + ]; + const content = generateSimpleMap(names, 'z-index', 'ZIndexTokenName', 'zIndexMap', 'zIndexTokenNames'); + writeFile(resolve(GENERATED_DIR, 'z-index-map.ts'), content); +} + +// --------------------------------------------------------------------------- +// Version +// --------------------------------------------------------------------------- +function generateVersion (): void { + const content = `${HEADER}export const tokensVersion = '${tokensPkg.version}';\n`; + writeFile(resolve(GENERATED_DIR, 'version.ts'), content); +} + +// --------------------------------------------------------------------------- +// Main +// --------------------------------------------------------------------------- +function main (): void { + mkdirSync(GENERATED_DIR, { recursive: true }); + + // eslint-disable-next-line no-console + console.info('Generating token maps...'); + + generateColorMap(); + generateTypographyMap(); + generateRadiusMap(); + generateSpacingMap(); + generateElevationMap(); + generateGradientMap(); + generateMotionMap(); + generateBreakpointMap(); + generateZIndexMap(); + generateVersion(); + + // eslint-disable-next-line no-console + console.info('Done.'); +} + +main(); diff --git a/packages/tools/pie-token-map/src/generated/breakpoint-map.ts b/packages/tools/pie-token-map/src/generated/breakpoint-map.ts new file mode 100644 index 0000000000..bb474786ab --- /dev/null +++ b/packages/tools/pie-token-map/src/generated/breakpoint-map.ts @@ -0,0 +1,22 @@ +// Auto-generated by scripts/generate-token-map.ts — do not edit manually. +import type { CssVarRef } from '../types.js'; + +export const breakpointTokenNames = [ + 'lg', + 'md', + 'sm', + 'xl', + 'xs', + 'xxl', +] as const; + +export type BreakpointTokenName = typeof breakpointTokenNames[number]; + +export const breakpointMap: Record = { + lg: 'var(--dt-breakpoint-lg)', + md: 'var(--dt-breakpoint-md)', + sm: 'var(--dt-breakpoint-sm)', + xl: 'var(--dt-breakpoint-xl)', + xs: 'var(--dt-breakpoint-xs)', + xxl: 'var(--dt-breakpoint-xxl)', +}; diff --git a/packages/tools/pie-token-map/src/generated/color-map.ts b/packages/tools/pie-token-map/src/generated/color-map.ts new file mode 100644 index 0000000000..0b063de7a9 --- /dev/null +++ b/packages/tools/pie-token-map/src/generated/color-map.ts @@ -0,0 +1,346 @@ +// Auto-generated by scripts/generate-token-map.ts — do not edit manually. +import type { CssVarRef } from '../types.js'; + +export const colorTokenNames = [ + 'active-01', + 'active-01-dark', + 'active-02', + 'active-02-light', + 'background-dark', + 'background-default', + 'background-subtle', + 'border-decorative-brand', + 'border-default', + 'border-form', + 'border-inverse', + 'border-neutral', + 'border-selected', + 'border-selected-brand', + 'border-strong', + 'border-subtle', + 'braze-container-01', + 'braze-container-03', + 'braze-container-04', + 'braze-container-05', + 'braze-container-08', + 'container-base', + 'container-base-dark', + 'container-dark', + 'container-default', + 'container-inverse', + 'container-inverse-alternative', + 'container-light', + 'container-neutral', + 'container-prominent', + 'container-strong', + 'container-subtle', + 'content-brand', + 'content-brand-solid', + 'content-dark', + 'content-dark-solid', + 'content-default', + 'content-default-solid', + 'content-disabled', + 'content-disabled-solid', + 'content-error', + 'content-error-solid', + 'content-interactive-brand', + 'content-interactive-brand-solid', + 'content-interactive-dark', + 'content-interactive-dark-solid', + 'content-interactive-error', + 'content-interactive-error-solid', + 'content-interactive-inverse', + 'content-interactive-inverse-solid', + 'content-interactive-light', + 'content-interactive-light-solid', + 'content-interactive-primary', + 'content-interactive-primary-solid', + 'content-interactive-secondary', + 'content-interactive-secondary-solid', + 'content-interactive-subdued', + 'content-interactive-subdued-solid', + 'content-interactive-tertiary', + 'content-inverse', + 'content-inverse-solid', + 'content-light', + 'content-light-solid', + 'content-link', + 'content-link-distinct', + 'content-link-inverse', + 'content-link-light', + 'content-link-visited', + 'content-link-visited-inverse', + 'content-placeholder', + 'content-placeholder-solid', + 'content-positive', + 'content-positive-solid', + 'content-subdued', + 'content-subdued-solid', + 'disabled-01', + 'disabled-01-inverse', + 'divider-default', + 'divider-inverse', + 'focus-inner', + 'focus-outer', + 'hover-01', + 'hover-01-dark', + 'hover-02', + 'hover-02-light', + 'interactive-brand', + 'interactive-dark', + 'interactive-error', + 'interactive-form', + 'interactive-inverse', + 'interactive-light', + 'interactive-primary', + 'interactive-secondary', + 'overlay', + 'resting', + 'skeleton-01', + 'skeleton-02', + 'skeleton-03', + 'skeleton-shimmer-01', + 'skeleton-shimmer-02', + 'skeleton-shimmer-03', + 'support-brand-01', + 'support-brand-02', + 'support-brand-03', + 'support-brand-03-subtle', + 'support-brand-03-tonal', + 'support-brand-04', + 'support-brand-04-subtle', + 'support-brand-04-tonal', + 'support-brand-05', + 'support-brand-05-subtle', + 'support-brand-05-tonal', + 'support-brand-06', + 'support-brand-06-subtle', + 'support-brand-06-tonal', + 'support-brand-07', + 'support-brand-07-subtle', + 'support-brand-08', + 'support-brand-08-subtle', + 'support-brand-08-tonal', + 'support-brand-tonal', + 'support-error', + 'support-error-02', + 'support-error-inverse', + 'support-error-tonal', + 'support-info', + 'support-info-02', + 'support-info-inverse', + 'support-info-tonal', + 'support-neutral', + 'support-neutral-persistent', + 'support-positive', + 'support-positive-02', + 'support-positive-inverse', + 'support-positive-tonal', + 'support-warning', + 'support-warning-02', + 'support-warning-inverse', + 'support-warning-tonal', + 'thirdparty-android-background-dashboard', + 'thirdparty-apple-container-primary', + 'thirdparty-apple-container-secondary', + 'thirdparty-apple-content-primary', + 'thirdparty-apple-content-secondary', + 'thirdparty-coop-container-primary', + 'thirdparty-facebook-container-primary', + 'thirdparty-facebook-container-secondary', + 'thirdparty-facebook-content-primary', + 'thirdparty-facebook-content-secondary', + 'thirdparty-google-border-primary', + 'thirdparty-google-border-secondary', + 'thirdparty-google-container-primary', + 'thirdparty-google-container-secondary', + 'thirdparty-google-content-primary', + 'thirdparty-google-content-primary-subdued', + 'thirdparty-google-content-secondary', + 'thirdparty-google-content-secondary-subdued', + 'thirdparty-microsoft-border-primary', + 'thirdparty-microsoft-border-secondary', + 'thirdparty-microsoft-container-primary', + 'thirdparty-microsoft-container-secondary', + 'thirdparty-microsoft-content-primary', + 'thirdparty-microsoft-content-secondary', + 'transparent', + 'web-container-base', + 'web-container-base-dark', + 'web-container-neutral', + 'web-container-prominent', +] as const; + +export type ColorTokenName = typeof colorTokenNames[number]; + +export const colorMap: Record = { + 'active-01': 'var(--dt-color-active-01)', + 'active-01-dark': 'var(--dt-color-active-01-dark)', + 'active-02': 'var(--dt-color-active-02)', + 'active-02-light': 'var(--dt-color-active-02-light)', + 'background-dark': 'var(--dt-color-background-dark)', + 'background-default': 'var(--dt-color-background-default)', + 'background-subtle': 'var(--dt-color-background-subtle)', + 'border-decorative-brand': 'var(--dt-color-border-decorative-brand)', + 'border-default': 'var(--dt-color-border-default)', + 'border-form': 'var(--dt-color-border-form)', + 'border-inverse': 'var(--dt-color-border-inverse)', + 'border-neutral': 'var(--dt-color-border-neutral)', + 'border-selected': 'var(--dt-color-border-selected)', + 'border-selected-brand': 'var(--dt-color-border-selected-brand)', + 'border-strong': 'var(--dt-color-border-strong)', + 'border-subtle': 'var(--dt-color-border-subtle)', + 'braze-container-01': 'var(--dt-color-braze-container-01)', + 'braze-container-03': 'var(--dt-color-braze-container-03)', + 'braze-container-04': 'var(--dt-color-braze-container-04)', + 'braze-container-05': 'var(--dt-color-braze-container-05)', + 'braze-container-08': 'var(--dt-color-braze-container-08)', + 'container-base': 'var(--dt-color-web-container-base)', + 'container-base-dark': 'var(--dt-color-web-container-base-dark)', + 'container-dark': 'var(--dt-color-container-dark)', + 'container-default': 'var(--dt-color-container-default)', + 'container-inverse': 'var(--dt-color-container-inverse)', + 'container-inverse-alternative': 'var(--dt-color-container-inverse-alternative)', + 'container-light': 'var(--dt-color-container-light)', + 'container-neutral': 'var(--dt-color-web-container-neutral)', + 'container-prominent': 'var(--dt-color-web-container-prominent)', + 'container-strong': 'var(--dt-color-container-strong)', + 'container-subtle': 'var(--dt-color-container-subtle)', + 'content-brand': 'var(--dt-color-content-brand)', + 'content-brand-solid': 'var(--dt-color-content-brand-solid)', + 'content-dark': 'var(--dt-color-content-dark)', + 'content-dark-solid': 'var(--dt-color-content-dark-solid)', + 'content-default': 'var(--dt-color-content-default)', + 'content-default-solid': 'var(--dt-color-content-default-solid)', + 'content-disabled': 'var(--dt-color-content-disabled)', + 'content-disabled-solid': 'var(--dt-color-content-disabled-solid)', + 'content-error': 'var(--dt-color-content-error)', + 'content-error-solid': 'var(--dt-color-content-error-solid)', + 'content-interactive-brand': 'var(--dt-color-content-interactive-brand)', + 'content-interactive-brand-solid': 'var(--dt-color-content-interactive-brand-solid)', + 'content-interactive-dark': 'var(--dt-color-content-interactive-dark)', + 'content-interactive-dark-solid': 'var(--dt-color-content-interactive-dark-solid)', + 'content-interactive-error': 'var(--dt-color-content-interactive-error)', + 'content-interactive-error-solid': 'var(--dt-color-content-interactive-error-solid)', + 'content-interactive-inverse': 'var(--dt-color-content-interactive-inverse)', + 'content-interactive-inverse-solid': 'var(--dt-color-content-interactive-inverse-solid)', + 'content-interactive-light': 'var(--dt-color-content-interactive-light)', + 'content-interactive-light-solid': 'var(--dt-color-content-interactive-light-solid)', + 'content-interactive-primary': 'var(--dt-color-content-interactive-primary)', + 'content-interactive-primary-solid': 'var(--dt-color-content-interactive-primary-solid)', + 'content-interactive-secondary': 'var(--dt-color-content-interactive-secondary)', + 'content-interactive-secondary-solid': 'var(--dt-color-content-interactive-secondary-solid)', + 'content-interactive-subdued': 'var(--dt-color-content-interactive-subdued)', + 'content-interactive-subdued-solid': 'var(--dt-color-content-interactive-subdued-solid)', + 'content-interactive-tertiary': 'var(--dt-color-content-interactive-tertiary)', + 'content-inverse': 'var(--dt-color-content-inverse)', + 'content-inverse-solid': 'var(--dt-color-content-inverse-solid)', + 'content-light': 'var(--dt-color-content-light)', + 'content-light-solid': 'var(--dt-color-content-light-solid)', + 'content-link': 'var(--dt-color-content-link)', + 'content-link-distinct': 'var(--dt-color-content-link-distinct)', + 'content-link-inverse': 'var(--dt-color-content-link-inverse)', + 'content-link-light': 'var(--dt-color-content-link-light)', + 'content-link-visited': 'var(--dt-color-content-link-visited)', + 'content-link-visited-inverse': 'var(--dt-color-content-link-visited-inverse)', + 'content-placeholder': 'var(--dt-color-content-placeholder)', + 'content-placeholder-solid': 'var(--dt-color-content-placeholder-solid)', + 'content-positive': 'var(--dt-color-content-positive)', + 'content-positive-solid': 'var(--dt-color-content-positive-solid)', + 'content-subdued': 'var(--dt-color-content-subdued)', + 'content-subdued-solid': 'var(--dt-color-content-subdued-solid)', + 'disabled-01': 'var(--dt-color-disabled-01)', + 'disabled-01-inverse': 'var(--dt-color-disabled-01-inverse)', + 'divider-default': 'var(--dt-color-divider-default)', + 'divider-inverse': 'var(--dt-color-divider-inverse)', + 'focus-inner': 'var(--dt-color-focus-inner)', + 'focus-outer': 'var(--dt-color-focus-outer)', + 'hover-01': 'var(--dt-color-hover-01)', + 'hover-01-dark': 'var(--dt-color-hover-01-dark)', + 'hover-02': 'var(--dt-color-hover-02)', + 'hover-02-light': 'var(--dt-color-hover-02-light)', + 'interactive-brand': 'var(--dt-color-interactive-brand)', + 'interactive-dark': 'var(--dt-color-interactive-dark)', + 'interactive-error': 'var(--dt-color-interactive-error)', + 'interactive-form': 'var(--dt-color-interactive-form)', + 'interactive-inverse': 'var(--dt-color-interactive-inverse)', + 'interactive-light': 'var(--dt-color-interactive-light)', + 'interactive-primary': 'var(--dt-color-interactive-primary)', + 'interactive-secondary': 'var(--dt-color-interactive-secondary)', + overlay: 'var(--dt-color-overlay)', + resting: 'var(--dt-color-resting)', + 'skeleton-01': 'var(--dt-color-skeleton-01)', + 'skeleton-02': 'var(--dt-color-skeleton-02)', + 'skeleton-03': 'var(--dt-color-skeleton-03)', + 'skeleton-shimmer-01': 'var(--dt-color-skeleton-shimmer-01)', + 'skeleton-shimmer-02': 'var(--dt-color-skeleton-shimmer-02)', + 'skeleton-shimmer-03': 'var(--dt-color-skeleton-shimmer-03)', + 'support-brand-01': 'var(--dt-color-support-brand-01)', + 'support-brand-02': 'var(--dt-color-support-brand-02)', + 'support-brand-03': 'var(--dt-color-support-brand-03)', + 'support-brand-03-subtle': 'var(--dt-color-support-brand-03-subtle)', + 'support-brand-03-tonal': 'var(--dt-color-support-brand-03-tonal)', + 'support-brand-04': 'var(--dt-color-support-brand-04)', + 'support-brand-04-subtle': 'var(--dt-color-support-brand-04-subtle)', + 'support-brand-04-tonal': 'var(--dt-color-support-brand-04-tonal)', + 'support-brand-05': 'var(--dt-color-support-brand-05)', + 'support-brand-05-subtle': 'var(--dt-color-support-brand-05-subtle)', + 'support-brand-05-tonal': 'var(--dt-color-support-brand-05-tonal)', + 'support-brand-06': 'var(--dt-color-support-brand-06)', + 'support-brand-06-subtle': 'var(--dt-color-support-brand-06-subtle)', + 'support-brand-06-tonal': 'var(--dt-color-support-brand-06-tonal)', + 'support-brand-07': 'var(--dt-color-support-brand-07)', + 'support-brand-07-subtle': 'var(--dt-color-support-brand-07-subtle)', + 'support-brand-08': 'var(--dt-color-support-brand-08)', + 'support-brand-08-subtle': 'var(--dt-color-support-brand-08-subtle)', + 'support-brand-08-tonal': 'var(--dt-color-support-brand-08-tonal)', + 'support-brand-tonal': 'var(--dt-color-support-brand-tonal)', + 'support-error': 'var(--dt-color-support-error)', + 'support-error-02': 'var(--dt-color-support-error-02)', + 'support-error-inverse': 'var(--dt-color-support-error-inverse)', + 'support-error-tonal': 'var(--dt-color-support-error-tonal)', + 'support-info': 'var(--dt-color-support-info)', + 'support-info-02': 'var(--dt-color-support-info-02)', + 'support-info-inverse': 'var(--dt-color-support-info-inverse)', + 'support-info-tonal': 'var(--dt-color-support-info-tonal)', + 'support-neutral': 'var(--dt-color-support-neutral)', + 'support-neutral-persistent': 'var(--dt-color-support-neutral-persistent)', + 'support-positive': 'var(--dt-color-support-positive)', + 'support-positive-02': 'var(--dt-color-support-positive-02)', + 'support-positive-inverse': 'var(--dt-color-support-positive-inverse)', + 'support-positive-tonal': 'var(--dt-color-support-positive-tonal)', + 'support-warning': 'var(--dt-color-support-warning)', + 'support-warning-02': 'var(--dt-color-support-warning-02)', + 'support-warning-inverse': 'var(--dt-color-support-warning-inverse)', + 'support-warning-tonal': 'var(--dt-color-support-warning-tonal)', + 'thirdparty-android-background-dashboard': 'var(--dt-color-thirdparty-android-background-dashboard)', + 'thirdparty-apple-container-primary': 'var(--dt-color-thirdparty-apple-container-primary)', + 'thirdparty-apple-container-secondary': 'var(--dt-color-thirdparty-apple-container-secondary)', + 'thirdparty-apple-content-primary': 'var(--dt-color-thirdparty-apple-content-primary)', + 'thirdparty-apple-content-secondary': 'var(--dt-color-thirdparty-apple-content-secondary)', + 'thirdparty-coop-container-primary': 'var(--dt-color-thirdparty-coop-container-primary)', + 'thirdparty-facebook-container-primary': 'var(--dt-color-thirdparty-facebook-container-primary)', + 'thirdparty-facebook-container-secondary': 'var(--dt-color-thirdparty-facebook-container-secondary)', + 'thirdparty-facebook-content-primary': 'var(--dt-color-thirdparty-facebook-content-primary)', + 'thirdparty-facebook-content-secondary': 'var(--dt-color-thirdparty-facebook-content-secondary)', + 'thirdparty-google-border-primary': 'var(--dt-color-thirdparty-google-border-primary)', + 'thirdparty-google-border-secondary': 'var(--dt-color-thirdparty-google-border-secondary)', + 'thirdparty-google-container-primary': 'var(--dt-color-thirdparty-google-container-primary)', + 'thirdparty-google-container-secondary': 'var(--dt-color-thirdparty-google-container-secondary)', + 'thirdparty-google-content-primary': 'var(--dt-color-thirdparty-google-content-primary)', + 'thirdparty-google-content-primary-subdued': 'var(--dt-color-thirdparty-google-content-primary-subdued)', + 'thirdparty-google-content-secondary': 'var(--dt-color-thirdparty-google-content-secondary)', + 'thirdparty-google-content-secondary-subdued': 'var(--dt-color-thirdparty-google-content-secondary-subdued)', + 'thirdparty-microsoft-border-primary': 'var(--dt-color-thirdparty-microsoft-border-primary)', + 'thirdparty-microsoft-border-secondary': 'var(--dt-color-thirdparty-microsoft-border-secondary)', + 'thirdparty-microsoft-container-primary': 'var(--dt-color-thirdparty-microsoft-container-primary)', + 'thirdparty-microsoft-container-secondary': 'var(--dt-color-thirdparty-microsoft-container-secondary)', + 'thirdparty-microsoft-content-primary': 'var(--dt-color-thirdparty-microsoft-content-primary)', + 'thirdparty-microsoft-content-secondary': 'var(--dt-color-thirdparty-microsoft-content-secondary)', + transparent: 'var(--dt-color-transparent)', + 'web-container-base': 'var(--dt-color-web-container-base)', + 'web-container-base-dark': 'var(--dt-color-web-container-base-dark)', + 'web-container-neutral': 'var(--dt-color-web-container-neutral)', + 'web-container-prominent': 'var(--dt-color-web-container-prominent)', +}; diff --git a/packages/tools/pie-token-map/src/generated/elevation-map.ts b/packages/tools/pie-token-map/src/generated/elevation-map.ts new file mode 100644 index 0000000000..ba0dd8924d --- /dev/null +++ b/packages/tools/pie-token-map/src/generated/elevation-map.ts @@ -0,0 +1,46 @@ +// Auto-generated by scripts/generate-token-map.ts — do not edit manually. +import type { CssVarRef } from '../types.js'; + +export const elevationTokenNames = [ + '01', + '02', + '03', + '04', + '05', + 'above-20', + 'below-10', + 'below-20', + 'card', + 'inverse-01', + 'inverse-02', + 'inverse-03', + 'inverse-04', + 'inverse-05', + 'inverse-above-20', + 'inverse-below-10', + 'inverse-below-20', + 'inverse-card', +] as const; + +export type ElevationTokenName = typeof elevationTokenNames[number]; + +export const elevationMap: Record = { + '01': 'var(--dt-elevation-01)', + '02': 'var(--dt-elevation-02)', + '03': 'var(--dt-elevation-03)', + '04': 'var(--dt-elevation-04)', + '05': 'var(--dt-elevation-05)', + 'above-20': 'var(--dt-elevation-above-20)', + 'below-10': 'var(--dt-elevation-below-10)', + 'below-20': 'var(--dt-elevation-below-20)', + card: 'var(--dt-elevation-card)', + 'inverse-01': 'var(--dt-elevation-inverse-01)', + 'inverse-02': 'var(--dt-elevation-inverse-02)', + 'inverse-03': 'var(--dt-elevation-inverse-03)', + 'inverse-04': 'var(--dt-elevation-inverse-04)', + 'inverse-05': 'var(--dt-elevation-inverse-05)', + 'inverse-above-20': 'var(--dt-elevation-inverse-above-20)', + 'inverse-below-10': 'var(--dt-elevation-inverse-below-10)', + 'inverse-below-20': 'var(--dt-elevation-inverse-below-20)', + 'inverse-card': 'var(--dt-elevation-inverse-card)', +}; diff --git a/packages/tools/pie-token-map/src/generated/gradient-map.ts b/packages/tools/pie-token-map/src/generated/gradient-map.ts new file mode 100644 index 0000000000..143a74fa34 --- /dev/null +++ b/packages/tools/pie-token-map/src/generated/gradient-map.ts @@ -0,0 +1,24 @@ +// Auto-generated by scripts/generate-token-map.ts — do not edit manually. +import type { CssVarRef } from '../types.js'; + +export const gradientTokenNames = [ + 'ai-border-default', + 'ai-container-default', + 'jetplus-brand-01', + 'jetplus-brand-02', + 'jetplus-brand-03', + 'jetplus-support-01', + 'jetplus-support-02', +] as const; + +export type GradientTokenName = typeof gradientTokenNames[number]; + +export const gradientMap: Record = { + 'ai-border-default': 'var(--dt-gradient-ai-border-default)', + 'ai-container-default': 'var(--dt-gradient-ai-container-default)', + 'jetplus-brand-01': 'var(--dt-gradient-jetplus-brand-01)', + 'jetplus-brand-02': 'var(--dt-gradient-jetplus-brand-02)', + 'jetplus-brand-03': 'var(--dt-gradient-jetplus-brand-03)', + 'jetplus-support-01': 'var(--dt-gradient-jetplus-support-01)', + 'jetplus-support-02': 'var(--dt-gradient-jetplus-support-02)', +}; diff --git a/packages/tools/pie-token-map/src/generated/motion-map.ts b/packages/tools/pie-token-map/src/generated/motion-map.ts new file mode 100644 index 0000000000..74c167976d --- /dev/null +++ b/packages/tools/pie-token-map/src/generated/motion-map.ts @@ -0,0 +1,30 @@ +// Auto-generated by scripts/generate-token-map.ts — do not edit manually. +import type { CssVarRef } from '../types.js'; + +export const motionTokenNames = [ + 'easing-in', + 'easing-out', + 'easing-persistent-expressive', + 'easing-persistent-functional', + 'timing-100', + 'timing-150', + 'timing-200', + 'timing-250', + 'timing-300', + 'timing-350', +] as const; + +export type MotionTokenName = typeof motionTokenNames[number]; + +export const motionMap: Record = { + 'easing-in': 'var(--dt-motion-easing-in)', + 'easing-out': 'var(--dt-motion-easing-out)', + 'easing-persistent-expressive': 'var(--dt-motion-easing-persistent-expressive)', + 'easing-persistent-functional': 'var(--dt-motion-easing-persistent-functional)', + 'timing-100': 'var(--dt-motion-timing-100)', + 'timing-150': 'var(--dt-motion-timing-150)', + 'timing-200': 'var(--dt-motion-timing-200)', + 'timing-250': 'var(--dt-motion-timing-250)', + 'timing-300': 'var(--dt-motion-timing-300)', + 'timing-350': 'var(--dt-motion-timing-350)', +}; diff --git a/packages/tools/pie-token-map/src/generated/radius-map.ts b/packages/tools/pie-token-map/src/generated/radius-map.ts new file mode 100644 index 0000000000..0a96efcc2c --- /dev/null +++ b/packages/tools/pie-token-map/src/generated/radius-map.ts @@ -0,0 +1,22 @@ +// Auto-generated by scripts/generate-token-map.ts — do not edit manually. +import type { CssVarRef } from '../types.js'; + +export const radiusTokenNames = [ + 'rounded-a', + 'rounded-b', + 'rounded-c', + 'rounded-d', + 'rounded-e', + 'rounded-none', +] as const; + +export type RadiusTokenName = typeof radiusTokenNames[number]; + +export const radiusMap: Record = { + 'rounded-a': 'var(--dt-radius-rounded-a)', + 'rounded-b': 'var(--dt-radius-rounded-b)', + 'rounded-c': 'var(--dt-radius-rounded-c)', + 'rounded-d': 'var(--dt-radius-rounded-d)', + 'rounded-e': 'var(--dt-radius-rounded-e)', + 'rounded-none': 'var(--dt-radius-rounded-none)', +}; diff --git a/packages/tools/pie-token-map/src/generated/spacing-map.ts b/packages/tools/pie-token-map/src/generated/spacing-map.ts new file mode 100644 index 0000000000..df89155ca6 --- /dev/null +++ b/packages/tools/pie-token-map/src/generated/spacing-map.ts @@ -0,0 +1,34 @@ +// Auto-generated by scripts/generate-token-map.ts — do not edit manually. +import type { CssVarRef } from '../types.js'; + +export const spacingTokenNames = [ + 'a', + 'a-small', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'none', +] as const; + +export type SpacingTokenName = typeof spacingTokenNames[number]; + +export const spacingMap: Record = { + a: 'var(--dt-spacing-a)', + 'a-small': 'var(--dt-spacing-a-small)', + b: 'var(--dt-spacing-b)', + c: 'var(--dt-spacing-c)', + d: 'var(--dt-spacing-d)', + e: 'var(--dt-spacing-e)', + f: 'var(--dt-spacing-f)', + g: 'var(--dt-spacing-g)', + h: 'var(--dt-spacing-h)', + i: 'var(--dt-spacing-i)', + j: 'var(--dt-spacing-j)', + none: 'var(--dt-spacing-none)', +}; diff --git a/packages/tools/pie-token-map/src/generated/typography-map.ts b/packages/tools/pie-token-map/src/generated/typography-map.ts new file mode 100644 index 0000000000..d09bed685d --- /dev/null +++ b/packages/tools/pie-token-map/src/generated/typography-map.ts @@ -0,0 +1,274 @@ +// Auto-generated by scripts/generate-token-map.ts — do not edit manually. +import type { TypographyTokenValue } from '../types.js'; + +export const typographyTokenNames = [ + 'body-l', + 'body-l-link', + 'body-s', + 'body-s-link', + 'body-strong-l', + 'body-strong-l-link', + 'body-strong-s', + 'body-strong-s-link', + 'caption', + 'caption-link', + 'caption-strong', + 'caption-strong-italic', + 'caption-strong-link', + 'heading-l', + 'heading-l-italic', + 'heading-m', + 'heading-m-italic', + 'heading-s', + 'heading-s-italic', + 'heading-xl', + 'heading-xl-italic', + 'heading-xs', + 'heading-xs-italic', + 'heading-xxl', + 'heading-xxl-italic', + 'interactive-l', + 'interactive-s', + 'interactive-xs', + 'subheading-l', + 'subheading-s', +] as const; + +export type TypographyTokenName = typeof typographyTokenNames[number]; + +export const typographyMap: Record = { + 'body-l': { + family: 'var(--dt-font-body-l-family)', + weight: 'var(--dt-font-body-l-weight)', + size: 'var(--dt-font-body-l-size)', + lineHeight: 'var(--dt-font-body-l-line-height)', + paragraph: 'var(--dt-font-body-l-paragraph)', + }, + 'body-l-link': { + family: 'var(--dt-font-body-l-link-family)', + weight: 'var(--dt-font-body-l-link-weight)', + size: 'var(--dt-font-body-l-link-size)', + lineHeight: 'var(--dt-font-body-l-link-line-height)', + textDecoration: 'var(--dt-font-body-l-link-text-decoration)', + paragraph: 'var(--dt-font-body-l-link-paragraph)', + }, + 'body-s': { + family: 'var(--dt-font-body-s-family)', + weight: 'var(--dt-font-body-s-weight)', + size: 'var(--dt-font-body-s-size)', + lineHeight: 'var(--dt-font-body-s-line-height)', + paragraph: 'var(--dt-font-body-s-paragraph)', + }, + 'body-s-link': { + family: 'var(--dt-font-body-s-link-family)', + weight: 'var(--dt-font-body-s-link-weight)', + size: 'var(--dt-font-body-s-link-size)', + lineHeight: 'var(--dt-font-body-s-link-line-height)', + textDecoration: 'var(--dt-font-body-s-link-text-decoration)', + paragraph: 'var(--dt-font-body-s-link-paragraph)', + }, + 'body-strong-l': { + family: 'var(--dt-font-body-strong-l-family)', + weight: 'var(--dt-font-body-strong-l-weight)', + size: 'var(--dt-font-body-strong-l-size)', + lineHeight: 'var(--dt-font-body-strong-l-line-height)', + paragraph: 'var(--dt-font-body-strong-l-paragraph)', + }, + 'body-strong-l-link': { + family: 'var(--dt-font-body-strong-l-link-family)', + weight: 'var(--dt-font-body-strong-l-link-weight)', + size: 'var(--dt-font-body-strong-l-link-size)', + lineHeight: 'var(--dt-font-body-strong-l-link-line-height)', + textDecoration: 'var(--dt-font-body-strong-l-link-text-decoration)', + paragraph: 'var(--dt-font-body-strong-l-link-paragraph)', + }, + 'body-strong-s': { + family: 'var(--dt-font-body-strong-s-family)', + weight: 'var(--dt-font-body-strong-s-weight)', + size: 'var(--dt-font-body-strong-s-size)', + lineHeight: 'var(--dt-font-body-strong-s-line-height)', + paragraph: 'var(--dt-font-body-strong-s-paragraph)', + }, + 'body-strong-s-link': { + family: 'var(--dt-font-body-strong-s-link-family)', + weight: 'var(--dt-font-body-strong-s-link-weight)', + size: 'var(--dt-font-body-strong-s-link-size)', + lineHeight: 'var(--dt-font-body-strong-s-link-line-height)', + textDecoration: 'var(--dt-font-body-strong-s-link-text-decoration)', + paragraph: 'var(--dt-font-body-strong-s-link-paragraph)', + }, + caption: { + family: 'var(--dt-font-caption-family)', + weight: 'var(--dt-font-caption-weight)', + size: 'var(--dt-font-caption-size)', + lineHeight: 'var(--dt-font-caption-line-height)', + paragraph: 'var(--dt-font-caption-paragraph)', + }, + 'caption-link': { + family: 'var(--dt-font-caption-link-family)', + weight: 'var(--dt-font-caption-link-weight)', + size: 'var(--dt-font-caption-link-size)', + lineHeight: 'var(--dt-font-caption-link-line-height)', + textDecoration: 'var(--dt-font-caption-link-text-decoration)', + paragraph: 'var(--dt-font-caption-link-paragraph)', + }, + 'caption-strong': { + family: 'var(--dt-font-caption-strong-family)', + weight: 'var(--dt-font-caption-strong-weight)', + size: 'var(--dt-font-caption-strong-size)', + lineHeight: 'var(--dt-font-caption-strong-line-height)', + paragraph: 'var(--dt-font-caption-strong-paragraph)', + }, + 'caption-strong-italic': { + family: 'var(--dt-font-caption-strong-italic-family)', + weight: 'var(--dt-font-caption-strong-italic-weight)', + size: 'var(--dt-font-caption-strong-italic-size)', + lineHeight: 'var(--dt-font-caption-strong-italic-line-height)', + fontStyle: 'var(--dt-font-caption-strong-italic-font-style)', + paragraph: 'var(--dt-font-caption-strong-italic-paragraph)', + }, + 'caption-strong-link': { + family: 'var(--dt-font-caption-strong-link-family)', + weight: 'var(--dt-font-caption-strong-link-weight)', + size: 'var(--dt-font-caption-strong-link-size)', + lineHeight: 'var(--dt-font-caption-strong-link-line-height)', + textDecoration: 'var(--dt-font-caption-strong-link-text-decoration)', + paragraph: 'var(--dt-font-caption-strong-link-paragraph)', + }, + 'heading-l': { + family: 'var(--dt-font-heading-l-family)', + weight: 'var(--dt-font-heading-l-weight)', + sizeWide: 'var(--dt-font-heading-l-size--wide)', + lineHeightWide: 'var(--dt-font-heading-l-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-l-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-l-line-height--narrow)', + }, + 'heading-l-italic': { + family: 'var(--dt-font-heading-l-italic-family)', + weight: 'var(--dt-font-heading-l-italic-weight)', + sizeWide: 'var(--dt-font-heading-l-italic-size--wide)', + lineHeightWide: 'var(--dt-font-heading-l-italic-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-l-italic-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-l-italic-line-height--narrow)', + fontStyle: 'var(--dt-font-heading-l-italic-font-style)', + }, + 'heading-m': { + family: 'var(--dt-font-heading-m-family)', + weight: 'var(--dt-font-heading-m-weight)', + sizeWide: 'var(--dt-font-heading-m-size--wide)', + lineHeightWide: 'var(--dt-font-heading-m-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-m-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-m-line-height--narrow)', + }, + 'heading-m-italic': { + family: 'var(--dt-font-heading-m-italic-family)', + weight: 'var(--dt-font-heading-m-italic-weight)', + sizeWide: 'var(--dt-font-heading-m-italic-size--wide)', + lineHeightWide: 'var(--dt-font-heading-m-italic-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-m-italic-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-m-italic-line-height--narrow)', + fontStyle: 'var(--dt-font-heading-m-italic-font-style)', + }, + 'heading-s': { + family: 'var(--dt-font-heading-s-family)', + weight: 'var(--dt-font-heading-s-weight)', + sizeWide: 'var(--dt-font-heading-s-size--wide)', + lineHeightWide: 'var(--dt-font-heading-s-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-s-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-s-line-height--narrow)', + }, + 'heading-s-italic': { + family: 'var(--dt-font-heading-s-italic-family)', + weight: 'var(--dt-font-heading-s-italic-weight)', + sizeWide: 'var(--dt-font-heading-s-italic-size--wide)', + lineHeightWide: 'var(--dt-font-heading-s-italic-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-s-italic-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-s-italic-line-height--narrow)', + fontStyle: 'var(--dt-font-heading-s-italic-font-style)', + }, + 'heading-xl': { + family: 'var(--dt-font-heading-xl-family)', + weight: 'var(--dt-font-heading-xl-weight)', + sizeWide: 'var(--dt-font-heading-xl-size--wide)', + lineHeightWide: 'var(--dt-font-heading-xl-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-xl-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-xl-line-height--narrow)', + }, + 'heading-xl-italic': { + family: 'var(--dt-font-heading-xl-italic-family)', + weight: 'var(--dt-font-heading-xl-italic-weight)', + sizeWide: 'var(--dt-font-heading-xl-italic-size--wide)', + lineHeightWide: 'var(--dt-font-heading-xl-italic-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-xl-italic-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-xl-italic-line-height--narrow)', + fontStyle: 'var(--dt-font-heading-xl-italic-font-style)', + }, + 'heading-xs': { + family: 'var(--dt-font-heading-xs-family)', + weight: 'var(--dt-font-heading-xs-weight)', + sizeWide: 'var(--dt-font-heading-xs-size--wide)', + lineHeightWide: 'var(--dt-font-heading-xs-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-xs-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-xs-line-height--narrow)', + }, + 'heading-xs-italic': { + family: 'var(--dt-font-heading-xs-italic-family)', + weight: 'var(--dt-font-heading-xs-italic-weight)', + sizeWide: 'var(--dt-font-heading-xs-italic-size--wide)', + lineHeightWide: 'var(--dt-font-heading-xs-italic-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-xs-italic-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-xs-italic-line-height--narrow)', + fontStyle: 'var(--dt-font-heading-xs-italic-font-style)', + }, + 'heading-xxl': { + family: 'var(--dt-font-heading-xxl-family)', + weight: 'var(--dt-font-heading-xxl-weight)', + sizeWide: 'var(--dt-font-heading-xxl-size--wide)', + lineHeightWide: 'var(--dt-font-heading-xxl-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-xxl-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-xxl-line-height--narrow)', + }, + 'heading-xxl-italic': { + family: 'var(--dt-font-heading-xxl-italic-family)', + weight: 'var(--dt-font-heading-xxl-italic-weight)', + sizeWide: 'var(--dt-font-heading-xxl-italic-size--wide)', + lineHeightWide: 'var(--dt-font-heading-xxl-italic-line-height--wide)', + sizeNarrow: 'var(--dt-font-heading-xxl-italic-size--narrow)', + lineHeightNarrow: 'var(--dt-font-heading-xxl-italic-line-height--narrow)', + fontStyle: 'var(--dt-font-heading-xxl-italic-font-style)', + }, + 'interactive-l': { + family: 'var(--dt-font-interactive-l-family)', + weight: 'var(--dt-font-interactive-l-weight)', + size: 'var(--dt-font-interactive-l-size)', + lineHeight: 'var(--dt-font-interactive-l-line-height)', + }, + 'interactive-s': { + family: 'var(--dt-font-interactive-s-family)', + weight: 'var(--dt-font-interactive-s-weight)', + size: 'var(--dt-font-interactive-s-size)', + lineHeight: 'var(--dt-font-interactive-s-line-height)', + }, + 'interactive-xs': { + family: 'var(--dt-font-interactive-xs-family)', + weight: 'var(--dt-font-interactive-xs-weight)', + size: 'var(--dt-font-interactive-xs-size)', + lineHeight: 'var(--dt-font-interactive-xs-line-height)', + }, + 'subheading-l': { + family: 'var(--dt-font-subheading-l-family)', + weight: 'var(--dt-font-subheading-l-weight)', + sizeWide: 'var(--dt-font-subheading-l-size--wide)', + lineHeightWide: 'var(--dt-font-subheading-l-line-height--wide)', + sizeNarrow: 'var(--dt-font-subheading-l-size--narrow)', + lineHeightNarrow: 'var(--dt-font-subheading-l-line-height--narrow)', + }, + 'subheading-s': { + family: 'var(--dt-font-subheading-s-family)', + weight: 'var(--dt-font-subheading-s-weight)', + sizeWide: 'var(--dt-font-subheading-s-size--wide)', + lineHeightWide: 'var(--dt-font-subheading-s-line-height--wide)', + sizeNarrow: 'var(--dt-font-subheading-s-size--narrow)', + lineHeightNarrow: 'var(--dt-font-subheading-s-line-height--narrow)', + }, +}; diff --git a/packages/tools/pie-token-map/src/generated/version.ts b/packages/tools/pie-token-map/src/generated/version.ts new file mode 100644 index 0000000000..b383446d05 --- /dev/null +++ b/packages/tools/pie-token-map/src/generated/version.ts @@ -0,0 +1,2 @@ +// Auto-generated by scripts/generate-token-map.ts — do not edit manually. +export const tokensVersion = '7.11.1'; diff --git a/packages/tools/pie-token-map/src/generated/z-index-map.ts b/packages/tools/pie-token-map/src/generated/z-index-map.ts new file mode 100644 index 0000000000..b3cfd06156 --- /dev/null +++ b/packages/tools/pie-token-map/src/generated/z-index-map.ts @@ -0,0 +1,30 @@ +// Auto-generated by scripts/generate-token-map.ts — do not edit manually. +import type { CssVarRef } from '../types.js'; + +export const zIndexTokenNames = [ + 'base', + 'bottom-sheet', + 'cookie-banner', + 'dropdown', + 'fab', + 'modal', + 'popover', + 'side-sheet', + 'toast', + 'tooltip', +] as const; + +export type ZIndexTokenName = typeof zIndexTokenNames[number]; + +export const zIndexMap: Record = { + base: 'var(--dt-z-index-base)', + 'bottom-sheet': 'var(--dt-z-index-bottom-sheet)', + 'cookie-banner': 'var(--dt-z-index-cookie-banner)', + dropdown: 'var(--dt-z-index-dropdown)', + fab: 'var(--dt-z-index-fab)', + modal: 'var(--dt-z-index-modal)', + popover: 'var(--dt-z-index-popover)', + 'side-sheet': 'var(--dt-z-index-side-sheet)', + toast: 'var(--dt-z-index-toast)', + tooltip: 'var(--dt-z-index-tooltip)', +}; diff --git a/packages/tools/pie-token-map/src/index.ts b/packages/tools/pie-token-map/src/index.ts new file mode 100644 index 0000000000..0af4e2262c --- /dev/null +++ b/packages/tools/pie-token-map/src/index.ts @@ -0,0 +1,110 @@ +import type { CssVarRef, TokenCategory, TypographyTokenValue } from './types.js'; +import { colorMap } from './generated/color-map.js'; +import { typographyMap } from './generated/typography-map.js'; +import { radiusMap } from './generated/radius-map.js'; +import { spacingMap } from './generated/spacing-map.js'; +import { elevationMap } from './generated/elevation-map.js'; +import { gradientMap } from './generated/gradient-map.js'; +import { motionMap } from './generated/motion-map.js'; +import { breakpointMap } from './generated/breakpoint-map.js'; +import { zIndexMap } from './generated/z-index-map.js'; +import { tokensVersion } from './generated/version.js'; + +export type { CssVarRef, TokenCategory, TypographyTokenValue } from './types.js'; +export { tokenCategories } from './types.js'; + +export type { ColorTokenName } from './generated/color-map.js'; +export type { TypographyTokenName } from './generated/typography-map.js'; +export type { RadiusTokenName } from './generated/radius-map.js'; +export type { SpacingTokenName } from './generated/spacing-map.js'; +export type { ElevationTokenName } from './generated/elevation-map.js'; +export type { GradientTokenName } from './generated/gradient-map.js'; +export type { MotionTokenName } from './generated/motion-map.js'; +export type { BreakpointTokenName } from './generated/breakpoint-map.js'; +export type { ZIndexTokenName } from './generated/z-index-map.js'; + +export { colorTokenNames, colorMap } from './generated/color-map.js'; +export { typographyTokenNames, typographyMap } from './generated/typography-map.js'; +export { radiusTokenNames, radiusMap } from './generated/radius-map.js'; +export { spacingTokenNames, spacingMap } from './generated/spacing-map.js'; +export { elevationTokenNames, elevationMap } from './generated/elevation-map.js'; +export { gradientTokenNames, gradientMap } from './generated/gradient-map.js'; +export { motionTokenNames, motionMap } from './generated/motion-map.js'; +export { breakpointTokenNames, breakpointMap } from './generated/breakpoint-map.js'; +export { zIndexTokenNames, zIndexMap } from './generated/z-index-map.js'; +export { tokensVersion } from './generated/version.js'; + +export function getColor (name: string): CssVarRef | undefined { + return (colorMap as Record)[name]; +} + +export function getTypography (name: string): TypographyTokenValue | undefined { + return (typographyMap as Record)[name]; +} + +export function getRadius (name: string): CssVarRef | undefined { + return (radiusMap as Record)[name]; +} + +export function getSpacing (name: string): CssVarRef | undefined { + return (spacingMap as Record)[name]; +} + +export function getElevation (name: string): CssVarRef | undefined { + return (elevationMap as Record)[name]; +} + +export function getGradient (name: string): CssVarRef | undefined { + return (gradientMap as Record)[name]; +} + +export function getMotion (name: string): CssVarRef | undefined { + return (motionMap as Record)[name]; +} + +export function getBreakpoint (name: string): CssVarRef | undefined { + return (breakpointMap as Record)[name]; +} + +export function getZIndex (name: string): CssVarRef | undefined { + return (zIndexMap as Record)[name]; +} + +const categoryMaps: Record> = { + color: colorMap, + typography: typographyMap, + radius: radiusMap, + spacing: spacingMap, + elevation: elevationMap, + gradient: gradientMap, + motion: motionMap, + breakpoint: breakpointMap, + zIndex: zIndexMap, +}; + +export function hasToken (category: TokenCategory, name: string): boolean { + return name in categoryMaps[category]; +} + +export const tokenMap = { + getColor, + getTypography, + getRadius, + getSpacing, + getElevation, + getGradient, + getMotion, + getBreakpoint, + getZIndex, + hasToken, + version: tokensVersion, + colors: colorMap, + typography: typographyMap, + radii: radiusMap, + spacing: spacingMap, + elevation: elevationMap, + gradients: gradientMap, + motion: motionMap, + breakpoints: breakpointMap, + zIndices: zIndexMap, +}; diff --git a/packages/tools/pie-token-map/src/types.ts b/packages/tools/pie-token-map/src/types.ts new file mode 100644 index 0000000000..4a73b6645c --- /dev/null +++ b/packages/tools/pie-token-map/src/types.ts @@ -0,0 +1,29 @@ +export const tokenCategories = [ + 'color', + 'typography', + 'radius', + 'spacing', + 'elevation', + 'gradient', + 'motion', + 'breakpoint', + 'zIndex', +] as const; + +export type TokenCategory = typeof tokenCategories[number]; + +export type CssVarRef = `var(--dt-${string})`; + +export interface TypographyTokenValue { + family: CssVarRef; + weight: CssVarRef; + size?: CssVarRef; + lineHeight?: CssVarRef; + sizeWide?: CssVarRef; + lineHeightWide?: CssVarRef; + sizeNarrow?: CssVarRef; + lineHeightNarrow?: CssVarRef; + fontStyle?: CssVarRef; + textDecoration?: CssVarRef; + paragraph?: CssVarRef; +} diff --git a/packages/tools/pie-token-map/test/unit/token-map.spec.ts b/packages/tools/pie-token-map/test/unit/token-map.spec.ts new file mode 100644 index 0000000000..f6dbcc9fee --- /dev/null +++ b/packages/tools/pie-token-map/test/unit/token-map.spec.ts @@ -0,0 +1,437 @@ +import { createRequire } from 'node:module'; +import { describe, it, expect } from 'vitest'; +import { + getColor, + getTypography, + getRadius, + getSpacing, + getElevation, + getGradient, + getMotion, + getBreakpoint, + getZIndex, + hasToken, + tokenMap, + colorMap, + typographyMap, + radiusMap, + spacingMap, + elevationMap, + gradientMap, + motionMap, + breakpointMap, + zIndexMap, + tokensVersion, + tokenCategories, +} from '../../src/index.js'; +import type { TypographyTokenValue } from '../../src/index.js'; + +const require = createRequire(import.meta.url); + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const tokens = require('@justeat/pie-design-tokens/dist/tokens.json'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const tokensPkg = require('@justeat/pie-design-tokens/package.json'); + +function getTypoToken (name: string): TypographyTokenValue { + return typographyMap[name as keyof typeof typographyMap]; +} + +describe('pie-token-map', () => { + describe('getColor', () => { + it('should return a CSS var ref for a known color token', () => { + expect(getColor('background-default')).toBe('var(--dt-color-background-default)'); + }); + + it('should return a CSS var ref for interactive-brand', () => { + expect(getColor('interactive-brand')).toBe('var(--dt-color-interactive-brand)'); + }); + + it('should return undefined for an unknown color token', () => { + expect(getColor('nonexistent-color')).toBeUndefined(); + }); + }); + + describe('getTypography', () => { + it('should return a composite value for a responsive heading token', () => { + const result = getTypography('heading-xs') as TypographyTokenValue; + expect(result).toBeDefined(); + expect(result.family).toBe('var(--dt-font-heading-xs-family)'); + expect(result.weight).toBe('var(--dt-font-heading-xs-weight)'); + expect(result.sizeWide).toBe('var(--dt-font-heading-xs-size--wide)'); + expect(result.lineHeightWide).toBe('var(--dt-font-heading-xs-line-height--wide)'); + expect(result.sizeNarrow).toBe('var(--dt-font-heading-xs-size--narrow)'); + expect(result.lineHeightNarrow).toBe('var(--dt-font-heading-xs-line-height--narrow)'); + expect(result.size).toBeUndefined(); + expect(result.lineHeight).toBeUndefined(); + }); + + it('should return a composite value for a non-responsive body token', () => { + const result = getTypography('body-s') as TypographyTokenValue; + expect(result).toBeDefined(); + expect(result.family).toBe('var(--dt-font-body-s-family)'); + expect(result.weight).toBe('var(--dt-font-body-s-weight)'); + expect(result.size).toBe('var(--dt-font-body-s-size)'); + expect(result.lineHeight).toBe('var(--dt-font-body-s-line-height)'); + expect(result.paragraph).toBe('var(--dt-font-body-s-paragraph)'); + expect(result.sizeWide).toBeUndefined(); + expect(result.sizeNarrow).toBeUndefined(); + }); + + it('should include fontStyle for italic heading tokens', () => { + const result = getTypography('heading-m-italic') as TypographyTokenValue; + expect(result).toBeDefined(); + expect(result.fontStyle).toBe('var(--dt-font-heading-m-italic-font-style)'); + expect(result.sizeWide).toBeDefined(); + }); + + it('should include textDecoration for link tokens', () => { + const result = getTypography('body-s-link') as TypographyTokenValue; + expect(result).toBeDefined(); + expect(result.textDecoration).toBe('var(--dt-font-body-s-link-text-decoration)'); + expect(result.paragraph).toBeDefined(); + }); + + it('should return undefined for an unknown typography token', () => { + expect(getTypography('nonexistent')).toBeUndefined(); + }); + }); + + describe('getRadius', () => { + it('should return a CSS var ref for a known radius token', () => { + expect(getRadius('rounded-a')).toBe('var(--dt-radius-rounded-a)'); + }); + + it('should return undefined for an unknown radius token', () => { + expect(getRadius('nonexistent')).toBeUndefined(); + }); + }); + + describe('getSpacing', () => { + it('should return a CSS var ref for a known spacing token', () => { + expect(getSpacing('b')).toBe('var(--dt-spacing-b)'); + }); + + it('should return undefined for an unknown spacing token', () => { + expect(getSpacing('zzz')).toBeUndefined(); + }); + }); + + describe('getElevation', () => { + it('should return a CSS var ref for a known elevation token', () => { + expect(getElevation('below-10')).toBe('var(--dt-elevation-below-10)'); + }); + + it('should return undefined for an unknown elevation token', () => { + expect(getElevation('nonexistent')).toBeUndefined(); + }); + }); + + describe('getGradient', () => { + it('should return a CSS var ref for a known gradient token', () => { + expect(getGradient('jetplus-brand-01')).toBe('var(--dt-gradient-jetplus-brand-01)'); + }); + + it('should return undefined for an unknown gradient token', () => { + expect(getGradient('nonexistent')).toBeUndefined(); + }); + }); + + describe('getMotion', () => { + it('should return a CSS var ref for a known motion token', () => { + expect(getMotion('easing-in')).toBe('var(--dt-motion-easing-in)'); + }); + + it('should return a CSS var ref for a timing token', () => { + expect(getMotion('timing-200')).toBe('var(--dt-motion-timing-200)'); + }); + + it('should return undefined for an unknown motion token', () => { + expect(getMotion('nonexistent')).toBeUndefined(); + }); + }); + + describe('getBreakpoint', () => { + it('should return a CSS var ref for a known breakpoint token', () => { + expect(getBreakpoint('sm')).toBe('var(--dt-breakpoint-sm)'); + }); + + it('should return undefined for an unknown breakpoint token', () => { + expect(getBreakpoint('nonexistent')).toBeUndefined(); + }); + }); + + describe('getZIndex', () => { + it('should return a CSS var ref for a known z-index token', () => { + expect(getZIndex('modal')).toBe('var(--dt-z-index-modal)'); + }); + + it('should return a CSS var ref for toast', () => { + expect(getZIndex('toast')).toBe('var(--dt-z-index-toast)'); + }); + + it('should return undefined for an unknown z-index token', () => { + expect(getZIndex('nonexistent')).toBeUndefined(); + }); + }); + + describe('hasToken', () => { + it('should return true for a known token', () => { + expect(hasToken('color', 'background-default')).toBe(true); + }); + + it('should return false for an unknown token', () => { + expect(hasToken('color', 'nonexistent')).toBe(false); + }); + + it('should work across all categories', () => { + expect(hasToken('typography', 'body-s')).toBe(true); + expect(hasToken('radius', 'rounded-a')).toBe(true); + expect(hasToken('spacing', 'b')).toBe(true); + expect(hasToken('elevation', 'below-10')).toBe(true); + expect(hasToken('gradient', 'jetplus-brand-01')).toBe(true); + expect(hasToken('motion', 'easing-in')).toBe(true); + expect(hasToken('breakpoint', 'sm')).toBe(true); + expect(hasToken('zIndex', 'modal')).toBe(true); + }); + }); + + describe('tokenMap convenience object', () => { + it('should expose all getter methods', () => { + expect(tokenMap.getColor('background-default')).toBe('var(--dt-color-background-default)'); + expect(tokenMap.getTypography('body-s')).toBeDefined(); + expect(tokenMap.getRadius('rounded-a')).toBe('var(--dt-radius-rounded-a)'); + expect(tokenMap.getSpacing('b')).toBe('var(--dt-spacing-b)'); + expect(tokenMap.getElevation('below-10')).toBe('var(--dt-elevation-below-10)'); + expect(tokenMap.getGradient('jetplus-brand-01')).toBe('var(--dt-gradient-jetplus-brand-01)'); + expect(tokenMap.getMotion('easing-in')).toBe('var(--dt-motion-easing-in)'); + expect(tokenMap.getBreakpoint('sm')).toBe('var(--dt-breakpoint-sm)'); + expect(tokenMap.getZIndex('modal')).toBe('var(--dt-z-index-modal)'); + }); + + it('should expose hasToken', () => { + expect(tokenMap.hasToken('color', 'background-default')).toBe(true); + expect(tokenMap.hasToken('color', 'nonexistent')).toBe(false); + }); + + it('should expose version', () => { + expect(tokenMap.version).toBe(tokensPkg.version); + }); + + it('should expose raw maps', () => { + expect(tokenMap.colors).toBe(colorMap); + expect(tokenMap.typography).toBe(typographyMap); + expect(tokenMap.radii).toBe(radiusMap); + expect(tokenMap.spacing).toBe(spacingMap); + expect(tokenMap.elevation).toBe(elevationMap); + expect(tokenMap.gradients).toBe(gradientMap); + expect(tokenMap.motion).toBe(motionMap); + expect(tokenMap.breakpoints).toBe(breakpointMap); + expect(tokenMap.zIndices).toBe(zIndexMap); + }); + }); + + describe('tokenCategories', () => { + it('should list all 9 categories', () => { + expect(tokenCategories).toHaveLength(9); + expect(tokenCategories).toContain('color'); + expect(tokenCategories).toContain('typography'); + expect(tokenCategories).toContain('radius'); + expect(tokenCategories).toContain('spacing'); + expect(tokenCategories).toContain('elevation'); + expect(tokenCategories).toContain('gradient'); + expect(tokenCategories).toContain('motion'); + expect(tokenCategories).toContain('breakpoint'); + expect(tokenCategories).toContain('zIndex'); + }); + }); + + describe('version', () => { + it('should match the design tokens package version', () => { + expect(tokensVersion).toBe(tokensPkg.version); + }); + }); + + // ----------------------------------------------------------------------- + // Completeness tests — ensure generated maps cover all tokens in source + // ----------------------------------------------------------------------- + describe('completeness', () => { + it('should include all color alias tokens from tokens.json (excluding platform-specific) plus platform-agnostic aliases', () => { + const platformPrefixes = ['android-', 'ios-']; + const sourceNames = Object.keys(tokens.theme.jet.color.alias.default) + .filter((name: string) => !platformPrefixes.some((prefix) => name.startsWith(prefix))); + const platformAliases = ['container-base', 'container-base-dark', 'container-neutral', 'container-prominent']; + + sourceNames.forEach((name) => { + expect(colorMap).toHaveProperty(name); + }); + platformAliases.forEach((name) => { + expect(colorMap).toHaveProperty(name); + }); + expect(Object.keys(colorMap)).toHaveLength(sourceNames.length + platformAliases.length); + }); + + it('should not include platform-specific (android-*, ios-*) color tokens', () => { + const platformNames = Object.keys(tokens.theme.jet.color.alias.default) + .filter((name: string) => name.startsWith('android-') || name.startsWith('ios-')); + expect(platformNames.length).toBeGreaterThan(0); // sanity check + platformNames.forEach((name) => { + expect(colorMap).not.toHaveProperty(name); + }); + }); + + it('should map platform-agnostic aliases to their web-prefixed CSS vars', () => { + expect(getColor('container-base')).toBe('var(--dt-color-web-container-base)'); + expect(getColor('container-base-dark')).toBe('var(--dt-color-web-container-base-dark)'); + expect(getColor('container-neutral')).toBe('var(--dt-color-web-container-neutral)'); + expect(getColor('container-prominent')).toBe('var(--dt-color-web-container-prominent)'); + }); + + it('should include all font alias tokens from tokens.json', () => { + const sourceNames = Object.keys(tokens.theme.jet.font.alias); + sourceNames.forEach((name) => { + expect(typographyMap).toHaveProperty(name); + }); + expect(Object.keys(typographyMap)).toHaveLength(sourceNames.length); + }); + + it('should include all radius alias tokens from tokens.json', () => { + const sourceNames = Object.keys(tokens.theme.jet.radius.alias); + sourceNames.forEach((name) => { + expect(radiusMap).toHaveProperty(name); + }); + expect(Object.keys(radiusMap)).toHaveLength(sourceNames.length); + }); + + it('should include all spacing alias tokens from tokens.json', () => { + const sourceNames = Object.keys(tokens.theme.jet.spacing.alias); + sourceNames.forEach((name) => { + expect(spacingMap).toHaveProperty(name); + }); + expect(Object.keys(spacingMap)).toHaveLength(sourceNames.length); + }); + + it('should include all elevation alias tokens from tokens.json', () => { + const sourceNames = Object.keys(tokens.theme.jet.elevation.alias.default); + sourceNames.forEach((name) => { + expect(elevationMap).toHaveProperty(name); + }); + expect(Object.keys(elevationMap)).toHaveLength(sourceNames.length); + }); + + it('should include all gradient alias tokens from tokens.json', () => { + const sourceNames = Object.keys(tokens.theme.jet.gradient.alias.default); + sourceNames.forEach((name) => { + expect(gradientMap).toHaveProperty(name); + }); + expect(Object.keys(gradientMap)).toHaveLength(sourceNames.length); + }); + + it('should include all motion global tokens from tokens.json', () => { + const sourceNames = Object.keys(tokens.theme.jet.motion.global); + sourceNames.forEach((name) => { + expect(motionMap).toHaveProperty(name); + }); + expect(Object.keys(motionMap)).toHaveLength(sourceNames.length); + }); + + it('should include all breakpoint alias tokens from tokens.json', () => { + const sourceNames = Object.keys(tokens.theme.jet.breakpoint.alias); + sourceNames.forEach((name) => { + expect(breakpointMap).toHaveProperty(name); + }); + expect(Object.keys(breakpointMap)).toHaveLength(sourceNames.length); + }); + + it('should include all 10 z-index tokens from pie-css', () => { + const expectedNames = [ + 'base', 'dropdown', 'fab', 'tooltip', 'popover', + 'bottom-sheet', 'side-sheet', 'modal', 'cookie-banner', 'toast', + ]; + expectedNames.forEach((name) => { + expect(zIndexMap).toHaveProperty(name); + }); + expect(Object.keys(zIndexMap)).toHaveLength(expectedNames.length); + }); + }); + + describe('typography token structure', () => { + it('should have responsive properties for all heading tokens', () => { + Object.keys(typographyMap) + .filter((n) => n.startsWith('heading-')) + .forEach((name) => { + const token = getTypoToken(name); + expect(token.sizeWide).toBeDefined(); + expect(token.sizeNarrow).toBeDefined(); + expect(token.lineHeightWide).toBeDefined(); + expect(token.lineHeightNarrow).toBeDefined(); + expect(token.size).toBeUndefined(); + expect(token.lineHeight).toBeUndefined(); + }); + }); + + it('should have responsive properties for all subheading tokens', () => { + Object.keys(typographyMap) + .filter((n) => n.startsWith('subheading-')) + .forEach((name) => { + const token = getTypoToken(name); + expect(token.sizeWide).toBeDefined(); + expect(token.sizeNarrow).toBeDefined(); + }); + }); + + it('should have non-responsive properties for body tokens', () => { + Object.keys(typographyMap) + .filter((n) => n.startsWith('body-')) + .forEach((name) => { + const token = getTypoToken(name); + expect(token.size).toBeDefined(); + expect(token.lineHeight).toBeDefined(); + expect(token.sizeWide).toBeUndefined(); + expect(token.sizeNarrow).toBeUndefined(); + }); + }); + + it('should include fontStyle only for italic heading tokens', () => { + Object.keys(typographyMap) + .filter((n) => n.includes('italic')) + .forEach((name) => { + expect(getTypoToken(name).fontStyle).toBeDefined(); + }); + + Object.keys(typographyMap) + .filter((n) => !n.includes('italic')) + .forEach((name) => { + expect(getTypoToken(name).fontStyle).toBeUndefined(); + }); + }); + + it('should include textDecoration only for link tokens', () => { + Object.keys(typographyMap) + .filter((n) => n.includes('link')) + .forEach((name) => { + expect(getTypoToken(name).textDecoration).toBeDefined(); + }); + + Object.keys(typographyMap) + .filter((n) => !n.includes('link')) + .forEach((name) => { + expect(getTypoToken(name).textDecoration).toBeUndefined(); + }); + }); + + it('should include paragraph for body and caption tokens but not interactive or heading', () => { + Object.keys(typographyMap) + .filter((n) => n.startsWith('interactive-')) + .forEach((name) => { + expect(getTypoToken(name).paragraph).toBeUndefined(); + }); + + Object.keys(typographyMap) + .filter((n) => n.startsWith('heading-')) + .forEach((name) => { + expect(getTypoToken(name).paragraph).toBeUndefined(); + }); + }); + }); +}); diff --git a/packages/tools/pie-token-map/tsconfig.json b/packages/tools/pie-token-map/tsconfig.json new file mode 100644 index 0000000000..f70529fbd4 --- /dev/null +++ b/packages/tools/pie-token-map/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@justeattakeaway/pie-components-config/tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "rootDir": "." + }, + "include": [ + "src/**/*.ts", + "scripts/**/*.ts", + "test/**/*.ts" + ] +} diff --git a/packages/tools/pie-token-map/turbo.json b/packages/tools/pie-token-map/turbo.json new file mode 100644 index 0000000000..d8e43c3ec0 --- /dev/null +++ b/packages/tools/pie-token-map/turbo.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://turborepo.org/schema.json", + "extends": [ + "//" + ], + "pipeline": { + "build": { + "cache": true, + "dependsOn": [ + "^build" + ], + "inputs": [ + "src/**", + "scripts/**" + ], + "outputs": [ + "dist/**" + ] + }, + "generate:verify": { + "cache": false, + "dependsOn": [ + "build" + ] + } + } +} diff --git a/packages/tools/pie-token-map/vite.config.ts b/packages/tools/pie-token-map/vite.config.ts new file mode 100644 index 0000000000..eb7ebbfbe0 --- /dev/null +++ b/packages/tools/pie-token-map/vite.config.ts @@ -0,0 +1,34 @@ +import { defineConfig } from 'vite'; +import dts from 'vite-plugin-dts'; +import { visualizer } from 'rollup-plugin-visualizer'; + +export default defineConfig({ + build: { + lib: { + entry: { + index: 'src/index.ts', + }, + formats: ['es'], + }, + }, + test: { + dir: '.', + environment: 'jsdom', + globals: true, + include: [ + './test/unit/**/*.{spec,test}.{js,ts}', + ], + exclude: ['**/node_modules/**'], + }, + plugins: [ + dts({ + insertTypesEntry: true, + outDir: 'dist', + rollupTypes: true, + }), + visualizer({ + gzipSize: true, + brotliSize: true, + }), + ], +}); diff --git a/yarn.lock b/yarn.lock index 965c8ac331..8056a74c7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3454,7 +3454,7 @@ __metadata: languageName: unknown linkType: soft -"@justeattakeaway/pie-components-config@0.21.0, @justeattakeaway/pie-components-config@workspace:configs/pie-components-config": +"@justeattakeaway/pie-components-config@*, @justeattakeaway/pie-components-config@0.21.0, @justeattakeaway/pie-components-config@workspace:configs/pie-components-config": version: 0.0.0-use.local resolution: "@justeattakeaway/pie-components-config@workspace:configs/pie-components-config" dependencies: @@ -3993,6 +3993,15 @@ __metadata: languageName: unknown linkType: soft +"@justeattakeaway/pie-token-map@workspace:packages/tools/pie-token-map": + version: 0.0.0-use.local + resolution: "@justeattakeaway/pie-token-map@workspace:packages/tools/pie-token-map" + dependencies: + "@justeat/pie-design-tokens": 7.11.1 + "@justeattakeaway/pie-components-config": "*" + languageName: unknown + linkType: soft + "@justeattakeaway/pie-webc-core@12.0.0, @justeattakeaway/pie-webc-core@workspace:packages/components/pie-webc-core": version: 0.0.0-use.local resolution: "@justeattakeaway/pie-webc-core@workspace:packages/components/pie-webc-core"