Target ekspor default figmascope adalah Jetpack Compose. Ini bukan hal yang sembarangan — model layout Compose (Column, Row, Box, Modifier) sangat sesuai dengan jenis node IR (stack, overlay, absolute, leaf). Stack vertikal di Figma adalah Column di Compose. Terjemahannya bersifat mekanis, yang membuatnya cocok untuk codegen yang digerakkan agen.

Panduan ini mencakup pemetaan IR-ke-Compose secara detail, menunjukkan fragmen JSON nyata beserta Composable yang sesuai, dan menjelaskan lapisan pemetaan token.

Mengapa Compose adalah target default

Tiga alasan struktural:

  1. Auto-layout ↔ Column/Row. Frame auto-layout Figma (mayoritas besar desain Figma modern) diekspor sebagai node kind: "stack". Node stack memiliki properti axisvertical dipetakan ke Column, horizontal dipetakan ke Row. Ini adalah pemetaan 1:1 tanpa langkah interpretasi.
  2. Token spacing ↔ nilai dp. Compose menggunakan Dp untuk semua dimensi layout. Nilai token dalam tokens.json adalah bilangan bulat tanpa satuan (mis. spacing.16 = 16) yang langsung dipetakan ke 16.dp. Tanpa konversi, tanpa skala.
  3. Token warna ↔ Color composable. Nilai hex Figma dalam tokens.json dipetakan ke Color(0xFFrrggbb) dengan satu transformasi. Kunci token menjadi nama variabel semantik dalam tema Anda.

Jenis node IR dan pemetaan Compose-nya

Jenis IRPropertiPrimitif Compose
stackaxis: "vertical"Column
stackaxis: "horizontal"Row
overlaychildren berlapisBox
absolutex, y, width, heightBox dengan Modifier.offset(x.dp, y.dp)
leaftype: "text"Text dengan TextStyle
leaftype: "rectangle" dengan fillBox(Modifier.background(Color(...)))

Semua jenis node dapat membawa properti spacing (jarak antar children) dan objek padding (atas/kanan/bawah/kiri). Keduanya merujuk pada kunci token.

Pemetaan token secara detail

File tokens.json terlihat seperti ini:

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

Aturan pemetaan:

Kunci token sengaja dibuat buram (string hex untuk warna, string numerik untuk spacing) agar tidak membiaskan model ke konvensi penamaan tertentu. Tema Compose Anda dapat mengaliaskannya ke nama semantik — colorPrimary, spacingMd — secara independen dari IR.

Contoh nyata: JSON home screen ke Composable

Berikut adalah IR home screen yang disederhanakan. Stack vertikal dengan leaf header dan daftar kartu:

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

Composable yang sesuai — apa yang harus dihasilkan agen dari IR ini:

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

Setiap nilai dalam Composable dapat ditelusuri ke kunci token dalam IR. Tidak ada yang hardcoded — 16.dp berasal dari spacing.16, 24.dp dari spacing.24, Color(0xFF7F5CFE) dari color.7f5cfe.

String refs — pemetaan stringResource

Setiap node teks dalam IR membawa stringRef dengan kunci notasi titik. File strings.json memetakan kunci ke nilai tampilan dan fallback:

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

Notasi titik dipetakan ke ID resource string Android dengan titik diganti garis bawah: home.titleR.string.home_title. Field fallback adalah yang Anda hardcode sebagai string literal jika resource belum ada di strings.xml:

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

Beri tahu agen untuk selalu menggunakan field fallback — bukan string kosong — agar layar dapat terbaca selama pengembangan sebelum strings.xml diisi.

Positioning absolut

Node dengan kind: "absolute" menggunakan koordinat Figma secara langsung. Ini muncul dalam desain dengan elemen yang saling tumpang tindih atau elemen yang ditambatkan ke posisi tertentu. Pemetaan Compose menggunakan Box sebagai parent dan Modifier.offset pada children:

// 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
    }
}

Positioning absolut jarang terjadi dalam file Figma yang terstruktur dengan baik (sebagian besar file modern menggunakan auto-layout). Ketika Anda menemukannya, periksa apakah maksud desain benar-benar absolut atau apakah desainer hanya tidak menerapkan constraint auto-layout — IR tidak dapat menyimpulkan maksud.

Keterbatasan yang jujur

Bundle ini menangani kasus umum dengan baik. Beberapa hal yang tidak ditangani:

Keterbatasan ini bersifat eksplisit — muncul di peringatan _meta.json dan CONTEXT.md. Agen tidak menebak-nebak melaluinya secara diam-diam.

Ekspor context bundle dari aplikasi figmascope utama, lalu gunakan bersama Claude Code atau Cursor untuk mengimplementasikan Composable langsung dari IR. Tanpa tebak-tebakan screenshot, tanpa nilai hardcoded.