# Figma Plugin (`@cloudflare/kumo-figma`)
Generates production-quality Figma components from `component-registry.json`. Destructive sync: purges and recreates all components per run.
**Parent:** See [root AGENTS.md](../../AGENTS.md) for monorepo context.
## STRUCTURE
```
kumo-figma/
├── src/
│ ├── code.ts # Plugin entry: GENERATORS array, page management
│ ├── ui.html # Plugin UI
│ ├── manifest.json # Figma manifest (main: "code.js")
│ ├── build.sh # Build script: codegen → esbuild (ES2017, IIFE)
│ ├── generators/ # → see src/generators/AGENTS.md
│ │ ├── shared.ts # ALL constants + utilities (~1544 lines, critical)
│ │ ├── icon-utils.ts # Icon creation, placeholder, color binding (308 lines)
│ │ ├── _test-utils.ts # Shared test assertions
│ │ ├── drift-detection.test.ts # Meta-test: registry ↔ generator sync (1733 lines)
│ │ └── {component}.ts # 37 component generators
│ ├── parsers/
│ │ ├── tailwind-to-figma.ts # Core: Tailwind classes → Figma values
│ │ ├── opacity-extractor.ts # `bg-kumo-brand/70` → opacity data
│ │ ├── component-registry.ts # Type-safe registry wrapper
│ │ └── tailwind-theme-parser.ts # Parses tailwindcss/theme.css (test-only)
│ └── generated/ # BUILD OUTPUT (gitignored): theme-data.json, etc.
├── scripts/
│ ├── sync-tokens-to-figma.ts # CSS → Figma Variables API (unidirectional)
│ ├── figma-api.ts # Low-level Figma REST client (723 lines)
│ ├── color-utils.ts # oklch → sRGB conversion (uses culori)
│ └── maybe-sync.ts # Conditional sync gate (skips if no FIGMA_TOKEN)
└── vitest.config.ts # Node env (no DOM)
```
## WHERE TO LOOK
| Task | Location | Notes |
| ------------------------ | ---------------------------------------------------- | ---------------------------------- |
| Add generator | `src/generators/` + register in `code.ts` GENERATORS | Also update drift-detection |
| Centralized constants | `src/generators/shared.ts` | ALL magic numbers must live here |
| Icon utilities | `src/generators/icon-utils.ts` | createIconInstance, bindIconColor |
| Tailwind → Figma parsing | `src/parsers/tailwind-to-figma.ts` | Scale lookups from theme-data.json |
| Token sync to Figma | `scripts/sync-tokens-to-figma.ts` | Requires FIGMA_TOKEN |
| Drift detection | `src/generators/drift-detection.test.ts` | Meta-test enforcing sync |
## CONVENTIONS
### Build Pipeline (Sequential, Order Matters)
```bash
pnpm build =
1. sync:maybe # Sync tokens if FIGMA_TOKEN present
2. build:data = # 4 codegen steps:
a. tsx build-theme-data.ts → generated/theme-data.json
b. tsx build-loader-data.ts → generated/loader-data.json
c. tsx build-phosphor-icons.ts → generated/phosphor-icons.json
d. tsx build-figma-variables.ts → generated/figma-variables.json
3. build:plugin = # esbuild → src/code.js (IIFE, ES2017)
```
### Testing Philosophy
- **Test structure, NOT values**: `"DO NOT test specific colors, sizes, or variant names"`
- Tests validate against registry as source of truth
- `_test-utils.ts`: `expectValidRegistryProp()`, `expectAllClassesParsable()`, `expectValidParsedTypes()`
- Drift detection enforces: every registry component has generator, no magic numbers
## ANTI-PATTERNS
| Pattern | Why | Instead |
| ------------------------------------------ | ----------------------- | ------------------------------------ |
| Hardcoded `SECTION_PADDING`, `SECTION_GAP` | Drift detection fails | Import from `shared.ts` |
| Hardcoded shadow effects | Test enforcement | Import `SHADOWS` from `shared.ts` |
| Hardcoded `opacity = 0.5` | Test enforcement | Use `OPACITY.disabled` |
| Hardcoded RGB `{ r: 0.5, ... }` | Test enforcement | Use `COLORS` from `shared.ts` |
| `.toBe(16)` for font sizes in tests | Fragile assertions | Use `FONT_SIZE.*` or registry values |
| `.toBe(600)` for font weights | Fragile assertions | Use `FALLBACK_VALUES.fontWeight.*` |
| Redeclaring constants from shared.ts | Drift detection catches | Always import |
| `??` operator | ES2017 doesn't support | Use `if (x === undefined)` pattern |
## NOTES
- **`generated/` is gitignored**: Run `pnpm build:data` after clone/branch switch. Tests fail without it.
- **ES2017 target**: Figma runtime constraint. Avoid `??`, output is IIFE not ESM.
- **`code.js` lives in `src/`**: Not `dist/`. Figma reads `manifest.json` which points to `code.js` in same dir.
- **Two color conversion paths**: `build-figma-variables.ts` (manual oklch→RGB), `scripts/color-utils.ts` (culori). Manual one is less accurate.
- **Opacity variables created at runtime**: `getOrCreateVariableWithOpacity("color-kumo-info/20")` alpha-blends against white/black on the fly.
- **Component name mapping** in drift-detection: `DropdownMenu→dropdown`, `Toasty→toast`, `Switch.Group→switch`
- **`VAR_NAMES`** in shared.ts has legacy aliases (both `color.surface` and `color.base` map to same variable)
- **Font**: Inter is required (default Figma font). `createTextNode()` handles async loading.
- **26 `@ts-ignore` instances**: All for `figma.combineAsVariants()` type mismatch at runtimecloudflare/kumo
Publicmirrored fromhttps://github.com/cloudflare/kumoAvailable
packages/kumo-figma/AGENTS.md
92lines · modepreview