figmascope का default export target Jetpack Compose है। यह arbitrary नहीं है — Compose का layout model (Column, Row, Box, Modifier) IR node kinds (stack, overlay, absolute, leaf) से closely map होता है। Figma में एक vertical stack Compose में एक Column है। Translation mechanical है, जो इसे agent-driven codegen के लिए well-suited बनाता है।

यह walkthrough IR-to-Compose mapping को detail में cover करता है, एक real JSON fragment और corresponding Composable दिखाता है, और token mapping layer explain करता है।

Compose default target क्यों है

तीन structural कारण:

  1. Auto-layout ↔ Column/Row। Figma के auto-layout frames (modern Figma designs का बड़ा हिस्सा) kind: "stack" nodes के रूप में export होते हैं। Stack nodes में axis property होती है — vertical Column से map होता है, horizontal Row से। यह कोई interpretation step के बिना 1:1 mapping है।
  2. Spacing tokens ↔ dp values। Compose सभी layout dimensions के लिए Dp use करता है। tokens.json में token values unitless integers हैं (जैसे spacing.16 = 16) जो directly 16.dp से map होते हैं। कोई conversion नहीं, कोई scaling नहीं।
  3. Color tokens ↔ Color composable। tokens.json में Figma hex values एक single transformation के साथ Color(0xFFrrggbb) से map होते हैं। Token key आपके theme में एक semantic variable name बन जाती है।

IR node kinds और उनके Compose mappings

IR kindPropertiesCompose primitive
stackaxis: "vertical"Column
stackaxis: "horizontal"Row
overlaylayered childrenBox
absolutex, y, width, heightBox with Modifier.offset(x.dp, y.dp)
leaftype: "text"Text with TextStyle
leaftype: "rectangle" with fillBox(Modifier.background(Color(...)))

सभी node kinds एक spacing property (children के बीच gap) और एक padding object (top/right/bottom/left) carry कर सकते हैं। दोनों token keys reference करते हैं।

Token mapping विस्तार में

एक tokens.json file इस तरह दिखती है:

{
  "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 }
  }
}

Mapping rules:

Token keys जानबूझकर opaque हैं (color के लिए hex strings, spacing के लिए numeric strings) ताकि वे model को किसी particular naming convention की ओर bias न करें। आपका Compose theme उन्हें semantic names पर alias कर सकता है — colorPrimary, spacingMd — IR से independently।

एक real उदाहरण: home screen JSON से Composable तक

यहां एक simplified home screen IR है। एक header leaf और एक card list के साथ vertical stack:

{
  "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"
            }
          ]
        }
      ]
    }
  ]
}

Corresponding Composable — एक agent को इस IR से क्या produce करना चाहिए:

@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)
                    )
                )
            }
        }
    }
}

Composable की हर value IR में एक token key तक trace होती है। कुछ भी hardcoded नहीं है — 16.dp spacing.16 से आता है, 24.dp spacing.24 से, Color(0xFF7F5CFE) color.7f5cfe से।

String refs — stringResource mapping

IR में हर text node dot-notation key के साथ एक stringRef carry करता है। strings.json file keys को display values और fallbacks से map करती है:

{
  "home.title": { "value": "Good morning", "fallback": "Good morning" },
  "home.card.label": { "value": "Today's summary", "fallback": "Summary" }
}

Dot-notation dots को underscores से replace करके Android string resource IDs पर map होता है: home.titleR.string.home_titlefallback field वह है जिसे आप literal string के रूप में hardcode करते हैं अगर resource अभी strings.xml में exist नहीं करता:

text = stringResource(R.string.home_title, "Good morning")

Agent को हमेशा fallback field use करने के लिए कहें — empty string नहीं — ताकि strings.xml populate होने से पहले development के दौरान screen readable हो।

Absolute positioning

kind: "absolute" वाले Nodes Figma coordinates directly use करते हैं। ये overlapping elements या specific positions पर anchored elements वाले designs में appear होते हैं। Compose mapping parent के रूप में Box और children पर Modifier.offset use करता है:

// 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)
    ) {
        // children
    }
}

Absolute positioning well-structured Figma files में uncommon है (अधिकांश modern files auto-layout use करते हैं)। जब आप इसे देखें, check करें कि design intent truly absolute है या designer ने बस auto-layout constraints apply नहीं किए — IR intent infer नहीं कर सकता।

ईमानदार gaps

Bundle common case को अच्छी तरह cover करता है। कुछ चीज़ें जो यह नहीं करता:

ये gaps explicit हैं — वे _meta.json warnings और CONTEXT.md में दिखते हैं। Agent उनके through silently guess नहीं करता।

main figmascope app से context bundle export करें, फिर इसे Claude Code या Cursor के साथ IR से directly Composables implement करने के लिए use करें। कोई screenshot guessing नहीं, कोई hardcoded values नहीं।