---
name: figma-styles-workflow
description: How to auto-generate server-side style JSON for an SDUI page from Figma pins + Figma node API
metadata:
  type: reference
---

# Figma → server-styles workflow

When wiring an SDUI page from hardcoded constants to
`parseTextStyle / parseBoxDecoration` lookups against
`view.styles[<key>]`, the **real values** for each key come from
Nusrat's WordPress admin form, not from Figma. But Figma is the
authoritative spec — every Nusrat pin labels which element uses which
`<key>_decoration`. We can extract the actual rendered values off the
Figma node tree and produce a reference JSON Nusrat can paste in.

## Inputs

- **Figma file key**: `MtDxxWCRgzanTKcV6qG6Nu` ("Fluent → Appza")
- **API token**: `FIGMA_TOKEN` env var (lives in `~/.zshrc`; current
  Claude shells don't auto-load it — pass explicitly if needed)
- **Parent frame node_id**: one per page (e.g. create-post-page is
  `1412:2600`, edit-profile was `1412:2180`, chat-details was `1412:2510`,
  profile-page-details is `1408:2018` — hosts both `FCommunity_BasicProfile`
  on the left (x < ~400) and `FCommunity_EditProfileButton` on the right
  (x > ~400) of the same canvas)

## Steps

1. `curl -s -H "X-Figma-Token: $FIGMA_TOKEN" "https://api.figma.com/v1/files/MtDxxWCRgzanTKcV6qG6Nu/comments" > /tmp/figma_comments.json`
2. `curl -s -H "X-Figma-Token: $FIGMA_TOKEN" "https://api.figma.com/v1/files/MtDxxWCRgzanTKcV6qG6Nu/nodes?ids=<parent_node_id>&depth=4" > /tmp/figma_<page>.json`
3. Filter comments where `client_meta.node_id == <parent_node_id>` and `user.handle == 'Nusrat Jahan'`. Each "X decoration" pin names a server key for the element at the pin's `node_offset`.
4. Walk the node tree, build `nbi = {n['id']: n for n in walk(root)}`.
5. For each pin, identify the corresponding visual TEXT/FRAME/ELLIPSE/VECTOR node — usually by content (e.g. "Title (optional)" → hint_text_decoration) or by offset proximity. Hand-curate for accuracy.
6. Extract:
   - TEXT → `style.fontFamily/fontSize/fontWeight/letterSpacing/lineHeightPx` + `fills[0].color` → server schema `font_family/font_size/font_weight/letter_spacing/text_height/color`
   - FRAME → `fills[0]/strokes[0]/strokeWeight/cornerRadius` → server schema `background_color/border_color/border_width/border_radius` + manual margin/padding
   - ELLIPSE (avatar) → `fills[0]` + `absoluteBoundingBox` → `color/width/height/shape:'circle'/boxfit/radius/alignment`
   - VECTOR (icon) → `strokes[0].color/strokeWeight` + bbox dims → `color/size/weight`
7. Output JSON keyed by `class_type` → key → value map.

Example script: `/tmp/build_create_post_styles.py` (one-off; should be promoted to `scripts/figma-styles.py` if reused).

## Color conversion

Figma uses linear RGBA in [0,1]. Server expects `0xAARRGGBB` hex.

```python
def rgba_hex(c):
    r, g, b = (int(round(c[k]*255)) for k in 'rgb')
    a = int(round(c.get('a', 1.0) * 255))
    return f'0x{a:02x}{r:02x}{g:02x}{b:02x}'
```

## Font weight conversion

Figma uses numeric (400/500/700). Server expects string names.

```python
fw = {400:'regular',500:'medium',600:'semibold',700:'bold'}.get(n, 'regular')
```

## Text height conversion

Figma ships `lineHeightPx` (absolute). Server expects multiplier.

```python
text_height = round(lineHeightPx / fontSize, 2)
```

## Caveats

- **Pin label `"Text decoration"` (no prefix) is the body input style** — but Figma usually pins it to the **placeholder** element (e.g. "Write something here..."), so the extracted color is gray. Nusrat needs to manually set a darker color for the typed text after pasting.
- **Server's `text_decoration.text_decoration` field is a separate CSS-like enum** (`none/underline/lineThrough`); the create-post placeholder ships `'lineThrough'` which would strike typed text. See [[create-post-styles-pending-nusrat]].
- **`avatar_decoration.width/height` placeholder is `'0.3'`** — fractional pixels. Always sanity-guard `>= 1` in client code; the Figma value is the correct width (e.g. `40`).
- **`selected_option_decoration` / `un_selected_option_decoration`** have no visual element in static mockups (dropdown overlay states) — Nusrat fills these from the dropdown spec, not Figma.
- **`general_decoration` is page-scaffold, never per-card** — ignore when extracting.
