Los design tokens son el vocabulario compartido entre diseñadores y desarrolladores. Han existido en diversas formas desde la era de Salesforce Lightning — Style Dictionary, Theo, el borrador de especificación del W3C Design Token Community Group. Lo que ha cambiado recientemente es que ahora también son el vocabulario compartido entre tu base de código y los agentes de codificación con IA.

Un agente que sabe que color.accent = #d96a3a usará ese valor. Un agente que recibe una captura de pantalla adivinará "naranja cálido" y producirá algo cercano pero incorrecto. La diferencia se acumula a lo largo de decenas de componentes.

figmascope exporta un tokens.json como parte de cada bundle de contexto. Este artículo explica el formato en detalle, cómo figmascope obtiene los tokens de Figma, el mecanismo de inferencia por frecuencia como alternativa para archivos sin Figma Variables, y cómo conectar el output a frameworks habituales. Para ver cómo encajan los tokens en el flujo de exportación completo, visita la app de figmascope o lee Figma a Cursor y Figma a Claude Code.

El formato de tokens.json

figmascope usa un formato inspirado en el W3C Design Token Community Group. Cada token es un objeto con campos $value y $type, anidado bajo claves de categoría semántica:

{
  "color": {
    "surface":      { "$value": "#f6f2ea", "$type": "color" },
    "surface-2":    { "$value": "#efe9dc", "$type": "color" },
    "ink":          { "$value": "#1f1d1a", "$type": "color" },
    "ink-muted":    { "$value": "#4a4641", "$type": "color" },
    "accent":       { "$value": "#d96a3a", "$type": "color" },
    "accent-soft":  { "$value": "#f2c7a8", "$type": "color" },
    "good":         { "$value": "#6a8f5a", "$type": "color" },
    "warn":         { "$value": "#c89a3a", "$type": "color" },
    "bad":          { "$value": "#b8553a", "$type": "color" }
  },
  "spacing": {
    "1":  { "$value": "4px",   "$type": "dimension" },
    "2":  { "$value": "8px",   "$type": "dimension" },
    "3":  { "$value": "12px",  "$type": "dimension" },
    "4":  { "$value": "16px",  "$type": "dimension" },
    "6":  { "$value": "24px",  "$type": "dimension" },
    "8":  { "$value": "32px",  "$type": "dimension" },
    "12": { "$value": "48px",  "$type": "dimension" },
    "16": { "$value": "64px",  "$type": "dimension" }
  },
  "radius": {
    "sm":   { "$value": "4px",  "$type": "dimension" },
    "md":   { "$value": "8px",  "$type": "dimension" },
    "lg":   { "$value": "16px", "$type": "dimension" },
    "full": { "$value": "9999px", "$type": "dimension" }
  },
  "typography": {
    "body": {
      "fontFamily": { "$value": "Inter",  "$type": "fontFamily" },
      "fontSize":   { "$value": "14px",   "$type": "dimension" },
      "fontWeight": { "$value": 400,       "$type": "number" },
      "lineHeight": { "$value": 1.45,     "$type": "number" }
    },
    "heading": {
      "sm": {
        "fontFamily": { "$value": "Inter",  "$type": "fontFamily" },
        "fontSize":   { "$value": "18px",   "$type": "dimension" },
        "fontWeight": { "$value": 600,       "$type": "number" },
        "lineHeight": { "$value": 1.25,     "$type": "number" }
      }
    },
    "mono": {
      "fontFamily": { "$value": "JetBrains Mono", "$type": "fontFamily" },
      "fontSize":   { "$value": "13px",   "$type": "dimension" },
      "fontWeight": { "$value": 400,       "$type": "number" }
    }
  }
}

¿Por qué "al estilo W3C" y no la especificación exacta?

La especificación del W3C Design Token Community Group sigue siendo un borrador. figmascope sigue las convenciones principales ($value, $type, grupos anidados) pero no implementa todos los casos extremos como tipos compuestos y cadenas de resolución de alias. El output es lo bastante estable para ser consumido por Style Dictionary, travesía directa de JSON o un modelo de lenguaje — que es el consumidor principal.

Tipos de tokens en uso

$type Categoría Formato de $value Ejemplo
color color cadena hexadecimal "#d96a3a"
dimension espaciado, radio cadena CSS con unidad "16px"
fontFamily tipografía cadena con nombre de fuente "Inter"
number tipografía número sin unidad 400, 1.45

