"DevRev Prototyper: Build interactive HTML prototypes with DevRev's design system (Arcade & DevRev App themes), Chip fonts, and Figma integration. Designed for designers — no technical knowledge required."
Resources
15Install
npx skillscat add asundiev-devrev/arcade-prototyper Install via the SkillsCat registry.
DevRev Prototyper
Build interactive HTML prototypes styled with DevRev's design system. Supports two themes: Arcade (Computer & Agent Studio) and DevRev App (System of Record). Prototypes are self-contained HTML files — no build step, no dependencies, no monorepo required.
Communication style — IMPORTANT
Your users are designers, not engineers. They think in screens, flows, and interactions — not in code, files, or terminal commands.
Rules:
- Never show or mention code, CSS, HTML, file paths, terminal commands, or tool names. The user should not see a single line of code or know which tools you used.
- Never explain how you're building the prototype. Don't say "I'm embedding CSS tokens" or "I'll inline the base64 fonts." Just do it silently.
- Never ask technical questions. Don't ask which theme token file to use — ask "Is this for Computer/Agent Studio, or the main DevRev app?" instead.
- Never surface errors as technical output. If something fails, say what happened in plain language and what you'll do to fix it.
- Speak in design language. Say "colors," "type styles," "spacing," "components," "layout" — not "CSS variables," "HSL values," "DOM structure," or "HTML scaffold."
- When asking clarifying questions, frame them around the design intent: "Should this be dark or light mode?", "What happens when someone clicks this button?", "Is this a full page or a smaller component?"
- When delivering a prototype, run
open ~/Desktop/prototype-name.htmlto open it in the browser automatically. NEVER ask the user to open the file themselves. Just open it and say "Here's your prototype — it should be open in your browser now." - Never mention Node.js, npm, Homebrew, git, CLI, or any developer tooling. If something needs installing, do it silently. If it needs a password, say "Your Mac might ask for your password" — nothing more.
The goal: the designer describes what they want, and a working prototype appears in their browser. Everything in between is invisible.
First-run setup (silent)
When this skill is first used, silently check that dependencies are ready. Do all of this in the background — never show the user any of these steps, commands, or output.
1. Check for Node.js
Designers typically don't have developer tools installed. Check silently and install if missing:
node --version 2>/dev/null || echo "MISSING"If Node.js is missing, install it silently via Homebrew (install Homebrew first if needed):
# Install Homebrew if missing
which brew >/dev/null 2>&1 || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install Node.js
brew install nodeIf Homebrew installation requires a password, tell the user in plain language: "I need to install a small helper tool. Your Mac might ask for your password — just type it in and hit Enter." Do NOT mention Node.js, Homebrew, npm, or any technical names.
2. Check for figma-cli
FIGMA_CLI=$(find ~ -maxdepth 2 -type d -name "figma-cli" 2>/dev/null | head -1)If not found, install it silently:
cd ~ && git clone https://github.com/silships/figma-cli.git && cd figma-cli && npm installIf git is missing, install it: brew install git (Homebrew should already be available from Step 1).
3. Check Figma connection
cd $FIGMA_CLI && node src/index.js daemon statusIf not connected and Figma Desktop is running:
cd $FIGMA_CLI && node src/index.js connectIf Figma Desktop isn't running, tell the user: "Open Figma on your Mac and I'll connect to it."
4. Ready
Once everything is in place, just respond to whatever the designer asked for. Don't announce that setup is complete — they shouldn't know it happened.
When to use this skill
Use when a designer asks you to:
- Create a UI prototype, mockup, or interactive demo
- Build a page layout or flow
- Prototype a component, feature, or interaction pattern
- Turn a Figma frame or screenshot into working HTML
- Pull a design from Figma and make it interactive
- Inspect or export anything from a Figma file
How it works
Every prototype is a single HTML file that includes the token CSS and component CSS inline. The file opens directly in any browser.
Templates
Pre-built, production-quality templates are available in the templates/ directory. Use these as starting points instead of building from scratch — they follow real DevRev designs and include correct spacing, icons, interactions, and token usage.
| Template | File | Use when |
|---|---|---|
| Chat | templates/chat.html |
AI chat interfaces, Computer UI, conversational flows, agent interactions |
| List | templates/list.html |
Data tables, item lists, dashboards with rows |
| Detail | templates/detail.html |
Object detail views, profiles, settings panels |
When to use which template
- User mentions "Computer", "chat", "conversation", "agent", "AI assistant", or "messaging" → Start from
templates/chat.html. This is the Computer / Agent Studio chat interface with sidebar, message bubbles, thinking state, progressive blur input, and purple focus glow. - User mentions "list", "table", "dashboard", "items", or "overview" → Start from
templates/list.html. - User mentions "detail", "profile", "settings", "object view", or "single item" → Start from
templates/detail.html.
How to use templates
Templates use /* {{FONTS}} */, /* {{TOKENS}} */, /* {{TYPOGRAPHY}} */, /* {{COMPONENTS}} */ placeholders in their <style> tag. To produce a working prototype:
- Read the template file from
templates/ - Read each CSS file (
chip-fonts.css, theme tokens,typography-spacing.css,arcade-components.css) - Replace each placeholder with the corresponding CSS file contents
- Save the hydrated file to the user's Desktop
# Hydration — replace placeholders with real CSS
replacements = {
'/* {{FONTS}} */': 'chip-fonts.css',
'/* {{TOKENS}} */': 'arcade-tokens.css', # or devrev-app-tokens.css
'/* {{TYPOGRAPHY}} */': 'typography-spacing.css',
'/* {{COMPONENTS}} */': 'arcade-components.css',
}
for placeholder, css_file in replacements.items():
html = html.replace(placeholder, read(css_file))Customizing templates
After hydrating, modify the HTML to match the user's specific needs — change text, add/remove sections, adjust layout. The templates are starting points, not rigid structures.
Themes
DevRev has two active themes. Ask the user which one to use if unclear.
| Theme | data-theme value |
Token file | Visual character |
|---|---|---|---|
| Arcade | arcade |
arcade-tokens.css |
Warm achromatic palette, fruit-named attribute colors. Used in Computer, Agent Studio. |
| DevRev App | devrev-app |
devrev-app-tokens.css |
Cool blue-indigo palette, standard attribute colors. Used in the main DevRev product (SoR). |
Both themes share the same arcade-components.css — component classes reference semantic token variables that each theme defines differently.
Files in this skill
| File | Purpose |
|---|---|
arcade-tokens.css |
Arcade theme: fruit-named palette + semantic tokens (dark + light). Verbatim from monorepo. |
devrev-app-tokens.css |
DevRev App theme: HSL primitive system + semantic tokens (dark + light). Verbatim from monorepo. |
typography-spacing.css |
Typography utility classes (25 text styles) + phi-ratio spacing system. Extracted from monorepo. |
arcade-components.css |
Ready-made component classes — works with both themes. References real token names. |
chip-fonts.css |
Chip font family — base64-embedded @font-face declarations (fully self-contained) |
templates/chat.html |
Computer-style AI chat interface — sidebar, messages, thinking state, progressive blur, focus glow |
templates/list.html |
Data list / table view with filters and actions |
templates/detail.html |
Object detail / settings panel layout |
SKILL.md |
This file — instructions for Computer |
Token provenance: arcade-tokens.css and devrev-app-tokens.css are verbatim extractions from the DevRev product monorepo (devrev-web/libs/design-system/shared/themes/). They are NOT approximations — they are the real production tokens.
Chip font family
DevRev uses the Chip font family across all products. Three variants:
| Font face | CSS variable | Usage |
|---|---|---|
| Chip Text Variable | var(--font-text) |
Body text, UI labels, buttons, inputs — all general text |
| Chip Display Variable | var(--font-display) |
Headings, titles, hero text |
| Chip Mono | var(--font-mono) |
Code blocks, monospace text |
All fonts are variable-weight (100-900). Key weights used in the design system:
- 440 — normal (
.font-normal) - 540 — medium (
.font-medium) - 660 — bold (
.font-bold)
The chip-fonts.css file contains base64-encoded font data, so prototypes render correctly with no network requests or external files. Always embed it in every prototype.
Building a prototype
Step 1: Choose a theme and create the HTML scaffold
Arcade theme (default for Computer/Agent Studio work):
<!DOCTYPE html>
<html lang="en" data-theme="arcade" class="light" data-device="web">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Prototype — [Name]</title>
<style>
/* Paste contents of chip-fonts.css here */
/* Paste contents of arcade-tokens.css here */
/* Paste contents of typography-spacing.css here */
/* Paste contents of arcade-components.css here */
/* === Prototype-specific styles below === */
</style>
</head>
<body>
<!-- Prototype content -->
</body>
</html>DevRev App theme (for main product / SoR work):
<!DOCTYPE html>
<html lang="en" data-theme="devrev-app" class="light" data-device="web">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Prototype — [Name]</title>
<style>
/* Paste contents of chip-fonts.css here */
/* Paste contents of devrev-app-tokens.css here */
/* Paste contents of typography-spacing.css here */
/* Paste contents of arcade-components.css here */
/* === Prototype-specific styles below === */
</style>
</head>
<body>
<!-- Prototype content -->
</body>
</html>Important: Read chip-fonts.css, the chosen token file, typography-spacing.css, AND arcade-components.css from this skill directory and embed their contents inside the <style> tag. This makes the file fully self-contained — fonts render without any external requests. The embed order matters: fonts → tokens → typography/spacing → components.
Step 2: Set the mode
- Light mode:
class="light"on<html>(default for most prototypes) - Dark mode: Remove the
class="light"attribute (or addclass="dark"for DevRev App) - Device:
data-device="web"(default),"desktop", or"mobile"
Step 3: Build with components
Use the component classes from arcade-components.css. All follow the pattern:
.arcade-{component}--{variant} .arcade-{component}--{size}The component classes work identically in both themes — only the visual appearance changes based on the token file.
Step 4: Add interactivity
For interactive prototypes, add vanilla JavaScript at the bottom of the file. Common patterns:
<script>
// Toggle dark/light mode
document.querySelector('[data-action="toggle-theme"]')?.addEventListener('click', () => {
document.documentElement.classList.toggle('light');
});
// Tab switching
document.querySelectorAll('.arcade-tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.arcade-tab').forEach(t => t.classList.remove('arcade-tab--active'));
tab.classList.add('arcade-tab--active');
// Show/hide panels as needed
});
});
// Dialog open/close
function openDialog(id) { document.getElementById(id).style.display = 'flex'; }
function closeDialog(id) { document.getElementById(id).style.display = 'none'; }
</script>Step 5: Save and open the prototype
Save to the user's Desktop:
~/Desktop/prototype-{name}.htmlThen immediately open it in the browser — do NOT ask the user to open it themselves:
open ~/Desktop/prototype-{name}.htmlTell the user: "Here's your prototype — it should be open in your browser now." If the file is already open from a previous iteration, the browser will refresh it automatically.
Component reference
Buttons
<button class="arcade-btn arcade-btn--primary">Primary</button>
<button class="arcade-btn arcade-btn--secondary">Secondary</button>
<button class="arcade-btn arcade-btn--tertiary">Tertiary</button>
<button class="arcade-btn arcade-btn--destructive">Delete</button>
<!-- Sizes: --S, --M (default), --L -->
<button class="arcade-btn arcade-btn--primary arcade-btn--S">Small</button>
<button class="arcade-btn arcade-btn--primary arcade-btn--L">Large</button>Input fields
<div class="arcade-field">
<label class="arcade-field__label">Email</label>
<input class="arcade-input" type="email" placeholder="name@example.com">
<span class="arcade-field__hint">We'll never share your email.</span>
</div>
<!-- Error state -->
<input class="arcade-input arcade-input--error" value="bad-input">
<span class="arcade-field__error">This field is required.</span>Textarea
<textarea class="arcade-textarea" placeholder="Write something..."></textarea>Badges
<span class="arcade-badge arcade-badge--default">Default</span>
<span class="arcade-badge arcade-badge--info">Info</span>
<span class="arcade-badge arcade-badge--success">Success</span>
<span class="arcade-badge arcade-badge--warning">Warning</span>
<span class="arcade-badge arcade-badge--alert">Alert</span>
<span class="arcade-badge arcade-badge--action">Action</span>
<span class="arcade-badge arcade-badge--intelligence">AI</span>
<!-- Attribute colors (1-8, for categorical labels) -->
<span class="arcade-badge arcade-badge--attr-1">Label 1</span>
<span class="arcade-badge arcade-badge--attr-5">Label 5</span>Cards
<div class="arcade-card">Basic card</div>
<div class="arcade-card arcade-card--elevated">Elevated</div>
<div class="arcade-card arcade-card--prominent">Prominent</div>
<div class="arcade-card arcade-card--interactive">Clickable card</div>Menu / List items
<div class="arcade-popover">
<button class="arcade-menu-item">
<span>Settings</span>
<span class="arcade-menu-item__secondary">⌘S</span>
</button>
<button class="arcade-menu-item arcade-menu-item--selected">Profile</button>
<hr class="arcade-divider">
<button class="arcade-menu-item" style="color: hsl(var(--color-feedback-alert));">Log out</button>
</div>Avatars
<div class="arcade-avatar arcade-avatar--M arcade-avatar--1">AS</div>
<div class="arcade-avatar arcade-avatar--L arcade-avatar--4">
<img src="https://i.pravatar.cc/80" alt="User">
</div>Toggle / Switch
<div class="arcade-toggle"></div>
<div class="arcade-toggle arcade-toggle--on"></div>Checkbox
<div class="arcade-checkbox"></div>
<div class="arcade-checkbox arcade-checkbox--checked"></div>Tabs
<div class="arcade-tabs">
<button class="arcade-tab arcade-tab--active">Overview</button>
<button class="arcade-tab">Details</button>
<button class="arcade-tab">Activity</button>
</div>Table
<table class="arcade-table">
<thead>
<tr><th>Name</th><th>Status</th><th>Date</th></tr>
</thead>
<tbody>
<tr><td>Item one</td><td><span class="arcade-badge arcade-badge--success">Active</span></td><td>Mar 13</td></tr>
<tr><td>Item two</td><td><span class="arcade-badge arcade-badge--warning">Pending</span></td><td>Mar 12</td></tr>
</tbody>
</table>Alert banner
<div class="arcade-alert arcade-alert--info">This is an informational message.</div>
<div class="arcade-alert arcade-alert--success">Operation completed.</div>
<div class="arcade-alert arcade-alert--warning">Proceed with caution.</div>
<div class="arcade-alert arcade-alert--error">Something went wrong.</div>Dialog / Modal
<div class="arcade-overlay" id="my-dialog" style="display: none;">
<div class="arcade-dialog">
<div class="arcade-dialog__header">
<span class="arcade-dialog__title">Confirm action</span>
<button class="arcade-btn arcade-btn--tertiary arcade-btn--S" onclick="closeDialog('my-dialog')">✕</button>
</div>
<div class="arcade-dialog__body">Are you sure you want to proceed?</div>
<div class="arcade-dialog__footer">
<button class="arcade-btn arcade-btn--secondary" onclick="closeDialog('my-dialog')">Cancel</button>
<button class="arcade-btn arcade-btn--primary">Confirm</button>
</div>
</div>
</div>Sidebar navigation
<div class="arcade-sidebar">
<span class="arcade-sidebar__section-title">Navigation</span>
<button class="arcade-menu-item arcade-menu-item--selected">Dashboard</button>
<button class="arcade-menu-item">Issues</button>
<button class="arcade-menu-item">Settings</button>
</div>Skeleton loading
<div class="arcade-skeleton" style="width: 200px; height: 24px;"></div>
<div class="arcade-skeleton arcade-skeleton--text"></div>
<div class="arcade-skeleton arcade-skeleton--circle" style="width: 40px; height: 40px;"></div>Empty state
<div class="arcade-empty">
<div class="arcade-empty__icon">📭</div>
<div class="arcade-empty__title">No items yet</div>
<div class="arcade-empty__description">Create your first item to get started.</div>
<button class="arcade-btn arcade-btn--primary">Create item</button>
</div>Layout patterns
App shell (sidebar + main)
<div style="display: flex; height: 100vh;">
<div class="arcade-sidebar">
<!-- nav items -->
</div>
<main style="flex: 1; overflow: auto; padding: var(--spacing-global-lg);">
<!-- page content -->
</main>
</div>Centered content
<div style="max-width: 640px; margin: 0 auto; padding: var(--spacing-global-xl) var(--spacing-global-lg);">
<!-- content -->
</div>Header bar
<header style="display: flex; align-items: center; justify-content: space-between; padding: var(--spacing-global-xs) var(--spacing-global-md); border-bottom: 1px solid hsl(var(--border-outline-01)); background: hsl(var(--bg-layer-01));">
<span class="text-subtitle-1 font-bold">Page Title</span>
<div style="display: flex; gap: var(--spacing-global-xs);">
<button class="arcade-btn arcade-btn--tertiary arcade-btn--S">Cancel</button>
<button class="arcade-btn arcade-btn--primary arcade-btn--S">Save</button>
</div>
</header>Typography classes
Use these classes for text styling (same in both themes):
| Class | Usage |
|---|---|
.text-title-large |
Hero headings (34px, bold) |
.text-title-1 |
Page titles (29px, bold) |
.text-title-2 |
Section headings (24px, bold) |
.text-title-3 |
Card titles (20px, bold) |
.text-subtitle-1 |
Emphasized labels (16px, bold) |
.text-subtitle-2 |
Sub-labels (14px, bold) |
.text-body |
Default body text (14px) |
.text-body-large |
Large body text (16px) |
.text-body-small |
Compact body (13px) |
.text-system |
UI text — buttons, inputs (13px, medium) |
.text-system-small |
Smaller UI text (12px, medium) |
.text-system-xsmall |
Extra small UI text (11px, medium) |
.text-caption |
Captions, hints (12px) |
.text-footnote |
Fine print (11px) |
.text-code |
Monospace code (13px) |
.text-code-small |
Small monospace (12px) |
Font weights: .font-normal (440), .font-medium (540), .font-bold (660)
Spacing classes
Gap: .gap-5xs through .gap-2xl
Padding: .p-5xs through .p-xl
Or use CSS variables directly:
var(--spacing-global-base)— fixed rem spacing (0.5rem)var(--spacing-dynamic-base)— em spacing that scales with font-size (0.5em)
Color token reference
All color tokens store raw HSL triplets (e.g., 0 0% 100%). You must wrap them with hsl() or hsla() when using them in CSS properties:
color: hsl(var(--text-color-primary));background: hsla(var(--bg-layer-01) / 0.5);
Arcade palette colors (direct use)
The Arcade theme includes a fruit-named palette. These are raw HSL triplets — wrap in hsl():
| Scale | Hue family | Usage |
|---|---|---|
--husk-100 to --husk-1300 |
Warm achromatic grays | Neutrals, backgrounds, text |
--shuiguo-100 to --shuiguo-600 |
Cyan-blue | Info, links |
--hardy-100 to --hardy-600 |
Green | Success states |
--persimmon-100 to --persimmon-600 |
Orange-red | Warning states |
--dragonfruit-100 to --dragonfruit-600 |
Pink-red | Alert/error states |
--jabuticaba-100 to --jabuticaba-600 |
Purple | Intelligence/AI |
--banginapalli-100 to --banginapalli-600 |
Yellow-gold | Action/brand |
--maoshigua-100 to --maoshigua-600 |
Blue | Decorative |
--day / --night |
White / near-black | Base extremes |
Aliases: --action-100 to --action-600 maps to banginapalli. --intelligence-100 to --intelligence-600 maps to jabuticaba.
Semantic tokens (available in both themes)
| Token | Purpose |
|---|---|
var(--text-color-primary) |
Main text |
var(--text-color-secondary) |
Secondary text |
var(--text-color-tertiary) |
Tertiary text |
var(--text-color-muted) |
Muted/placeholder text |
var(--color-on-fill) |
Text on filled backgrounds |
var(--bg-layer-00) |
Deepest page background |
var(--bg-layer-01) |
Card/surface background |
var(--bg-layer-02) |
Nested surface |
var(--bg-layer-03) |
Third-level surface |
var(--bg-interactive-primary-resting) |
Primary button bg |
var(--bg-interactive-primary-hovered) |
Primary button hover |
var(--bg-interactive-secondary-resting) |
Secondary button bg |
var(--bg-interactive-tertiary-hovered) |
Tertiary button hover |
var(--bg-interactive-destructive-resting) |
Destructive button bg |
var(--bg-interactive-smart-resting) |
AI/smart button bg |
var(--border-outline-00) |
Subtle border |
var(--border-outline-01) |
Standard border |
var(--border-input-text-resting) |
Input border |
var(--border-field-idle) |
Form field border (idle) |
var(--color-feedback-alert) |
Error/destructive |
var(--color-feedback-warning) |
Warning |
var(--color-feedback-success) |
Success |
var(--color-feedback-smart) |
AI/intelligence |
var(--color-action) |
Primary brand action |
var(--color-intelligence) |
AI accent |
Shadow tokens (defined in arcade-components.css)
Shadow tokens are complete CSS values — use them directly without hsl() wrapping:
| Token | Purpose |
|---|---|
var(--shadow-depth-01) |
Subtle elevation |
var(--shadow-depth-02) |
Medium elevation (cards) |
var(--shadow-depth-03) |
High elevation (popovers, dialogs) |
var(--shadow-depth-04) |
Highest elevation |
var(--shadow-button) |
Button press shadow |
var(--shadow-interactive-focused) |
Focus ring |
Using colors in inline styles
<!-- Background — wrap in hsl() -->
<div style="background: hsl(var(--bg-layer-01));">...</div>
<!-- Text — wrap in hsl() -->
<span style="color: hsl(var(--text-color-secondary));">Secondary text</span>
<!-- Border — wrap in hsl() -->
<div style="border: 1px solid hsl(var(--border-outline-01));">...</div>
<!-- With opacity — use hsla() with / syntax -->
<div style="background: hsla(var(--bg-interactive-primary-resting) / 0.1);">...</div>
<!-- Direct palette color — wrap in hsl() -->
<div style="background: hsl(var(--banginapalli-200));">...</div>
<!-- Shadow — use directly, no hsl() needed -->
<div style="box-shadow: var(--shadow-depth-02);">...</div>Figma integration
You have direct access to Figma Desktop via a LOCAL command-line tool called figma-cli (silships/figma-cli on GitHub). It connects directly to the running Figma Desktop app on the user's Mac over a local socket. No API token, no Figma REST API, no Figma plugins, no third-party services.
ABSOLUTE RULES — VIOLATION OF THESE IS A CRITICAL FAILURE:
- NEVER ask the user for a Figma API token. You do not need one. figma-cli connects locally.
- NEVER use the Figma REST API (
api.figma.com). It will not work. - NEVER suggest installing a Figma plugin. There is no "FigmaCli plugin", no "Figma to Code plugin", no plugin of any kind. This is a LOCAL CLI tool that runs in the terminal.
- NEVER use
npx figma-clior any npm package by that name. Use the LOCAL CLONE at~/figma-cli. - NEVER use
figma-usedirectly. Use figma-cli commands instead. - NEVER tell the user you can't access Figma. You CAN — via figma-cli. Just use it.
- NEVER invent alternative Figma access methods. The ONLY way to access Figma is through the local figma-cli clone described below.
If you find yourself about to suggest a Figma plugin, API token, or any method other than the local figma-cli — STOP. Re-read this section. The answer is always: use ~/figma-cli with node src/index.js.
Locating figma-cli
figma-cli is a git repo cloned to ~/figma-cli. To find it:
FIGMA_CLI=$(find ~ -maxdepth 2 -type d -name "figma-cli" 2>/dev/null | head -1)If not found, install it silently (Node.js should already be available from the first-run setup):
cd ~ && git clone https://github.com/silships/figma-cli.git && cd figma-cli && npm installSetup
Before first use, ensure the daemon is connected:
cd $FIGMA_CLI && node src/index.js daemon statusIf not connected:
cd $FIGMA_CLI && node src/index.js connectKey commands for prototyping
All commands run from the figma-cli directory.
| Task | Command |
|---|---|
| Check connection | node src/index.js daemon status |
| List open files | node src/index.js files |
| Find a node by name | node src/index.js find "Button" |
| Get node properties | node src/index.js get "1038:14518" |
| Get node tree | node src/index.js node tree "1038:14518" -d 3 |
| Export node as PNG | node src/index.js export node "1038:14518" -o /tmp/frame.png -s 2 |
| Export screenshot | node src/index.js export screenshot -o /tmp/screen.png -s 2 |
| Select a node | node src/index.js select "1038:14518" |
| What's on canvas | node src/index.js canvas info |
Node ID format: Use colon format (1038:14518), not dash format (1038-14518). Figma URLs show dashes in node-id= params — convert them to colons.
Reading Figma annotations
Designers use Figma annotations (Dev Mode notes) to document behavior, specifications, and design intent on frames and components. You MUST read annotations when they exist — they contain critical context for building accurate prototypes.
figma-cli does not have a built-in annotations command, but the eval command gives full access to the Figma Plugin API, which exposes node.annotations.
Read annotations on a specific node:
cd $FIGMA_CLI && node src/index.js eval "(function() {
var node = figma.getNodeById('NODE_ID');
if (!node) return 'Node not found';
if (!node.annotations || node.annotations.length === 0) return 'No annotations';
return JSON.stringify(node.annotations.map(function(a) {
return { label: a.label || '', labelMarkdown: a.labelMarkdown || '', properties: a.properties || [] };
}), null, 2);
})()"Read annotations on the current selection:
cd $FIGMA_CLI && node src/index.js eval "(function() {
var node = figma.currentPage.selection[0];
if (!node) return 'Nothing selected';
if (!node.annotations || node.annotations.length === 0) return 'No annotations';
return JSON.stringify(node.annotations.map(function(a) {
return { label: a.label || '', labelMarkdown: a.labelMarkdown || '', properties: a.properties || [] };
}), null, 2);
})()"Find ALL annotated nodes on the current page:
cd $FIGMA_CLI && node src/index.js eval "(function() {
var results = [];
function walk(n) {
if (n.annotations && n.annotations.length > 0) {
results.push({ id: n.id, name: n.name, type: n.type, annotations: n.annotations.map(function(a) {
return { label: a.label || '', labelMarkdown: a.labelMarkdown || '', properties: a.properties || [] };
})});
}
if (n.children) n.children.forEach(walk);
}
walk(figma.currentPage);
return JSON.stringify(results, null, 2);
})()"What annotations contain:
label— plain text note from the designerlabelMarkdown— rich text note (Markdown format)properties— pinned style properties (fills, strokes, spacing, etc.)
When to read annotations:
- Always when prototyping a Figma frame — check for annotations as part of the standard workflow (Step 3 below).
- If annotations describe behavior (e.g., "opens a modal on click", "shows loading state for 2 seconds"), implement that behavior in the prototype.
- If annotations describe content (e.g., "this text comes from the ticket title"), use realistic sample data that matches.
- If annotations specify states (e.g., "hover state", "error state", "empty state"), include those states in the prototype with appropriate interactivity.
Figma-to-prototype workflow
When the user shares a Figma URL or asks to prototype a frame, follow ALL of these steps. Steps 1-6 gather data from Figma. Steps 7-8 build the prototype. Step 9 validates accuracy. Do not skip any step.
Phase 1: Extract everything from Figma
Extract the node ID from the URL. Convert
node-id=1038-14518→1038:14518.Export the full frame as a reference image (2x for clarity):
node src/index.js export node "1038:14518" -o /tmp/figma-ref.png -s 2Read annotations on the node and its children — these contain behavioral specs from the designer:
node src/index.js eval "(function() { var results = []; var root = figma.getNodeById('1038:14518'); if (!root) return 'Node not found'; function walk(n) { if (n.annotations && n.annotations.length > 0) { results.push({ id: n.id, name: n.name, annotations: n.annotations.map(function(a) { return { label: a.label || '', labelMarkdown: a.labelMarkdown || '' }; })}); } if (n.children) n.children.forEach(walk); } walk(root); return results.length ? JSON.stringify(results, null, 2) : 'No annotations found'; })()"Get the full node tree to understand the design's layer structure and hierarchy:
node src/index.js node tree "1038:14518" -d 5Use depth 5+ to capture nested icon groups and small components. Read this tree carefully — every named layer is intentional.
Export icons and small components as SVGs. Walk the node tree and identify all VECTOR, BOOLEAN_OPERATION, and small FRAME/GROUP nodes that represent icons, logos, or illustrations. Export each one as SVG using the Figma Plugin API:
# Export a single icon node as SVG node src/index.js eval "(function() { var node = figma.getNodeById('NODE_ID'); if (!node) return 'Node not found'; return node.exportAsync({ format: 'SVG' }).then(function(data) { return String.fromCharCode.apply(null, data); }); })()"Save each SVG to
/tmp/figma-icons/with a descriptive filename based on the layer name (e.g.,search-icon.svg,clock-icon.svg,home-icon.svg).Rules for icon export:
- Export every icon, logo mark, and illustration — never hand-draw SVGs or approximate them.
- If a node has
type: 'INSTANCE', it's a component instance — export the whole instance as one SVG. - If an icon is inside a wrapper frame, export the inner vector/group, not the padding frame.
- After export, inspect each SVG briefly: remove unnecessary wrapping
<g>tags, and ensurefill="currentColor"is set (or the correct fill) so the icon inherits text color in HTML.
Batch export — when there are many icons, export them all at once:
node src/index.js eval "(function() { var root = figma.getNodeById('ROOT_NODE_ID'); var icons = []; function walk(n) { var isIcon = (n.type === 'VECTOR' || n.type === 'BOOLEAN_OPERATION' || n.type === 'INSTANCE') && n.width <= 32 && n.height <= 32; var isSmallGroup = (n.type === 'FRAME' || n.type === 'GROUP') && n.width <= 32 && n.height <= 32 && n.children && n.children.some(function(c) { return c.type === 'VECTOR' || c.type === 'BOOLEAN_OPERATION'; }); if (isIcon || isSmallGroup) { icons.push({ id: n.id, name: n.name, type: n.type, width: n.width, height: n.height }); } if (n.children && !isIcon && !isSmallGroup) n.children.forEach(walk); } walk(root); return JSON.stringify(icons, null, 2); })()"Then export each identified icon node individually using the SVG export snippet above.
Extract color and style values from key nodes and map them to design system tokens.
For each visually distinct element (backgrounds, text layers, borders, shadows), get its fill/stroke values:
node src/index.js eval "(function() { var node = figma.getNodeById('NODE_ID'); if (!node) return 'Node not found'; var result = { name: node.name, type: node.type }; if (node.fills) result.fills = node.fills.filter(function(f) { return f.visible !== false; }); if (node.strokes) result.strokes = node.strokes; if (node.effects) result.effects = node.effects; if (node.opacity !== undefined) result.opacity = node.opacity; if (node.cornerRadius !== undefined) result.cornerRadius = node.cornerRadius; if (node.paddingLeft !== undefined) result.padding = { left: node.paddingLeft, right: node.paddingRight, top: node.paddingTop, bottom: node.paddingBottom }; if (node.itemSpacing !== undefined) result.itemSpacing = node.itemSpacing; if (node.fontSize !== undefined) { result.fontSize = node.fontSize; result.fontWeight = node.fontWeight; result.lineHeight = node.lineHeight; result.letterSpacing = node.letterSpacing; } return JSON.stringify(result, null, 2); })()"Then map every extracted value to a design system token. This is critical — never hardcode hex or RGB values in the prototype.
Phase 2: Map Figma values to design system tokens
This is the most important accuracy step. Every color in the prototype MUST come from the token files (arcade-tokens.css or devrev-app-tokens.css), never from hardcoded hex values.
How to map a Figma color to a token:
Figma gives you fills as
{ r, g, b, a }in 0-1 range. Convert to HSL:- Multiply r, g, b by 255 to get 0-255 range
- Convert RGB to HSL using standard conversion
- Round to the nearest integer values
Look up the resulting HSL values in the token file. Token values are stored as
H S% L%triplets (e.g.,0 0% 100%for white,330 2% 18%for--husk-1000).Find the closest match. The palette tokens in
arcade-tokens.csscover the full range:- Grays/neutrals →
--husk-*scale (100-1300) - Extremes →
--day(white) and--night(near-black) - Blues →
--shuiguo-*or--maoshigua-* - Greens →
--hardy-* - Reds/pinks →
--dragonfruit-* - Orange →
--persimmon-* - Purple →
--jabuticaba-* - Yellow/gold →
--banginapalli-*
- Grays/neutrals →
Prefer semantic tokens over palette tokens when available. If a color maps to
--husk-1000, but the element is a surface background, use--bg-layer-01(which resolves to--husk-1000in dark mode). Semantic tokens adapt correctly across light/dark modes. Key semantic tokens:- Text:
--text-color-primary,--text-color-secondary,--text-color-tertiary,--text-color-muted - Surfaces:
--bg-layer-00through--bg-layer-04 - Borders:
--border-outline-00,--border-outline-01 - Interactive:
--bg-interactive-*-resting,--bg-interactive-*-hovered
- Text:
For typography, extract
fontSize,fontWeight,lineHeight, andletterSpacingfrom Figma nodes and match them to the typography utility classes intypography-spacing.css(e.g.,.text-body,.text-subtitle-1,.text-system). Don't hardcode font sizes — use the closest matching class.
Build a token mapping table before writing any HTML. For example:
| Element | Figma fill (RGB) | HSL | Token | CSS usage |
|---|---|---|---|---|
| Sidebar bg | rgb(22,22,22) | 0 0% 9% | --husk-1300 / --bg-layer-00 |
background: hsl(var(--bg-layer-00)) |
| Card bg | rgb(46,44,44) | 330 2% 18% | --husk-1000 / --bg-layer-01 |
background: hsl(var(--bg-layer-01)) |
| Primary text | rgb(244,244,246) | 240 14% 96% | --husk-300 / --text-color-primary |
color: hsl(var(--text-color-primary)) |
| Muted text | rgb(123,123,123) | 0 0% 48% | --husk-700 / --text-color-tertiary |
color: hsl(var(--text-color-tertiary)) |
Every color in the prototype must trace back to this table.
Phase 3: Build the prototype
Build the HTML prototype. Use the token mapping table from Phase 2 to write all styles. Use the exported SVGs from Step 5 for all icons — inline them directly in the HTML. Follow these rules:
- Every
color,background,border-color, andfillproperty MUST use ahsl(var(--token-name))value. Zero exceptions. - Every icon and illustration MUST come from the Figma SVG exports. Never draw SVGs by hand or approximate icon shapes.
- Use typography utility classes (
.text-body,.text-subtitle-1, etc.) for text styling. Don't hardcodefont-sizeorfont-weight. - Use spacing variables (
var(--spacing-global-*)) for padding and gaps where possible. - Match the Figma layout structure: use the node tree from Step 4 as a blueprint for your HTML hierarchy.
- Every
Save and open the prototype:
open ~/Desktop/prototype-name.html
Phase 4: Validate accuracy
Visual validation — compare prototype against Figma reference. This step is mandatory. After opening the prototype:
a. Take a screenshot of the prototype in the browser at the same dimensions as the Figma export. Use the browser's screenshot capabilities or a tool.
b. View the Figma reference (
/tmp/figma-ref.png) and the prototype screenshot side by side. Compare:- Layout and spacing — are elements positioned correctly relative to each other?
- Colors — do backgrounds, text, and borders match? (They should, since you used tokens.)
- Icons — do they match the originals exactly? (They should, since you exported SVGs.)
- Typography — are sizes, weights, and line heights correct?
- Corner radii, shadows, borders — are they present and correct?
c. Fix any deviations before showing the result to the user. If you spot differences:
- Check your token mapping table — did you pick the wrong token?
- Check your SVG exports — did you export the right node?
- Check spacing — did you use the right spacing variable?
d. Only deliver the prototype to the user after validation passes. The goal is that the user sees no visible differences between the Figma design and the prototype.
Full reference
See $FIGMA_CLI/CLAUDE.md for quick start and $FIGMA_CLI/REFERENCE.md for the complete command reference.
Tips
Accuracy principles
- Never hardcode colors. Every
color,background,border-color,fill, andbox-shadowin the prototype MUST use a design system token (hsl(var(--token-name))). If you find yourself typing a hex code like#615E5For an rgb value, stop — find the matching token instead. - Never hand-draw icons. Always export SVGs from Figma using the Plugin API (
node.exportAsync({ format: 'SVG' })). Even simple shapes like a search icon or a chevron should come from Figma, not from your imagination. The designer chose specific icons for a reason. - Always validate before delivery. After building the prototype, compare it against the Figma reference image. Fix any visible deviations before showing it to the user.
- Map values systematically. Build a token mapping table (element → Figma fill → HSL → token → CSS) before writing HTML. This prevents drift and makes it easy to verify every color choice.
CSS and tokens
- Always embed all four CSS files inline —
chip-fonts.css+ theme tokens +typography-spacing.css+arcade-components.css. This makes prototypes fully self-contained. - Embed order matters: fonts → tokens → typography/spacing → components. Components depend on tokens; typography classes are standalone utilities.
- Always wrap color tokens in
hsl()— token values are raw HSL triplets (e.g.,0 0% 100%), NOT completehsl()calls. Writecolor: hsl(var(--text-color-primary))notcolor: var(--text-color-primary). - Shadows are different — shadow tokens are complete values, use them directly:
box-shadow: var(--shadow-depth-02). - Prefer semantic tokens over palette tokens — use
--bg-layer-01instead of--husk-1000when the element is a surface. Semantic tokens adapt across light/dark modes. - Tokens are real production tokens — extracted verbatim from the DevRev product monorepo. If something looks wrong, it may be a component CSS mapping issue, not a token issue.
Typography and fonts
- Chip fonts are mandatory — never use Inter, system fonts, or Google Fonts as the primary typeface. Chip is DevRev's design system font.
- Use typography utility classes (
.text-body,.text-subtitle-1,.text-system, etc.) instead of hardcodingfont-sizeandfont-weight. Match the Figma text node's size and weight to the closest class.
General
- Default to light mode (
class="light") unless the user asks for dark. - Arcade sub-themes: The Arcade theme supports sub-themes via
data-arcade-themeattribute —"jabuticaba"(default) or"dragonfruit". This changes which palette maps to--actionand--intelligencealiases. - Keep it semantic — use the right component for the job (badges for status, cards for grouping, etc.).
- Add hover states — they make prototypes feel alive. The component CSS includes them by default.
- Mobile prototypes: Set
data-device="mobile"and add<meta name="viewport" content="width=device-width, initial-scale=1.0">. - When the user provides a Figma screenshot (not a Figma file link), map visual elements to the closest component class and tokens. You won't have direct Figma node access, so do your best to match — but flag to the user that accuracy is higher when working from a Figma file link.
- Theme comparison: To show both themes side by side, create two prototypes with different
data-themevalues. Don't mix themes in one file.