Tokeny projektowania to wspólny słownik między projektantami a programistami. Istnieją w różnych formach od ery Salesforce Lightning — Style Dictionary, Theo, projekt specyfikacji W3C Design Token Community Group. Co ostatnio się zmieniło, to że są teraz również wspólnym słownikiem między bazą kodu a agentami AI do kodowania.
Agent, który wie, że color.accent = #d96a3a, użyje tej wartości. Agent, który dostaje zrzut ekranu, zgadnie „ciepła pomarańcza" i wyprodukuje coś zbliżonego, ale niepoprawnego. Różnica kumuluje się przez dziesiątki komponentów.
figmascope eksportuje tokens.json jako część każdego pakietu kontekstu. Ten artykuł szczegółowo wyjaśnia format, jak figmascope pobiera tokeny z Figmy, fallback wnioskowania z częstotliwości dla plików bez zmiennych Figmy i jak podłączyć wynik do popularnych frameworków. Aby zobaczyć, jak tokeny pasują do pełnego przepływu eksportu, odwiedź aplikację figmascope lub przeczytaj Figma do Cursor i Figma do Claude Code.
Format tokens.json
figmascope używa formatu inspirowanego W3C Design Token Community Group. Każdy token to obiekt z polami $value i $type, zagnieżdżonymi pod kluczami kategorii semantycznych:
{
"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" }
}
}
}
Dlaczego „w stylu W3C", a nie dokładnie zgodnie ze specyfikacją?
Specyfikacja W3C Design Token Community Group jest nadal projektem. figmascope przestrzega podstawowych konwencji ($value, $type, zagnieżdżone grupy), ale nie implementuje wszystkich przypadków krawędziowych, takich jak typy złożone i łańcuchy rozwiązywania aliasów. Wynik jest wystarczająco stabilny, aby był konsumowany przez Style Dictionary, bezpośrednie przechodzenie JSON lub model językowy — który jest głównym konsumentem.
Typy tokenów w użyciu
| $type | Kategoria | Format $value | Przykład |
|---|---|---|---|
color |
kolor | ciąg hex | "#d96a3a" |
dimension |
odstępy, promień | ciąg CSS z jednostką | "16px" |
fontFamily |
typografia | ciąg nazwy czcionki | "Inter" |
number |
typografia | liczba bez jednostki | 400, 1.45 |
Kolory są zawsze wyświetlane jako ciągi hex. Jeśli źródło Figmy używa RGBA z niepełną przezroczystością, kanał alfa jest zachowany w 8-cyfrowym hex (#d96a3a80).
Jak figmascope pobiera tokeny
figmascope używa dwupoziomowej strategii pozyskiwania. Zmienne Figmy są preferowanym źródłem; wnioskowanie z częstotliwości jest fallbackiem.
Poziom 1: Zmienne Figmy
Jeśli plik Figmy ma zdefiniowane zmienne (natywny system tokenów Figmy, dostępny w planach Professional i Organization), figmascope odczytuje je przez endpoint /v1/files/:key/variables/local REST API. Nazwy zmiennych są używane jako klucze tokenów, sanityzowane do kebab-case. Tryby są zwijane do domyślnego trybu, chyba że plik ma jeden tryb, w którym to przypadku wartości tego trybu są używane bezpośrednio.
Kolekcje zmiennych mapują do kluczy kategorii najwyższego poziomu w tokens.json. Kolekcja o nazwie „Color" produkuje tokens.color.*; „Spacing" produkuje tokens.spacing.*. Jeśli twój plik Figmy używa niestandardowych nazw kolekcji, figmascope próbuje wywnioskować kategorię z rozwiązanego typu zmiennej.
Poziom 2: Wnioskowanie z częstotliwości
Wiele plików Figmy — szczególnie starszych lub plików od klientów, którzy nie przyjęli zmiennych — nie ma definicji zmiennych. figmascope obsługuje to, przechodząc przez całe drzewo węzłów i budując histogramy częstotliwości kolorów wypełnień, wartości odstępów, promieni rogów i właściwości typografii.
Wartości pojawiające się powyżej progu częstotliwości stają się kandydatami na tokeny. Są nazwane przez kategorię i kolejny indeks (color.0, color.1...), chyba że można wywnioskować nazwę czytelną dla człowieka na podstawie sposobu użycia wartości (np. kolor używany tylko na tłach w wielu ramkach staje się color.surface).
Manifest _meta.json zapisuje tokensSource jako "variables" lub "inferred", więc twój prompt agenta może odnotować, kiedy użyto wnioskowania:
// _meta.json — inferred fallback
{
"tokensSource": "inferred",
"warnings": [
{
"code": "tokens-inferred",
"message": "No Figma Variables found. Tokens were inferred from usage frequency."
}
]
}
Tokeny wywnioskowane są przydatne, ale nie autorytatywne. Traktuj je jako punkt wyjścia, nie specyfikację systemu projektowania.
Odwołania do tokenów w IR układu
Pliki IR per-ekran (screens/*.json) odwołują się do tokenów przez ścieżkę używając ciągów $ref zamiast osadzania surowych wartości. Dzięki temu IR jest kompaktowy i zapewnia, że agent zawsze rozwiązuje wartości z jednego źródła prawdy:
{
"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" }
}
]
}
Agent przetwarzający ten węzeł rozwiązuje color.accent do #d96a3a i spacing.2 do 8px z równoległego pliku tokens.json. Brak niejednoznaczności, brak halucynacji wartości.
Zobacz wyjaśnienie IR per-ekran po pełną dokumentację schematu węzłów.
Używanie tokens.json ze Style Dictionary
Wynik figmascope jest kompatybilny z Style Dictionary przy minimalnej konfiguracji. Ponieważ już używa konwencji $value / $type, możesz wskazać Style Dictionary bezpośrednio na 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' }]
}
}
};
Uruchomienie style-dictionary build wyprodukuje właściwości niestandardowe CSS i eksporty modułów ES z tego samego pliku źródłowego, którego używa agent.
Używanie tokens.json z Tailwind
Mały skrypt konwertuje tokens.json na rozszerzenie motywu Tailwind. Struktura jest wystarczająco płaska, aby przechodzić bez rekursji dla typowych przypadków:
// 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
Następnie w tailwind.config.ts:
import extend from './design/tailwind-extend.json';
export default {
content: ['./src/**/*.{ts,tsx}'],
theme: { extend },
};
Używanie tokens.json z Jetpack Compose
Dla projektów Android struktura tokenów mapuje czysto do Kotlin object. Możesz generować to programistycznie lub poprosić Claude Code, aby to zrobił. Dla tokenów kolorów:
// 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
}
}
Zobacz Jetpack Compose z Figmy po kompletny przewodnik obejmujący integrację MaterialTheme.
Czego tokens.json nie zawiera
Znajomość ograniczeń zakresu pomaga ustawić właściwe oczekiwania:
- Cienie — figmascope obecnie nie eksportuje tokenów cienia zewnętrznego ani wewnętrznego. Wartości cieni pojawiają się inline na węzłach w IR, gdzie są obecne.
- Gradienty — wypełnienia gradientowe są eksportowane tak jak są na węzłach liści w IR, nie jako nazwane tokeny.
- Animacja — wartości czasu, łagodzenia i przejścia są poza zakresem. Obsługa animacji Figmy jest w trybie Prototype, którego figmascope nie odczytuje.
- Punkty przerwania — ograniczenia responsywne nie są konceptem Figmy na poziomie zmiennych; wymagają oddzielnych decyzji projektowych.
Tablica warnings w _meta.json odnotuje wszelkie wartości, których nie można było wyeksportować czysto. Przejrzyj ją przed przekazaniem pakietu agentowi.
Jak wyprowadzane są nazwy tokenów
Gdy figmascope odczytuje zmienne Figmy, nazwa zmiennej w Figmie staje się kluczem tokenu. Zmienna o nazwie Colors/Surface/Primary w kolekcji o nazwie Color staje się tokens.color.surface.primary w tokens.json — separator ścieżki to / w Figmie, . w zagnieżdżonych kluczach JSON.
Figma pozwala na nazwy zmiennych ze spacjami, wielkimi literami i znakami specjalnymi. figmascope je normalizuje:
- Spacje stają się myślnikami:
Primary Surface→primary-surface - Wielkie litery są zamieniane na małe:
AccentOrange→accent-orange - Znaki kontrolne i znaczniki bidi Unicode są usuwane przez
sanitizeName - Cyrylica i inne skrypty nie-łacińskie są transliterowane dla kompatybilności slugów
Krok transliteracji ma znaczenie dla międzynarodowych zespołów. Ukraiński zespół projektowy używający cyrylickich nazw zmiennych, takich jak Фон (tło), otrzymuje stabilny klucz ASCII (fon) w wynikach, który jest użyteczny w nazwach klas CSS i JSON bez problemów z kodowaniem. Oryginalna nazwa jest zachowana jako pole $description, jeśli jest obecna w metadanych zmiennej Figmy.
Sprawdzanie pokrycia tokenów przed codegen
Przed przekazaniem pakietu agentowi warto sprawdzić, czy pokrycie tokenów wygląda rozsądnie. Szybki skrypt Node sprawdzający, czy wszystkie wartości kolorów przywoływane w IR ekranów rozwiązują do wpisów w 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.`);
Zero brakujących odwołań oznacza, że agent będzie mógł rozwiązać każde $ref w IR układu bez wymyślania wartości. Jeśli są brakujące odwołania, zazwyczaj oznacza to, że plik Figmy zmienił się po wyeksportowaniu pakietu — uruchom ponownie figmascope, aby uzyskać świeży eksport. Możesz też sprawdzić blog figmascope po więcej wskazówek dotyczących utrzymania pokrycia tokenów w iteracjach projektowych.