Files
galaxy/resources/bundled/mcp_skills/figma/figma-generate-library/references/discovery-phase.md
T

19 KiB
Raw Blame History

Part of the figma-generate-library skill.

Discovery Phase Reference

This document covers everything needed for Phase 0 of a design system build: analyzing the codebase for tokens, inspecting the Figma file for existing conventions, searching subscribed libraries, building the plan, and resolving conflicts before any write operations begin.


1. Codebase Analysis — Finding Token Sources

Search Priority Order

Look for token sources in this order. Stop as soon as you find a definitive source; multiple formats can coexist:

  1. Design token files: *.tokens.json, tokens/*.json, src/tokens/**
  2. CSS variable files: variables.css, tokens.css, theme.css, global.css
  3. Tailwind config: tailwind.config.js, tailwind.config.ts
  4. CSS-in-JS theme objects: theme.ts, createTheme, ThemeProvider
  5. Platform-specific: iOS Asset catalogs (.xcassets), Android themes.xml, colors.xml

CSS Custom Properties (Most Common for Web)

What to search for:

:root { ... }
@theme { ... }          ← Tailwind v4
--color-*, --spacing-*, --radius-*, --shadow-*, --font-*

Pattern: /--[\w-]+:\s*[^;]+/g

Common file locations: src/styles/tokens.css, src/styles/variables.css, src/theme/*.css

Extraction and naming translation:

CSS Property Figma Variable Name Figma Type WEB Code Syntax
--color-bg-primary: #fff color/bg/primary COLOR var(--color-bg-primary)
--color-text-secondary: #757575 color/text/secondary COLOR var(--color-text-secondary)
--spacing-sm: 8px spacing/sm FLOAT var(--spacing-sm)
--radius-md: 8px radius/md FLOAT var(--radius-md)
--font-body: "Inter" typography/body/font-family STRING var(--font-body)

Naming rule: Replace hyphens with slashes at category boundaries. Keep hyphens within the final path segment: --color-bg-primarycolor/bg/primary, but --color-bg-primary-hovercolor/bg/primary-hover.

Always store the original CSS variable name as the code syntax value — never derive it from the Figma variable name. If the codebase uses --sds-color-background-brand-default, use exactly that string in setVariableCodeSyntax('WEB', '--sds-color-background-brand-default').

Tailwind Configuration

What to look for in tailwind.config.js or tailwind.config.ts:

// theme.extend.colors → Figma color variables
{ primary: { DEFAULT: '#3366FF', light: '#6699FF', dark: '#0033CC' } }
// → color/primary/default, color/primary/light, color/primary/dark

// theme.extend.spacing → Figma FLOAT variables
{ 'xs': '4px', 'sm': '8px', 'md': '16px' }
// → spacing/xs = 4, spacing/sm = 8, spacing/md = 16

// theme.extend.borderRadius → Figma FLOAT variables
{ 'sm': '4px', 'md': '8px', 'lg': '16px' }
// → radius/sm = 4, radius/md = 8, radius/lg = 16

Tailwind utility class names (bg-blue-500, p-4) are not tokens — extract values from the config object, not the class names.

Design Token Community Group (DTCG) Format

Pattern: *.tokens.json or tokens/*.json. Find source files, not generated outputs from Style Dictionary or Tokens Studio.

{
  "color": {
    "bg": {
      "primary": { "$type": "color", "$value": "#ffffff" },
      "secondary": { "$type": "color", "$value": "#f5f5f5" }
    }
  },
  "spacing": {
    "sm": { "$type": "dimension", "$value": "8px" }
  }
}

Nested keys map to slash-separated Figma names: color.bg.primarycolor/bg/primary.

CSS-in-JS / Theme Objects

What to search for: createTheme, ThemeProvider, theme = {}, styled-components, Emotion, Stitches, vanilla-extract

// theme.colors.bg.primary → Figma variable: color/bg/primary
// theme.spacing.sm        → Figma variable: spacing/sm
// Multiple theme objects (lightTheme, darkTheme) → modes in the same collection

iOS Token Sources

// Asset catalog colors in .xcassets/Colors.xcassets
// extension Color { static let bgPrimary = Color("bg-primary") }
// Look for traitCollection.userInterfaceStyle for dark mode detection

Android Token Sources

// res/values/colors.xml  <color name="primary">#3366FF</color>
// res/values-night/colors.xml  (dark mode overrides)
// MaterialTheme.colorScheme.primary in Compose
// val Primary = Color(0xFF3366FF)

Detecting Dark Mode

Platform Signal
Web (CSS) @media (prefers-color-scheme: dark), .dark { }, [data-theme="dark"]
Web (Tailwind) darkMode: 'class' or darkMode: 'media' in config
Web (JS) Separate darkTheme object alongside lightTheme
iOS Color(uiColor:) with traitCollection.userInterfaceStyle, dual-appearance asset catalog
Android themes.xml with Theme.*.Night, isSystemInDarkTheme() in Compose, values-night/ folder

Figma mapping: If dark mode exists → minimum 2 modes (Light/Dark) in the semantic color collection. Primitive collections stay single-mode.

Shadow/Elevation Extraction

Shadows cannot be Figma variables — they become Effect Styles.

/* Look for: box-shadow, --shadow-* */
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.10);
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.10);

