figma-local 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -18
- package/bin/fig-start +28 -12
- package/package.json +8 -6
- package/plugin/code.js +178 -0
- package/plugin/manifest.json +14 -0
- package/plugin/ui.html +285 -0
- package/skills/figma-css/SKILL.md +119 -0
- package/skills/figma-document/SKILL.md +129 -0
- package/skills/figma-inspect/SKILL.md +98 -0
- package/skills/figma-local/SKILL.md +170 -0
- package/skills/figma-measure/SKILL.md +59 -0
- package/skills/figma-styles/SKILL.md +114 -0
- package/src/daemon.js +1 -1
- package/src/index.js +1772 -25
package/plugin/ui.html
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<style>
|
|
5
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
6
|
+
body {
|
|
7
|
+
font-family: Inter, -apple-system, sans-serif;
|
|
8
|
+
font-size: 11px;
|
|
9
|
+
background: #2c2c2c;
|
|
10
|
+
color: #fff;
|
|
11
|
+
height: 100%;
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
padding: 10px 12px;
|
|
15
|
+
gap: 6px;
|
|
16
|
+
}
|
|
17
|
+
.row {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
gap: 6px;
|
|
21
|
+
}
|
|
22
|
+
.dot {
|
|
23
|
+
width: 8px;
|
|
24
|
+
height: 8px;
|
|
25
|
+
border-radius: 50%;
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
background: #666;
|
|
28
|
+
}
|
|
29
|
+
.dot.connecting { background: #f59e0b; animation: pulse 1.2s infinite; }
|
|
30
|
+
.dot.connected { background: #22c55e; }
|
|
31
|
+
.dot.waiting { background: #666; }
|
|
32
|
+
.dot.error { background: #ef4444; }
|
|
33
|
+
@keyframes pulse {
|
|
34
|
+
0%, 100% { opacity: 1; }
|
|
35
|
+
50% { opacity: 0.3; }
|
|
36
|
+
}
|
|
37
|
+
.title { font-weight: 600; font-size: 12px; }
|
|
38
|
+
#status { color: #aaa; }
|
|
39
|
+
#instructions {
|
|
40
|
+
display: none;
|
|
41
|
+
margin-top: 2px;
|
|
42
|
+
padding: 7px 8px;
|
|
43
|
+
background: #1e1e1e;
|
|
44
|
+
border-radius: 5px;
|
|
45
|
+
gap: 4px;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
}
|
|
48
|
+
#instructions.visible { display: flex; }
|
|
49
|
+
.inst-label { color: #888; font-size: 9px; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
50
|
+
.inst-cmd {
|
|
51
|
+
font-family: 'SF Mono', 'Fira Mono', monospace;
|
|
52
|
+
font-size: 10px;
|
|
53
|
+
color: #7dd3fc;
|
|
54
|
+
word-break: break-all;
|
|
55
|
+
}
|
|
56
|
+
.hint { font-size: 9px; color: #555; }
|
|
57
|
+
</style>
|
|
58
|
+
</head>
|
|
59
|
+
<body>
|
|
60
|
+
<div class="title">Figma Local</div>
|
|
61
|
+
<div class="row">
|
|
62
|
+
<div id="dot" class="dot connecting"></div>
|
|
63
|
+
<span id="status">Connecting...</span>
|
|
64
|
+
</div>
|
|
65
|
+
<div id="instructions">
|
|
66
|
+
<span class="inst-label">Start the CLI in your terminal:</span>
|
|
67
|
+
<span class="inst-cmd">fig connect --safe</span>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="hint" id="hint">Keep this window open</div>
|
|
70
|
+
|
|
71
|
+
<script>
|
|
72
|
+
const PORTS = [3456, 3457, 3458, 3459, 3460];
|
|
73
|
+
let ws = null;
|
|
74
|
+
let reconnectTimer = null;
|
|
75
|
+
let keepaliveTimer = null;
|
|
76
|
+
let firstAttemptDone = false;
|
|
77
|
+
let waitingTimer = null;
|
|
78
|
+
let pendingNonce = null; // holds the challenge nonce until hello is sent
|
|
79
|
+
|
|
80
|
+
const statusEl = document.getElementById('status');
|
|
81
|
+
const dotEl = document.getElementById('dot');
|
|
82
|
+
const instructEl = document.getElementById('instructions');
|
|
83
|
+
const hintEl = document.getElementById('hint');
|
|
84
|
+
|
|
85
|
+
function setStatus(text, state) {
|
|
86
|
+
statusEl.textContent = text;
|
|
87
|
+
dotEl.className = 'dot ' + state;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function showInstructions() {
|
|
91
|
+
instructEl.classList.add('visible');
|
|
92
|
+
hintEl.textContent = 'Waiting for CLI — retrying automatically';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function hideInstructions() {
|
|
96
|
+
instructEl.classList.remove('visible');
|
|
97
|
+
hintEl.textContent = 'Keep this window open';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Try all ports in parallel, use first successful connection
|
|
101
|
+
function connectParallel() {
|
|
102
|
+
if (ws && ws.readyState === WebSocket.OPEN) return;
|
|
103
|
+
|
|
104
|
+
// Only show "Scanning..." on the very first attempt
|
|
105
|
+
if (!firstAttemptDone) {
|
|
106
|
+
setStatus('Connecting...', 'connecting');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let connected = false;
|
|
110
|
+
const attempts = [];
|
|
111
|
+
|
|
112
|
+
PORTS.forEach(port => {
|
|
113
|
+
const url = `ws://127.0.0.1:${port}/plugin`;
|
|
114
|
+
try {
|
|
115
|
+
const socket = new WebSocket(url);
|
|
116
|
+
attempts.push(socket);
|
|
117
|
+
|
|
118
|
+
socket.onopen = () => {
|
|
119
|
+
if (connected) { socket.close(); return; }
|
|
120
|
+
connected = true;
|
|
121
|
+
ws = socket;
|
|
122
|
+
|
|
123
|
+
clearTimeout(waitingTimer);
|
|
124
|
+
|
|
125
|
+
// Close other attempts silently
|
|
126
|
+
attempts.forEach(s => {
|
|
127
|
+
if (s !== socket) {
|
|
128
|
+
s.onclose = null;
|
|
129
|
+
s.onerror = null;
|
|
130
|
+
try { s.close(); } catch (e) {}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
ws.onmessage = handleMessage;
|
|
135
|
+
ws.onclose = handleClose;
|
|
136
|
+
ws.onerror = handleClose;
|
|
137
|
+
|
|
138
|
+
pendingNonce = null; // reset; will be set when challenge arrives
|
|
139
|
+
hideInstructions();
|
|
140
|
+
setStatus('Authenticating...', 'connecting');
|
|
141
|
+
// Do NOT send hello here — wait for daemon's challenge message,
|
|
142
|
+
// then handleMessage will send hello with the echoed nonce.
|
|
143
|
+
startKeepalive();
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
socket.onclose = () => {};
|
|
147
|
+
socket.onerror = () => {};
|
|
148
|
+
} catch (e) {}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// After 1.5s, if still not connected: schedule retry
|
|
152
|
+
setTimeout(() => {
|
|
153
|
+
if (!connected) {
|
|
154
|
+
firstAttemptDone = true;
|
|
155
|
+
attempts.forEach(s => {
|
|
156
|
+
s.onclose = null;
|
|
157
|
+
try { s.close(); } catch (e) {}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Show instructions after the first failed attempt
|
|
161
|
+
setStatus('Waiting for CLI...', 'waiting');
|
|
162
|
+
showInstructions();
|
|
163
|
+
|
|
164
|
+
clearTimeout(reconnectTimer);
|
|
165
|
+
reconnectTimer = setTimeout(connectParallel, 2000);
|
|
166
|
+
}
|
|
167
|
+
}, 1500);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function handleMessage(event) {
|
|
171
|
+
try {
|
|
172
|
+
const msg = JSON.parse(event.data);
|
|
173
|
+
|
|
174
|
+
// Receive nonce challenge from daemon — echo it back in hello to authenticate
|
|
175
|
+
if (msg.type === 'challenge' && typeof msg.nonce === 'string') {
|
|
176
|
+
pendingNonce = msg.nonce;
|
|
177
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
178
|
+
ws.send(JSON.stringify({ type: 'hello', mode: 'plugin', version: '1.3.0', nonce: pendingNonce }));
|
|
179
|
+
setStatus('Connected', 'connected');
|
|
180
|
+
parent.postMessage({ pluginMessage: { type: 'connected' } }, '*');
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (msg.action === 'eval') {
|
|
186
|
+
parent.postMessage({
|
|
187
|
+
pluginMessage: { type: 'eval', id: msg.id, code: msg.code }
|
|
188
|
+
}, '*');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Batch eval support
|
|
192
|
+
if (msg.action === 'eval-batch') {
|
|
193
|
+
parent.postMessage({
|
|
194
|
+
pluginMessage: { type: 'eval-batch', id: msg.id, codes: msg.codes }
|
|
195
|
+
}, '*');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Daemon pings us
|
|
199
|
+
if (msg.action === 'ping') {
|
|
200
|
+
ws.send(JSON.stringify({ type: 'pong', id: msg.id }));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Daemon responds to our ping
|
|
204
|
+
if (msg.type === 'pong') {
|
|
205
|
+
lastPong = Date.now();
|
|
206
|
+
}
|
|
207
|
+
} catch (e) {
|
|
208
|
+
console.error('Message parse error:', e);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function handleClose() {
|
|
213
|
+
if (ws && ws.readyState !== WebSocket.OPEN) {
|
|
214
|
+
ws = null;
|
|
215
|
+
firstAttemptDone = false; // reset so next attempt shows "Connecting..."
|
|
216
|
+
setStatus('Disconnected — reconnecting...', 'connecting');
|
|
217
|
+
stopKeepalive();
|
|
218
|
+
parent.postMessage({ pluginMessage: { type: 'disconnected' } }, '*');
|
|
219
|
+
clearTimeout(reconnectTimer);
|
|
220
|
+
reconnectTimer = setTimeout(connectParallel, 200);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let lastPong = Date.now();
|
|
225
|
+
|
|
226
|
+
function startKeepalive() {
|
|
227
|
+
stopKeepalive();
|
|
228
|
+
lastPong = Date.now();
|
|
229
|
+
keepaliveTimer = setInterval(() => {
|
|
230
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
231
|
+
// Allow up to 90s without pong — Figma throttles timers when in background
|
|
232
|
+
if (Date.now() - lastPong > 90000) {
|
|
233
|
+
console.log('No pong in 90s, reconnecting...');
|
|
234
|
+
ws.close();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
ws.send(JSON.stringify({ type: 'ping' }));
|
|
238
|
+
}
|
|
239
|
+
}, 15000); // Ping every 15s — relaxed to survive background throttling
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function stopKeepalive() {
|
|
243
|
+
if (keepaliveTimer) {
|
|
244
|
+
clearInterval(keepaliveTimer);
|
|
245
|
+
keepaliveTimer = null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Legacy function name for compatibility
|
|
250
|
+
function connect() { connectParallel(); }
|
|
251
|
+
|
|
252
|
+
// Receive results from plugin main thread
|
|
253
|
+
window.onmessage = (event) => {
|
|
254
|
+
const msg = event.data.pluginMessage;
|
|
255
|
+
if (!msg) return;
|
|
256
|
+
|
|
257
|
+
if (msg.type === 'result') {
|
|
258
|
+
// Send result back to CLI via WebSocket
|
|
259
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
260
|
+
ws.send(JSON.stringify({
|
|
261
|
+
type: 'result',
|
|
262
|
+
id: msg.id,
|
|
263
|
+
result: msg.result,
|
|
264
|
+
error: msg.error
|
|
265
|
+
}));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Batch result forwarding (CRITICAL for render-batch stability)
|
|
270
|
+
if (msg.type === 'batch-result') {
|
|
271
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
272
|
+
ws.send(JSON.stringify({
|
|
273
|
+
type: 'batch-result',
|
|
274
|
+
id: msg.id,
|
|
275
|
+
results: msg.results
|
|
276
|
+
}));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// Start connection
|
|
282
|
+
connect();
|
|
283
|
+
</script>
|
|
284
|
+
</body>
|
|
285
|
+
</html>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: figma-css
|
|
3
|
+
description: |
|
|
4
|
+
Use this skill when the user wants to generate CSS or Tailwind classes from a Figma design element. Triggers on: "generate CSS", "CSS from Figma", "Tailwind classes", "get the styles as code", "convert to CSS", "translate to Tailwind", "what CSS would this be", "style this like the design", "code from Figma". Also use when implementing a UI component and needing the exact CSS properties to match the Figma design. Requires an element to be selected in Figma or a node ID / Figma link.
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Bash(fig css *)
|
|
7
|
+
- Bash(fig css)
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Figma CSS
|
|
11
|
+
|
|
12
|
+
Generate ready-to-use CSS or Tailwind utility classes from any Figma element.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
The `fig` CLI must be connected. Check with `fig daemon status`. If not connected: `fig connect --safe`.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### CSS from current selection (rem units — default)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
fig css
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Output example:**
|
|
27
|
+
```css
|
|
28
|
+
/* card [FRAME] */
|
|
29
|
+
.card {
|
|
30
|
+
width: 20rem;
|
|
31
|
+
height: 12.5rem;
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
gap: 0.5rem;
|
|
35
|
+
padding: 1.5rem;
|
|
36
|
+
border-radius: 0.75rem;
|
|
37
|
+
background-color: #ffffff;
|
|
38
|
+
box-shadow: 0rem 0.063rem 0.188rem 0rem rgba(0,0,0,0.1);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### CSS in px units
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
fig css --px
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Tailwind utility classes
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
fig css --tailwind
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Output example:**
|
|
55
|
+
```
|
|
56
|
+
{/* card */}
|
|
57
|
+
className="w-[20rem] h-[12.5rem] flex flex-col gap-[0.5rem] p-[1.5rem] rounded-[0.75rem] bg-[#ffffff] shadow-[0rem_0.063rem_0.188rem_0rem_rgba(0,0,0,0.1)]"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### CSS from a Figma link
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
fig css --link "https://www.figma.com/design/FILEID/Name?node-id=123-456"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### CSS for a specific node
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
fig css --node "123:456"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Raw JSON output
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
fig css --json
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## What it extracts
|
|
79
|
+
|
|
80
|
+
| CSS Property | Figma Source |
|
|
81
|
+
|-------------|-------------|
|
|
82
|
+
| `width`, `height` | Node dimensions |
|
|
83
|
+
| `display: flex`, `flex-direction` | Auto layout mode |
|
|
84
|
+
| `gap` | Auto layout item spacing |
|
|
85
|
+
| `padding` | Auto layout padding (shorthand when possible) |
|
|
86
|
+
| `justify-content`, `align-items` | Auto layout alignment |
|
|
87
|
+
| `flex-wrap` | Auto layout wrap |
|
|
88
|
+
| `border-radius` | Corner radius (uniform or per-corner) |
|
|
89
|
+
| `background-color` | Solid fills (hex or rgba) |
|
|
90
|
+
| `background` | Gradient fills (linear-gradient) |
|
|
91
|
+
| `border` | Strokes (width + style + color) |
|
|
92
|
+
| `font-family` | Text font name |
|
|
93
|
+
| `font-size` | Text size |
|
|
94
|
+
| `font-weight` | Font style → weight mapping |
|
|
95
|
+
| `font-style` | Italic detection |
|
|
96
|
+
| `line-height` | Text line height (px/rem or unitless) |
|
|
97
|
+
| `letter-spacing` | Text letter spacing (px/rem or em) |
|
|
98
|
+
| `text-align` | Horizontal text alignment |
|
|
99
|
+
| `color` | Text fill color |
|
|
100
|
+
| `box-shadow` | Drop shadows and inner shadows |
|
|
101
|
+
| `opacity` | Layer opacity |
|
|
102
|
+
| `overflow: hidden` | Clip content |
|
|
103
|
+
|
|
104
|
+
## Workflow: Design to Code
|
|
105
|
+
|
|
106
|
+
1. Select the element in Figma
|
|
107
|
+
2. Run `fig css` (or `fig css --tailwind`)
|
|
108
|
+
3. Copy the output into your component file
|
|
109
|
+
4. Adjust class names or variable references as needed
|
|
110
|
+
5. Run `fig verify` to visually compare
|
|
111
|
+
|
|
112
|
+
## Tips
|
|
113
|
+
|
|
114
|
+
- For text elements, `color` is used instead of `background-color`
|
|
115
|
+
- Padding is automatically collapsed to shorthand when sides match (e.g., `1rem` instead of `1rem 1rem 1rem 1rem`)
|
|
116
|
+
- Use `--px` when your project uses px-based design systems
|
|
117
|
+
- Use `--tailwind` for React/Vue/Svelte projects using Tailwind CSS
|
|
118
|
+
- The generated class name is derived from the Figma layer name (sanitized to kebab-case)
|
|
119
|
+
- Select multiple elements to generate CSS for all of them at once (up to 10)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: figma-document
|
|
3
|
+
description: |
|
|
4
|
+
Use this skill when the user wants complete documentation of a Figma component, frame, or screen — a full recursive breakdown of every element with all specs (colors, spacing, typography, layout, effects, variable bindings) in a structured format that coding agents can directly use to replicate in code. Triggers on: "document this component", "document this screen", "get full specs", "component documentation", "break down this design", "analyze this component for code", "full spec sheet", "replicate this in code", "all the specs for this", "deep inspect", "complete breakdown". Best for when you need everything about a component in one call — not just the top-level element. Requires an element to be selected in Figma or a node ID / Figma link.
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Bash(fig document *)
|
|
7
|
+
- Bash(fig document)
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Figma Document
|
|
11
|
+
|
|
12
|
+
Generate complete, recursive component documentation from Figma. Unlike `fig inspect` (which shows one element), `fig document` walks the **entire tree** of children and produces:
|
|
13
|
+
|
|
14
|
+
1. **Summary** — component name, type, dimensions, total node count
|
|
15
|
+
2. **Design tokens** — all unique colors, typography, spacing, radii, and shadows used across the tree, with usage counts
|
|
16
|
+
3. **Component tree** — full recursive JSON of every element with all specs (layout, fills, strokes, typography, effects, variable bindings)
|
|
17
|
+
|
|
18
|
+
All values in both **px and rem** (base 16px).
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
The `fig` CLI must be connected. Check with `fig daemon status`. If not connected: `fig connect --safe`.
|
|
23
|
+
|
|
24
|
+
## Commands
|
|
25
|
+
|
|
26
|
+
### Document current selection
|
|
27
|
+
```bash
|
|
28
|
+
fig document
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### JSON output (best for coding agents)
|
|
32
|
+
```bash
|
|
33
|
+
fig document --json
|
|
34
|
+
```
|
|
35
|
+
Returns structured JSON with `summary`, `tokens`, and `tree` — directly parseable for code generation.
|
|
36
|
+
|
|
37
|
+
### Document a specific node
|
|
38
|
+
```bash
|
|
39
|
+
fig document --node "123:456"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Document from a Figma link
|
|
43
|
+
```bash
|
|
44
|
+
fig document --link "https://www.figma.com/design/abc/file?node-id=123-456"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Tokens only
|
|
48
|
+
```bash
|
|
49
|
+
fig document --tokens-only
|
|
50
|
+
```
|
|
51
|
+
Just the design tokens summary (colors, fonts, spacing, radii, shadows) without the full tree.
|
|
52
|
+
|
|
53
|
+
## Output Structure (JSON mode)
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"summary": {
|
|
58
|
+
"name": "Button",
|
|
59
|
+
"type": "COMPONENT",
|
|
60
|
+
"totalNodes": 5,
|
|
61
|
+
"width": 120,
|
|
62
|
+
"height": 40,
|
|
63
|
+
"isComponent": true
|
|
64
|
+
},
|
|
65
|
+
"tokens": {
|
|
66
|
+
"colors": [
|
|
67
|
+
{ "value": "#6366f1", "count": 2 }
|
|
68
|
+
],
|
|
69
|
+
"typography": [
|
|
70
|
+
{ "value": "Inter Medium / 14px", "count": 1 }
|
|
71
|
+
],
|
|
72
|
+
"spacing": [
|
|
73
|
+
{ "value": "8px", "rem": "0.5rem", "count": 1 },
|
|
74
|
+
{ "value": "16px", "rem": "1rem", "count": 2 }
|
|
75
|
+
],
|
|
76
|
+
"radii": [
|
|
77
|
+
{ "value": "8px", "rem": "0.5rem", "count": 1 }
|
|
78
|
+
],
|
|
79
|
+
"shadows": []
|
|
80
|
+
},
|
|
81
|
+
"tree": {
|
|
82
|
+
"name": "Button",
|
|
83
|
+
"type": "COMPONENT",
|
|
84
|
+
"width": { "px": 120, "rem": 7.5 },
|
|
85
|
+
"height": { "px": 40, "rem": 2.5 },
|
|
86
|
+
"layout": {
|
|
87
|
+
"direction": "row",
|
|
88
|
+
"gap": { "px": 8, "rem": 0.5 },
|
|
89
|
+
"padding": { "top": { "px": 8 }, "right": { "px": 16 }, "bottom": { "px": 8 }, "left": { "px": 16 } },
|
|
90
|
+
"mainAxisAlign": "CENTER",
|
|
91
|
+
"crossAxisAlign": "CENTER"
|
|
92
|
+
},
|
|
93
|
+
"fills": [{ "type": "SOLID", "hex": "#6366f1" }],
|
|
94
|
+
"borderRadius": { "px": 8, "rem": 0.5 },
|
|
95
|
+
"children": [
|
|
96
|
+
{
|
|
97
|
+
"name": "Label",
|
|
98
|
+
"type": "TEXT",
|
|
99
|
+
"text": {
|
|
100
|
+
"content": "Click me",
|
|
101
|
+
"fontFamily": "Inter",
|
|
102
|
+
"fontStyle": "Medium",
|
|
103
|
+
"fontSize": { "px": 14, "rem": 0.875 },
|
|
104
|
+
"textAlign": "center"
|
|
105
|
+
},
|
|
106
|
+
"fills": [{ "type": "SOLID", "hex": "#ffffff" }]
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## When to use `document` vs other commands
|
|
114
|
+
|
|
115
|
+
| Need | Command |
|
|
116
|
+
|------|---------|
|
|
117
|
+
| Quick look at one element | `fig inspect` |
|
|
118
|
+
| Full component with all children | `fig document` |
|
|
119
|
+
| Just CSS for one element | `fig css` |
|
|
120
|
+
| All unique styles in a frame | `fig styles` |
|
|
121
|
+
| Spacing between two elements | `fig measure` |
|
|
122
|
+
|
|
123
|
+
## Workflow: Design to Code
|
|
124
|
+
|
|
125
|
+
1. Select the component in Figma
|
|
126
|
+
2. Run `fig document --json` to get the full spec
|
|
127
|
+
3. Use the JSON tree to build the component — each node maps to an element, children map to nested elements
|
|
128
|
+
4. Use the `tokens` section to set up CSS variables or Tailwind config
|
|
129
|
+
5. Verify with `fig verify`
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: figma-inspect
|
|
3
|
+
description: |
|
|
4
|
+
Use this skill when the user wants detailed design specifications from Figma — dimensions, spacing, padding, gap, colors, typography, shadows, border radius, opacity, or variable bindings. Triggers on: "inspect", "get specs", "what are the dimensions", "what font is this", "what color", "spacing", "padding", "design specs", "pixel values", "check the measurements". Also use when translating a Figma design to code and needing exact values. Requires an element to be selected in Figma or a node ID / Figma link.
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Bash(fig inspect *)
|
|
7
|
+
- Bash(fig inspect)
|
|
8
|
+
- Bash(fig measure *)
|
|
9
|
+
- Bash(fig measure)
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Figma Inspect
|
|
13
|
+
|
|
14
|
+
Get detailed design specs for any element in Figma. All values are returned in both **px and rem** (base 16px).
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
The `fig` CLI must be connected. Check with `fig daemon status`. If not connected: `fig connect --safe`.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Inspect current selection
|
|
23
|
+
|
|
24
|
+
The user must select an element in Figma first, then:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
fig inspect
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Returns:**
|
|
31
|
+
- **Dimensions** — width, height (px + rem)
|
|
32
|
+
- **Position** — x, y (px + rem)
|
|
33
|
+
- **Layout** — flex direction, gap, padding (all 4 sides), main/cross axis alignment, wrap
|
|
34
|
+
- **Border radius** — uniform or per-corner (px + rem)
|
|
35
|
+
- **Fills** — hex color, rgba, opacity, gradient stops
|
|
36
|
+
- **Strokes** — color, weight, alignment
|
|
37
|
+
- **Typography** — font family, style, size, weight, line-height, letter-spacing, text-align, decoration, transform
|
|
38
|
+
- **Effects** — drop shadow, inner shadow, blur (offset, radius, spread, color)
|
|
39
|
+
- **Opacity** — when < 1
|
|
40
|
+
- **Variable bindings** — which design tokens are bound to which properties
|
|
41
|
+
- **Component info** — name, set, main vs instance
|
|
42
|
+
|
|
43
|
+
### Inspect from a Figma link
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
fig inspect --link "https://www.figma.com/design/FILEID/Name?node-id=123-456"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Inspect a specific node
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
fig inspect --node "123:456"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Inspect element + all children
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
fig inspect --deep
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This inspects the selected element and then each of its direct children, giving you full specs for a complete component (e.g., a card with its title, description, button).
|
|
62
|
+
|
|
63
|
+
### Raw JSON output
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
fig inspect --json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Use `--json` when you need to programmatically process the specs or pass them to another tool.
|
|
70
|
+
|
|
71
|
+
## Measuring spacing between elements
|
|
72
|
+
|
|
73
|
+
Select exactly **2 elements** in Figma, then:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
fig measure
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
- Horizontal gap between the two elements (px + rem)
|
|
81
|
+
- Vertical gap between the two elements (px + rem)
|
|
82
|
+
- Center-to-center distance on both axes
|
|
83
|
+
|
|
84
|
+
## Workflow: From Specs to Code
|
|
85
|
+
|
|
86
|
+
1. User says "make this button match the Figma design"
|
|
87
|
+
2. Ask user to select the button in Figma
|
|
88
|
+
3. Run `fig inspect` to get exact specs
|
|
89
|
+
4. Run `fig inspect --deep` if it has children (label, icon, etc.)
|
|
90
|
+
5. Apply dimensions, padding, colors, typography, border-radius to code
|
|
91
|
+
6. Run `fig css` if you want ready-to-paste CSS
|
|
92
|
+
|
|
93
|
+
## Tips
|
|
94
|
+
|
|
95
|
+
- If inspect returns nothing for fills/colors, the element itself may have no fill — check its children with `--deep`
|
|
96
|
+
- For spacing between sibling elements, use `fig measure` instead of calculating manually
|
|
97
|
+
- Use `fig inspect --json | jq '.specs[0].typography'` to extract just typography
|
|
98
|
+
- Variable bindings tell you which design tokens are used — map these to your CSS variables or Tailwind config
|