Consumer Integration Guide

This guide is for any project that consumes @verdigristech/design-tokens: Patina, www, the evaluator pipeline, the Cowork skill, internal tooling, and external partners.

If you read top-to-bottom, you will be running with the package in about 5 minutes. The rest is reference.

Quick start (5 minutes)

npm install @verdigristech/design-tokens

The package is published to GitHub Packages under the @verdigristech scope. Authenticate first with a ~/.npmrc entry:

//npm.pkg.github.com/:_authToken=YOUR_GITHUB_PAT
@verdigristech:registry=https://npm.pkg.github.com

Pin to an exact version in package.json. Floats invite drift across consumers:

{
  "dependencies": {
    "@verdigristech/design-tokens": "0.8.0"
  }
}

That is enough to import any of the surfaces below.

What ships in the package

Surface Path Use when
JS module @verdigristech/design-tokens Reading token values from JS / TS
Tailwind preset @verdigristech/design-tokens/tailwind Adding tokens to a Tailwind project
CSS variables (OKLch) @verdigristech/design-tokens/css/oklch Patina-style modern stylesheets
CSS variables (HSL) @verdigristech/design-tokens/css/hsl Legacy stylesheets without OKLch support
Hex JSON @verdigristech/design-tokens/hex Email, print, Figma exports
Print stylesheets @verdigristech/design-tokens/print/<name>.css Whitepaper, slides, one-pagers, case studies
Token JSON @verdigristech/design-tokens/tokens/<path> Raw W3C DTCG token files
Visual rules @verdigristech/design-tokens/rules/<name> Evaluator pipeline rule loading
Voice recipes @verdigristech/design-tokens/voice/<file> Content generation systems

voice/team/* is intentionally not exported. It contains real personal voice profiles and is repo-internal per Loop 5O. The npm run test:package check enforces this at build time.

Tailwind preset

Use the preset to inherit the full token surface (colors, spacing, font sizes, radii) with one import. Extend it with project-specific overrides.

// tailwind.config.js
import designPreset from '@verdigristech/design-tokens/tailwind';

export default {
  presets: [designPreset],
  content: ['./src/**/*.{ts,tsx,html}'],
  theme: {
    extend: {
      // project-specific additions go here, not in the preset
      colors: {
        'patina-feature': 'oklch(0.6 0.18 90)',
      },
    },
  },
};

Failure mode prevented: hand-coding #0fc8c3 across files. When the OKLch reference shifts in v5, your preset picks it up; hand-coded hex stays wrong forever.

Raw JS token consumption

The default export module exposes flat-key dictionaries. Keys are the full token path; values are resolved primitives. Use this pattern for Canvas, Remotion, evaluator code, or any non-Tailwind consumer.

// ESM
import {
  hexColors,
  typography,
  spacing,
  elevation,
  radius,
  viz,
  durations,
  easings,
} from '@verdigristech/design-tokens';

hexColors['color.brand.verdigris'];      // '#0fc8c3'
typography['fontSize.h1'];               // '4rem'
spacing['spacing.4'];                    // '1rem'
spacing['spacing.print.cover.layout.deck-to-byline-min']; // '1.4in' (unit-split groups: .layout / .type)
elevation['shadow.md'];                  // '0 4px 6px -1px rgb(0 0 0 / 0.1), ...'
elevation['zIndex.overlay'];             // 50
radius['radius.base'];                   // '0.625rem'
viz.trace.primary;                       // '#0fc8c3'
durations.normal;                        // 200
easings.revealCubic(0.5);                // 0.875
// CommonJS (Node, older toolchains)
const tokens = require('@verdigristech/design-tokens');
console.log(tokens.hexColors['color.brand.verdigris']);

For raw token JSON without the resolved-primitive convenience:

// Modern bundlers (Vite, Webpack 5+, esbuild, Rollup with @rollup/plugin-json)
import baseColor from '@verdigristech/design-tokens/tokens/color/base.json';

// Node.js (no bundler) — works on any LTS without import-attributes flags
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const baseColorPath = require.resolve('@verdigristech/design-tokens/tokens/color/base.json');
const baseColor = JSON.parse(readFileSync(baseColorPath, 'utf8'));

Avoid import ... with { type: 'json' } (ES2025 import attributes) — it works in modern Node + Chromium but trips older bundler toolchains. The two patterns above cover every consumer.

Failure mode prevented: parsing tokens directly from disk in each consumer. Each parser invents its own reference-resolution and dotted-path convention; over a year, three consumers ship three subtly different views of the same token.

CSS reference stylesheets

/* Modern browsers, Patina pattern */
@import '@verdigristech/design-tokens/css/oklch';

.hero { color: var(--color-brand-verdigris); }
/* Legacy fallback. Prefer OKLch when you can. */
@import '@verdigristech/design-tokens/css/hsl';

For paged PDF formats, import the matching print stylesheet:

@import '@verdigristech/design-tokens/print/cover.css';        /* whitepaper covers */
@import '@verdigristech/design-tokens/print/slides.css';       /* sales decks */
@import '@verdigristech/design-tokens/print/one-pager.css';    /* leave-behinds */
@import '@verdigristech/design-tokens/print/case-study.css';   /* customer stories */