CSS 0 4px 6px -1px rgba(0,0,0,0.1) → Figma:

{ type: "DROP_SHADOW", offset: {x:0, y:4}, radius: 6, spread: -1, color: {r:0, g:0, b:0, a:0.1} }

Typography Extraction

Code token Maps to
font-size: 16px FLOAT variable (scope FONT_SIZE) or Text Style fontSize
line-height: 1.5 Text Style lineHeight: {value: 24, unit: "PIXELS"}
font-weight: 600 Text Style fontName: {family: "Inter", style: "Semi Bold"}
letter-spacing: -0.02em Text Style letterSpacing: {value: -2, unit: "PERCENT"}
font-family: "Inter" STRING variable (scope FONT_FAMILY) or Text Style fontName.family

Composite text styles (all properties bundled) → Figma Text Styles. Individual properties → Figma variables with appropriate scopes.

Component Extraction

For each component, extract:

  1. Name → Figma component set name
  2. Union-type props → VARIANT properties
  3. String content props → TEXT properties
  4. Boolean props → BOOLEAN properties (and VARIANT State when combined with interaction states)
  5. Child/slot props → INSTANCE_SWAP properties
// React example:
interface ButtonProps {
  size: 'sm' | 'md' | 'lg';          // → VARIANT: Size = sm|md|lg
  variant: 'primary' | 'secondary';   // → VARIANT: Style = primary|secondary
  disabled?: boolean;                  // → VARIANT: State (combine: default|hover|pressed|disabled)
  label: string;                       // → TEXT: Label
  icon?: ReactNode;                    // → INSTANCE_SWAP: Icon + BOOLEAN: Show Icon
}
// → Component Set "Button", variant count: 3 sizes × 2 styles × 4 states = 24

2. Figma File Inspection

Run these use_figma snippets at the start of every build. All are read-only and safe to run before any user checkpoint.

List All Pages

const pages = figma.root.children.map((p, i) => ({
  index: i,
  name: p.name,
  id: p.id,
  childCount: p.children.length
}));
return { pages };

Interpret: note page names for naming convention (are they PascalCase? sentence case?), count separator pages (---), identify existing component pages vs foundations pages.

List Variable Collections With Modes

const collections = await figma.variables.getLocalVariableCollectionsAsync();
const result = collections.map(c => ({
  id: c.id,
  name: c.name,
  modes: c.modes,                    // [{modeId, name}, ...]
  variableCount: c.variableIds.length,
  defaultModeId: c.defaultModeId
}));
return { collections: result };

Interpret: identify existing primitive/semantic split, note mode names (do they use "Light/Dark" or "SDS Light/SDS Dark"?), count variables to understand scope.

List Variables in a Collection (with names, types, scopes, and sample values)

const collections = await figma.variables.getLocalVariableCollectionsAsync();
const targetName = "Color"; // change to the collection you want to inspect
const coll = collections.find(c => c.name === targetName);
if (!coll) { return { error: `Collection "${targetName}" not found` }; }

const allVars = await figma.variables.getLocalVariablesAsync();
const vars = allVars.filter(v => v.variableCollectionId === coll.id);

const result = vars.map(v => ({
  id: v.id,
  name: v.name,
  resolvedType: v.resolvedType,
  scopes: v.scopes,
  codeSyntax: v.codeSyntax,
  // First mode value only, for a sample
  sampleValue: v.valuesByMode[coll.defaultModeId]
}));

return { collection: coll.name, variableCount: result.length, variables: result };

Interpret: check if variables use ALL_SCOPES (bad), check naming convention (slash-separated hierarchy?), check if code syntax is set, identify alias chains.

List Component Sets with Properties

