Claude Code Agent Development
anthropics/claude-plugins-official
Explains how to write agents for Claude Code plugins: the markdown file with YAML frontmatter, trigger descriptions, model and color settings, and system prompt design.
Subagent for figma-from-code Phase 4. An agent skill from bitovi/ai-enablement-prompts.
$ npx skills add bitovi/ai-enablement-prompts --skill figma-from-code-build-screens -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install bitovi/ai-enablement-prompts figma-from-code-build-screens --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/bitovi/ai-enablement-prompts.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/figma-from-code/skills/figma-from-code/8-build-screens .claude/skills/figma-from-code-build-screens && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "figma-from-code-build-screens" agent skill from https://github.com/bitovi/ai-enablement-prompts/tree/main/plugins/figma-from-code/skills/figma-from-code/8-build-screens into .claude/skills/figma-from-code-build-screens/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "figma-from-code-build-screens", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/bitovi/ai-enablement-prompts/tree/main/plugins/figma-from-code/skills/figma-from-code/8-build-screensType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add bitovi/ai-enablement-prompts --skill figma-from-code-build-screens -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install bitovi/ai-enablement-prompts figma-from-code-build-screens --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/bitovi/ai-enablement-prompts.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/figma-from-code/skills/figma-from-code/8-build-screens .agents/skills/figma-from-code-build-screens && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "figma-from-code-build-screens" agent skill from https://github.com/bitovi/ai-enablement-prompts/tree/main/plugins/figma-from-code/skills/figma-from-code/8-build-screens into .agents/skills/figma-from-code-build-screens/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "figma-from-code-build-screens", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add bitovi/ai-enablement-prompts --skill figma-from-code-build-screens -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install bitovi/ai-enablement-prompts figma-from-code-build-screens --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/bitovi/ai-enablement-prompts.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/figma-from-code/skills/figma-from-code/8-build-screens .cursor/skills/figma-from-code-build-screens && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "figma-from-code-build-screens" agent skill from https://github.com/bitovi/ai-enablement-prompts/tree/main/plugins/figma-from-code/skills/figma-from-code/8-build-screens into .cursor/skills/figma-from-code-build-screens/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "figma-from-code-build-screens", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/bitovi/ai-enablement-prompts.git --path plugins/figma-from-code/skills/figma-from-code/8-build-screens--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add bitovi/ai-enablement-prompts --skill figma-from-code-build-screens -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install bitovi/ai-enablement-prompts figma-from-code-build-screens --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/bitovi/ai-enablement-prompts.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/figma-from-code/skills/figma-from-code/8-build-screens .gemini/skills/figma-from-code-build-screens && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "figma-from-code-build-screens" agent skill from https://github.com/bitovi/ai-enablement-prompts/tree/main/plugins/figma-from-code/skills/figma-from-code/8-build-screens into .gemini/skills/figma-from-code-build-screens/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "figma-from-code-build-screens", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install bitovi/ai-enablement-prompts figma-from-code-build-screensInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add bitovi/ai-enablement-prompts --skill figma-from-code-build-screens -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/bitovi/ai-enablement-prompts.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/figma-from-code/skills/figma-from-code/8-build-screens .github/skills/figma-from-code-build-screens && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "figma-from-code-build-screens" agent skill from https://github.com/bitovi/ai-enablement-prompts/tree/main/plugins/figma-from-code/skills/figma-from-code/8-build-screens into .github/skills/figma-from-code-build-screens/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "figma-from-code-build-screens", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add bitovi/ai-enablement-prompts --skill figma-from-code-build-screens -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install bitovi/ai-enablement-prompts figma-from-code-build-screens --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/bitovi/ai-enablement-prompts.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/figma-from-code/skills/figma-from-code/8-build-screens .opencode/skills/figma-from-code-build-screens && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "figma-from-code-build-screens" agent skill from https://github.com/bitovi/ai-enablement-prompts/tree/main/plugins/figma-from-code/skills/figma-from-code/8-build-screens into .opencode/skills/figma-from-code-build-screens/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "figma-from-code-build-screens", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
figma-from-code-build-screensSubagent for figma-from-code Phase 4. An agent skill from bitovi/ai-enablement-prompts.
Figma From Code Build Screens is an agent skill from bitovi/ai-enablement-prompts. Subagent for figma-from-code Phase 4. Builds full-page screen frames in Figma by composing built component instances into 1440×900 layouts. Validates each screen against app screenshots and iterates up to 3 fix passes.
Its SKILL.md is about 11k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file (for example `README.md`).
It sits in Agent Workflows, covering Subagents. It works with Figma. The repository describes itself as: Prompts Bitovi uses for software development. The licence is MIT.
8 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit df229b1. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
nodecurlFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
figma.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Figma From Code Build Screens loads about 11k tokens when it runs. Until then it costs about 62 tokens; SKILL.md has 3,496 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from bitovi/ai-enablement-prompts at commit df229b1, republished under its MIT licence (© bitovi). 3,496 words, ~10,634 tokens.
.claude/skills/figma-from-code-build-screens/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.Builds full-page screen frames in Figma by composing built component instances into screenBodySize layouts (default 1440x900). Each screen assembles navigation, list panels, and detail/form panels using instances of components already created in Phase 3, then validates them visually against an app screenshot — iterating up to 3 fix passes to converge on visual fidelity.
All Figma MCP tools (
use_figma,get_screenshot, etc.) are available. This skill runs its entire workflow — analyze, build, screenshot, compare, fix — inline when dispatched by the orchestrator.
figma-from-code reaches Phase 4| Input | Description | Source |
|---|---|---|
screenName | PascalCase name (e.g., CasesPage, CreateCasePage) | Route → PascalCase conversion |
route | URL path on the dev server (e.g., /cases, /cases/new) | component-map.json → routes |
pageSourceFile | Path to the page's .tsx source | Component source discovery |
fileKey | Figma file key | State ledger or caller |
screensFrameId | Node ID of the Screens container to append into | state.json → figmaNodes |
appScreenshot | Path to the app screenshot PNG | .temp/figma-from-code/screenshots/screens/{name}/app.png |
textContent | Extracted text JSON from the live page | .temp/figma-from-code/screenshots/screens/{name}/text.json |
keyComponents | Top-level components rendered on the route + their descendants | component-map.json → tree |
builtComponents | Map of {componentName: nodeId} for all components built in Phase 3 | state.json → builtComponents |
preExistingScreens | Immutable snapshot of screen frames that existed in Figma BEFORE this orchestrator run started | state.json → preExistingScreens (Phase 0a snapshot) |
screenshotDir | Directory for saving Figma screenshots and diff artifacts | .temp/figma-from-code/screenshots/screens/{name}/ |
| Input | Description |
|---|---|
computedStyles | Resolved CSS values from computed-styles.json (produced by inspect-styles.js against the page root). Authoritative for colors, spacing, typography on screen chrome (the page's own elements, not its component children) |
screenBodySize | Body dimensions read from state.json → screenBodySize if the project uses a non-default screen size (default 1440x900) |
Config placeholders like
{pagesRoot}resolve fromstate.json → config(fields:devServerUrl,devServerStart,sourceDir,componentsRoot,pagesRoot,cssPath,tailwindConfigPath,iconLibrary,skillRoot).
Before doing any work that resolves to a node ID in preExistingScreens, stop. That node existed in Figma before this run; modifying it (rebuild, resize, delete + recreate, restructure) requires explicit user authorization.
Concretely:
screenName itself maps to a node in preExistingScreens and the caller's intent is to rebuild that node: write a result file with "status": "needs_authorization" and "preExistingTouched": ["<name>"], then return. Do not call use_figma to delete, replace, resize, or restyle the existing node. Building a fresh screen with the same name into the same screensFrameId is also a modification (creates a duplicate the orchestrator must reconcile) — don't do it without authorization.preExistingComponents is fine — that's reuse, not modification.preExistingScreens. If the comparison says you need to, escalate to the orchestrator instead.This rule overrides Steps 2–5 of the workflow when in conflict.
The orchestrator dispatches one subagent per screen. Each subagent runs the entire workflow (Steps 1–7) independently — analyze, build via use_figma, screenshot via get_screenshot, compare, fix loop, and return results.
The subagent writes its result to .temp/figma-from-code/build-results/screens/{screenName}.json. The orchestrator collects results after all subagents complete using collect-screen-results.js.
Screens can run in parallel — they only instantiate (not modify) already-built Phase 3 components.
The orchestrator dispatches one agent per screen. Send all agents in a single message so they run in parallel.
Build a Figma screen frame by composing built component instances, then validate visually.
Follow this skill's workflow (all 7 steps):
0. Verify all referenced components exist in builtComponents
1. FIRST study the app screenshot to understand what the default view shows,
THEN analyze the page source — only include components visible in the screenshot.
Exclude components behind conditional branches (ternaries, state guards, URL params)
and portal-rendered overlays (Dialog, Sheet, etc.) that aren't visible by default.
2. Build the screen in Figma via use_figma (`screenBodySize` frame, default 1440x900)
3. Screenshot the result via get_screenshot
4. Structural check (content must match), then sizing check, then pixel diff
5. If mismatch, diagnose and fix (up to 3 iterations)
6. Write figma-screen.json tracking file
7. Write results to .temp/figma-from-code/build-results/screens/{screenName}.json
Inputs:
- Screen name: {screenName}
- Route: {route}
- Page source file: {pageSourceFile}
- Figma file key: {fileKey}
- Screens frame node ID: {screensFrameId}
- App screenshot: .temp/figma-from-code/screenshots/screens/{screenName}/app.png
- Text content: .temp/figma-from-code/screenshots/screens/{screenName}/text.json
- Screenshot dir: .temp/figma-from-code/screenshots/screens/{screenName}/
- Built components (for instance reuse): .temp/figma-from-code/builtComponents.json
- Pre-existing screens (DO NOT MODIFY): {JSON.stringify(preExistingScreens)}
Read plugins/figma-from-code/skills/figma-from-code/8-build-screens/SKILL.md for the full workflow,
fixSizing() function, variant resolution, and common pitfalls.Step 0 Prereqs Verify all referenced components exist in builtComponents
Step 1 Analyze Study appScreenshot first, then read page source; filter out non-default conditional branches
Step 2 Build Create the screen frame in Figma via use_figma
Step 3 Screenshot Capture the Figma result via get_screenshot
Step 4 Compare Structural check + sizing sanity check + pixel diff against the app screenshot
Step 5 Fix Loop If mismatch, diagnose and fix (up to 3 iterations)
Step 6 Track Write figma-screen.json into the page folder
Step 7 Return Report result with node ID, match score, and any remaining issuesBefore building any screen, verify that every component referenced by the screen exists in builtComponents from state.json.
Identify the screen's key components from component-map.json → tree (the top-level components on that route and all their descendants). Check each one against builtComponents. Also check every icon imported by the page source (e.g., Icon/Check).
If any component or icon is missing from builtComponents:
STOP — do not proceed to Step 1. Return immediately with a rejection result:
{
"screenName": "CasesPage",
"status": "rejected",
"reason": "missing_components",
"missingComponents": ["CaseDetails", "MenuList"],
"missingIcons": ["Icon/Trash"],
"availableComponents": ["AppHeader", "Sidebar", "Button"]
}Write this to .temp/figma-from-code/build-results/screens/{screenName}.json so the orchestrator can see what's missing.
Standalone (no orchestrator) — if the caller is the user directly, surface the rejection in the conversation and ask how to proceed. Don't fall back to inlining the missing components, building stubs, or downgrading the build into "best effort" — those produce a different artifact than the skill is supposed to produce. The right options are: (a) build the missing components first via plugins/figma-from-code/skills/figma-from-code/7-build-component/SKILL.md, (b) abandon the screen, or (c) get explicit user authorization to deviate.
Only proceed to Step 1 if every required component and icon is confirmed present.
Before writing any use_figma code, analyze all inputs to plan the screen structure.
Pre-flight: dev server is required for Step 1f (live inspection). Step 1f — inspecting the rendered page in a browser via
inspect-styles.js— is the authoritative source for colors, spacing, and layout on the page chrome (the elements the page itself renders, outside of component children). Do not silently skip it. If you don't already have a dev server URL (from the orchestrator state ledger, project memory, or the caller's arguments), pause and ask the user for one before proceeding past Step 1. Only skip Step 1f if the user explicitly says no dev server is available.
Before reading source code, look at appScreenshot (app.png). This screenshot shows the ACTUAL default view of the page as rendered in the browser with no user interaction. It is the ground truth for what the screen should look like.
Study the screenshot and identify:
The source code analysis in Steps 1a–1b must MATCH what the screenshot shows, not the full component tree from the JSX. If the source code contains components behind conditional branches that are not visible in the screenshot, those components must be excluded from composition.
Read the page source file and determine:
flex-col), horizontal row (flex, flex-row), or a grid (grid)?screenBodySize (default 1440x900; read from state.json → screenBodySize). The outermost screen frame is primaryAxisSizingMode='FIXED' and counterAxisSizingMode='FIXED'. Capture this explicitly — Step 4a verifies it.gap-* classes on the page root map to itemSpacing. p-*, px-*, py-* map to padding.bg-* class on the page root. Resolve through CSS variables if needed (see Step 1g of plugins/figma-from-code/skills/figma-from-code/7-build-component/step-1-analyze.md for the full chain).Walk the page source JSX and list component references, but only include components visible in the default state (as shown in appScreenshot from Step 1-pre).
For each included component:
builtComponents[name] — that's the node ID to instantiate<Button variant="primary" size="lg">) — these resolve to a specific variant inside the component setclassName="w-full", className="flex-1") — these translate to layoutSizingHorizontal='FILL' etc. on the instance// Source has: <Sidebar className="w-64" />
// builtComponents has: { "Sidebar": "230:5" }
// → Instantiate Sidebar at fixed width 256 inside the page frameWhen walking the JSX, identify and handle conditional patterns:
Ternary expressions (condition ? <A /> : <B />): Determine which branch is visible when the page first loads with no user interaction and no URL params beyond the base route. Cross-reference with appScreenshot — only include the branch that matches.
// Example: {id ? <CaseDetails /> : <EmptyState />}
// If appScreenshot shows "No case selected" → include EmptyState, EXCLUDE CaseDetailsURL-param-gated components: For components guarded by useParams() values (e.g., id ? <Detail /> : <Fallback />), use the param-absent branch as default since the screen captures the base route.
State-hook-gated components: Components behind useState(false) or useState(null) guards are hidden by default — exclude them.
// Example: const [isOpen, setIsOpen] = useState(false);
// {isOpen && <Modal />} → EXCLUDE Modal from default screenLogical AND guards ({flag && <C />}): Exclude unless the guard's initial value is truthy.
DO NOT compose components from non-default conditional branches. Those belong in separate screen variants if the pipeline supports them.
Portal-rendered overlays — Dialog, Sheet, ConfirmationDialog, DropdownMenu, Popover, Drawer, AlertDialog, Tooltip, etc. — are NEVER visible in the default resting state. Exclude them from screen composition unless the appScreenshot explicitly shows them open.
These components are already handled by the component-level State axis in Phase 3 (see 7-build-component/step-1-analyze.md → "Conditional rendering of overlays"). Do not duplicate them in the screen frame.
If the page renders any Lucide icons directly (not via a child component), map each to its builtComponents entry (Icon/{Name}) and size from the className. See Step 1c of plugins/figma-from-code/skills/figma-from-code/7-build-component/step-1-analyze.md for the size mapping table.
Use textContent (from text.json) for any text the page itself renders (page titles, section headings, empty states). Never use generic placeholders. Text rendered inside a component instance is handled by that component's own master — don't try to override it from the screen.
Check whether screenName is in preExistingScreens. If yes — apply the Pre-Existing Screens Rule above and stop.
Before building, inspect the actual rendered page in the browser to capture computed styles on the page chrome. This provides ground-truth values for the page-level background, padding, and layout — more reliable than inferring from Tailwind classes alone.
Run inspect-styles.js against the page root selector on the dev server:
node {skillRoot}/scripts/inspect-styles.js \
"{devServerUrl}/{route}" \
--selector "[data-page='{ScreenName}'], main, #root > div" \
--output ".temp/figma-from-code/screenshots/screens/{ScreenName}/"This produces:
| File | Contents |
|---|---|
computed-styles.json | Resolved CSS properties on the page root (background, padding, layout direction, gap), the element's class list, and layoutContext (viewport, offsetWidth/Height) |
How to use the outputs:
Use exact padding and gap values to set Figma properties — these are the authoritative values for the page chrome. For colors, the computed RGB values are authoritative for what the color is, but not for how to apply it: reverse-match each one against the variable index first (node {skillRoot}/scripts/resolve-color.js 'rgb(...)' --context fill) and bind the matched variable (see §2e). Hardcode the RGB only when the match is "none".
Dev server is required — don't silently skip this step.
See Step 1g of plugins/figma-from-code/skills/figma-from-code/7-build-component/step-1-analyze.md for the full decision tree: orchestrator-dispatched vs standalone vs auto/non-interactive, how to derive selectors, and when escalation to the user is required.
// use_figma
const screensFrame = figma.getNodeById('{screensFrameId}');
screensFrame.layoutWrap = 'WRAP';
screensFrame.counterAxisSpacing = 80;
const screen = figma.createFrame();
screen.name = '{screenName}';
screen.resize(1440, 900); // or screenBodySize values
screen.layoutMode = '{VERTICAL or HORIZONTAL}'; // from page root direction
screen.primaryAxisSizingMode = 'FIXED';
screen.counterAxisSizingMode = 'FIXED';
screen.itemSpacing = { gapValue };
screen.paddingTop = { pt };
screen.paddingBottom = { pb };
screen.paddingLeft = { pl };
screen.paddingRight = { pr };
screen.fills = [{ type: 'SOLID', color: { ...pageBackground } }];
// Bind the background variable when §2e reverse-match found one (it almost always does):
// screen.setBoundVariable('fills', 0, await figma.variables.getVariableByIdAsync('{backgroundVariableId}'));
screen.clipsContent = true;
// ... add region frames and component instances (Step 2b) ...
screensFrame.appendChild(screen); // do NOT set x/y — wrap layout positions it
return JSON.stringify({ name: screen.name, id: screen.id });For each component identified in Step 1b:
const comp = figma.getNodeById(builtComponents['{ComponentName}']);
// If component is a COMPONENT_SET, resolve the target variant
let master = comp;
if (comp.type === 'COMPONENT_SET') {
const targetProps = { Variant: 'primary', Size: 'regular' }; // from source props
master =
comp.children.find((child) =>
Object.entries(targetProps).every(
([k, v]) => child.variantProperties?.[k]?.toLowerCase() === v.toLowerCase()
)
) ?? comp.children[0];
}
const instance = master.createInstance();
// Apply call-site sizing classes
if (callSiteHasWFull) instance.layoutSizingHorizontal = 'FILL';
if (callSiteHasFlex1) instance.layoutSizingHorizontal = 'FILL';
if (callSiteHasHFull) instance.layoutSizingVertical = 'FILL';
parent.appendChild(instance);When the page source nests multiple components inside a layout container (e.g., a sidebar + main content row), create a region frame:
const row = figma.createFrame();
row.layoutMode = 'HORIZONTAL';
row.primaryAxisSizingMode = 'FIXED';
row.counterAxisSizingMode = 'FIXED';
row.layoutSizingHorizontal = 'FILL';
row.layoutSizingVertical = 'FILL';
row.itemSpacing = 0;
row.fills = [];
// ... append child instances ...
screen.appendChild(row);Read
{skillRoot}/7-build-component/figma-utils.mdfor the canonicalfixSizing()definition and the Tailwind→Figma mapping table.
When the page root uses semantic colors (bg-background, bg-muted), resolve and bind the Figma variable — page chrome colors are exactly the colors that should stay coupled to tokens. The reverse-match-before-hardcode rule from 7-build-component/step-2-build.md §2e Step 0 applies to screens too:
computed-styles.json (Step 1f) via the lookup CLI:node {skillRoot}/scripts/resolve-color.js 'bg-background' --context fill
# or, from computed styles:
node {skillRoot}/scripts/resolve-color.js 'rgb(255, 255, 255)' --context fillmatch: "exact" or "tolerance", set the fill then bind: screen.setBoundVariable('fills', 0, await figma.variables.getVariableByIdAsync('{id}')).match: "none", hardcode the RGB from computed-styles.json.(If tailwindConfigPath is null in state, class-based lookups still work — the CLI falls back to stripping known prefixes and matching CSS variable names directly.)
get_screenshot(fileKey, screenFrameId)Save to {screenshotDir}/figma.png:
curl -sL "{image_url}" -o "{screenshotDir}/figma.png"Before running any automated comparison, visually inspect both app.png and figma.png side by side. Check whether the two images show the same content structure:
If the two images show fundamentally different content — not just styling differences, but different components or states entirely — flag as structural_mismatch. This indicates the composition in Step 1b included wrong conditional branches and the screen must be rebuilt, not patched.
A structural_mismatch overrides any pixel score. Do not accept a high match percentage as valid when the content is visibly different — structural similarity in shared chrome (headers, sidebars) can inflate pixel scores even when the main content area is completely wrong.
"comparison": {
"structuralCheck": {
"verdict": "pass" | "structural_mismatch",
"issues": ["Figma shows case detail view but app shows empty state 'No case selected'"]
}
}If structural_mismatch: enter Step 5 fix loop, but the fix is to re-run Step 1b with stricter screenshot-informed filtering, not to patch colors or spacing.
A pixel diff against app.png can pass even when the screen is built much smaller than 1440x900 — most commonly when the outermost frame collapsed to hug content. Run this check first; it is independent of the screenshot.
Inspect the built screen (use_figma) and read its top-level frame:
const node = figma.getNodeById('{screenNodeId}');
const built = {
w: Math.round(node.width),
h: Math.round(node.height),
primaryAxisSizingMode: node.primaryAxisSizingMode,
counterAxisSizingMode: node.counterAxisSizingMode,
layoutMode: node.layoutMode,
};Compare against the expected screen body size (default 1440x900, or screenBodySize from state):
| Check | Pass criteria | Flag if … |
|---|---|---|
| Width | built.w === expectedW ± 2px | Off by more than 2px |
| Height | built.h === expectedH ± 2px | Off by more than 2px |
| Sizing modes | primaryAxisSizingMode === 'FIXED' AND counterAxisSizingMode === 'FIXED' | Either is 'AUTO' |
| Layout mode | Set ('VERTICAL' or 'HORIZONTAL') | 'NONE' — children won't auto-layout |
| Top-level region count | Matches the page source structure (e.g., 2 for nav + body, 3 for header + sidebar row + footer) | Region count differs from source |
| Fill children | Any child whose call-site has flex-1 / w-full has layoutSizingHorizontal='FILL' | Source says fill, built says hug |
If any check fails, treat this as a size_mismatch discrepancy and feed it into Step 5 alongside (or before) the pixel diff results. Do not declare a match based on pixel score alone if the sizing check failed — pixel match against a too-small app.png is a false positive.
Record the sizing check result in the eventual result file:
"comparison": {
"sizingCheck": {
"verdict": "pass" | "fail",
"issues": ["counterAxisSizingMode='AUTO' (expected FIXED)", "built height 412 (expected 900)"],
"builtSize": {"w": 1440, "h": 412},
"expectedSize": {"w": 1440, "h": 900}
},
"matchPct": 95.26,
...
}Run the pixel diff comparison:
node {skillRoot}/scripts/compare.js \
"{screenshotDir}/app.png" \
"{screenshotDir}/figma.png" \
"{screenshotDir}/"This produces:
diff.png — red pixels mark differences, matching pixels dimmedcomparison.json — { matchPct, borderMatchPct, verdict, borderVerdict }Verdict thresholds (combined with Step 4a result):
matchPct >= 88% AND borderMatchPct >= 80% → match (done)matchPct 72-88% or borderMatchPct < 80% → minor_diff (needs fixing)matchPct < 72% → mismatch (needs fixing)Screen thresholds are slightly more lenient than component thresholds because screens contain many instances whose internal pixels are already validated at the component level — a small per-instance drift compounds across the page.
A passing pixel verdict alone is NOT enough — 4a must also pass. Otherwise the build is silently wrong-sized and the validation phase will reject it later.
If no app screenshot exists (appScreenshot is null), skip pixel comparison — but still run Step 4a. Report no_app_reference only if 4a also passes; otherwise report size_mismatch.
If the verdict is minor_diff, mismatch, or size_mismatch, enter the fix loop.
5a. Diagnose the discrepancy
Use all five inputs together to identify specific differences:
diff.png — red regions show exactly where pixels differapp.png — what the screen should look likefigma.png — what was actually built.tsx — Tailwind classes reveal intended valuesCross-reference to identify the exact Figma properties that need correction. Common screen-level discrepancy patterns:
| Symptom | Likely Cause | Fix |
|---|---|---|
| 4a failed (frame collapsed to hug) | Outermost frame has *SizingMode='AUTO' | node.primaryAxisSizingMode='FIXED'; node.counterAxisSizingMode='FIXED'; node.resizeWithoutConstraints(1440, 900). Order matters — modes before resize. |
| 4a failed (region didn't fill width) | Child region missing layoutSizingHorizontal='FILL' | Set child.layoutSizingHorizontal='FILL' (and Vertical if appropriate) |
| Sidebar / header in wrong position | Layout direction wrong, or x/y manually set inside auto-layout | Set screen.layoutMode correctly; remove any manual x/y assignments |
| Component instance shows wrong variant | Targeted the wrong variant during Step 2b | instance.setProperties({ Variant: 'secondary' }) or recreate from correct master |
| Whole page shifted by ~24px | Wrong padding on the screen frame | Adjust paddingTop/Bottom/Left/Right |
| Background color wrong | Wrong fill on the screen frame | Adjust screen.fills — prefer computed-styles.json resolved RGB |
| Two components touching where source has gap | Wrong itemSpacing on the parent region | Set region.itemSpacing to match source gap-* class |
| Missing region (e.g., footer absent) | Region frame not created during Step 2c | Add the missing region with its children |
| Component appears tiny in the corner | Instance added before auto-layout was set, or appended to wrong parent | Re-parent the instance; verify screen.layoutMode is set before appending children |
| Screen positioned at wrong x/y inside Screens frame | Manual x/y set despite screensFrame.layoutWrap='WRAP' | Remove x/y assignments — let the wrap layout position it |
5b. Apply the fix via use_figma
Write a targeted fix — change only the properties identified in diagnosis:
// use_figma — fix specific property
const node = figma.getNodeById('{nodeId}');
node.primaryAxisSizingMode = 'FIXED';
node.counterAxisSizingMode = 'FIXED';
node.resizeWithoutConstraints(1440, 900);
fixSizing(node, { exemptRoot: true }); // root stays FIXED, descendants may auto
return 'fixed';5c. Re-screenshot and re-compare
get_screenshot(fileKey, screenFrameId)Save to {screenshotDir}/figma.png (overwrite previous).
node {skillRoot}/scripts/compare.js \
"{screenshotDir}/app.png" \
"{screenshotDir}/figma.png" \
"{screenshotDir}/"5d. Evaluate and continue or stop
match → exit loop, report as fixedminor_diff, mismatch, or size_mismatch → continue to next iteration// use_figma
function auditScreen(node) {
const issues = [];
if (node.layoutMode === 'NONE') {
issues.push({ type: 'no_layout_mode', node: node.id, name: node.name });
}
if (node.primaryAxisSizingMode === 'AUTO' || node.counterAxisSizingMode === 'AUTO') {
issues.push({ type: 'screen_not_fixed', node: node.id });
}
for (const child of node.children) {
if (child.x !== 0 && child.parent?.layoutMode && child.parent.layoutMode !== 'NONE') {
issues.push({ type: 'manual_xy_in_autolayout', node: child.id });
}
}
return issues;
}
const node = figma.getNodeById('{screenNodeId}');
return JSON.stringify(auditScreen(node));Fix structural issues before screenshotting — manual x/y inside an auto-layout parent causes silent layout drift, and missing layoutMode collapses all children to (0,0).
Write a tracking record to the page's source folder so the codebase has a durable link back to the Figma screen node.
Path: Resolve from pageSourceFile. If the page is {pagesRoot}/CasesPage.tsx, write to {pagesRoot}/CasesPage.figma-screen.json. If pages live in a folder ({pagesRoot}/CasesPage/index.tsx), write to {pagesRoot}/CasesPage/figma-screen.json.
Schema:
{
"fileKey": "{figmaFileKey}",
"nodeId": "{screenFrameId}",
"url": "https://figma.com/design/{fileKey}?node-id={nodeIdWithDashes}",
"screenName": "CasesPage",
"route": "/cases",
"createdAt": "2026-05-15T14:32:00Z",
"updatedAt": "2026-05-15T14:32:00Z"
}Read-then-write semantics:
figma-screen.json already exists at the target path: parse it, preserve the existing createdAt, and refresh nodeId, url, updatedAt (and screenName/route if they changed) with current values.createdAt and updatedAt both set to the current ISO 8601 UTC timestamp.Failure handling: if the write fails (permission, missing parent path that can't be created), log the failure and continue — do not fail the build. Surface the failure in the Step 7 return result under a trackingFile field with { written: false, error: "..." } so the orchestrator can report it.
Return a structured result for the caller:
{
"screenName": "CasesPage",
"nodeId": "600:1",
"route": "/cases",
"comparison": {
"matchPct": 92.4,
"borderMatchPct": 86.0,
"verdict": "match",
"iterations": 1,
"fixes": ["counterAxisSizingMode AUTO -> FIXED, resize to 1440x900"],
"sizingCheck": {
"verdict": "pass",
"builtSize": { "w": 1440, "h": 900 },
"expectedSize": { "w": 1440, "h": 900 }
}
},
"figmaScreenshot": ".temp/figma-from-code/screenshots/screens/CasesPage/figma.png",
"trackingFile": {
"written": true,
"path": "{pagesRoot}/CasesPage.figma-screen.json"
}
}If no app screenshot was available:
{
"screenName": "EmptyStatePage",
"nodeId": "600:9",
"route": "/empty",
"comparison": {
"verdict": "no_app_reference",
"matchPct": null,
"iterations": 0,
"sizingCheck": {
"verdict": "pass",
"builtSize": { "w": 1440, "h": 900 },
"expectedSize": { "w": 1440, "h": 900 }
}
}
}If rejected for missing components:
{
"screenName": "CasesPage",
"status": "rejected",
"reason": "missing_components",
"missingComponents": ["CaseDetails"]
}Across all screens, write .temp/figma-from-code/build-screens.json:
{
"screens": [
{
"name": "CasesPage",
"nodeId": "600:1",
"verdict": "match",
"matchPct": 92.4,
"iterations": 1
}
],
"failed": [],
"rejected": []
}Read
{skillRoot}/7-build-component/figma-utils.mdfor the canonicalfixSizing()definition and the Tailwind→Figma mapping table.
The screen frame itself must stay FIXED on both axes (screenBodySize, default 1440x900). Descendant frames may need fixSizing() to release height locks introduced by resize() calls. Call it with { exemptRoot: true } to preserve the root frame's FIXED sizing while releasing descendants.
Call fixSizing(screen, { exemptRoot: true }) after composition — the root stays at FIXED screenBodySize (default 1440x900) while descendants are released to grow with content.
| Pitfall | Prevention |
|---|---|
| Screen frame collapses to hug content | Set primaryAxisSizingMode='FIXED' and counterAxisSizingMode='FIXED' BEFORE resize(1440, 900) |
| Children stacked at (0,0) | screen.layoutMode not set — children need an auto-layout parent to position |
| Screen positioned at hardcoded x/y inside Screens frame | Use screensFrame.layoutWrap='WRAP' + appendChild; never set x/y |
| Wrong component variant rendered | Resolve COMPONENT_SET to the specific variant matching source props before createInstance() |
| Sidebar appears as a thin strip | Forgot layoutSizingVertical='FILL' on the sidebar instance |
| Page background missing | Set screen.fills to the resolved page-root background, or [] if transparent |
| Manual padding inside an auto-layout child | Use parent itemSpacing for gaps, child padding* for insets — never manual x offsets |
fixSizing() collapsed the screen to hug | Always pass { exemptRoot: true } when calling fixSizing on the screen frame |
| Modifying a pre-existing screen without authorization | Check preExistingScreens in Step 1e before building |
| Wrong-text inside component instance | Don't override component instance text from the screen — the component master owns its text |
| Screen shows non-default conditional branch | Study appScreenshot in Step 1-pre; exclude components behind ternaries/guards that aren't visible |
| Portal overlay (Dialog, Sheet, etc.) visible on screen | Portals are never visible by default — exclude unless appScreenshot explicitly shows them open |
| High pixel match but fundamentally different content | Run Step 4-pre structural check; don't trust pixel score when content structure differs |
| Scenario | Action |
|---|---|
Component missing from builtComponents | Reject the entire build — return status: "rejected" with the missing components list (Step 0) |
Icon missing from builtComponents | Reject — return status: "rejected" with the missing icon in missingIcons (Step 0) |
use_figma fails | Diagnose error, fix script, retry once. If it fails again, return screen as failed |
use_figma incremental limit | Split the build across multiple use_figma calls. Create the screen frame and regions first, then append instances in follow-up calls |
get_screenshot fails | Retry once. If still failing, return screen as built but unvalidated |
compare.js fails | Report comparison error, return the screen with nodeId but no match score |
| App screenshot missing | Build from source code alone, run Step 4a sizing check, report no_app_reference if 4a passes |
| Pre-existing screen targeted | Return status: "needs_authorization" with preExistingTouched — do not modify |
| Dev server unavailable for Step 1f | Ask the user (standalone) or flag liveInspection: "skipped_no_dev_server" (auto mode) |
Never fail silently. Every error or skip must appear in the returned result.
© bitovi, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file in plugins/figma-from-code/skills/figma-from-code/8-build-screens of bitovi/ai-enablement-prompts.
Open the folder on GitHubat commit df229b1
Figma From Code Build Screens next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Figma From Code Build Screens this skillbitovi/ai-enablement-prompts | 121 | — | ~11k | Automated safety check: Pass | MIT | |
| Claude Code Agent Developmentanthropics/claude-plugins-official | 38k | 7 repos | ~2.8k | Automated safety check: Pass | Apache-2.0 | |
| Subagent Driven DevelopmentAsvarox/allkaraoke | 261 | 37 repos | ~1.2k | Automated safety check: Pass | None | |
| Dispatching Parallel Agentsultralisp/ultralisp | 258 | 40 repos | ~1.5k | Automated safety check: Pass | None | |
| Paseo Advisor Second Opiniongetpaseo/paseo | 20k | 1 repos | ~756 | Automated safety check: Pass | Custom licence | |
| Task Observerrebelytics/one-skill-to-rule-them-all | 3.2k | 1 repos | ~11k | Automated safety check: Pass | CC-BY-4.0 |
anthropics/claude-plugins-official
Explains how to write agents for Claude Code plugins: the markdown file with YAML frontmatter, trigger descriptions, model and color settings, and system prompt design.
Asvarox/allkaraoke
A skill your agent uses when executing implementation plans with independent tasks in the current session
ultralisp/ultralisp
A skill your agent uses when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies
getpaseo/paseo
Launches one separate agent through Paseo to give a second opinion on the current task, with a self-contained briefing and no permission to edit files.
rebelytics/one-skill-to-rule-them-all
Monitors task execution for skill improvement opportunities.
openobserve/openobserve
Splits a change into planner, coder and independent reviewer roles: you confirm a spec, a subagent implements it, and a separate reviewer checks each round's local WIP commit.
bitovi/ai-enablement-prompts
Track reusable UI components and unextracted patterns. An agent skill from bitovi/ai-enablement-prompts.
bitovi/ai-enablement-prompts
Extract and compare computed CSS styles between a baseline URL and a dev/Storybook URL using Playwright MCP evaluate calls.
bitovi/ai-enablement-prompts
A skill your agent uses when the user asks to "create a plugin", "add a plugin", "make a new plugin", "build a plugin", or wants to package skills into an installable plugin for this marketplace.
bitovi/ai-enablement-prompts
Create React components, hooks, or utilities following the modlet pattern.
bitovi/ai-enablement-prompts
A skill your agent uses when the user asks to "create a skill", "add a skill", "make a new skill", "build a skill", or wants to automate a repeated workflow into a reusable prompt.
bitovi/ai-enablement-prompts
Create new Agent Skills for this project. An agent skill from bitovi/ai-enablement-prompts.
Works with
Categories
Subagent for figma-from-code Phase 4. An agent skill from bitovi/ai-enablement-prompts. Figma From Code Build Screens is an agent skill from bitovi/ai-enablement-prompts. Subagent for figma-from-code Phase 4.
Figma From Code Build Screens fits situations like: tasks that involve Subagents.
Run `npx skills add bitovi/ai-enablement-prompts --skill figma-from-code-build-screens -a claude-code`. Or copy the skill folder (plugins/figma-from-code/skills/figma-from-code/8-build-screens in bitovi/ai-enablement-prompts) into .claude/skills/figma-from-code-build-screens in your project. Claude Code loads it when a task matches its description.
Run `npx skills add bitovi/ai-enablement-prompts --skill figma-from-code-build-screens -a codex`. Or copy the skill folder (plugins/figma-from-code/skills/figma-from-code/8-build-screens in bitovi/ai-enablement-prompts) into .agents/skills/figma-from-code-build-screens in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add bitovi/ai-enablement-prompts --skill figma-from-code-build-screens -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/figma-from-code-build-screens, .gemini/skills/figma-from-code-build-screens, .github/skills/figma-from-code-build-screens and .opencode/skills/figma-from-code-build-screens in your project.
Going by SKILL.md and its folder, Figma From Code Build Screens needs the command-line tools its instructions call (node and curl).
SKILL.md names 1 domain. In commands or code: figma.com; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Figma From Code Build Screens is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 11k tokens (SKILL.md is roughly 43k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Figma From Code Build Screens: Claude Code Agent Development (anthropics/claude-plugins-official, 38k stars), Subagent Driven Development (Asvarox/allkaraoke, 261 stars), Dispatching Parallel Agents (ultralisp/ultralisp, 258 stars) and Paseo Advisor Second Opinion (getpaseo/paseo, 20k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
bitovi (a GitHub organization) maintains it in bitovi/ai-enablement-prompts, which has 121 GitHub stars. The repository holds 40 skills in this directory. The repository was last updated on September 11, 2026.
Source: bitovi/ai-enablement-prompts on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.