When to choose oklch.css vs hsl.css: ship OKLch if your audience is on a recent Chromium, Safari, or Firefox release. Use HSL only as a fallback for legacy SSR pipelines or email clients. Ship both with @supports if you need to bridge.

Failure mode prevented: redefining brand colors in each downstream stylesheet. The CSS variables are the canonical surface; hand-redefining --color-brand-verdigris locally means a brand refresh requires N find-replace passes.

Voice recipes and ingredients

Content generation systems (Cowork skill, AI agents writing on Verdigris’s behalf) consume the public voice surface:

import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import yaml from 'js-yaml';
import { createRequire } from 'node:module';

const require = createRequire(import.meta.url);
const recipesPath = require.resolve(
  '@verdigristech/design-tokens/voice/recipes.yaml'
);
const recipes = yaml.load(readFileSync(recipesPath, 'utf-8'));

// Pick the recipe for the surface you are writing for
const homepage = recipes.recipes.homepage;
// homepage.mix is { strategic_narrative: 35, technical_precision: 25, ... }

Three companion files round out the public voice surface:

File Purpose
voice/recipes.yaml Ratio mixes per surface (homepage, case study, slide deck, etc.)
voice/ingredients.yaml The ingredient definitions referenced by every recipe
voice/feelings.yaml Target emotional outcomes per audience
voice/USE.md Read this once before generating any content
voice/README.md Overview of the voice system

voice/team/*.yaml is not part of the public surface. Those files contain individual voice profiles and stay inside this repo only. Do not vendor them into a downstream codebase even if you have a copy locally.

Failure mode prevented: Cowork or any other skill picking a recipe by guessing. Ground every generation in a recipe lookup; if no recipe fits, file a Linear issue against this repo to add one.

Visual rules YAML

The evaluator pipeline (and any future linter) consumes rules/visual-rules.yml to enforce design constraints across surfaces.

import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import yaml from 'js-yaml';

const require = createRequire(import.meta.url);
const rulesPath = require.resolve(
  '@verdigristech/design-tokens/rules/visual-rules.yml'
);
const rules = yaml.load(readFileSync(rulesPath, 'utf-8'));
// rules.rules is an array of { id, severity, type, description, test, maturity? }

Schema notes for evaluator authors:

Failure mode prevented: an evaluator silently passing rules it does not understand. Validate the schema version before loading; the file declares it in the header (Version: 4.x.x).

Worked example: Cowork skill

Cowork generates content on Verdigris’s behalf. It needs three things from this package: a voice recipe, the brand colors that will appear in any rendered preview, and the rules YAML for an evaluator pre-flight check.

import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import yaml from 'js-yaml';
import { hexColors } from '@verdigristech/design-tokens';

const require = createRequire(import.meta.url);

function loadYaml(spec) {
  return yaml.load(readFileSync(require.resolve(spec), 'utf-8'));
}

const recipes = loadYaml('@verdigristech/design-tokens/voice/recipes.yaml');
const ingredients = loadYaml(
  '@verdigristech/design-tokens/voice/ingredients.yaml'
);
const rules = loadYaml(
  '@verdigristech/design-tokens/rules/visual-rules.yml'
);

export function buildContext(surface) {
  const recipe = recipes.recipes[surface];
  if (!recipe) {
    throw new Error(`No recipe for surface "${surface}". File a Linear issue.`);
  }
  return {
    recipe,
    ingredients: ingredients.ingredients,
    rules: rules.rules,
    palette: {
      primary: hexColors['color.brand.verdigris'],
      ink: hexColors['color.neutral.950'],
      paper: hexColors['color.neutral.50'],
    },
  };
}

This pattern keeps Cowork pinned to a known recipe matrix. When this repo adds a new recipe (say, partner-enablement-deck), Cowork picks it up after a npm install of the new version. No Cowork code change required.

Versioning and breaking-change policy

This package follows semver. Releases are automated by the auto-release.yml workflow on merge to main.

Bump Triggers Examples
Major PR labeled major, or BREAKING CHANGE in commit body Renamed token paths, removed tokens, schema changes that break evaluator pipelines
Minor PR labeled minor, or any feat() commit prefix New tokens, new rules, new recipes, new compositions
Patch Default Value tweaks, doc updates, YAML corrections

What is part of the contract:

Renaming or removing any of the above requires a major bump.

What is not part of the contract:

The npm run test:package script enforces the package surface boundary. Adding a directory to package.json#files requires updating the test’s positive list.

Pinning strategy

Consumer type Recommended pin
Patina (reference implementation) Track main via git dependency for fast iteration
www site Pin to the latest minor; bump on release
Evaluator pipeline Pin to an exact version; coordinate bumps with rule authors
External partners Pin to a specific minor; review changelog before bumping
AI skills (Cowork, internal tools) Pin to an exact version; CI bumps via dependabot

Floating ranges (^0.8.0, ~0.8) work but make change attribution harder. Pin exactly when you can.

Filing integration issues

If anything in this guide is wrong, missing, or unclear, file an issue against VerdigrisTech/design with the label consumer-integration and a one-line description of what you tried to do.

For Linear users: file under team Z2O, project “Design System: VerdigrisTech/design”. Include:

Most consumer issues are documentation gaps. Filing the issue closes that gap for the next consumer.