await figma.setCurrentPageAsync(figma.currentPage); // ensures page context
const componentSets = figma.currentPage.findAll(n => n.type === 'COMPONENT_SET');
const result = componentSets.map(cs => ({
  id: cs.id,
  name: cs.name,
  variantCount: cs.children.length,
  properties: Object.entries(cs.componentPropertyDefinitions).map(([key, def]) => ({
    name: key,
    type: def.type,
    variantOptions: def.variantOptions || null,
    defaultValue: def.defaultValue
  }))
}));
return { componentSets: result, count: result.length };

Note: to search ALL pages, iterate figma.root.children and setCurrentPageAsync for each.

List All Styles

const [textStyles, effectStyles, paintStyles] = await Promise.all([
  figma.getLocalTextStylesAsync(),
  figma.getLocalEffectStylesAsync(),
  figma.getLocalPaintStylesAsync()
]);

return {
  textStyles: textStyles.map(s => ({ id: s.id, name: s.name, fontSize: s.fontSize, fontName: s.fontName })),
  effectStyles: effectStyles.map(s => ({ id: s.id, name: s.name, effectCount: s.effects.length })),
  paintStyles: paintStyles.map(s => ({ id: s.id, name: s.name })),
  counts: { text: textStyles.length, effect: effectStyles.length, paint: paintStyles.length }
};

Check Naming Conventions on an Existing Component

// Replace with the node ID of an existing component to analyze
const node = await figma.getNodeByIdAsync("YOUR_NODE_ID");
if (!node) { return { error: "Node not found" }; }

// Check fills for variable bindings
const fillInfo = [];
if ('fills' in node && Array.isArray(node.fills)) {
  for (const fill of node.fills) {
    if (fill.type === 'SOLID' && fill.boundVariables?.color) {
      fillInfo.push({ type: 'variable_alias', id: fill.boundVariables.color.id });
    } else if (fill.type === 'SOLID') {
      fillInfo.push({ type: 'hardcoded', r: fill.color.r, g: fill.color.g, b: fill.color.b });
    }
  }
}

return {
  name: node.name,
  type: node.type,
  fills: fillInfo,
  sharedPluginData: node.getSharedPluginData('dsb', 'key') || null
};

3. Using search_design_system

What It Searches

search_design_system runs three parallel searches against subscribed design libraries for the given file:

  1. Components — published library components, searched by name/description via a recommendation engine (relevance-ranked, not exact match)
  2. Variables — design tokens (colors, spacing, etc.) across subscribed libraries
  3. Styles — paint styles, text styles, and effect styles

Only libraries the file has subscribed to are searched. If results are empty, the file may not be subscribed to any design system libraries.

Input

search_design_system({
  query: "button",              // required — text query
  fileKey: "abc123",            // required — your file key
  includeComponents: true,      // default true
  includeVariables: true,       // default true
  includeStyles: true           // default true
})

What It Returns

{
  "components": [
    {
      "name": "Button",
      "libraryName": "Design System",
      "assetType": "component_set",
      "componentKey": "abc123def",
      "description": "Primary action button"
    }
  ],
  "variables": [
    {
      "name": "colors/primary/500",
      "variableType": "COLOR",
      "variableSetKey": "set1key",
      "key": "var1key",
      "scopes": ["FRAME_FILL", "SHAPE_FILL"],
      "variableCollectionName": "Colors"
    }
  ],
  "styles": [
    {
      "name": "Heading/H1",
      "styleType": "TEXT",
      "key": "style1key"
    }
  ]
}

How to Interpret Results

Components: The componentKey can be used in use_figma to import the component:

const component = await figma.importComponentByKeyAsync("abc123def");
// or for component sets:
const componentSet = await figma.importComponentSetByKeyAsync("abc123def");

Variables: The variableSetKey is the collection key. The key is the variable key. Use these to understand what naming conventions are in use, and what tokens are available to alias from.

Styles: The key is usable with figma.importStyleByKeyAsync(key) to import into the current file.

  • Phase 0, step 0c: Search broadly (query: "button", query: "color", query: "spacing") before planning anything. This establishes the reuse baseline.
  • Immediately before each component creation: Search for the specific component name before writing any use_figma creation code.

Reuse decision:

Condition Decision
Found component with matching variant API, same token model Import and reuse
Found component but wrong variant properties or hardcoded values Rebuild
Found component that matches visually but API is incompatible Wrap: nest as instance inside a new wrapper component

4. Building the Plan

