Skip to content

Migration from Theo ​

Step-by-step guide to migrate from Salesforce Theo to TokiForge.

Overview ​

Theo and TokiForge share similar token philosophies. This guide will help you migrate smoothly with minimal code changes.


Key Differences ​

FeatureTheoTokiForge
FormatJSON with aliasesTypeScript/JSON with references
BuildCLI-based transformsRuntime + Build-time
PlatformsMultiple output formatsExport to 5 formats
ThemesSeparate filesSingle config
Framework IntegrationManualBuilt-in adapters

Migration Steps ​

Step 1: Analyze Theo Setup ​

Typical Theo structure:

tokens/
├── aliases.yml
├── globals.yml
├── components/
│   └── button.yml
└── .theo.yml

Step 2: Convert Token Format ​

Theo Format ​

yaml
# aliases.yml
aliases:
  BLUE: "#3b82f6"
  GRAY: "#6b7280"

# globals.yml
props:
  COLOR_PRIMARY:
    value: "{!BLUE}"
  COLOR_TEXT:
    value: "{!GRAY}"

TokiForge Format ​

typescript
const tokens = {
  // Aliases (Core tokens)
  _core: {
    blue: { value: '#3b82f6' },
    gray: { value: '#6b7280' },
  },
  // Semantic tokens
  color: {
    primary: { value: '{_core.blue}' },
    text: { value: '{_core.gray}' },
  },
};

Step 3: Handle Theo Aliases ​

Theo uses {!alias} syntax, TokiForge uses {path.to.token}:

typescript
// Theo
{
  "value": "{!BLUE}"
}

// TokiForge
{
  "value": "{color.core.blue}"
}

Migration script:

typescript
function convertTheoToTokiForge(theoTokens: any): DesignTokens {
  const result: any = {};
  
  // Convert aliases
  if (theoTokens.aliases) {
    result._core = {};
    for (const [key, value] of Object.entries(theoTokens.aliases)) {
      const tokenName = key.toLowerCase().replace(/_/g, '-');
      result._core[tokenName] = { value };
    }
  }
  
  // Convert props
  if (theoTokens.props) {
    for (const [key, token] of Object.entries(theoTokens.props)) {
      const path = key.toLowerCase().split('_');
      let current = result;
      
      for (let i = 0; i < path.length - 1; i++) {
        if (!current[path[i]]) current[path[i]] = {};
        current = current[path[i]];
      }
      
      const tokenValue = (token as any).value;
      const convertedValue = tokenValue.replace(/\{!(\w+)\}/g, (_, alias) => {
        return `{_core.${alias.toLowerCase().replace(/_/g, '-')}}`;
      });
      
      current[path[path.length - 1]] = {
        value: convertedValue,
        type: (token as any).type,
      };
    }
  }
  
  return result;
}

Step 4: Migrate Build Configuration ​

Before (Theo) ​

yaml
# .theo.yml
transforms:
  web:
    - color/rgb
platform:
  web:
    transformGroup: web
    buildPath: build/
    files:
      - destination: tokens.scss
        format: scss

After (TokiForge) ​

typescript
// scripts/build-tokens.ts
import { TokenExporter } from '@tokiforge/core';
import { writeFileSync } from 'fs';
import tokens from '../tokens';

// SCSS output
const scss = TokenExporter.exportSCSS(tokens, { prefix: 'app' });
writeFileSync('build/tokens.scss', scss);

// CSS output
const css = TokenExporter.exportCSS(tokens, {
  selector: ':root',
  prefix: 'app',
});
writeFileSync('build/tokens.css', css);

// JSON output
const json = TokenExporter.exportJSON(tokens);
writeFileSync('build/tokens.json', json);

Step 5: Update Component Usage ​

Before (Theo - SCSS) ​

scss
@import '../build/tokens.scss';

.button {
  background: $color-primary;
  padding: $spacing-md;
}

After (TokiForge - CSS Variables) ​

vue
<template>
  <button class="button">
    <slot />
  </button>
</template>

<style scoped>
.button {
  background: var(--app-color-primary);
  padding: var(--app-spacing-md);
}
</style>

Transform Mapping ​

Theo TransformTokiForge Equivalent
color/rgbUse hex format directly
color/hexNative support
size/remUse rem in value
size/pxUse px in value

Alias Migration ​

Theo Aliases ​

yaml
aliases:
  COLOR_BRAND_PRIMARY: "#3b82f6"
  COLOR_BRAND_SECONDARY: "#8b5cf6"

TokiForge Core Tokens ​

typescript
const tokens = {
  color: {
    brand: {
      primary: { value: '#3b82f6' },
      secondary: { value: '#8b5cf6' },
    },
  },
};

Complete Example ​

Before (Theo) ​

project/
├── tokens/
│   ├── aliases.yml
│   └── globals.yml
├── .theo.yml
└── src/
    └── styles/
        └── main.scss
yaml
# aliases.yml
aliases:
  BLUE_500: "#3b82f6"
  SPACING_BASE: "8px"

# globals.yml
props:
  COLOR_PRIMARY:
    value: "{!BLUE_500}"
    type: "color"
  SPACING_MD:
    value: "{!SPACING_BASE}"
    type: "size"
scss
// main.scss
@import '../../build/tokens.scss';

.button {
  background: $color-primary;
  padding: $spacing-md;
}

After (TokiForge) ​

project/
├── tokens/
│   └── index.ts
└── src/
    ├── App.vue
    └── components/
        └── Button.vue
typescript
// tokens/index.ts
export const tokens = {
  color: {
    primary: { value: '#3b82f6', type: 'color' },
  },
  spacing: {
    md: { value: '8px', type: 'dimension' },
  },
};
vue
<!-- Button.vue -->
<script setup>
import { provideTheme } from '@tokiforge/vue';
import { tokens } from '../tokens';

provideTheme({
  themes: [{ name: 'default', tokens }],
});
</script>

<template>
  <button class="button">
    <slot />
  </button>
</template>

<style scoped>
.button {
  background: var(--hf-color-primary);
  padding: var(--hf-spacing-md);
}
</style>

Benefits After Migration ​

✅ Runtime theme switching - No rebuild needed
✅ Simpler configuration - No YAML, no CLI
✅ TypeScript support - Full type safety
✅ Framework integration - Vue/React/Angular adapters
✅ Smaller builds - Only include what you use


Migration Checklist ​

  • [ ] Convert Theo aliases to core tokens
  • [ ] Map props to semantic tokens
  • [ ] Update reference syntax ({!alias} → {path})
  • [ ] Replace build scripts
  • [ ] Convert SCSS to CSS variables
  • [ ] Add framework adapter
  • [ ] Test theme switching
  • [ ] Update documentation

Common Issues ​

Issue 1: Alias Not Found ​

Theo: {!UNKNOWN_ALIAS}
Error: Reference not found

Solution: Ensure all aliases are converted to core tokens


Issue 2: Transform Syntax ​

Problem: Theo transforms don't automatically apply

Solution: Apply transforms in build script or use TokiForge transforms


Issue 3: Build Output Different ​

Problem: Generated CSS looks different

Solution: Customize prefix in TokenExporter

typescript
TokenExporter.exportCSS(tokens, {
  prefix: 'theo', // Match Theo prefix
});

Need Help? ​


Next: Migration from Figma Tokens