Skip to main content

JavaScript Integration

Integrate Tokencraft design tokens into your JavaScript or TypeScript applications.

Quick Start

1. Install Dependencies

npm install node-fetch
# or
npm install axios

2. Fetch Tokens

const TOKENCRAFT_TOKEN = process.env.TOKENCRAFT_TOKEN;
const API_BASE = 'https://app.tokencraft.dev/api/v1';

async function fetchTokens(tokensetId, modeId, format = 'json') {
  const response = await fetch(
    `${API_BASE}/tokensets/${tokensetId}/modes/${modeId}/export?format=${format}`,
    {
      headers: {
        'Authorization': `Bearer ${TOKENCRAFT_TOKEN}`
      }
    }
  );
  
  if (!response.ok) {
    throw new Error(`Failed to fetch tokens: ${response.statusText}`);
  }
  
  return format === 'json' ? await response.json() : await response.text();
}

// Usage
const tokens = await fetchTokens('tokenset-123', 'mode-light', 'json');

Build-Time Integration

Next.js

Fetch tokens at build time:
// scripts/fetch-tokens.js
const fs = require('fs');
const path = require('path');

async function fetchTokens() {
  const response = await fetch(
    `${process.env.TOKENCRAFT_API_BASE}/tokensets/${process.env.TOKENSET_ID}/modes/light/export?format=json`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.TOKENCRAFT_TOKEN}`
      }
    }
  );
  
  const tokens = await response.json();
  
  // Save to file
  fs.writeFileSync(
    path.join(__dirname, '../src/tokens.json'),
    JSON.stringify(tokens, null, 2)
  );
  
  console.log('✓ Tokens fetched successfully');
}

fetchTokens().catch(console.error);
// package.json
{
  "scripts": {
    "fetch-tokens": "node scripts/fetch-tokens.js",
    "prebuild": "npm run fetch-tokens",
    "build": "next build"
  }
}
// src/tokens.ts
import tokens from './tokens.json';

export const colors = tokens.colors;
export const spacing = tokens.spacing;

// Type-safe access
export function getColor(path: string): string {
  const keys = path.split('.');
  let value: any = tokens;
  
  for (const key of keys) {
    value = value[key];
    if (!value) throw new Error(`Token not found: ${path}`);
  }
  
  return value.value;
}

React

Use tokens in React components:
import { colors } from '@/tokens';

export function Button({ variant = 'primary' }) {
  const bg = variant === 'primary' 
    ? colors.primary['500'].value
    : colors.secondary['500'].value;
  
  return (
    <button style={{ backgroundColor: bg }}>
      Click me
    </button>
  );
}

Vue

<script setup>
import tokens from '@/tokens.json';

const primaryColor = tokens.colors.primary['500'].value;
</script>

<template>
  <button :style="{ backgroundColor: primaryColor }">
    Click me
  </button>
</template>

Runtime Integration

Fetch on App Load

// src/lib/tokens.ts
class TokenService {
  private tokens: any = null;
  
  async load() {
    if (this.tokens) return this.tokens;
    
    const response = await fetch('/api/tokens');
    this.tokens = await response.json();
    return this.tokens;
  }
  
  get(path: string): any {
    if (!this.tokens) {
      throw new Error('Tokens not loaded. Call load() first.');
    }
    
    const keys = path.split('.');
    let value = this.tokens;
    
    for (const key of keys) {
      value = value[key];
      if (!value) throw new Error(`Token not found: ${path}`);
    }
    
    return value.value;
  }
}

export const tokenService = new TokenService();
// app/layout.tsx
import { tokenService } from '@/lib/tokens';

export default async function RootLayout({ children }) {
  await tokenService.load();
  
  return (
    <html>
      <body>{children}</body>
    </html>
  );
}

CSS Variables

Generate CSS

// scripts/generate-css.js
const fs = require('fs');

function tokensToCss(tokens, prefix = '--') {
  let css = ':root {\n';
  
  function traverse(obj, path = []) {
    for (const [key, value] of Object.entries(obj)) {
      if (value.value !== undefined) {
        const varName = prefix + [...path, key].join('-');
        css += `  ${varName}: ${value.value};\n`;
      } else {
        traverse(value, [...path, key]);
      }
    }
  }
  
  traverse(tokens);
  css += '}\n';
  
  return css;
}

async function generateCss() {
  const tokens = await fetchTokens('tokenset-123', 'mode-light', 'json');
  const css = tokensToCss(tokens);
  
  fs.writeFileSync('src/styles/tokens.css', css);
  console.log('✓ CSS generated');
}

generateCss();

Use in Styles

/* src/styles/globals.css */
@import './tokens.css';

.button {
  background-color: var(--colors-primary-500);
  padding: var(--spacing-base);
  border-radius: var(--radii-md);
}

TypeScript Types

Generate Types

// scripts/generate-types.ts
import fs from 'fs';

function generateTypes(tokens: any): string {
  let types = 'export interface DesignTokens {\n';
  
  function traverse(obj: any, indent = 1) {
    let result = '';
    const spaces = '  '.repeat(indent);
    
    for (const [key, value] of Object.entries(obj)) {
      if (value.value !== undefined) {
        result += `${spaces}${key}: {\n`;
        result += `${spaces}  value: string;\n`;
        result += `${spaces}  type: string;\n`;
        result += `${spaces}};\n`;
      } else {
        result += `${spaces}${key}: {\n`;
        result += traverse(value, indent + 1);
        result += `${spaces}};\n`;
      }
    }
    
    return result;
  }
  
  types += traverse(tokens);
  types += '}\n';
  
  return types;
}

// Generate and save
const types = generateTypes(tokens);
fs.writeFileSync('src/tokens.d.ts', types);

Use Types

import tokens from './tokens.json';
import type { DesignTokens } from './tokens';

const typedTokens: DesignTokens = tokens;

// Type-safe access
const primary = typedTokens.colors.primary['500'].value; // ✓
const invalid = typedTokens.colors.invalid['500'].value; // ✗ Type error

API Client

// src/lib/tokencraft.ts
export class TokencraftClient {
  constructor(
    private token: string,
    private baseUrl = 'https://app.tokencraft.dev/api/v1'
  ) {}
  
  private async request(endpoint: string) {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      headers: {
        'Authorization': `Bearer ${this.token}`
      }
    });
    
    if (!response.ok) {
      throw new Error(`API error: ${response.statusText}`);
    }
    
    return response.json();
  }
  
  async getWorkspaces() {
    return this.request('/workspaces');
  }
  
  async getTokenset(id: string) {
    return this.request(`/tokensets/${id}`);
  }
  
  async exportTokens(tokensetId: string, modeId: string, format = 'json') {
    const endpoint = `/tokensets/${tokensetId}/modes/${modeId}/export?format=${format}`;
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      headers: {
        'Authorization': `Bearer ${this.token}`
      }
    });
    
    if (!response.ok) {
      throw new Error(`Export failed: ${response.statusText}`);
    }
    
    return format === 'json' ? response.json() : response.text();
  }
}

// Usage
const client = new TokencraftClient(process.env.TOKENCRAFT_TOKEN!);
const workspaces = await client.getWorkspaces();

Caching

class TokenCache {
  private cache = new Map<string, { data: any; timestamp: number }>();
  private ttl = 5 * 60 * 1000; // 5 minutes
  
  get(key: string): any | null {
    const item = this.cache.get(key);
    if (!item) return null;
    
    if (Date.now() - item.timestamp > this.ttl) {
      this.cache.delete(key);
      return null;
    }
    
    return item.data;
  }
  
  set(key: string, data: any): void {
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });
  }
}

const cache = new TokenCache();

async function getCachedTokens(tokensetId: string, modeId: string) {
  const cacheKey = `${tokensetId}-${modeId}`;
  
  let tokens = cache.get(cacheKey);
  if (tokens) return tokens;
  
  tokens = await fetchTokens(tokensetId, modeId);
  cache.set(cacheKey, tokens);
  
  return tokens;
}

Next Steps