figma-code-agent 1.0.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 +133 -0
- package/bin/install.js +328 -0
- package/knowledge/README.md +62 -0
- package/knowledge/css-strategy.md +973 -0
- package/knowledge/design-to-code-assets.md +855 -0
- package/knowledge/design-to-code-layout.md +929 -0
- package/knowledge/design-to-code-semantic.md +1085 -0
- package/knowledge/design-to-code-typography.md +1003 -0
- package/knowledge/design-to-code-visual.md +1145 -0
- package/knowledge/design-tokens-variables.md +1261 -0
- package/knowledge/design-tokens.md +960 -0
- package/knowledge/figma-api-devmode.md +894 -0
- package/knowledge/figma-api-plugin.md +920 -0
- package/knowledge/figma-api-rest.md +742 -0
- package/knowledge/figma-api-variables.md +848 -0
- package/knowledge/figma-api-webhooks.md +876 -0
- package/knowledge/payload-blocks.md +1184 -0
- package/knowledge/payload-figma-mapping.md +1210 -0
- package/knowledge/payload-visual-builder.md +1004 -0
- package/knowledge/plugin-architecture.md +1176 -0
- package/knowledge/plugin-best-practices.md +1206 -0
- package/knowledge/plugin-codegen.md +1313 -0
- package/package.json +31 -0
- package/skills/README.md +103 -0
- package/skills/audit-plugin/SKILL.md +244 -0
- package/skills/build-codegen-plugin/SKILL.md +279 -0
- package/skills/build-importer/SKILL.md +320 -0
- package/skills/build-plugin/SKILL.md +199 -0
- package/skills/build-token-pipeline/SKILL.md +363 -0
- package/skills/ref-html/SKILL.md +290 -0
- package/skills/ref-layout/SKILL.md +150 -0
- package/skills/ref-payload-block/SKILL.md +415 -0
- package/skills/ref-react/SKILL.md +222 -0
- package/skills/ref-tokens/SKILL.md +347 -0
|
@@ -0,0 +1,929 @@
|
|
|
1
|
+
# Auto Layout → CSS Flexbox Mapping
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Authoritative reference for converting Figma Auto Layout properties to CSS Flexbox. Encodes production-proven mapping rules covering the complete pipeline: extracting Auto Layout data from Figma nodes, transforming it into intermediate types, and generating pixel-accurate CSS. This is the foundational design-to-code module that all component generation depends on.
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
Reference this module when you need to:
|
|
10
|
+
|
|
11
|
+
- Convert a Figma Auto Layout frame into CSS Flexbox (`display: flex`)
|
|
12
|
+
- Determine the correct CSS for Figma sizing modes (FIXED, HUG, FILL)
|
|
13
|
+
- Map Figma alignment properties to `justify-content` and `align-items`
|
|
14
|
+
- Generate gap, padding, and spacing CSS from Figma properties
|
|
15
|
+
- Handle flex-wrap and `align-content` for wrapped layouts
|
|
16
|
+
- Apply min/max constraints to flex containers and children
|
|
17
|
+
- Position absolute children within Auto Layout parents
|
|
18
|
+
- Convert non-Auto-Layout frames (GROUP, legacy FRAME) to CSS
|
|
19
|
+
- Build responsive CSS from multiple Figma frames representing breakpoints
|
|
20
|
+
- Avoid common design-to-code pitfalls (flex-basis, STRETCH, sizing interactions)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Content
|
|
25
|
+
|
|
26
|
+
### 1. Auto Layout → Flexbox Core Mapping
|
|
27
|
+
|
|
28
|
+
Figma's Auto Layout maps directly to CSS Flexbox. Any FRAME, COMPONENT, or INSTANCE node with `layoutMode` set to `HORIZONTAL` or `VERTICAL` is an Auto Layout container.
|
|
29
|
+
|
|
30
|
+
#### Container Detection
|
|
31
|
+
|
|
32
|
+
Only these node types can have Auto Layout:
|
|
33
|
+
- `FRAME`
|
|
34
|
+
- `COMPONENT`
|
|
35
|
+
- `INSTANCE`
|
|
36
|
+
|
|
37
|
+
When `layoutMode` is `NONE`, the frame has no Auto Layout — children use absolute or constraint-based positioning (see Section 7).
|
|
38
|
+
|
|
39
|
+
#### Flex Direction
|
|
40
|
+
|
|
41
|
+
| Figma `layoutMode` | CSS `flex-direction` | Intermediate Mode |
|
|
42
|
+
|---------------------|----------------------|-------------------|
|
|
43
|
+
| `HORIZONTAL` | `row` | `FLEX_ROW` |
|
|
44
|
+
| `VERTICAL` | `column` | `FLEX_COL` |
|
|
45
|
+
| `NONE` | _(no flexbox)_ | `NONE` |
|
|
46
|
+
|
|
47
|
+
Every Auto Layout container generates:
|
|
48
|
+
|
|
49
|
+
```css
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: row; /* or column */
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Primary Axis Alignment (justify-content)
|
|
55
|
+
|
|
56
|
+
`primaryAxisAlignItems` controls distribution along the flex direction.
|
|
57
|
+
|
|
58
|
+
| Figma `primaryAxisAlignItems` | CSS `justify-content` |
|
|
59
|
+
|-------------------------------|-----------------------|
|
|
60
|
+
| `MIN` | `flex-start` |
|
|
61
|
+
| `CENTER` | `center` |
|
|
62
|
+
| `MAX` | `flex-end` |
|
|
63
|
+
| `SPACE_BETWEEN` | `space-between` |
|
|
64
|
+
|
|
65
|
+
#### Counter Axis Alignment (align-items)
|
|
66
|
+
|
|
67
|
+
`counterAxisAlignItems` controls alignment perpendicular to the flex direction.
|
|
68
|
+
|
|
69
|
+
| Figma `counterAxisAlignItems` | CSS `align-items` |
|
|
70
|
+
|-------------------------------|-------------------|
|
|
71
|
+
| `MIN` | `flex-start` |
|
|
72
|
+
| `CENTER` | `center` |
|
|
73
|
+
| `MAX` | `flex-end` |
|
|
74
|
+
| `BASELINE` | `baseline` |
|
|
75
|
+
|
|
76
|
+
> **Important:** `STRETCH` does not appear in the `counterAxisAlignItems` extraction mapping. Stretch behavior is controlled at the child level through sizing modes (FILL on the counter-axis). See Section 2 for how FILL on the counter-axis produces `align-self: stretch`.
|
|
77
|
+
|
|
78
|
+
#### Complete Container Example
|
|
79
|
+
|
|
80
|
+
A horizontal Auto Layout frame with center alignment and space-between distribution:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
Figma properties:
|
|
84
|
+
layoutMode: HORIZONTAL
|
|
85
|
+
primaryAxisAlignItems: SPACE_BETWEEN
|
|
86
|
+
counterAxisAlignItems: CENTER
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-direction: row;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
align-items: center;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### 2. Sizing Modes (CRITICAL)
|
|
99
|
+
|
|
100
|
+
Sizing modes are the **most common source of bugs** in Figma-to-CSS generation. Every child in an Auto Layout container has two sizing properties:
|
|
101
|
+
|
|
102
|
+
- `layoutSizingHorizontal`: `FIXED` | `HUG` | `FILL`
|
|
103
|
+
- `layoutSizingVertical`: `FIXED` | `HUG` | `FILL`
|
|
104
|
+
|
|
105
|
+
These interact with the parent's flex direction. The same property (e.g., `layoutSizingHorizontal`) produces different CSS depending on whether horizontal is the **primary axis** (parent is `FLEX_ROW`) or the **counter axis** (parent is `FLEX_COL`).
|
|
106
|
+
|
|
107
|
+
#### Intermediate Types
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
interface LayoutChildProperties {
|
|
111
|
+
flexGrow: number; // From layoutGrow (0 or 1)
|
|
112
|
+
alignSelf: 'auto' | 'flex-start' | 'center' | 'flex-end' | 'stretch';
|
|
113
|
+
sizingHorizontal: 'FIXED' | 'HUG' | 'FILL';
|
|
114
|
+
sizingVertical: 'FIXED' | 'HUG' | 'FILL';
|
|
115
|
+
minWidth?: number;
|
|
116
|
+
maxWidth?: number;
|
|
117
|
+
minHeight?: number;
|
|
118
|
+
maxHeight?: number;
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Sizing Mode → CSS Decision Tree
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
For each child in an Auto Layout parent:
|
|
126
|
+
|
|
127
|
+
1. Determine which axis is PRIMARY (same as parent flex-direction)
|
|
128
|
+
- Parent FLEX_ROW → horizontal = primary, vertical = counter
|
|
129
|
+
- Parent FLEX_COL → vertical = primary, horizontal = counter
|
|
130
|
+
|
|
131
|
+
2. PRIMARY AXIS sizing:
|
|
132
|
+
├─ FILL → flex-grow: 1; flex-basis: 0;
|
|
133
|
+
├─ FIXED → flex-shrink: 0; (explicit width/height set separately from bounds)
|
|
134
|
+
└─ HUG → (omit — let content determine size)
|
|
135
|
+
|
|
136
|
+
3. COUNTER AXIS sizing:
|
|
137
|
+
├─ FILL → align-self: stretch;
|
|
138
|
+
│ (EXCEPTION: if child has max-width/max-height constraint,
|
|
139
|
+
│ use width: 100% / height: 100% instead — see Section 2.5)
|
|
140
|
+
├─ FIXED → (explicit width/height set separately from bounds)
|
|
141
|
+
└─ HUG → (omit — let content determine size)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### 2.1 FILL on Primary Axis
|
|
145
|
+
|
|
146
|
+
FILL on the primary axis means the child expands to take available space. **Both properties are required:**
|
|
147
|
+
|
|
148
|
+
```css
|
|
149
|
+
flex-grow: 1;
|
|
150
|
+
flex-basis: 0;
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
> **CRITICAL: `flex-basis: 0` is essential.** Without it, `flex-basis` defaults to `auto`, which uses the element's content size as the starting point. This causes content-heavy siblings to take disproportionately more space while empty elements (like image placeholders) get squeezed to near-zero width. Setting `flex-basis: 0` ensures all FILL children start from zero and share space equally based on their `flex-grow` factor.
|
|
154
|
+
|
|
155
|
+
**Example — two FILL children in a row:**
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
Parent: layoutMode: HORIZONTAL
|
|
159
|
+
Child A: layoutSizingHorizontal: FILL (lots of text content)
|
|
160
|
+
Child B: layoutSizingHorizontal: FILL (empty image placeholder)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Correct CSS:
|
|
164
|
+
```css
|
|
165
|
+
/* Both children get equal space */
|
|
166
|
+
.child-a { flex-grow: 1; flex-basis: 0; }
|
|
167
|
+
.child-b { flex-grow: 1; flex-basis: 0; }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Wrong CSS (without flex-basis: 0):
|
|
171
|
+
```css
|
|
172
|
+
/* Child A takes 80%+ because its content is larger */
|
|
173
|
+
.child-a { flex-grow: 1; } /* BAD — flex-basis defaults to auto */
|
|
174
|
+
.child-b { flex-grow: 1; } /* BAD — gets squeezed */
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### 2.2 FIXED on Primary Axis
|
|
178
|
+
|
|
179
|
+
FIXED means the child maintains an exact size. On the primary axis, prevent flex shrinking:
|
|
180
|
+
|
|
181
|
+
```css
|
|
182
|
+
flex-shrink: 0;
|
|
183
|
+
width: 200px; /* or height, depending on axis */
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The explicit dimension comes from the node's bounds (set elsewhere in generation). The `flex-shrink: 0` prevents the flex container from compressing this element below its specified size.
|
|
187
|
+
|
|
188
|
+
#### 2.3 HUG (Content-Based)
|
|
189
|
+
|
|
190
|
+
HUG means the element sizes to its content. No explicit sizing CSS is needed — the default flex behavior handles it:
|
|
191
|
+
|
|
192
|
+
```css
|
|
193
|
+
/* No width/height, no flex-grow, no flex-shrink override */
|
|
194
|
+
/* The element naturally sizes to its content */
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### 2.4 FILL on Counter Axis
|
|
198
|
+
|
|
199
|
+
FILL on the counter axis means the child stretches to fill the parent's cross-axis dimension:
|
|
200
|
+
|
|
201
|
+
```css
|
|
202
|
+
align-self: stretch;
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Example — column layout with horizontal FILL child:**
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
Parent: layoutMode: VERTICAL
|
|
209
|
+
Child: layoutSizingHorizontal: FILL
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```css
|
|
213
|
+
.child {
|
|
214
|
+
align-self: stretch;
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### 2.5 FILL on Counter Axis with Max Constraint (Special Case)
|
|
219
|
+
|
|
220
|
+
When a child has FILL on the counter-axis **and** a max-width or max-height constraint, using `align-self: stretch` would ignore the constraint in some cases. Instead, use percentage sizing:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
Parent: layoutMode: HORIZONTAL (primary = horizontal)
|
|
224
|
+
Child: layoutSizingVertical: FILL, maxHeight: 300
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```css
|
|
228
|
+
.child {
|
|
229
|
+
height: 100%;
|
|
230
|
+
max-height: 300px;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
This allows the parent's `align-items` to position the element correctly when the max constraint caps its size (e.g., centering a capped-height element).
|
|
235
|
+
|
|
236
|
+
The rule:
|
|
237
|
+
- **FILL counter-axis + max constraint on counter dimension** → `width: 100%` / `height: 100%` instead of `align-self: stretch`
|
|
238
|
+
- **FILL counter-axis without max constraint** → `align-self: stretch`
|
|
239
|
+
|
|
240
|
+
#### 2.6 Mixed Sizing
|
|
241
|
+
|
|
242
|
+
Children within the same parent can have different sizing modes. This is valid and common:
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
Parent: layoutMode: HORIZONTAL
|
|
246
|
+
|
|
247
|
+
Child 1: sizingHorizontal: FIXED (sidebar, 280px)
|
|
248
|
+
Child 2: sizingHorizontal: FILL (main content, takes remaining)
|
|
249
|
+
Child 3: sizingHorizontal: HUG (icon, sizes to content)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
```css
|
|
253
|
+
.sidebar { width: 280px; flex-shrink: 0; }
|
|
254
|
+
.main { flex-grow: 1; flex-basis: 0; }
|
|
255
|
+
.icon { /* no explicit sizing */ }
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
#### 2.7 Align Self Override
|
|
259
|
+
|
|
260
|
+
A child's `layoutAlign` property in Figma can override the parent's `counterAxisAlignItems`. This maps to `align-self`:
|
|
261
|
+
|
|
262
|
+
| Figma `layoutAlign` | CSS `align-self` |
|
|
263
|
+
|----------------------|------------------|
|
|
264
|
+
| `INHERIT` | `auto` |
|
|
265
|
+
| `MIN` | `flex-start` |
|
|
266
|
+
| `CENTER` | `center` |
|
|
267
|
+
| `MAX` | `flex-end` |
|
|
268
|
+
| `STRETCH` | `stretch` |
|
|
269
|
+
|
|
270
|
+
> **Note:** `align-self` is only emitted when it differs from `auto` **and** the child is not already using `width: 100%` / `height: 100%` for the FILL + max-constraint pattern. The max-constraint pattern takes precedence.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
### 3. Gap & Spacing
|
|
275
|
+
|
|
276
|
+
#### Primary Axis Gap
|
|
277
|
+
|
|
278
|
+
`itemSpacing` sets the gap between children along the primary axis:
|
|
279
|
+
|
|
280
|
+
| Figma Property | CSS Property |
|
|
281
|
+
|-----------------|--------------|
|
|
282
|
+
| `itemSpacing` | `gap` |
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
Figma: itemSpacing: 16
|
|
286
|
+
CSS: gap: 16px;
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
> **Important:** `itemSpacing` is spacing **between** children, not padding around them. This maps to CSS `gap`, not `padding`.
|
|
290
|
+
|
|
291
|
+
#### Counter Axis Gap (Wrap Mode Only)
|
|
292
|
+
|
|
293
|
+
`counterAxisSpacing` sets the gap between wrapped rows/columns. It only applies when `layoutWrap: WRAP`:
|
|
294
|
+
|
|
295
|
+
| Figma Property | CSS Property |
|
|
296
|
+
|-----------------------|----------------|
|
|
297
|
+
| `counterAxisSpacing` | `row-gap` or `column-gap` (depending on direction) |
|
|
298
|
+
|
|
299
|
+
#### Gap Direction Mapping
|
|
300
|
+
|
|
301
|
+
The CSS gap properties depend on the flex direction:
|
|
302
|
+
|
|
303
|
+
| Layout Mode | Primary Gap (`itemSpacing`) | Cross Gap (`counterAxisSpacing`) |
|
|
304
|
+
|-------------|----------------------------|----------------------------------|
|
|
305
|
+
| `FLEX_ROW` | `column-gap` | `row-gap` |
|
|
306
|
+
| `FLEX_COL` | `row-gap` | `column-gap` |
|
|
307
|
+
|
|
308
|
+
**Shorthand optimization:** When both `row-gap` and `column-gap` are equal, use the `gap` shorthand:
|
|
309
|
+
|
|
310
|
+
```css
|
|
311
|
+
/* Both gaps equal → shorthand */
|
|
312
|
+
gap: 16px;
|
|
313
|
+
|
|
314
|
+
/* Gaps differ → individual properties */
|
|
315
|
+
row-gap: 8px;
|
|
316
|
+
column-gap: 16px;
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### Variable Bindings for Gap
|
|
320
|
+
|
|
321
|
+
When `itemSpacing` or `counterAxisSpacing` is bound to a Figma Variable, generate CSS `var()` references instead of raw pixel values:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
interface VariableReference {
|
|
325
|
+
id: string; // Figma variable ID
|
|
326
|
+
name: string; // Variable name path (e.g., "spacing/md")
|
|
327
|
+
isLocal: boolean; // Local to this file vs. external library
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**CSS generation with variables:**
|
|
332
|
+
|
|
333
|
+
```css
|
|
334
|
+
/* Local variable (defined in same file) — no fallback needed */
|
|
335
|
+
gap: var(--spacing-md);
|
|
336
|
+
|
|
337
|
+
/* External library variable — include px fallback */
|
|
338
|
+
gap: var(--spacing-md, 16px);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
The variable name is converted from Figma's slash-delimited path to CSS custom property naming: `spacing/md` → `--spacing-md`.
|
|
342
|
+
|
|
343
|
+
#### Padding
|
|
344
|
+
|
|
345
|
+
Figma padding is per-side: `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft`.
|
|
346
|
+
|
|
347
|
+
**Shorthand optimization:**
|
|
348
|
+
|
|
349
|
+
```css
|
|
350
|
+
/* All four sides equal */
|
|
351
|
+
padding: 24px;
|
|
352
|
+
|
|
353
|
+
/* Mixed sides — full longhand */
|
|
354
|
+
padding: 24px 16px 24px 16px;
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**With variable bindings:**
|
|
358
|
+
|
|
359
|
+
```css
|
|
360
|
+
/* All four sides bound to the same variable */
|
|
361
|
+
padding: var(--spacing-lg);
|
|
362
|
+
|
|
363
|
+
/* Mixed — some variable-bound, some raw */
|
|
364
|
+
padding: var(--spacing-lg, 24px) 16px var(--spacing-lg, 24px) 16px;
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
The shorthand optimization checks both the numeric values **and** the variable IDs. All four sides must have the same value and the same variable binding (or all unbound) to use the single-value shorthand.
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
### 4. Wrap Mode
|
|
372
|
+
|
|
373
|
+
#### Enabling Wrap
|
|
374
|
+
|
|
375
|
+
| Figma Property | CSS Property |
|
|
376
|
+
|----------------|-----------------|
|
|
377
|
+
| `layoutWrap: WRAP` | `flex-wrap: wrap` |
|
|
378
|
+
|
|
379
|
+
When `layoutWrap` is not `WRAP` (default `NO_WRAP`), no `flex-wrap` property is emitted.
|
|
380
|
+
|
|
381
|
+
#### Align Content (Wrapped Line Distribution)
|
|
382
|
+
|
|
383
|
+
When wrapping is enabled, `counterAxisAlignContent` controls how wrapped lines are distributed:
|
|
384
|
+
|
|
385
|
+
| Figma `counterAxisAlignContent` | CSS `align-content` |
|
|
386
|
+
|---------------------------------|---------------------|
|
|
387
|
+
| `AUTO` | `flex-start` |
|
|
388
|
+
| `SPACE_BETWEEN` | `space-between` |
|
|
389
|
+
|
|
390
|
+
`align-content` is only emitted when `layoutWrap: WRAP`.
|
|
391
|
+
|
|
392
|
+
#### Counter Axis Gap in Wrap Mode
|
|
393
|
+
|
|
394
|
+
`counterAxisSpacing` becomes relevant in wrap mode — it controls the spacing between wrapped rows/columns. Without wrap, `counterAxisSpacing` has no visual effect.
|
|
395
|
+
|
|
396
|
+
#### Wrap Example
|
|
397
|
+
|
|
398
|
+
```
|
|
399
|
+
Figma:
|
|
400
|
+
layoutMode: HORIZONTAL
|
|
401
|
+
layoutWrap: WRAP
|
|
402
|
+
itemSpacing: 16
|
|
403
|
+
counterAxisSpacing: 12
|
|
404
|
+
counterAxisAlignContent: SPACE_BETWEEN
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
```css
|
|
408
|
+
display: flex;
|
|
409
|
+
flex-direction: row;
|
|
410
|
+
flex-wrap: wrap;
|
|
411
|
+
column-gap: 16px;
|
|
412
|
+
row-gap: 12px;
|
|
413
|
+
align-content: space-between;
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
### 5. Min/Max Constraints
|
|
419
|
+
|
|
420
|
+
Figma nodes can have min/max size constraints that map directly to CSS:
|
|
421
|
+
|
|
422
|
+
| Figma Property | CSS Property | Extraction Rule |
|
|
423
|
+
|----------------|--------------|-----------------|
|
|
424
|
+
| `minWidth` | `min-width` | Only if > 0 |
|
|
425
|
+
| `maxWidth` | `max-width` | Only if < Infinity |
|
|
426
|
+
| `minHeight` | `min-height` | Only if > 0 |
|
|
427
|
+
| `maxHeight` | `max-height` | Only if < Infinity |
|
|
428
|
+
|
|
429
|
+
Constraints apply to both containers and children. They are extracted when the value is meaningful (not zero for min, not infinity for max).
|
|
430
|
+
|
|
431
|
+
#### Container Constraints
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// Extraction: only include non-trivial values
|
|
435
|
+
if (frame.minWidth !== null && frame.minWidth > 0) {
|
|
436
|
+
layout.minWidth = frame.minWidth;
|
|
437
|
+
}
|
|
438
|
+
if (frame.maxWidth !== null && frame.maxWidth < Infinity) {
|
|
439
|
+
layout.maxWidth = frame.maxWidth;
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
```css
|
|
444
|
+
/* Container with constraints */
|
|
445
|
+
.card {
|
|
446
|
+
display: flex;
|
|
447
|
+
flex-direction: column;
|
|
448
|
+
min-width: 200px;
|
|
449
|
+
max-width: 600px;
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
#### Child Constraints
|
|
454
|
+
|
|
455
|
+
Children in Auto Layout also support min/max. Common patterns:
|
|
456
|
+
|
|
457
|
+
```css
|
|
458
|
+
/* FILL child with max-width — prevents over-expansion */
|
|
459
|
+
.content {
|
|
460
|
+
flex-grow: 1;
|
|
461
|
+
flex-basis: 0;
|
|
462
|
+
max-width: 800px;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/* FILL child with min-width — prevents collapse */
|
|
466
|
+
.sidebar {
|
|
467
|
+
flex-grow: 1;
|
|
468
|
+
flex-basis: 0;
|
|
469
|
+
min-width: 200px;
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
#### Interaction with Sizing Modes
|
|
474
|
+
|
|
475
|
+
- **FILL + max constraint**: The element grows to fill available space but caps at max. See Section 2.5 for the counter-axis special case.
|
|
476
|
+
- **FIXED + min constraint**: The element has a fixed size but won't go below min (relevant when `flex-shrink` allows some compression).
|
|
477
|
+
- **HUG + max constraint**: Content-sized but capped — useful for text containers that shouldn't exceed a reading width.
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
### 6. Absolute Position Children
|
|
482
|
+
|
|
483
|
+
Figma allows placing children with `layoutPositioning: ABSOLUTE` inside an Auto Layout parent. These children are taken out of the Auto Layout flow.
|
|
484
|
+
|
|
485
|
+
#### CSS Generation
|
|
486
|
+
|
|
487
|
+
```css
|
|
488
|
+
/* Auto Layout parent automatically becomes positioning context */
|
|
489
|
+
.parent {
|
|
490
|
+
display: flex;
|
|
491
|
+
flex-direction: column;
|
|
492
|
+
position: relative; /* Required for absolute children */
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/* Absolutely positioned child within Auto Layout */
|
|
496
|
+
.overlay {
|
|
497
|
+
position: absolute;
|
|
498
|
+
/* Offsets derived from constraint-based positioning */
|
|
499
|
+
top: 8px;
|
|
500
|
+
right: 8px;
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
#### Key Rules
|
|
505
|
+
|
|
506
|
+
1. **Parent needs `position: relative`**: In Figma, the Auto Layout frame is implicitly the positioning context. In CSS, you must add `position: relative` explicitly.
|
|
507
|
+
2. **Child is removed from flex flow**: The absolute child does not participate in flex sizing or gap distribution.
|
|
508
|
+
3. **Offset calculation**: The child's position within the parent is determined by its constraints (see Section 7 for constraint mapping), not by flex ordering.
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
### 7. Non-Auto-Layout Frames (GROUP, Legacy FRAME)
|
|
513
|
+
|
|
514
|
+
When a frame has `layoutMode: NONE` or the node is a `GROUP`, children are positioned using absolute coordinates and constraints.
|
|
515
|
+
|
|
516
|
+
#### GROUP Nodes
|
|
517
|
+
|
|
518
|
+
GROUP nodes have no layout mode. Their children use absolute positioning based on bounds:
|
|
519
|
+
|
|
520
|
+
```css
|
|
521
|
+
/* GROUP container */
|
|
522
|
+
.group {
|
|
523
|
+
position: relative;
|
|
524
|
+
overflow: hidden; /* Matches Figma's default "Clip content" */
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/* GROUP children — absolute positioned */
|
|
528
|
+
.group__child {
|
|
529
|
+
position: absolute;
|
|
530
|
+
left: 120px;
|
|
531
|
+
top: 45px;
|
|
532
|
+
width: 200px;
|
|
533
|
+
height: 80px;
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
#### Coordinate System Difference (CRITICAL for GROUPs)
|
|
538
|
+
|
|
539
|
+
Figma has different coordinate systems for FRAME vs GROUP children:
|
|
540
|
+
|
|
541
|
+
| Parent Type | Child coordinates (`x`, `y`) are relative to... |
|
|
542
|
+
|-------------|--------------------------------------------------|
|
|
543
|
+
| `FRAME` | The frame itself (use directly) |
|
|
544
|
+
| `GROUP` | The containing FRAME, not the group |
|
|
545
|
+
|
|
546
|
+
For GROUP children, you must **subtract the group's position** to get coordinates relative to the group:
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
const isGroupParent = parentType === 'GROUP';
|
|
550
|
+
const relativeX = isGroupParent && parentBounds
|
|
551
|
+
? bounds.x - parentBounds.x
|
|
552
|
+
: bounds.x;
|
|
553
|
+
const relativeY = isGroupParent && parentBounds
|
|
554
|
+
? bounds.y - parentBounds.y
|
|
555
|
+
: bounds.y;
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
#### Legacy FRAME (No Auto Layout)
|
|
559
|
+
|
|
560
|
+
A FRAME with `layoutMode: NONE` behaves like a GROUP for positioning purposes but uses frame-relative coordinates (no subtraction needed):
|
|
561
|
+
|
|
562
|
+
```css
|
|
563
|
+
.legacy-frame {
|
|
564
|
+
position: relative;
|
|
565
|
+
overflow: hidden;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.legacy-frame__child {
|
|
569
|
+
position: absolute;
|
|
570
|
+
left: 50px;
|
|
571
|
+
top: 30px;
|
|
572
|
+
width: 300px;
|
|
573
|
+
height: 150px;
|
|
574
|
+
}
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
#### Constraint Mapping
|
|
578
|
+
|
|
579
|
+
Figma's constraint system positions children relative to their parent frame edges. Constraints apply when the parent resizes:
|
|
580
|
+
|
|
581
|
+
| Figma Constraint | CSS Positioning | Description |
|
|
582
|
+
|------------------|-----------------|-------------|
|
|
583
|
+
| `LEFT` | `left: Xpx` | Fixed distance from left edge |
|
|
584
|
+
| `RIGHT` | `right: Xpx` | Fixed distance from right edge |
|
|
585
|
+
| `TOP` | `top: Xpx` | Fixed distance from top edge |
|
|
586
|
+
| `BOTTOM` | `bottom: Xpx` | Fixed distance from bottom edge |
|
|
587
|
+
| `CENTER` | Centered via `left: 50%; transform: translateX(-50%)` | Centered on axis |
|
|
588
|
+
| `SCALE` | Percentage-based positioning | Scales proportionally |
|
|
589
|
+
| `LEFT_RIGHT` | `left: Xpx; right: Ypx` | Stretches horizontally |
|
|
590
|
+
| `TOP_BOTTOM` | `top: Xpx; bottom: Ypx` | Stretches vertically |
|
|
591
|
+
|
|
592
|
+
#### Z-Index in Non-Auto-Layout
|
|
593
|
+
|
|
594
|
+
For absolutely positioned children, later children (higher index in Figma's layer order) render on top:
|
|
595
|
+
|
|
596
|
+
```css
|
|
597
|
+
.child-0 { z-index: 0; }
|
|
598
|
+
.child-1 { z-index: 1; }
|
|
599
|
+
.child-2 { z-index: 2; }
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
Z-index is only set when there are multiple children.
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
### 8. Responsive Multi-Frame Pattern
|
|
607
|
+
|
|
608
|
+
Figma does not have built-in responsive breakpoints. The pattern for responsive design is to create **multiple frames** — one for each breakpoint — and merge them into unified CSS with media queries.
|
|
609
|
+
|
|
610
|
+
#### Two Approaches for Responsive Grouping
|
|
611
|
+
|
|
612
|
+
##### Approach A: #Breakpoint Suffix (Preferred)
|
|
613
|
+
|
|
614
|
+
Frames are grouped by a `#breakpoint` suffix in their name:
|
|
615
|
+
|
|
616
|
+
```
|
|
617
|
+
Card #mobile → base styles (smallest)
|
|
618
|
+
Card #tablet → @media (min-width: 768px)
|
|
619
|
+
Card #desktop → @media (min-width: 1024px)
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
Recognized suffix patterns:
|
|
623
|
+
- `"Card - #desktop"` — dash separator
|
|
624
|
+
- `"Card #tablet"` — space separator
|
|
625
|
+
- `"Card(#mobile)"` — parentheses
|
|
626
|
+
- `"Card [#desktop]"` — brackets
|
|
627
|
+
|
|
628
|
+
The `#` prefix is the strict marker. Only `#mobile`, `#tablet`, `#desktop` are recognized (case-insensitive).
|
|
629
|
+
|
|
630
|
+
**Classification rules:**
|
|
631
|
+
- A valid responsive group requires **2+ frames** with the same base name and different `#breakpoint` suffixes
|
|
632
|
+
- Frames without `#breakpoint` suffix are treated as standalone layout frames
|
|
633
|
+
- Orphan variants (only one frame for a base name) are treated as standalone layout frames
|
|
634
|
+
- Duplicate breakpoints within the same group are treated as standalone layout frames
|
|
635
|
+
|
|
636
|
+
##### Approach B: Variant Component Detection
|
|
637
|
+
|
|
638
|
+
For COMPONENT_SET nodes, responsive variants are detected through variant properties:
|
|
639
|
+
|
|
640
|
+
**Recognized responsive property names** (case-insensitive):
|
|
641
|
+
- `Device`, `Breakpoint`, `Screen`, `Viewport`, `Responsive`, `Size`
|
|
642
|
+
|
|
643
|
+
**Value-to-breakpoint mapping:**
|
|
644
|
+
|
|
645
|
+
| Variant Value | Maps To |
|
|
646
|
+
|---------------|------------|
|
|
647
|
+
| `mobile`, `phone`, `small`, `sm`, `xs` | `mobile` |
|
|
648
|
+
| `tablet`, `medium`, `md` | `tablet` |
|
|
649
|
+
| `desktop`, `large`, `lg`, `xl` | `desktop` |
|
|
650
|
+
|
|
651
|
+
Detection requires at least 2 values that map to different standard breakpoint names.
|
|
652
|
+
|
|
653
|
+
#### Standard Breakpoints (Mobile-First)
|
|
654
|
+
|
|
655
|
+
| Breakpoint | min-width | max-width | CSS |
|
|
656
|
+
|------------|-----------|-----------|-----|
|
|
657
|
+
| `mobile` | _(base)_ | 767px | No media query (base styles) |
|
|
658
|
+
| `tablet` | 768px | 1023px | `@media (min-width: 768px)` |
|
|
659
|
+
| `desktop` | 1024px | _(none)_ | `@media (min-width: 1024px)` |
|
|
660
|
+
|
|
661
|
+
Breakpoint detection also falls back to frame dimensions:
|
|
662
|
+
- Width < 768px → mobile
|
|
663
|
+
- Width 768–1023px → tablet
|
|
664
|
+
- Width >= 1024px → desktop
|
|
665
|
+
|
|
666
|
+
#### Mobile-First CSS Generation
|
|
667
|
+
|
|
668
|
+
The smallest frame provides **base styles** (no media query). Larger frames contribute **override styles** wrapped in `@media (min-width: ...)` blocks:
|
|
669
|
+
|
|
670
|
+
```css
|
|
671
|
+
/* Base styles from mobile frame */
|
|
672
|
+
.card {
|
|
673
|
+
display: flex;
|
|
674
|
+
flex-direction: column;
|
|
675
|
+
gap: 12px;
|
|
676
|
+
padding: 16px;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.card__title {
|
|
680
|
+
font-size: 18px;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/* Tablet overrides — only properties that differ */
|
|
684
|
+
@media (min-width: 768px) {
|
|
685
|
+
.card {
|
|
686
|
+
flex-direction: row;
|
|
687
|
+
gap: 24px;
|
|
688
|
+
padding: 24px;
|
|
689
|
+
}
|
|
690
|
+
.card__title {
|
|
691
|
+
font-size: 22px;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/* Desktop overrides — only properties that differ from base */
|
|
696
|
+
@media (min-width: 1024px) {
|
|
697
|
+
.card {
|
|
698
|
+
gap: 32px;
|
|
699
|
+
padding: 32px;
|
|
700
|
+
}
|
|
701
|
+
.card__title {
|
|
702
|
+
font-size: 28px;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
#### BEM Suffix Matching
|
|
708
|
+
|
|
709
|
+
Elements are matched across breakpoint frames using **BEM class suffix matching**. The BEM block name differs between frames (e.g., `.card---mobile` vs `.card---desktop`), but the element/modifier suffix is identical for elements at the same position:
|
|
710
|
+
|
|
711
|
+
```
|
|
712
|
+
Mobile frame: .card---mobile → suffix: "" (root)
|
|
713
|
+
.card---mobile__title → suffix: "__title"
|
|
714
|
+
.card---mobile__body → suffix: "__body"
|
|
715
|
+
|
|
716
|
+
Desktop frame: .card---desktop → suffix: "" (root)
|
|
717
|
+
.card---desktop__title → suffix: "__title"
|
|
718
|
+
.card---desktop__body → suffix: "__body"
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
Matching by suffix: `""` matches `""`, `"__title"` matches `"__title"`, etc.
|
|
722
|
+
|
|
723
|
+
After matching, selectors are remapped to a unified component class: `.card---mobile__title` and `.card---desktop__title` both become `.card__title` in the output.
|
|
724
|
+
|
|
725
|
+
#### Style Diffing and Overrides
|
|
726
|
+
|
|
727
|
+
Only **changed properties** are emitted in media query blocks. The diff algorithm:
|
|
728
|
+
|
|
729
|
+
1. Compare each property in the larger breakpoint against the base
|
|
730
|
+
2. Include properties that are new or have different values
|
|
731
|
+
3. **Reset layout properties** that exist in base but not in the larger breakpoint
|
|
732
|
+
|
|
733
|
+
Layout properties that need explicit resets to prevent cascade leaking:
|
|
734
|
+
|
|
735
|
+
| Property | Reset Value |
|
|
736
|
+
|---------------|-------------|
|
|
737
|
+
| `align-self` | `auto` |
|
|
738
|
+
| `flex-grow` | `0` |
|
|
739
|
+
| `flex-shrink` | `1` |
|
|
740
|
+
| `flex-basis` | `auto` |
|
|
741
|
+
|
|
742
|
+
**Example of reset:**
|
|
743
|
+
|
|
744
|
+
```css
|
|
745
|
+
/* Mobile base: column layout, child stretches */
|
|
746
|
+
.card__image {
|
|
747
|
+
align-self: stretch;
|
|
748
|
+
flex-grow: 1;
|
|
749
|
+
flex-basis: 0;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/* Desktop: row layout, child is fixed — must reset mobile layout props */
|
|
753
|
+
@media (min-width: 1024px) {
|
|
754
|
+
.card__image {
|
|
755
|
+
width: 100%;
|
|
756
|
+
max-width: 277px;
|
|
757
|
+
align-self: auto; /* Reset — no longer stretching */
|
|
758
|
+
flex-grow: 0; /* Reset — no longer filling */
|
|
759
|
+
flex-shrink: 1; /* Reset — back to default */
|
|
760
|
+
flex-basis: auto; /* Reset — back to default */
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
#### Responsive Width Transformation
|
|
766
|
+
|
|
767
|
+
Fixed pixel widths in responsive overrides are transformed to a fluid pattern to prevent overflow at intermediate viewport widths:
|
|
768
|
+
|
|
769
|
+
```css
|
|
770
|
+
/* Before transformation */
|
|
771
|
+
.card__sidebar {
|
|
772
|
+
width: 277px;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/* After transformation — fluid with cap */
|
|
776
|
+
.card__sidebar {
|
|
777
|
+
width: 100%;
|
|
778
|
+
max-width: 277px;
|
|
779
|
+
}
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
This only applies to responsive override styles (not base styles) and only when no `max-width` is already explicitly set.
|
|
783
|
+
|
|
784
|
+
---
|
|
785
|
+
|
|
786
|
+
### 9. Common Pitfalls & Edge Cases
|
|
787
|
+
|
|
788
|
+
#### Pitfall: Missing `flex-basis: 0` for FILL Items
|
|
789
|
+
|
|
790
|
+
**Problem:** Without `flex-basis: 0`, FILL children distribute space based on content size, not equally.
|
|
791
|
+
|
|
792
|
+
**Rule:** FILL on primary axis always requires both `flex-grow: 1` AND `flex-basis: 0`.
|
|
793
|
+
|
|
794
|
+
```css
|
|
795
|
+
/* CORRECT */
|
|
796
|
+
.fill-child { flex-grow: 1; flex-basis: 0; }
|
|
797
|
+
|
|
798
|
+
/* WRONG — content-based distribution */
|
|
799
|
+
.fill-child { flex-grow: 1; }
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
#### Pitfall: STRETCH Only Exists on Counter Axis
|
|
803
|
+
|
|
804
|
+
**Problem:** Trying to apply STRETCH behavior on the primary axis.
|
|
805
|
+
|
|
806
|
+
**Rule:** `STRETCH` / `align-self: stretch` only affects the **counter axis**. On the primary axis, expanding behavior is achieved through FILL (`flex-grow: 1; flex-basis: 0`), not stretch.
|
|
807
|
+
|
|
808
|
+
#### Pitfall: `itemSpacing` Is Not Padding
|
|
809
|
+
|
|
810
|
+
**Problem:** Confusing `itemSpacing` (gap between children) with `padding` (space around children).
|
|
811
|
+
|
|
812
|
+
**Rule:** `itemSpacing` → CSS `gap`. Padding uses the separate `paddingTop/Right/Bottom/Left` properties.
|
|
813
|
+
|
|
814
|
+
#### Pitfall: GROUP Nodes Have No Layout Mode
|
|
815
|
+
|
|
816
|
+
**Problem:** Treating GROUP children as flex items.
|
|
817
|
+
|
|
818
|
+
**Rule:** GROUP nodes never have Auto Layout. All GROUP children must use `position: absolute` with coordinates adjusted for the GROUP's coordinate system (subtract parent position).
|
|
819
|
+
|
|
820
|
+
#### Pitfall: "Auto" in Figma UI Means HUG, Not FILL
|
|
821
|
+
|
|
822
|
+
**Problem:** When Figma's UI shows "Auto" for width/height, it maps to `HUG` (content-based sizing), not `FILL` (expand to fill).
|
|
823
|
+
|
|
824
|
+
**Rule:** "Auto" in the Figma UI → `layoutSizingHorizontal: HUG` or `layoutSizingVertical: HUG`. FILL is shown as "Fill container" in the UI.
|
|
825
|
+
|
|
826
|
+
#### Pitfall: Layout Property Cascade Leaking
|
|
827
|
+
|
|
828
|
+
**Problem:** In responsive CSS, mobile base styles for `flex-grow`, `flex-basis`, `align-self`, and `flex-shrink` leak into larger breakpoints via the cascade when the desktop layout no longer uses those properties.
|
|
829
|
+
|
|
830
|
+
**Rule:** When a layout property exists in the base breakpoint but not in a larger breakpoint, explicitly reset it. See Section 8 for the reset values.
|
|
831
|
+
|
|
832
|
+
#### Edge Case: FILL Counter-Axis + Max Constraint
|
|
833
|
+
|
|
834
|
+
When a child has FILL on the counter-axis and a max-width/max-height constraint, use `width: 100%` / `height: 100%` instead of `align-self: stretch`. This preserves the parent's ability to position the element with `align-items` when the constraint caps its size. See Section 2.5.
|
|
835
|
+
|
|
836
|
+
#### Edge Case: Container primaryAxisSizing and counterAxisSizing
|
|
837
|
+
|
|
838
|
+
The Auto Layout container itself has sizing modes for its own dimensions:
|
|
839
|
+
|
|
840
|
+
| Property | Value | Meaning |
|
|
841
|
+
|----------|-------|---------|
|
|
842
|
+
| `primaryAxisSizingMode` | `FIXED` | Container has explicit size on primary axis |
|
|
843
|
+
| `primaryAxisSizingMode` | `AUTO` | Container hugs its content on primary axis |
|
|
844
|
+
| `counterAxisSizingMode` | `FIXED` | Container has explicit size on counter axis |
|
|
845
|
+
| `counterAxisSizingMode` | `AUTO` | Container hugs its content on counter axis |
|
|
846
|
+
|
|
847
|
+
These determine whether the container itself gets an explicit width/height or sizes to content. They are separate from the child-level `layoutSizingHorizontal`/`layoutSizingVertical` properties.
|
|
848
|
+
|
|
849
|
+
#### Edge Case: Variable Fallback Strategy
|
|
850
|
+
|
|
851
|
+
When a Figma Variable is from an **external library** (not local to the file), include a pixel fallback in the `var()` expression:
|
|
852
|
+
|
|
853
|
+
```css
|
|
854
|
+
/* Local variable — no fallback */
|
|
855
|
+
gap: var(--spacing-md);
|
|
856
|
+
|
|
857
|
+
/* External library variable — include fallback */
|
|
858
|
+
gap: var(--spacing-md, 16px);
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
This ensures the CSS works even if the variable definition is not available in the consuming project.
|
|
862
|
+
|
|
863
|
+
---
|
|
864
|
+
|
|
865
|
+
### Intermediate Type Reference
|
|
866
|
+
|
|
867
|
+
The complete intermediate types used between extraction and generation:
|
|
868
|
+
|
|
869
|
+
```typescript
|
|
870
|
+
interface LayoutProperties {
|
|
871
|
+
mode: 'NONE' | 'FLEX_ROW' | 'FLEX_COL' | 'GRID';
|
|
872
|
+
primaryAxisSizing: 'FIXED' | 'AUTO';
|
|
873
|
+
counterAxisSizing: 'FIXED' | 'AUTO';
|
|
874
|
+
justify: 'flex-start' | 'flex-end' | 'center' | 'space-between';
|
|
875
|
+
align: 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch';
|
|
876
|
+
gap: number;
|
|
877
|
+
gapVariable?: VariableReference;
|
|
878
|
+
counterAxisGap?: number;
|
|
879
|
+
counterAxisGapVariable?: VariableReference;
|
|
880
|
+
padding: {
|
|
881
|
+
top: number;
|
|
882
|
+
right: number;
|
|
883
|
+
bottom: number;
|
|
884
|
+
left: number;
|
|
885
|
+
};
|
|
886
|
+
paddingVariables?: {
|
|
887
|
+
top?: VariableReference;
|
|
888
|
+
right?: VariableReference;
|
|
889
|
+
bottom?: VariableReference;
|
|
890
|
+
left?: VariableReference;
|
|
891
|
+
};
|
|
892
|
+
wrap: boolean;
|
|
893
|
+
wrapAlign?: 'flex-start' | 'space-between';
|
|
894
|
+
minWidth?: number;
|
|
895
|
+
maxWidth?: number;
|
|
896
|
+
minHeight?: number;
|
|
897
|
+
maxHeight?: number;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
interface LayoutChildProperties {
|
|
901
|
+
flexGrow: number;
|
|
902
|
+
alignSelf: 'auto' | 'flex-start' | 'center' | 'flex-end' | 'stretch';
|
|
903
|
+
sizingHorizontal: 'FIXED' | 'HUG' | 'FILL';
|
|
904
|
+
sizingVertical: 'FIXED' | 'HUG' | 'FILL';
|
|
905
|
+
minWidth?: number;
|
|
906
|
+
maxWidth?: number;
|
|
907
|
+
minHeight?: number;
|
|
908
|
+
maxHeight?: number;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
interface VariableReference {
|
|
912
|
+
id: string;
|
|
913
|
+
name: string;
|
|
914
|
+
isLocal: boolean;
|
|
915
|
+
}
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
---
|
|
919
|
+
|
|
920
|
+
## Cross-References
|
|
921
|
+
|
|
922
|
+
- **`figma-api-rest.md`** — Node tree structure, `absoluteBoundingBox` for bounds, file/node fetching for accessing layout data via REST API
|
|
923
|
+
- **`figma-api-plugin.md`** — SceneNode types (FrameNode, ComponentNode, InstanceNode, GroupNode), LayoutMixin properties (`layoutMode`, `layoutSizingHorizontal`, `layoutAlign`, `itemSpacing`, etc.), plugin sandbox model for extraction code
|
|
924
|
+
- **`figma-api-variables.md`** — Variables API for resolving `boundVariables` references, variable collections, modes, and the `figma.variables.getVariableByIdAsync()` method used in extraction
|
|
925
|
+
- **`design-to-code-visual.md`** — Visual properties (fills, strokes, effects) that combine with layout for complete component CSS; visual and layout styles are generated independently and merged
|
|
926
|
+
- **`design-to-code-typography.md`** — Text properties that interact with sizing modes (text auto-resize vs layout sizing), vertical alignment requiring flex, styled segments
|
|
927
|
+
- **`design-to-code-assets.md`** — Vector container detection interacts with Auto Layout detection (Auto Layout overrides vector container), asset nodes skip layout child sizing
|
|
928
|
+
- **`design-to-code-semantic.md`** — BEM naming conventions used in responsive matching and class generation, semantic tag selection that consumes layout context
|
|
929
|
+
- **`css-strategy.md`** — Layered CSS approach (Tailwind for layout bones, CSS Modules for visual skin) that consumes layout CSS output. Property placement decision tree for layout properties (Layer 1).
|