Figmas nodträd är rikt men brusigt. Frame, Group, Component, Instance, Text, Rectangle, Ellipse, Vector — var och en har dussintals valfria fält, varav några är i konflikt med varandra. En agent som arbetar direkt från det råa Figma API-svaret spenderar kognitiv budget på att förstå schemat istället för att skriva kod.
figmascope normaliserar trädet till fyra nodtyper: stack, overlay, absolute och leaf. Varje nod i varje screens/*.json-fil är en av dessa fyra. Inget annat.
De fyra typerna
stack
En stack är en nod med Figma Auto Layout — layoutMode: "VERTICAL" eller layoutMode: "HORIZONTAL". Dess underordnade är flödespositionerade längs axeln. Gaps och utfyllnad är explicita.
{
"kind": "stack",
"id": "123:456",
"name": "ContentColumn",
"axis": "vertical",
"gap": 16,
"paddingTop": 24,
"paddingBottom": 24,
"paddingLeft": 20,
"paddingRight": 20,
"primaryAxisAlignItems": "SPACE_BETWEEN",
"counterAxisAlignItems": "CENTER",
"width": 390,
"height": 844,
"fills": [{ "type": "SOLID", "color": "#1a1a2e" }],
"children": [ ... ]
}
Mappar till Jetpack Compose som en Column (vertikal axel) eller Row (horisontell axel). Fälten primaryAxisAlignItems och counterAxisAlignItems mappar till Arrangement respektive Alignment. gap blir Arrangement.spacedBy().
overlay
En overlay är en nod vars underordnade har absoluta positioner inom den. Figma representerar detta som en Frame med layoutMode: "NONE" där underordnade har absoluteBoundingBox-koordinater. Overlay-typen fångar detta utan att kräva att agenten resonerar om råa koordinater för att förstå strukturen.
{
"kind": "overlay",
"id": "123:789",
"name": "CardOverlay",
"width": 358,
"height": 200,
"fills": [{ "type": "SOLID", "color": "#ffffff" }],
"children": [
{
"kind": "absolute",
"offset": { "x": 16, "y": 16 },
"child": { ... }
}
]
}
Mappar till Compose som en Box. Underordnade positioneras med Modifier.offset() eller Modifier.align() beroende på hur deras positioner förhåller sig till föräldrañsens gränser.
absolute
En absolute-nod är en omslutning som bär ett enda underordnat barn vid en specifik (x, y)-offset inom sin förälder-overlay. Det är en tunn strukturell nod — den finns för att bevara den rumsliga relationen från Figma utan att blanda ihop position med innehåll.
{
"kind": "absolute",
"offset": { "x": 24, "y": 140 },
"child": {
"kind": "leaf",
"name": "BadgeLabel",
"text": "NEW",
"stringRef": "badge.new",
...
}
}
I Compose blir detta typiskt ett underordnat barn till en Box med Modifier.offset(x.dp, y.dp). Offsetvärdena är kandidater för tokensubstitution om avståndstokens finns i intervallet.
leaf
Ett leaf är en terminalnod — inga underordnade. Det har styling (fyllningar, streck, hörnradie) och valfritt textinnehåll. Textleaves har ett text-fält (literalsträngen) och ett stringRef-fält (i18n-resursnyckeln från strings.json).
{
"kind": "leaf",
"id": "123:101",
"name": "SpeedLabel",
"text": "Speed Test",
"stringRef": "speed.test",
"fontSize": 18,
"fontWeight": 600,
"fills": [{ "type": "SOLID", "color": "#ffffff" }],
"width": 160,
"height": 28
}
Mappar till Compose som en Text()-composable för textinnehåll, eller som en Surface, Box eller Image för icke-text-leaves beroende på fyllningstyp.
De fyra typerna mappar en-till-en till den kompositionella modellen för vilket UI-ramverk som helst. Stacks är flödeslayouter. Overlays är z-positionerade behållare. Absolutes bär rumslig metadata. Leaves är innehåll. Det är allt. Figmas nodtaxonomi har 12+ nodtyper — IR reducerar det till fyra.
Hur typ bestäms
Klassificeringslogiken:
- Om
layoutModeär"VERTICAL"eller"HORIZONTAL"→stack - Om
layoutModeär"NONE"och noden har underordnade →overlay(underordnade omsluts somabsolute) - Om en nod är direkt underordnad till en overlay och bär en position →
absolute - Om noden inte har underordnade och inte är en absolute-omslutning →
leaf
Varningen layout-mode-none-inferred i _meta.json utlöses när en Frame med layoutMode: "NONE" har underordnade med icke-trivialt överlappande begränsningsrutor. figmascope behandlar den som en overlay men noterar inferensen för att vissa layoutMode: "NONE"-frames i praktiken bara är behållare för ett enda underordnat barn (noll överlapp) och skulle kunna förenklas. Varningen låter agenten bestämma hur den ska hantera det.
absoluteBoundingBox — varför det bevaras
Varje nod i IR behåller sin absoluteBoundingBox från Figma, även när föräldern är en stack (där absoluta positioner teoretiskt är irrelevanta för layout):
{
"kind": "stack",
"absoluteBoundingBox": { "x": 0, "y": 88, "width": 390, "height": 756 },
...
}
Det här finns för agenter som behöver resonera om rumsliga relationer mellan noder som inte delar ett direkt föräldra-barn-förhållande. Att överlappa två element från olika grenar av trädet kräver att man känner till deras absoluta positioner, inte bara deras relativa offsets. Koordinatsystemet är Figmas canvas — ursprung överst till vänster, y ökar nedåt.
Det hjälper också med PNG-korsreferens. _meta.json inkluderar ett pngCount-fält och de exporterade PNG-filerna namnges efter skärmens slug. Om du jämför IR med skärmbilden låter absoluteBoundingBox dig lokalisera specifika noder i bilden. För att se det här på din egen design, exportera ett paket från figmascope.dev.
Fyllningar och streck på behållartyper
En vanlig förvirringspoäng: stack- och overlay-noder kan ha fyllningar, inte bara leaves. En kolumn med en bakgrundsfärg är fortfarande en stack — den har bara också en fills-array. figmascope bevarar detta:
{
"kind": "stack",
"axis": "vertical",
"fills": [{ "type": "SOLID", "color": "#f6f2ea" }],
"cornerRadius": 12,
...
}
I Compose blir detta typiskt en Column inuti en Surface eller Card, med fyllningsfärgen och hörnradien applicerade på behållaren. IR kollapsar inte fyllningen till de underordnade — den behåller den på noden där Figma satte den.
Gradientfyllningar på behållartyper ger en background-gradient-not-supported:<name>-varning. Noden finns fortfarande i IR med sina övriga fält intakta. Agenten bör behandla fyllningen som ett TODO och antingen approximera med en solid färg eller generera ett anpassat ritanrop, per CONTEXT.md scope-anteckningarna.
stringRef + text-relationen
Textleaf-noder bär båda:
text: literalsträngsvärdet från Figma (t.ex."Speed Test")stringRef: resursnyckeln frånstrings.json(t.ex."speed.test")
Om en textnods innehåll drogs bort från strings.json på grund av en kollision eller filter (enbart numerisk, tom, för kort), har leafet fortfarande text men stringRef kommer att saknas. Agenten bör falla tillbaka till literalen i det fallet. Se strings.json för den fullständiga kollisionshanteringslogiken.
När båda finns säger CONTEXT.md-begränsningen att använda stringRef. Fältet text finns för agentens resonemang (så att den vet vad strängen säger utan att öppna strings.json) och som fallback.
Komponentinstanser i IR
När en Figma-nod är en INSTANCE av en komponent bär IR-noden två ytterligare fält:
{
"kind": "stack",
"componentId": "789:012",
"componentName": "PrimaryButton",
...
}
Det här kopplar tillbaka till components/inventory.json. Agenten vet att den här noden är en instans av PrimaryButton snarare än en bespoke layout, och bör referera till den befintliga komponenten snarare än att generera duplicerad kod. Den fullständiga täckningen av detta finns i Komponentinventering.
Verkligt skärmexempel
Ett förenklat men strukturellt korrekt exempel på en skärm med en header, innehållskolumn och flytande knapp:
{
"screen": "home",
"root": {
"kind": "overlay",
"name": "HomeScreen",
"width": 390,
"height": 844,
"fills": [{ "type": "SOLID", "color": "#0d0d1a" }],
"children": [
{
"kind": "absolute",
"offset": { "x": 0, "y": 0 },
"child": {
"kind": "stack",
"name": "ContentArea",
"axis": "vertical",
"gap": 24,
"paddingTop": 56,
"paddingLeft": 20,
"paddingRight": 20,
"children": [
{
"kind": "leaf",
"name": "Title",
"text": "Speed Test",
"stringRef": "speed.test",
"fontSize": 28,
"fontWeight": 700
}
]
}
},
{
"kind": "absolute",
"offset": { "x": 111, "y": 752 },
"child": {
"kind": "stack",
"name": "StartButton",
"componentId": "321:654",
"componentName": "PrimaryButton",
"axis": "horizontal",
"gap": 8,
"paddingTop": 16,
"paddingBottom": 16,
"paddingLeft": 32,
"paddingRight": 32,
"cornerRadius": 24,
"fills": [{ "type": "SOLID", "color": "#7f5cfe" }]
}
}
]
}
}
Från detta kan agenten korrekt generera en Compose Box-skärm med en absolutpositionerad Column för innehåll och en absolutpositionerad PrimaryButton-komponent längst ner. Varje layoutbeslut är härledbart från IR utan gissning.
För hur tokens tillämpas på avstånd- och färgvärden i den här strukturen, se tokens.json förklarad. För det fullständiga agentarbetsflödet med denna IR med Cursor eller Claude Code, se Jetpack Compose från Figma. Prova det själv genom att klistra in din Figma URL i figmascope.