figmascopes standardexportmål är Jetpack Compose. Det är inte godtyckligt — Composes layoutmodell (Column, Row, Box, Modifier) mappar nära till IR-nodtyperna (stack, overlay, absolute, leaf). En vertikal stack i Figma är en Column i Compose. Översättningen är mekanisk, vilket gör den väl lämpad för agentdriven kodgenerering.
Den här genomgången täcker IR-till-Compose-mappningen i detalj, visar ett verkligt JSON-fragment med motsvarande Composable och förklarar tokenmappningslagret.
Varför Compose är standardmålet
Tre strukturella skäl:
- Auto-layout ↔ Column/Row. Figmas auto-layout-ramar (den stora majoriteten av moderna Figma-designer) exporteras som
kind: "stack"-noder. Stack-noder har enaxis-egenskap —verticalmappar tillColumn,horizontalmappar tillRow. Det är en 1:1-mappning utan tolkningssteg. - Avståndstokens ↔ dp-värden. Compose använder
Dpför alla layoutdimensioner. Tokenvärden itokens.jsonär enhetslösa heltal (t.ex.spacing.16 = 16) som mappar direkt till16.dp. Ingen konvertering, ingen skalning. - Färgtokens ↔ Color composable. Figma hexvärden i
tokens.jsonmappar tillColor(0xFFrrggbb)med en enda transformation. Tokenyckeln blir ett semantiskt variabelnamn i ditt tema.
IR-nodtyperna och deras Compose-mappningar
| IR-typ | Egenskaper | Compose-primitiv |
|---|---|---|
stack | axis: "vertical" | Column |
stack | axis: "horizontal" | Row |
overlay | skiktade underordnade | Box |
absolute | x, y, width, height | Box med Modifier.offset(x.dp, y.dp) |
leaf | type: "text" | Text med TextStyle |
leaf | type: "rectangle" med fyllning | Box(Modifier.background(Color(...))) |
Alla nodtyper kan bära en spacing-egenskap (gap mellan underordnade) och ett padding-objekt (topp/höger/botten/vänster). Båda refererar till tokennyclar.
Tokenmappning i detalj
En tokens.json-fil ser ut så här:
{
"spacing": {
"4": 4, "8": 8, "12": 12, "16": 16,
"20": 20, "24": 24, "32": 32, "48": 48
},
"radius": {
"4": 4, "8": 8, "12": 12, "16": 16, "full": 9999
},
"color": {
"7f5cfe": "#7F5CFE",
"1a1a2e": "#1A1A2E",
"f6f2ea": "#F6F2EA",
"ffffff": "#FFFFFF",
"e53935": "#E53935"
},
"typography": {
"heading.24": { "size": 24, "weight": 700, "lineHeight": 1.2 },
"body.14": { "size": 14, "weight": 400, "lineHeight": 1.5 },
"label.12": { "size": 12, "weight": 500, "lineHeight": 1.4 }
}
}
Mappningsreglerna:
spacing.16→16.dpradius.12→RoundedCornerShape(12.dp)color.7f5cfe→Color(0xFF7F5CFE)(lägg till0xFFför full alfa)typography.heading.24→TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold, lineHeight = 28.8.sp)
Tokennycklarna är avsiktligt ogenomskinliga (hexsträngar för färg, numeriska strängar för avstånd) så att de inte styr modellen mot någon särskild namnkonvention. Ditt Compose-tema kan aliasa dem till semantiska namn — colorPrimary, spacingMd — oberoende av IR.
Ett verkligt exempel: hemskärm JSON till Composable
Här är en förenklad hemskärm IR. En vertikal stack med ett header-leaf och en kortlista:
{
"name": "home",
"kind": "stack",
"axis": "vertical",
"spacing": "spacing.24",
"padding": { "top": "spacing.16", "right": "spacing.16",
"bottom": "spacing.16", "left": "spacing.16" },
"fill": "color.f6f2ea",
"children": [
{
"kind": "leaf",
"type": "text",
"stringRef": "home.title",
"typography": "typography.heading.24",
"fill": "color.1a1a2e"
},
{
"kind": "stack",
"axis": "vertical",
"spacing": "spacing.12",
"children": [
{
"kind": "overlay",
"radius": "radius.12",
"fill": "color.ffffff",
"padding": { "top": "spacing.16", "right": "spacing.16",
"bottom": "spacing.16", "left": "spacing.16" },
"children": [
{
"kind": "leaf",
"type": "text",
"stringRef": "home.card.label",
"typography": "typography.label.12",
"fill": "color.7f5cfe"
}
]
}
]
}
]
}
Motsvarande Composable — vad en agent bör producera från den här IR:n:
@Composable
fun HomeScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF6F2EA))
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
Text(
text = stringResource(R.string.home_title),
style = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
lineHeight = 28.8.sp,
color = Color(0xFF1A1A2E)
)
)
Column(
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(12.dp))
.background(Color(0xFFFFFFFF))
.padding(16.dp)
) {
Text(
text = stringResource(R.string.home_card_label),
style = TextStyle(
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
lineHeight = 16.8.sp,
color = Color(0xFF7F5CFE)
)
)
}
}
}
}
Varje värde i Composable-funktionen spåras tillbaka till en tokennyckel i IR. Ingenting är hårdkodat — 16.dp kommer från spacing.16, 24.dp från spacing.24, Color(0xFF7F5CFE) från color.7f5cfe.
Strängreferenser — stringResource-mappning
Varje textnod i IR bär en stringRef med en punktnotationsnyckel. strings.json-filen mappar nycklar till visningsvärden och fallbacks:
{
"home.title": { "value": "Good morning", "fallback": "Good morning" },
"home.card.label": { "value": "Today's summary", "fallback": "Summary" }
}
Punktnotationen mappar till Android-strängresurs-ID:n med punkter ersatta av understreck: home.title → R.string.home_title. Fältet fallback är vad du hårdkodar som literalsträngen om resursen inte finns i strings.xml ännu:
text = stringResource(R.string.home_title, "Good morning")
Säg till agenten att alltid använda fallback-fältet — inte en tom sträng — så att skärmen är läsbar under utveckling innan strings.xml är ifylld.
Absolutpositionering
Noder med kind: "absolute" använder Figma-koordinater direkt. Dessa förekommer i designer med överlappande element eller element förankrade till specifika positioner. Compose-mappningen använder Box som förälder och Modifier.offset på underordnade:
// IR: { "kind": "absolute", "x": 24, "y": 80, "width": 120, "height": 40 }
Box(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.offset(x = 24.dp, y = 80.dp)
.size(width = 120.dp, height = 40.dp)
) {
// underordnade
}
}
Absolutpositionering är ovanligt i välstrukturerade Figma-filer (de flesta moderna filer använder auto-layout). När du ser det, kontrollera om designavsikten är verkligt absolut eller om designern bara inte tillämpade auto-layout-begränsningar — IR kan inte härleda avsikt.
Ärliga luckor
Paketet täcker det vanliga fallet väl. Några saker det inte gör:
- Gradientfyllningar. IR exporterar en varning när den stöter på en gradient. Nodens bakgrundsfyllning utelämnas. Lämna ett
// TODO: gradientoch implementera det manuellt. - Komplexa effekter. Droppskuggor och oskärpor representeras inte i IR. De visas i
_meta.json-varningarna om de finns. - Vektorikoner. IR lagrar ett referens-ID för ikonnoder, inte sökvägsdata. Du behöver lösa upp ikonen till en faktisk drawable eller compose-ikon separat.
- Kapslade komponenter. IR inkluderar
componentIdpå instansnoder.components/inventory.jsonmappar ID:n till namn. Implementera komponenten separat och referera till den med namn i föräldra-Composable:n.
Dessa luckor är explicita — de visas i _meta.json-varningarna och CONTEXT.md. Agenten gissar inte igenom dem tyst.
Exportera kontextpaketet från figmascope-huvudappen, använd det sedan med Claude Code eller Cursor för att implementera Composable-funktioner direkt från IR. Ingen skärmbildsgissning, inga hårdkodade värden.