Domyślnym celem eksportu figmascope jest Jetpack Compose. Ale pakiet jest agnostyczny względem agenta — rodzaje węzłów IR, format tokenów i referencje do ciągów mapują równie czysto na React + Tailwind. Po prostu mówisz agentowi, by celował w JSX zamiast Kotlin.
Ten przewodnik omawia mapowanie IR na JSX, jak rozszerzyć tailwind.config.js tokenami z tokens.json i wzorce promptowania, które powodują najmniejszy dryft po stronie React.
Ważne zastrzeżenie na początku
Compose to to, co figmascope testuje najczęściej. IR, format tokenów i ograniczenia CONTEXT.md były projektowane z Compose na uwadze. React + Tailwind działa — IR mapuje czysto — ale zobaczysz nieco więcej dryftu, szczególnie wokół typografii i układów overlay. Oznaczaj wszystko, co wygląda nieprawidłowo i uruchom sprawdzenie tokenów po pierwszym przebiegu.
Krok 1: Wygeneruj i rozpakuj pakiet
unzip ~/Downloads/context-bundle.zip -d ./design/
ls design/
# CONTEXT.md _meta.json components/ screens/ strings.json tokens.json
Krok 2: Rozszerz konfigurację Tailwind o design tokens
Przed promptowaniem zmapuj tokens.json do tailwind.config.js. To kluczowy krok, który sprawia, że Tailwind jest świadomy tokenów — zamiast zahardkodowanych wartości hex w ciągach className, otrzymujesz semantyczne nazwy, które dają się prześledzić do projektu.
// tailwind.config.js
const tokens = require('./design/tokens.json')
// Buduj mapę kolorów: { 'brand-7f5cfe': '#7F5CFE', ... }
const colors = Object.fromEntries(
Object.entries(tokens.color).map(([key, val]) => [
`brand-${key}`, val
])
)
// Buduj mapę odstępów: { 'ds-4': '4px', 'ds-8': '8px', ... }
const spacing = Object.fromEntries(
Object.entries(tokens.spacing).map(([key, val]) => [
`ds-${key}`, `${val}px`
])
)
// Buduj mapę border-radius
const borderRadius = Object.fromEntries(
Object.entries(tokens.radius).map(([key, val]) => [
`ds-${key}`, val === 9999 ? '9999px' : `${val}px`
])
)
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {
colors,
spacing,
borderRadius,
}
}
}
Produkuje to klasy Tailwind takie jak bg-brand-7f5cfe, p-ds-16, rounded-ds-12. Nie najpiękniejsze nazwy klas, ale dają się prześledzić — każda klasa mapuje na klucz tokenu.
Jeśli wolisz semantyczne aliasy, dodaj warstwę aliasowania:
// w theme.extend.colors:
primary: tokens.color['7f5cfe'],
background: tokens.color['f6f2ea'],
surface: tokens.color['ffffff'],
Daj agentowi oba — surową konfigurację tokenów i semantyczne aliasy — żeby mógł wybrać właściwą nazwę w kontekście.
Krok 3: Poproś agenta o cel React + Tailwind
CONTEXT.md mówi, że domyślnym celem jest Compose. Nadpisz to explicite w promptu:
Implement ./design/screens/home.json as a React functional component using Tailwind CSS.
Framework override: ignore the Compose instructions in CONTEXT.md. Target React + Tailwind.
Rules:
- Read ./design/tokens.json. The tokens are extended into Tailwind config —
use Tailwind classes that correspond to token keys (e.g. bg-brand-7f5cfe, p-ds-16).
- UI strings come from ./design/strings.json. Import and reference them by key.
- IR node kind mapping:
stack (axis:vertical) → <div className="flex flex-col gap-[Xpx]">
stack (axis:horizontal) → <div className="flex flex-row gap-[Xpx]">
overlay → <div className="relative"> with absolute-positioned children
absolute → <div className="absolute" style={{"{{"}}top, left, width, height{{"}}"}}>
leaf (text) → <span> or <p> with Tailwind text classes
leaf (rectangle) → <div> with bg and rounded classes
- Do not hardcode hex values. All colors must use Tailwind classes from the token config.
- Do not hardcode strings. Use the strings.json keys.
Output to: src/screens/HomeScreen.tsx
Mapowanie IR na JSX
Pełna tabela mapowania dla React + Tailwind:
| Rodzaj IR | Właściwości | Wzorzec JSX |
|---|---|---|
stack | axis: "vertical" | <div className="flex flex-col gap-ds-{n}"> |
stack | axis: "horizontal" | <div className="flex flex-row gap-ds-{n}"> |
overlay | warstwowane dzieci | <div className="relative"> z dziećmi className="absolute ..." |
absolute | x, y, w, h | <div className="absolute" style={{"{{"}}top: y, left: x, width: w, height: h{{"}}"}}> |
leaf | type: "text" | <span className="text-{size} font-{weight} leading-{lh} text-brand-{color}"> |
leaf | type: "rectangle" | <div className="bg-brand-{color} rounded-ds-{r}"> |
Wartości gap i padding: używaj klas odstępów ds-{n} z rozszerzenia konfiguracji Tailwind. Jeśli wartość gap nie jest czystym kluczem tokenu, użyj składni wartości arbitralnych Tailwind: gap-[20px].
Realny przykład: IR ekranu głównego na JSX
Używając tego samego IR co w przewodniku Compose:
import strings from '../../design/strings.json'
export function HomeScreen() {
return (
<div className="flex flex-col gap-ds-24 p-ds-16 bg-brand-f6f2ea min-h-screen">
<h1 className="text-2xl font-bold leading-tight text-brand-1a1a2e">
{strings['home.title'].value}
</h1>
<div className="flex flex-col gap-ds-12">
<div className="bg-white rounded-ds-12 p-ds-16">
<span className="text-xs font-medium text-brand-7f5cfe">
{strings['home.card.label'].value}
</span>
</div>
</div>
</div>
)
}
Każda className jest możliwa do prześledzenia. gap-ds-24 → spacing.24 → 24px. text-brand-7f5cfe → color.7f5cfe → #7F5CFE. Jeśli wartość dryfuje (np. agent pisze gap-6 zamiast gap-ds-24), jest to natychmiast widoczne w przeglądzie kodu.
Dlaczego tokens.json + konfiguracja Tailwind działa
Oba systemy są token-first. Tailwind rozszerza bazową konfigurację o niestandardowe wartości; tokens.json jest już ustrukturyzowane jako mapa niestandardowych wartości. Krok rozszerzenia (Krok 2 powyżej) to jednorazowa konfiguracja — potem agent używa nazw klas Tailwind semantycznie powiązanych z systemem projektowania zamiast arbitralnych klas użytkowych.
Wynik: gdy token projektu się zmienia (powiedzmy, kolor główny przesuwa się z #7F5CFE na #6B4EE6), aktualizujesz jedną wartość w tokens.json, ponownie uruchamiasz import konfiguracji i Tailwind regeneruje. Kod komponentu się nie zmienia.
Sprawdzanie dryftu tokenów
Po implementacji poproś agenta o audyt dryftu:
Audit src/screens/HomeScreen.tsx for token drift.
Check:
1. Any hex color values not referenced through a Tailwind class from the token config.
2. Any hardcoded px or rem spacing values that should be ds-{n} classes.
3. Any UI strings not sourced from design/strings.json.
List violations. Produce a diff that fixes them.
Overlay i absolute — trudne przypadki
Węzły overlay potrzebują rodzica relative i pozycjonowanych absolute dzieci. IR wymienia dzieci w kolejności z (pierwsze = dolna warstwa). Powiedz agentowi, by zachował tę kolejność w JSX — React renderuje w kolejności DOM, a CSS position: absolute układa się odpowiednio.
Węzły absolute używają surowych pikseli z Figmy. Prawie zawsze pochodzą z projektów niebudowanych z auto-layout. Jeśli widzisz wiele absolutnych węzłów, zazwyczaj oznacza to, że plik Figma ma ręczne pozycjonowanie — wygenerowany kod będzie kruchy przy różnych rozmiarach viewportu. Rozważ oznaczenie tego i refaktoryzację do układów flex.
Obsługa ciągów w React
W przeciwieństwie do Androida (gdzie klucze strings.json mapują na ID zasobów R.string.*), w React importujesz JSON bezpośrednio:
import strings from '../../design/strings.json'
// użycie
{strings['home.title'].value}
// lub z fallbackiem
{strings['home.title']?.value ?? strings['home.title']?.fallback ?? 'Untitled'}
Jeśli używasz bibliotek i18n (react-i18next, next-intl), klucze w notacji z kropką w strings.json mapują bezpośrednio na przestrzenie nazw kluczy tłumaczeń. Powiedz agentowi, której biblioteki i18n używasz, żeby generował właściwy wzorzec wywołania.
Uczciwe luki po stronie React
Narzędzia typografii. Narzędzia tekstowe Tailwind (text-xs, text-2xl) nie mapują 1:1 na tokeny typography w tokens.json. Tailwind ma stałą skalę typów; plik tokenów ma arbitralne rozmiary. Musisz albo rozszerzyć konfigurację fontSize Tailwind wartościami tokenów, albo używać wartości arbitralnych (text-[24px]). Obie działają; rozszerzanie konfiguracji jest czystsze.
Wysokość linii. Ten sam problem — narzędzia leading Tailwind nie pasują do arbitralnych wartości lineHeight. Używaj leading-[{value}] lub rozszerz konfigurację.
Gradienty. Nieobsługiwane w IR. Każde wypełnienie gradientowe pojawia się jako ostrzeżenie w _meta.json i jest pomijane z właściwości fill węzła. Obsłuż ręcznie.
Żadna z tych kwestii nie jest blokująca — to znane luki. Fundament świadomy tokenów jest solidny; krawędzie wymagają po prostu ręcznej obsługi.
Zacznij od figmascope, by wyeksportować pakiet, a następnie użyj tego przewodnika obok workflow Cursor lub workflow Claude Code, by prowadzić implementację.