After codebase analysis and Figma inspection, produce a mapping table and present it to the user.

Token → Variable Mapping Table

For each token found in code, record:

Code Token CSS Name Raw Value Figma Collection Figma Variable Name Figma Type Mode(s)
theme.colors.blue[500] --color-blue-500 #3B82F6 Primitives blue/500 COLOR Value
theme.colors.bg.primary --color-bg-primary (light: blue/50, dark: gray/900) Color color/bg/primary COLOR Light, Dark
theme.spacing.sm --spacing-sm 8px Spacing spacing/sm FLOAT Value
theme.radii.md --radius-md 8px Spacing radius/md FLOAT Value
theme.shadows.md --shadow-md 0 4px 6px rgba(0,0,0,0.1) Effect Style

Component → Component Set Mapping Table

Code Component Props → Variant Axes Variant Count Figma Page Reuse?
Button size (sm/md/lg) × variant (primary/secondary) × state (default/hover/disabled) 18 Buttons Search first
Avatar size (sm/md/lg) × type (image/initials/icon) 9 Avatars Search first

Gap Identification

Compare what was found in code vs what already exists in Figma:

  • New: tokens or components that exist in code but not in Figma → create
  • Existing: tokens or components already in Figma with matching names → verify scope/code-syntax, skip or update
  • Conflict: same name, different value → escalate to user (see section 5)
  • Figma-only: exists in Figma but not in code → flag for user, likely skip

User-Facing Checkpoint Message Template

Present this message before proceeding. Never begin Phase 1 without explicit user approval.

Here's what I found and what I plan to build:

CODEBASE ANALYSIS
  Colors: {N} primitives ({families}), {M} semantic tokens ({light/dark if applicable})
  Spacing: {N} tokens ({range})
  Typography: {N} text styles, {M} individual scale tokens
  Shadows: {N} levels → will become Effect Styles
  Components: {list of component names}

EXISTING FIGMA FILE
  Collections: {N} existing collections
  Variables: {M} existing variables
  Styles: {K} text, {L} effect, {J} paint styles
  Components: {list}

PLAN
  New collections: {list with mode counts}
  New variables: ~{N} ({breakdown by collection})
  New styles: {N} text, {M} effect
  New components: {list}
  Libraries to search before each component: {list}

GAPS / CONFLICTS NEEDING DECISIONS
  ⚠ {conflict description} — Code says X, Figma already has Y. Which wins?

WHAT I WON'T BUILD (and why)
  - {item}: already exists in Figma with matching conventions
  - {item}: not supported as a Figma variable (e.g. z-index, animation timing)

Shall I proceed?

5. Conflict Resolution — When Code and Figma Disagree

When the same token/component exists in both code and Figma but with different values, names, or structures, always ask the user. Never silently pick one.

Decision Framework

Scenario Ask the user
Same CSS name, different hex value (e.g., --color-accent is #3366FF in code but #5B7FFF in Figma) "Code says #3366FF, Figma currently has #5B7FFF for color/accent/default. Which is correct?"
Same component name, different variant axes (code has size: sm/md/lg, Figma has Size: Small/Large) "Code uses 3 sizes (sm/md/lg) but Figma has 2 (Small/Large). Should I add Medium, or rename to match code?"
Code has a semantic token with no primitive layer; Figma already has a fully-layered system "The codebase uses a flat single-layer token model. The Figma file uses a primitive/semantic split. Should I match the Figma architecture or the code architecture?"
Figma variable exists but has ALL_SCOPES (incorrect per best practice) "I found color/bg/primary already exists but it uses ALL_SCOPES. I recommend changing it to FRAME_FILL, SHAPE_FILL. May I update the scope?"
Code uses camelCase (backgroundColor), Figma uses slash-separated (color/bg/default) "The codebase uses camelCase naming. The Figma file uses slash-separated hierarchy. For new variables, should I use slash-separated (Figma standard) and map via code syntax?"

Code Wins

Default to code as the source of truth for:

  • Hex values (code is the live production value)
  • Token naming (the CSS variable names become code syntax)
  • Mode values (light/dark split comes from code)

Figma Wins

Default to Figma as the source of truth for:

  • Collection architecture (if a well-structured system already exists, extend it rather than replace it)
  • Variable naming hierarchy (if designers are already using the system with specific names)
  • Page structure (match the existing page organization pattern)

Neither: Negotiate

When neither is clearly correct, propose a resolution and ask:

"I'd suggest [option]. This way both the code token name and the Figma naming convention are preserved. Does that work?"