Los colores siempre se exportan como cadenas hexadecimales. Si la fuente en Figma usa RGBA con opacidad no completa, el canal alfa se preserva en hexadecimal de 8 dígitos (#d96a3a80).

Cómo figmascope obtiene los tokens

figmascope usa una estrategia de obtención en dos niveles. Las Figma Variables son la fuente preferida; la inferencia por frecuencia es la alternativa.

Nivel 1: Figma Variables

Si el archivo de Figma tiene Variables definidas (el sistema nativo de tokens de Figma, disponible en los planes Professional y Organization), figmascope las lee a través del endpoint /v1/files/:key/variables/local de la REST API. Los nombres de las variables se usan como claves de tokens, normalizados a kebab-case. Los modos se colapsan al modo por defecto, a menos que el archivo tenga un único modo, en cuyo caso se usan directamente los valores de ese modo.

Las colecciones de variables se mapean a las claves de categoría de nivel superior en tokens.json. Una colección llamada "Color" produce tokens.color.*; "Spacing" produce tokens.spacing.*. Si tu archivo de Figma usa nombres de colección no estándar, figmascope intenta inferir la categoría a partir del tipo resuelto de la variable.

Nivel 2: Inferencia por frecuencia

Muchos archivos de Figma — especialmente los más antiguos o los de clientes que no han adoptado Variables — no tienen definiciones de variables. figmascope lo gestiona recorriendo todo el árbol de nodos y construyendo histogramas de frecuencia de colores de relleno, valores de espaciado, radios de esquina y propiedades tipográficas.

Los valores que aparecen por encima de un umbral de frecuencia se convierten en tokens candidatos. Se nombran por su categoría y un índice secuencial (color.0, color.1...) a menos que pueda inferirse un nombre legible por humanos a partir de cómo se usa el valor (p. ej., un color usado únicamente en fondos en múltiples frames se convierte en color.surface).

El manifiesto _meta.json registra tokensSource como "variables" o "inferred", para que el prompt de tu agente pueda indicar cuándo se usó inferencia:

// _meta.json — inferred fallback
{
  "tokensSource": "inferred",
  "warnings": [
    {
      "code": "tokens-inferred",
      "message": "No Figma Variables found. Tokens were inferred from usage frequency."
    }
  ]
}

Los tokens inferidos son útiles pero no autoritativos. Tratalos como punto de partida, no como especificación del design system.

Referencias a tokens en el IR de layout

Los archivos IR por pantalla (screens/*.json) referencian tokens por ruta usando cadenas $ref en lugar de incrustar valores directos. Esto mantiene el IR compacto y asegura que el agente siempre resuelva valores desde una única fuente de verdad:

{
  "kind": "stack",
  "name": "PrimaryButton",
  "direction": "horizontal",
  "gap": { "$ref": "spacing.2" },
  "padding": {
    "top": 10, "right": 16, "bottom": 10, "left": 16
  },
  "background": { "$ref": "color.accent" },
  "radius": { "$ref": "radius.sm" },
  "children": [
    {
      "kind": "leaf",
      "name": "ButtonLabel",
      "type": "text",
      "style": { "$ref": "typography.body" },
      "color": { "$ref": "color.surface" }
    }
  ]
}

Un agente que procesa este nodo resuelve color.accent a #d96a3a y spacing.2 a 8px desde el archivo paralelo tokens.json. Sin ambigüedad, sin alucinación de valores.

Consulta el IR por pantalla explicado para la documentación completa del esquema de nodos.

Usar tokens.json con Style Dictionary

El output de figmascope es compatible con Style Dictionary con configuración mínima. Como ya usa la convención $value / $type, puedes apuntar Style Dictionary directamente a tokens.json:

// style-dictionary.config.js
module.exports = {
  source: ['design/tokens.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      prefix: 'fs',
      buildPath: 'src/styles/',
      files: [{ destination: 'tokens.css', format: 'css/variables' }]
    },
    js: {
      transformGroup: 'js',
      buildPath: 'src/',
      files: [{ destination: 'tokens.js', format: 'javascript/es6' }]
    }
  }
};

Ejecutar style-dictionary build producirá propiedades CSS personalizadas y exports de módulo ES desde el mismo archivo fuente que usa el agente.

Usar tokens.json con Tailwind

Un pequeño script convierte tokens.json a una extensión de tema de Tailwind. La estructura es lo bastante plana para recorrerla sin recursión en los casos habituales:

// scripts/tokens-to-tailwind.js
const fs = require('fs');
const tokens = JSON.parse(fs.readFileSync('design/tokens.json', 'utf8'));

function flattenGroup(group) {
  return Object.fromEntries(
    Object.entries(group).map(([k, v]) => [k, v.$value])
  );
}

const extend = {
  colors:      flattenGroup(tokens.color),
  spacing:     flattenGroup(tokens.spacing),
  borderRadius: flattenGroup(tokens.radius),
};

console.log(JSON.stringify(extend, null, 2));
node scripts/tokens-to-tailwind.js > design/tailwind-extend.json

Luego en tailwind.config.ts:

import extend from './design/tailwind-extend.json';

export default {
  content: ['./src/**/*.{ts,tsx}'],
  theme: { extend },
};

Usar tokens.json con Jetpack Compose

Para proyectos Android, la estructura de tokens se mapea limpiamente a un object de Kotlin. Puedes generarlo programáticamente o pedirle a Claude Code que lo haga. Para tokens de color:

// Generated from design/tokens.json
object DesignTokens {
  object Color {
    val surface    = Color(0xFFF6F2EA)
    val ink        = Color(0xFF1F1D1A)
    val accent     = Color(0xFFD96A3A)
    val accentSoft = Color(0xFFF2C7A8)
  }
  object Spacing {
    val s1 = 4.dp
    val s2 = 8.dp
    val s4 = 16.dp
    val s8 = 32.dp
  }
}

Consulta Jetpack Compose desde Figma para una guía completa que incluye integración con MaterialTheme.

Qué no contiene tokens.json

Conocer los límites del alcance ayuda a establecer expectativas correctas:

El array warnings en _meta.json anotará cualquier valor que no pudo exportarse correctamente. Revísalo antes de pasar el bundle a un agente.

Cómo se derivan los nombres de tokens

Cuando figmascope lee Figma Variables, el nombre de la variable en Figma se convierte en la clave del token. Una variable llamada Colors/Surface/Primary en una colección llamada Color se convierte en tokens.color.surface.primary en tokens.json — el separador de ruta es / en Figma y . en las claves anidadas de JSON.

Figma permite nombres de variables con espacios, mayúsculas y caracteres especiales. figmascope los normaliza:

El paso de transliteración importa para equipos internacionales. Un equipo de diseño ucraniano que usa nombres de variables en cirílico como Фон (fondo) obtiene una clave ASCII estable (fon) en el output, utilizable en nombres de clases CSS y JSON sin problemas de codificación. El nombre original se preserva como campo $description si está presente en los metadatos de la Variable de Figma.

Verificar la cobertura de tokens antes de la generación de código

Antes de pasar el bundle a un agente, vale la pena verificar que la cobertura de tokens parece razonable. Un script rápido de Node que comprueba si todos los valores de color referenciados en los IRs de pantalla resuelven a entradas en tokens.json:

// scripts/check-token-coverage.js
const fs = require('fs');
const glob = require('glob');

const tokens = JSON.parse(fs.readFileSync('design/tokens.json', 'utf8'));
const screenFiles = glob.sync('design/screens/*.json');

function getRef(obj) {
  const refs = [];
  if (obj && typeof obj === 'object') {
    if (obj.$ref) refs.push(obj.$ref);
    for (const v of Object.values(obj)) refs.push(...getRef(v));
  }
  return refs;
}

function resolveRef(ref, tokens) {
  return ref.split('.').reduce((o, k) => o?.[k], tokens);
}

let missing = 0;
for (const file of screenFiles) {
  const screen = JSON.parse(fs.readFileSync(file, 'utf8'));
  for (const ref of getRef(screen)) {
    if (!resolveRef(ref, tokens)) {
      console.error(`Missing token: ${ref} (in ${file})`);
      missing++;
    }
  }
}
if (missing === 0) console.log('All token refs resolve.');
else console.error(`${missing} unresolved token refs.`);

Cero referencias faltantes significa que el agente podrá resolver cada $ref en el IR de layout sin inventar valores. Si hay referencias faltantes, generalmente significa que el archivo de Figma cambió después de que se exportó el bundle — vuelve a ejecutar figmascope para obtener una exportación fresca. También puedes consultar el blog de figmascope para más consejos sobre cómo mantener la cobertura de tokens a lo largo de las iteraciones de diseño.