@rogieking/figui3 8.9.10 → 8.9.12
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/.cursor/skills/fig-editor/SKILL.md +184 -0
- package/.cursor/skills/fig-editor/reference.md +46 -0
- package/.cursor/skills/fig-lab/SKILL.md +159 -0
- package/.cursor/skills/fig-lab/reference.md +67 -0
- package/.cursor/skills/figui3/SKILL.md +160 -188
- package/.cursor/skills/figui3/reference.md +91 -0
- package/.cursor/skills/propkit/SKILL.md +66 -185
- package/README.md +66 -1
- package/components.css +109 -30
- package/dist/components.css +1 -1
- package/dist/fig-editor.css +1 -1
- package/dist/fig-editor.js +1 -1
- package/dist/fig.css +1 -1
- package/dist/fig.js +162 -3
- package/fig-editor.css +11 -91
- package/fig-editor.js +5 -0
- package/fig.js +525 -122
- package/package.json +1 -1
|
@@ -1,54 +1,34 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: propkit
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
Guides Figma-style property panel composition in the /propskit playground using
|
|
5
|
+
fig-field rows and FigUI3 controls. Use when building or editing
|
|
6
|
+
playground/src/data/sections.ts, generating field prompts, or choosing between
|
|
7
|
+
raw fig-* rows and propskit-* wrappers (fig-lab).
|
|
4
8
|
user-invocable: false
|
|
5
9
|
---
|
|
6
10
|
|
|
7
11
|
# PropKit
|
|
8
12
|
|
|
9
|
-
Patterns for
|
|
13
|
+
Patterns for Figma property panels. Two layers:
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
| Surface | Route | What to use |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| **PropsKit playground** | `/propskit` | Horizontal `fig-field` + core `fig-*` |
|
|
18
|
+
| **Lab wrappers** | `/propskit/lab` | `propskit-*` (see `fig-lab` skill) |
|
|
12
19
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```json
|
|
16
|
-
!`node -e "const fs=require('fs'); const ok=fs.existsSync('playground/src/main.tsx'); console.log(JSON.stringify({playground:ok, route:'/propkit', example:'horizontal fig-field + label + fig-* control'},null,2))" 2>/dev/null || echo '{"error":"context unavailable"}'`
|
|
17
|
-
```
|
|
20
|
+
Canonical `/propskit` examples: `playground/src/data/sections.ts`.
|
|
21
|
+
Core control APIs: `figui3` skill. Select/fill picker: `fig-editor`. Labeled wrappers: `fig-lab`.
|
|
18
22
|
|
|
19
23
|
## Principles
|
|
20
24
|
|
|
21
|
-
1.
|
|
22
|
-
2.
|
|
23
|
-
3.
|
|
24
|
-
4.
|
|
25
|
-
5.
|
|
26
|
-
|
|
27
|
-
## React + Vite PropKit Usage
|
|
28
|
-
|
|
29
|
-
### Include FigUI3 in React projects
|
|
30
|
-
|
|
31
|
-
- Import once in app bootstrap:
|
|
32
|
-
- `import "@rogieking/figui3/fig.css";`
|
|
33
|
-
- `await import("@rogieking/figui3/fig.js");`
|
|
34
|
-
- Register components before first React render to avoid undefined custom elements.
|
|
35
|
-
- Keep this setup in entry files (`main.tsx` / `main.jsx`), not scattered across feature components.
|
|
36
|
-
|
|
37
|
-
### Vite setup and tree-shaking behavior
|
|
25
|
+
1. Default to horizontal `fig-field` rows.
|
|
26
|
+
2. One concise label per control.
|
|
27
|
+
3. For new labeled property controls in lab, prefer `propskit-*` over duplicating field chrome.
|
|
28
|
+
4. In `/propskit` demos, keep composing from `fig-*` so examples stay core-only unless the section needs lab.
|
|
29
|
+
5. Panel width ~240px. Match existing section density.
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
```ts
|
|
42
|
-
import { defineConfig } from "vite";
|
|
43
|
-
import react from "@vitejs/plugin-react";
|
|
44
|
-
|
|
45
|
-
export default defineConfig({
|
|
46
|
-
plugins: [react()],
|
|
47
|
-
});
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
- In production, FigUI3 side-effect registration can be tree-shaken if only imported for side effects.
|
|
51
|
-
- Preferred pattern (from `webgpu-effects`) is explicit async bootstrap:
|
|
31
|
+
## React bootstrap
|
|
52
32
|
|
|
53
33
|
```tsx
|
|
54
34
|
import "@rogieking/figui3/fig.css";
|
|
@@ -57,169 +37,70 @@ const bootstrap = async () => {
|
|
|
57
37
|
await import("@rogieking/figui3/fig.js");
|
|
58
38
|
createRoot(document.getElementById("app")!).render(<App />);
|
|
59
39
|
};
|
|
60
|
-
|
|
61
40
|
bootstrap();
|
|
62
41
|
```
|
|
63
42
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- Use refs and native event listeners (`input`, `change`) for reliable control updates.
|
|
70
|
-
|
|
71
|
-
## Critical Rules
|
|
72
|
-
|
|
73
|
-
### Field Composition
|
|
74
|
-
|
|
75
|
-
- Default pattern: label + single primary control inside one horizontal `fig-field`.
|
|
76
|
-
- Keep control-specific options on the component itself (not hidden wrapper logic).
|
|
77
|
-
- Use `full` where property controls should stretch within row constraints.
|
|
78
|
-
- Avoid mixing unrelated controls in a single field row unless intentionally grouped.
|
|
79
|
-
|
|
80
|
-
### Prompt Generation Style
|
|
81
|
-
|
|
82
|
-
- Write prompts as imperative build instructions.
|
|
83
|
-
- Include field direction, control tag, and meaningful attrs.
|
|
84
|
-
- Prefer short explicit phrasing over vague prose.
|
|
85
|
-
- Keep wording consistent:
|
|
86
|
-
- `Use a horizontal fig-field...`
|
|
87
|
-
- `With a label of ...`
|
|
88
|
-
- Include concrete defaults when relevant (value, min/max, step, units, mode, variant) so generated fields are deterministic.
|
|
89
|
-
- Avoid placeholder-only prompts for numeric controls; always specify range semantics.
|
|
90
|
-
|
|
91
|
-
### Control Guidance
|
|
92
|
-
|
|
93
|
-
- **Image:** prefer `fig-image` with `upload`, `fit`, and `aspect-ratio` where needed.
|
|
94
|
-
- **Color:** use `fig-input-color` with `text="true"` and optional `alpha`.
|
|
95
|
-
- **Fill:** use `fig-input-fill` for multi-mode fills; keep value JSON valid.
|
|
96
|
-
- **Slider:** choose proper type (`range`, `opacity`, `hue`, `stepper`, `delta`) and include units/transform intentionally.
|
|
97
|
-
- **Dropdown:** use `fig-dropdown`; include sensible default options.
|
|
98
|
-
- **Boolean:** use `fig-switch`; avoid using dropdowns for true/false.
|
|
99
|
-
- **Discrete choices:** use `fig-segmented-control` + `fig-segment`.
|
|
100
|
-
- **Motion easing:** use `fig-easing-curve` with/without presets depending on context.
|
|
101
|
-
- **Angle:** use `fig-input-angle` with `text="true"` for precision workflows.
|
|
102
|
-
|
|
103
|
-
### Slider Types and Variants
|
|
104
|
-
|
|
105
|
-
- Default to `type="range"` for generic numeric properties (opacity %, size, spacing, intensity).
|
|
106
|
-
- Use `type="opacity"` when color context is needed (set `color` and usually `units="%"`).
|
|
107
|
-
- Use `type="hue"` only for hue selection workflows.
|
|
108
|
-
- Use `type="stepper"` for discrete snap points (include a `datalist` with valid stops).
|
|
109
|
-
- Use `type="delta"` for offset/relative adjustments around a neutral point (typically include `default`, and often symmetric min/max).
|
|
110
|
-
- Text input is shown by default; use `text="false"` only for compact/simplified rows.
|
|
111
|
-
- Use `transform` when internal value scale differs from UI display (example: internal `0..1`, display `0..100%`).
|
|
112
|
-
- Variants:
|
|
113
|
-
- Default variant for most property panels.
|
|
114
|
-
- `variant="classic"` only when the previous slider appearance is needed.
|
|
115
|
-
- Always set explicit `min`, `max`, and `step` (and `units` where applicable) to keep behavior predictable.
|
|
116
|
-
- Set `default` on PropsKit value controls when reset behavior should differ from the initial value.
|
|
117
|
-
- PropsKit value controls support `resetToDefault()` and a right-click **Reset** menu; `propskit-slider` also supports double-click reset.
|
|
118
|
-
|
|
119
|
-
### Control Selection Heuristics
|
|
120
|
-
|
|
121
|
-
- Use `fig-slider` for scrub-friendly continuous values (opacity, intensity, scale, blur amount).
|
|
122
|
-
- Use `fig-input-number` for precise direct entry (sizes, coordinates, exact typed values).
|
|
123
|
-
- Use slider + text (`text="true"`) when users need both quick scrubbing and precise adjustment.
|
|
124
|
-
- Use `fig-segmented-control` for small discrete sets (2-5 fixed options).
|
|
125
|
-
- Use `fig-dropdown` for larger or less frequently switched option sets.
|
|
126
|
-
- Use `fig-switch` for binary state, never slider/dropdown for pure on/off.
|
|
127
|
-
|
|
128
|
-
### UX Consistency
|
|
129
|
-
|
|
130
|
-
- Keep panel patterns visually consistent across sections.
|
|
131
|
-
- Preserve theme behavior (light/dark) and avoid non-token color overrides.
|
|
132
|
-
- Ensure labels and controls remain keyboard and screen-reader usable.
|
|
133
|
-
|
|
134
|
-
## Key Patterns
|
|
43
|
+
Add `fig-editor` when using `fig-select` / fill picker. Add `fig-lab` when using `propskit-*`.
|
|
44
|
+
|
|
45
|
+
On `fig-*` and `<dialog is="fig-...">`, use `class` not `className`.
|
|
46
|
+
|
|
47
|
+
## Field composition
|
|
135
48
|
|
|
136
49
|
```html
|
|
137
|
-
<!-- Canonical PropKit row -->
|
|
138
50
|
<fig-field direction="horizontal">
|
|
139
51
|
<label>Opacity</label>
|
|
140
52
|
<fig-slider value="75" min="0" max="100" text="true" units="%" full></fig-slider>
|
|
141
53
|
</fig-field>
|
|
142
54
|
```
|
|
143
55
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
56
|
+
- Put control attrs on the control, not a wrapper.
|
|
57
|
+
- Use `full` when the control should stretch.
|
|
58
|
+
- Do not mix unrelated controls in one row unless grouped on purpose.
|
|
59
|
+
|
|
60
|
+
## Control heuristics
|
|
61
|
+
|
|
62
|
+
| Intent | `/propskit` (core) | Lab wrapper |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| Boolean | `fig-switch` | `propskit-switch` |
|
|
65
|
+
| Continuous number | `fig-slider` | `propskit-slider` |
|
|
66
|
+
| Exact number | `fig-input-number` | `propskit-number` |
|
|
67
|
+
| Text | `fig-input-text` | `propskit-text` |
|
|
68
|
+
| Small discrete set (2–5) | `fig-segmented-control` | — |
|
|
69
|
+
| Larger / rich list | `fig-select` (editor) | `propskit-select` |
|
|
70
|
+
| Native select only | `fig-dropdown` | — |
|
|
71
|
+
| Color | `fig-input-color` `text="true"` | `propskit-color` |
|
|
72
|
+
| Fill | `fig-input-fill` | — |
|
|
73
|
+
| Gradient | `fig-input-gradient` | `propskit-gradient` |
|
|
74
|
+
| Image | `fig-image` `upload` | — |
|
|
75
|
+
| Easing | `fig-easing-curve` | — |
|
|
76
|
+
| Angle | `fig-input-angle` (**lab**) | — |
|
|
77
|
+
| X/Y | `fig-joystick` or two numbers | `propskit-position` |
|
|
78
|
+
|
|
79
|
+
Do not use dropdown/slider for pure on/off. Do not use `fig-dropdown` for Figma-style property selects when `fig-select` is available.
|
|
80
|
+
|
|
81
|
+
## Slider rules
|
|
82
|
+
|
|
83
|
+
- Default `type="range"`. Always set `min`, `max`, `step`.
|
|
84
|
+
- `opacity`: set `color`, usually `units="%"`
|
|
85
|
+
- `hue`: hue workflows only
|
|
86
|
+
- `stepper`: include a datalist of stops
|
|
87
|
+
- `delta`: include `default`, often symmetric min/max
|
|
88
|
+
- Text field on by default; `text="false"` for compact rows
|
|
89
|
+
- `transform` when internal scale ≠ display scale
|
|
90
|
+
- `variant="classic"` only when the old look is required
|
|
91
|
+
|
|
92
|
+
Prompt style: imperative, include direction, tag, and behavior-critical attrs.
|
|
166
93
|
|
|
167
94
|
```txt
|
|
168
|
-
Prompt pattern:
|
|
169
95
|
Use a horizontal fig-field, with a fig-slider, min=0 max=100 text=true units=%. With a label of Opacity.
|
|
170
96
|
```
|
|
171
97
|
|
|
172
|
-
```html
|
|
173
|
-
<!-- Slider type/variant examples -->
|
|
174
|
-
<fig-field direction="horizontal">
|
|
175
|
-
<label>Opacity</label>
|
|
176
|
-
<fig-slider type="opacity" value="0.75" color="#0D99FF" units="%" text="true" full></fig-slider>
|
|
177
|
-
</fig-field>
|
|
178
|
-
<fig-field direction="horizontal">
|
|
179
|
-
<label>Hue</label>
|
|
180
|
-
<fig-slider type="hue" value="180" text="true" full></fig-slider>
|
|
181
|
-
</fig-field>
|
|
182
|
-
<fig-field direction="horizontal">
|
|
183
|
-
<label>Offset</label>
|
|
184
|
-
<fig-slider type="delta" value="0" default="0" min="-5" max="5" step="0.25" text="true" full></fig-slider>
|
|
185
|
-
</fig-field>
|
|
186
|
-
```
|
|
187
|
-
|
|
188
98
|
## Workflow
|
|
189
99
|
|
|
190
|
-
1.
|
|
191
|
-
2.
|
|
192
|
-
3.
|
|
193
|
-
4.
|
|
194
|
-
5.
|
|
195
|
-
6. **Validate events and interactions.** Ensure controls emit usable `input`/`change` and behave well in keyboard workflows.
|
|
196
|
-
|
|
197
|
-
## Delivery Checklist
|
|
198
|
-
|
|
199
|
-
- Confirm prompts include all behavior-critical attrs (`value`, `min`, `max`, `step`, `units`, `type`, `variant` as needed).
|
|
200
|
-
- Confirm control choice matches intent (continuous vs discrete vs boolean vs exact numeric entry).
|
|
201
|
-
- Verify row density and panel width feel consistent with existing PropKit sections.
|
|
202
|
-
- Verify keyboard navigation and label association for every field row.
|
|
203
|
-
- Verify changes in `playground/src/data/sections.ts` still mirror recommended patterns in this skill.
|
|
204
|
-
|
|
205
|
-
## Quick Reference
|
|
206
|
-
|
|
207
|
-
```txt
|
|
208
|
-
Common PropKit controls:
|
|
209
|
-
- fig-image
|
|
210
|
-
- fig-input-color
|
|
211
|
-
- fig-input-fill
|
|
212
|
-
- fig-slider
|
|
213
|
-
- fig-switch
|
|
214
|
-
- fig-dropdown
|
|
215
|
-
- fig-segmented-control
|
|
216
|
-
- fig-easing-curve
|
|
217
|
-
- fig-input-angle
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
## Primary Files
|
|
100
|
+
1. Identify intent (boolean, discrete, continuous, color/fill, media, motion).
|
|
101
|
+
2. Pick core vs lab wrapper.
|
|
102
|
+
3. Compose the row; set defaults explicitly.
|
|
103
|
+
4. Check `/propskit` or `/propskit/lab` for an existing example before inventing markup.
|
|
104
|
+
5. Verify `input`/`change` and keyboard.
|
|
221
105
|
|
|
222
|
-
|
|
223
|
-
- `fig.js` - control behavior and emitted events
|
|
224
|
-
- `components.css` - visual treatment and layout constraints
|
|
225
|
-
- `README.md` - component API details and usage
|
|
106
|
+
Primary files: `playground/src/data/sections.ts`, `playground/src/data/labSections.ts`, `fig.js`, `fig-lab.js`.
|
package/README.md
CHANGED
|
@@ -72,6 +72,13 @@ Or use a CDN:
|
|
|
72
72
|
<script type="module" src="https://unpkg.com/@rogieking/figui3@latest/dist/fig.js"></script>
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
Agent skills ship in `.cursor/skills/` (included in the npm package):
|
|
76
|
+
|
|
77
|
+
- `figui3` — core `fig.js` components
|
|
78
|
+
- `fig-editor` — `fig-select` and `fig-fill-picker`
|
|
79
|
+
- `fig-lab` — experimental `propskit-*`, canvas, AI, angle, reorder
|
|
80
|
+
- `propkit` — `/propskit` property-row composition
|
|
81
|
+
|
|
75
82
|
Minimal example:
|
|
76
83
|
|
|
77
84
|
```html
|
|
@@ -90,6 +97,7 @@ Minimal example:
|
|
|
90
97
|
|---|---|---|
|
|
91
98
|
| [Button](#button) | `<fig-button>` | Buttons with variants, toggle, select, upload |
|
|
92
99
|
| [Dropdown](#dropdown) | `<fig-dropdown>` | Native select wrapper with Figma styling |
|
|
100
|
+
| [Select](#select) | `<fig-select>` | Custom listbox select (requires `fig-editor.js`) |
|
|
93
101
|
| [Combo Input](#combo-input) | `<fig-combo-input>` | Text input with dropdown suggestions |
|
|
94
102
|
| [Checkbox](#checkbox) | `<fig-checkbox>` | Checkbox with indeterminate state |
|
|
95
103
|
| [Radio](#radio) | `<fig-radio>` | Radio button |
|
|
@@ -192,6 +200,7 @@ Minimal example:
|
|
|
192
200
|
| `value` | string | — | Selected value |
|
|
193
201
|
| `type` | string | `"select"` | `"select"` or `"dropdown"` |
|
|
194
202
|
| `label` | string | — | Accessible label for the generated native `<select>` |
|
|
203
|
+
| `variant` | string | — | `"ghost"` for a borderless control with secondary hover fill |
|
|
195
204
|
| `disabled` | boolean | `false` | Disabled state |
|
|
196
205
|
|
|
197
206
|
```html
|
|
@@ -205,6 +214,38 @@ Keyboard activation follows the native select pattern. Enter opens the closed pi
|
|
|
205
214
|
|
|
206
215
|
---
|
|
207
216
|
|
|
217
|
+
#### Select
|
|
218
|
+
|
|
219
|
+
`<fig-select>` — [demo](https://rog.ie/figui3/#select)
|
|
220
|
+
|
|
221
|
+
Custom listbox select with overflow chevrons, grouped options, and sticky separators. Import `fig-editor.js` and `fig-editor.css`. Prefer this over `fig-dropdown` for Figma-style menus.
|
|
222
|
+
|
|
223
|
+
| Attribute | Type | Default | Description |
|
|
224
|
+
|---|---|---|---|
|
|
225
|
+
| `value` | string | — | Selected option value |
|
|
226
|
+
| `label` | string | — | Closed-state / accessible label |
|
|
227
|
+
| `options` | string | — | Comma, newline, or JSON options if no authored `fig-select-option` children |
|
|
228
|
+
| `variant` | string | — | `"ghost"` for a borderless control with secondary hover fill |
|
|
229
|
+
| `full` | boolean | `false` | Stretch to available width |
|
|
230
|
+
| `position` | string | `"bottom left"` | Popup position |
|
|
231
|
+
| `disabled` | boolean | `false` | Disabled state |
|
|
232
|
+
|
|
233
|
+
Author options in `<fig-select-options>`, or pass `options`. Use `label` on `<fig-select-option>` when the option content is rich. `fig-separator` with `sticky` pins group labels while scrolling.
|
|
234
|
+
|
|
235
|
+
```html
|
|
236
|
+
<fig-select value="center" label="Align">
|
|
237
|
+
<fig-select-options>
|
|
238
|
+
<fig-select-option value="left">Left</fig-select-option>
|
|
239
|
+
<fig-select-option value="center">Center</fig-select-option>
|
|
240
|
+
<fig-select-option value="right">Right</fig-select-option>
|
|
241
|
+
</fig-select-options>
|
|
242
|
+
</fig-select>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Events:** `input`, `change`, `optionhover` (`detail` is the hovered option value).
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
208
249
|
#### Combo Input
|
|
209
250
|
|
|
210
251
|
`<fig-combo-input>` — [demo](https://rog.ie/figui3/#combo-input)
|
|
@@ -1328,6 +1369,7 @@ An anchored floating surface built on `<dialog>` with collision-aware positionin
|
|
|
1328
1369
|
| `drag` | boolean | `false` | Draggable |
|
|
1329
1370
|
| `handle` | string | — | CSS selector for drag handle |
|
|
1330
1371
|
| `autoresize` | boolean | `false` | Auto-resize to content |
|
|
1372
|
+
| `title` | string | — | Auto-generated header (same as `fig-dialog`) |
|
|
1331
1373
|
|
|
1332
1374
|
```html
|
|
1333
1375
|
<dialog is="fig-popup" anchor="#my-button" position="center right" variant="popover">
|
|
@@ -1410,6 +1452,10 @@ A visual divider between content groups. The optional `label` attribute adds a g
|
|
|
1410
1452
|
|
|
1411
1453
|
Triggered menu with native keyboard patterns. The trigger gets `aria-haspopup="menu"`, `aria-expanded`, and `aria-controls`; menu items use `role="menuitem"` and disabled items are skipped by keyboard navigation.
|
|
1412
1454
|
|
|
1455
|
+
Items stay in the menu's light DOM and project into the popup through slots (same pattern as `fig-select` / `fig-select-options`). The trigger is assigned `slot="trigger"` automatically; items use the default slot. React can add or remove `fig-menu-item` children without `removeChild` errors.
|
|
1456
|
+
|
|
1457
|
+
`fig-menu-item` also works as a list row outside `fig-menu` — typically in `<dialog is="fig-popup">` with `<fig-content padding="none">`, sticky `<fig-separator>`s, and a nested `<fig-menu>` for row actions. Item color inherits from the parent surface.
|
|
1458
|
+
|
|
1413
1459
|
| Attribute | Type | Default | Description |
|
|
1414
1460
|
|---|---|---|---|
|
|
1415
1461
|
| `open` | boolean | `false` | Open state |
|
|
@@ -1435,6 +1481,25 @@ Triggered menu with native keyboard patterns. The trigger gets `aria-haspopup="m
|
|
|
1435
1481
|
</fig-menu>
|
|
1436
1482
|
```
|
|
1437
1483
|
|
|
1484
|
+
Popup list (no wrapping `fig-menu`):
|
|
1485
|
+
|
|
1486
|
+
```html
|
|
1487
|
+
<dialog is="fig-popup" title="Version history" anchor="#versions" position="bottom left">
|
|
1488
|
+
<fig-content padding="none">
|
|
1489
|
+
<fig-separator sticky label="Today"></fig-separator>
|
|
1490
|
+
<fig-menu-item value="v9" subtle>
|
|
1491
|
+
Version 9
|
|
1492
|
+
<fig-menu position="bottom right">
|
|
1493
|
+
<fig-button fig-menu-trigger variant="ghost" icon aria-label="More">
|
|
1494
|
+
<fig-icon name="more"></fig-icon>
|
|
1495
|
+
</fig-button>
|
|
1496
|
+
<fig-menu-item value="restore">Restore this version</fig-menu-item>
|
|
1497
|
+
</fig-menu>
|
|
1498
|
+
</fig-menu-item>
|
|
1499
|
+
</fig-content>
|
|
1500
|
+
</dialog>
|
|
1501
|
+
```
|
|
1502
|
+
|
|
1438
1503
|
`fig-separator` and `fig-menu-separator` accept optional `label` — renders the rule, then secondary group text underneath.
|
|
1439
1504
|
|
|
1440
1505
|
---
|
|
@@ -1456,7 +1521,7 @@ A section header component.
|
|
|
1456
1521
|
`<fig-layer>` — [demo](https://rog.ie/figui3/#layer)
|
|
1457
1522
|
|
|
1458
1523
|
A collapsible layer list item with expand/collapse and visibility toggling. Supports nesting and exposes `role="treeitem"`, `aria-expanded`, `aria-hidden`, `aria-disabled`, and a keyboard-toggleable chevron button.
|
|
1459
|
-
Import `fig-layer.js` and `fig-layer.css` to register and style it.
|
|
1524
|
+
Import `fig-layer.js` and `fig-layer.css` to register and style it.
|
|
1460
1525
|
|
|
1461
1526
|
| Attribute | Type | Default | Description |
|
|
1462
1527
|
|---|---|---|---|
|
package/components.css
CHANGED
|
@@ -3143,6 +3143,27 @@ dialog[is="fig-popup"] {
|
|
|
3143
3143
|
display: block;
|
|
3144
3144
|
}
|
|
3145
3145
|
|
|
3146
|
+
&[open]:not([open="false"]):has(> fig-content) {
|
|
3147
|
+
display: flex;
|
|
3148
|
+
flex-direction: column;
|
|
3149
|
+
overflow: hidden;
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
> fig-header {
|
|
3153
|
+
position: sticky;
|
|
3154
|
+
top: 0;
|
|
3155
|
+
z-index: 2;
|
|
3156
|
+
background: var(--fig-popup-bg-color, var(--figma-color-bg));
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3159
|
+
&:has(> fig-header) {
|
|
3160
|
+
--fig-sticky-header-size: var(--spacer-6);
|
|
3161
|
+
|
|
3162
|
+
> :is(fig-separator, fig-menu-separator)[sticky]:not([sticky="false"]) {
|
|
3163
|
+
top: var(--fig-sticky-header-size);
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
|
|
3146
3167
|
|
|
3147
3168
|
&[theme="dark"] {
|
|
3148
3169
|
color-scheme: dark;
|
|
@@ -3346,6 +3367,20 @@ fig-dropdown {
|
|
|
3346
3367
|
display: flex;
|
|
3347
3368
|
width: 100%;
|
|
3348
3369
|
}
|
|
3370
|
+
|
|
3371
|
+
&[variant="ghost"] {
|
|
3372
|
+
border-radius: var(--radius-medium);
|
|
3373
|
+
background: transparent;
|
|
3374
|
+
|
|
3375
|
+
> select {
|
|
3376
|
+
box-shadow: none;
|
|
3377
|
+
background: transparent;
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
&:hover:not([disabled]):not([disabled="false"]) {
|
|
3381
|
+
background-color: var(--figma-color-bg-secondary);
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3349
3384
|
}
|
|
3350
3385
|
|
|
3351
3386
|
fig-checkbox,
|
|
@@ -3597,13 +3632,17 @@ fig-content {
|
|
|
3597
3632
|
}
|
|
3598
3633
|
}
|
|
3599
3634
|
|
|
3600
|
-
fig-content
|
|
3601
|
-
padding
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3635
|
+
fig-content{
|
|
3636
|
+
&[padding]:not([padding="false"]):not([padding="none"]):not(:empty) {
|
|
3637
|
+
padding-inline: var(--spacer-3);
|
|
3638
|
+
}
|
|
3639
|
+
&:empty,
|
|
3640
|
+
&::not(:has(*)) {
|
|
3641
|
+
padding: 0;
|
|
3642
|
+
}
|
|
3643
|
+
&[padding="none"]{
|
|
3644
|
+
padding: 0;
|
|
3645
|
+
}
|
|
3607
3646
|
}
|
|
3608
3647
|
|
|
3609
3648
|
vstack,
|
|
@@ -5521,23 +5560,49 @@ fig-color-tip {
|
|
|
5521
5560
|
/* Menu */
|
|
5522
5561
|
fig-menu {
|
|
5523
5562
|
display: contents;
|
|
5563
|
+
}
|
|
5524
5564
|
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5565
|
+
/*
|
|
5566
|
+
* Internal menu is a fig-popup inside fig-menu's shadow, so document
|
|
5567
|
+
* dialog[is="fig-popup"] rules don't apply. Expose part=listbox and mirror
|
|
5568
|
+
* the menu-theme popup chrome (same tokens as fig-select::part(listbox)).
|
|
5569
|
+
*/
|
|
5570
|
+
fig-menu::part(listbox) {
|
|
5571
|
+
--z-index: 999999;
|
|
5572
|
+
--fig-popup-radius: var(--radius-large);
|
|
5573
|
+
--fig-popup-bg-color: var(--figma-color-bg-menu);
|
|
5574
|
+
|
|
5575
|
+
z-index: var(--z-index);
|
|
5576
|
+
position: fixed;
|
|
5577
|
+
margin: 0;
|
|
5578
|
+
border: 0;
|
|
5579
|
+
outline: 0;
|
|
5580
|
+
padding: 0;
|
|
5581
|
+
overflow: hidden;
|
|
5582
|
+
border-radius: var(--fig-popup-radius);
|
|
5583
|
+
background-color: var(--fig-popup-bg-color);
|
|
5584
|
+
color: var(--figma-color-text-menu);
|
|
5585
|
+
color-scheme: dark;
|
|
5586
|
+
box-shadow: var(--figma-elevation-400-menu-panel);
|
|
5587
|
+
min-width: 6rem;
|
|
5588
|
+
max-height: calc(100vh - 1rem);
|
|
5589
|
+
height: max-content;
|
|
5529
5590
|
}
|
|
5530
5591
|
|
|
5531
5592
|
fig-menu-item {
|
|
5532
|
-
--fig-menu-item-padding: var(--spacer-4);
|
|
5593
|
+
--fig-menu-item-padding: var(--spacer-1) var(--spacer-4);
|
|
5594
|
+
box-sizing: border-box;
|
|
5533
5595
|
display: flex;
|
|
5596
|
+
flex-shrink: 0;
|
|
5534
5597
|
align-items: center;
|
|
5535
5598
|
gap: var(--spacer-2);
|
|
5536
|
-
|
|
5537
|
-
|
|
5599
|
+
width: 100%;
|
|
5600
|
+
padding: var(--fig-menu-item-padding);
|
|
5601
|
+
min-height: var(--spacer-4);
|
|
5602
|
+
height: auto;
|
|
5538
5603
|
cursor: default;
|
|
5539
5604
|
border-radius: var(--radius-medium);
|
|
5540
|
-
color:
|
|
5605
|
+
color: inherit;
|
|
5541
5606
|
font-weight: var(--body-medium-fontWeight);
|
|
5542
5607
|
position: relative;
|
|
5543
5608
|
|
|
@@ -5545,6 +5610,23 @@ fig-menu-item {
|
|
|
5545
5610
|
margin: 0 0 var(--spacer-1) 0;
|
|
5546
5611
|
}
|
|
5547
5612
|
|
|
5613
|
+
label {
|
|
5614
|
+
color: var(--figma-color-text-secondary);
|
|
5615
|
+
}
|
|
5616
|
+
|
|
5617
|
+
> :not(fig-icon):not(fig-menu) {
|
|
5618
|
+
flex: 1 1 auto;
|
|
5619
|
+
min-width: 0;
|
|
5620
|
+
}
|
|
5621
|
+
|
|
5622
|
+
> fig-menu {
|
|
5623
|
+
flex: 0 0 auto;
|
|
5624
|
+
}
|
|
5625
|
+
|
|
5626
|
+
&[size="large"]{
|
|
5627
|
+
--fig-menu-item-padding: var(--spacer-2) var(--spacer-4);
|
|
5628
|
+
}
|
|
5629
|
+
|
|
5548
5630
|
&::before {
|
|
5549
5631
|
content: "";
|
|
5550
5632
|
display: block;
|
|
@@ -5582,14 +5664,15 @@ fig-menu-item {
|
|
|
5582
5664
|
:is(fig-separator, fig-menu-separator) {
|
|
5583
5665
|
--fig-separator-color: var(--figma-color-border);
|
|
5584
5666
|
--fig-separator-label-color: var(--figma-color-text-secondary);
|
|
5585
|
-
--fig-
|
|
5667
|
+
--fig-separator-padding: 0 var(--spacer-3);
|
|
5586
5668
|
|
|
5587
5669
|
display: block;
|
|
5588
5670
|
box-sizing: border-box;
|
|
5671
|
+
flex-shrink: 0;
|
|
5589
5672
|
width: 100%;
|
|
5590
5673
|
height: 1px;
|
|
5591
5674
|
margin: var(--spacer-2) 0;
|
|
5592
|
-
padding:
|
|
5675
|
+
padding: var(--fig-separator-padding);
|
|
5593
5676
|
border: 0;
|
|
5594
5677
|
background: var(--fig-separator-color);
|
|
5595
5678
|
color: var(--fig-separator-label-color);
|
|
@@ -5621,7 +5704,6 @@ fig-menu-item {
|
|
|
5621
5704
|
height: auto;
|
|
5622
5705
|
margin: var(--spacer-2) 0;
|
|
5623
5706
|
margin-bottom: var(--spacer-1);
|
|
5624
|
-
padding: var(--fig-menu-inline-padding);
|
|
5625
5707
|
background: transparent;
|
|
5626
5708
|
|
|
5627
5709
|
&::before {
|
|
@@ -5652,7 +5734,14 @@ fig-menu-item {
|
|
|
5652
5734
|
z-index: 1;
|
|
5653
5735
|
background: var(--fig-popup-bg-color, var(--figma-color-bg));
|
|
5654
5736
|
|
|
5655
|
-
|
|
5737
|
+
&[stuck] {
|
|
5738
|
+
--fig-separator-padding: var(--spacer-2) var(--spacer-3);
|
|
5739
|
+
&::before {
|
|
5740
|
+
display: none;
|
|
5741
|
+
}
|
|
5742
|
+
}
|
|
5743
|
+
|
|
5744
|
+
:is(fig-select-options, .fig-menu-options, fig-menu).overflow-start > & {
|
|
5656
5745
|
top: var(--fig-vertical-overflow-size, var(--spacer-4));
|
|
5657
5746
|
}
|
|
5658
5747
|
}
|
|
@@ -5666,18 +5755,8 @@ fig-select-options > :is(fig-separator, fig-menu-separator):first-child[label]:n
|
|
|
5666
5755
|
}
|
|
5667
5756
|
|
|
5668
5757
|
dialog[is="fig-popup"][theme="menu"] :is(fig-separator, fig-menu-separator),
|
|
5758
|
+
fig-menu > :is(fig-separator, fig-menu-separator),
|
|
5669
5759
|
fig-select-options > :is(fig-separator, fig-menu-separator) {
|
|
5670
5760
|
--fig-separator-color: var(--figma-color-border-menu);
|
|
5671
5761
|
--fig-separator-label-color: var(--figma-color-text-menu-secondary);
|
|
5672
5762
|
}
|
|
5673
|
-
|
|
5674
|
-
dialog[is="fig-popup"][theme="menu"] :is(fig-separator, fig-menu-separator) {
|
|
5675
|
-
--fig-menu-inline-padding: 0
|
|
5676
|
-
var(--fig-menu-item-padding, var(--spacer-4));
|
|
5677
|
-
|
|
5678
|
-
&[label]:not([label=""])::before {
|
|
5679
|
-
margin-inline: calc(
|
|
5680
|
-
-1 * var(--fig-menu-item-padding, var(--spacer-4))
|
|
5681
|
-
);
|
|
5682
|
-
}
|
|
5683
|
-
}
|