@rogieking/figui3 8.9.11 → 8.9.13
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/figui3/SKILL.md +3 -3
- package/.cursor/skills/figui3/reference.md +4 -3
- package/README.md +24 -0
- package/components.css +96 -31
- package/dist/components.css +1 -1
- package/dist/fig-editor.css +1 -1
- package/dist/fig.css +1 -1
- package/dist/fig.js +162 -3
- package/fig-editor.css +2 -91
- package/fig.js +523 -121
- package/package.json +1 -1
|
@@ -72,9 +72,9 @@ Color picker modes: `fig-fill-picker` is optional editor. Do not use `picker` /
|
|
|
72
72
|
## Overlay rules
|
|
73
73
|
|
|
74
74
|
- `<dialog is="fig-dialog">` — modal/task dialog. `position` is viewport placement. No `anchor`.
|
|
75
|
-
- `<dialog is="fig-popup">` — anchored float (`anchor`, `position`, `offset`, `viewport-margin`). `variant="popover"` uses CSS `filter` (containing block for `position: fixed`).
|
|
75
|
+
- `<dialog is="fig-popup">` — anchored float (`anchor`, `position`, `offset`, `viewport-margin`). `title` auto-builds a header. `variant="popover"` uses CSS `filter` (containing block for `position: fixed`). Sticky `fig-separator` + `fig-menu-item` lists scroll on the popup.
|
|
76
76
|
- `<dialog is="fig-toast">` — call `showToast()`. `theme`, `duration`, `live`, `dismiss`, `icon`.
|
|
77
|
-
- `fig-menu` and `fig-select` use `popover="manual"` so lists escape filter-containing popups to the top layer. Nested menus inside popovers must keep that.
|
|
77
|
+
- `fig-menu` and `fig-select` use `popover="manual"` so lists escape filter-containing popups to the top layer. Nested menus inside popovers must keep that. `fig-menu` slots items (does not relocate them); triggers get `slot="trigger"`.
|
|
78
78
|
|
|
79
79
|
```html
|
|
80
80
|
<dialog is="fig-dialog" drag handle="fig-header">
|
|
@@ -156,7 +156,7 @@ Playground hashes: `/figui3#{id}`. Full attrs: [reference.md](reference.md).
|
|
|
156
156
|
| `fig-segmented-control` / `fig-segment` | `#segmented-control` | Radio-group pattern |
|
|
157
157
|
| `fig-chooser` / `fig-choice` | (containers) | Listbox selection |
|
|
158
158
|
| `fig-separator` / `fig-menu-separator` | `#separator` | Optional `label`, `sticky`, `borderless` |
|
|
159
|
-
| `fig-menu` / `fig-menu-item` | `#menu` | `fig-menu-trigger`, `trigger="contextmenu"`, `position`, `offset` |
|
|
159
|
+
| `fig-menu` / `fig-menu-item` | `#menu` | `fig-menu-trigger`, `trigger="contextmenu"`, `position`, `offset`. Item also works in `fig-popup` (sticky separators, nested row menus). |
|
|
160
160
|
| `fig-icon` | `#icon` | Token mask (`name`, `size="small"`, `color`) |
|
|
161
161
|
| `fig-avatar` | `#avatar` | `src` / `name`, `size="large"` |
|
|
162
162
|
| `fig-truncate` | `#truncate` | `position="right\|left\|middle"`, `tooltip`, `tail` |
|
|
@@ -49,11 +49,12 @@ Toast: `theme`, `duration`, `offset`, `dismiss`, `live="polite|assertive"`, `ico
|
|
|
49
49
|
|
|
50
50
|
## `fig-menu`
|
|
51
51
|
|
|
52
|
-
- Trigger: child with `fig-menu-trigger
|
|
52
|
+
- Trigger: child with `fig-menu-trigger` (auto `slot="trigger"`), or `trigger="contextmenu"`
|
|
53
53
|
- `position`, `offset`, `closedby="auto|any|none"`, `open`
|
|
54
|
-
- Items
|
|
54
|
+
- Items stay in light DOM and slot into the popup (do not relocate)
|
|
55
|
+
- Items: `fig-menu-item` (`value`, `disabled`, `subtle`). Also a list row outside `fig-menu` (popup, sticky separators, nested row `fig-menu`).
|
|
55
56
|
- Dividers: `fig-separator` / `fig-menu-separator` (`label`, `sticky`, `borderless`)
|
|
56
|
-
- Popup uses `popover="manual"` (top layer)
|
|
57
|
+
- Popup uses `popover="manual"` (top layer) and stays in the menu shadow so slots keep working
|
|
57
58
|
|
|
58
59
|
## Color / fill values
|
|
59
60
|
|
package/README.md
CHANGED
|
@@ -1369,6 +1369,7 @@ An anchored floating surface built on `<dialog>` with collision-aware positionin
|
|
|
1369
1369
|
| `drag` | boolean | `false` | Draggable |
|
|
1370
1370
|
| `handle` | string | — | CSS selector for drag handle |
|
|
1371
1371
|
| `autoresize` | boolean | `false` | Auto-resize to content |
|
|
1372
|
+
| `title` | string | — | Auto-generated header (same as `fig-dialog`) |
|
|
1372
1373
|
|
|
1373
1374
|
```html
|
|
1374
1375
|
<dialog is="fig-popup" anchor="#my-button" position="center right" variant="popover">
|
|
@@ -1451,6 +1452,10 @@ A visual divider between content groups. The optional `label` attribute adds a g
|
|
|
1451
1452
|
|
|
1452
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.
|
|
1453
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
|
+
|
|
1454
1459
|
| Attribute | Type | Default | Description |
|
|
1455
1460
|
|---|---|---|---|
|
|
1456
1461
|
| `open` | boolean | `false` | Open state |
|
|
@@ -1476,6 +1481,25 @@ Triggered menu with native keyboard patterns. The trigger gets `aria-haspopup="m
|
|
|
1476
1481
|
</fig-menu>
|
|
1477
1482
|
```
|
|
1478
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
|
+
|
|
1479
1503
|
`fig-separator` and `fig-menu-separator` accept optional `label` — renders the rule, then secondary group text underneath.
|
|
1480
1504
|
|
|
1481
1505
|
---
|
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;
|
|
@@ -3611,13 +3632,17 @@ fig-content {
|
|
|
3611
3632
|
}
|
|
3612
3633
|
}
|
|
3613
3634
|
|
|
3614
|
-
fig-content
|
|
3615
|
-
padding
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
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
|
+
}
|
|
3621
3646
|
}
|
|
3622
3647
|
|
|
3623
3648
|
vstack,
|
|
@@ -5535,23 +5560,49 @@ fig-color-tip {
|
|
|
5535
5560
|
/* Menu */
|
|
5536
5561
|
fig-menu {
|
|
5537
5562
|
display: contents;
|
|
5563
|
+
}
|
|
5538
5564
|
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
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;
|
|
5543
5590
|
}
|
|
5544
5591
|
|
|
5545
5592
|
fig-menu-item {
|
|
5546
|
-
--fig-menu-item-padding: var(--spacer-4);
|
|
5593
|
+
--fig-menu-item-padding: var(--spacer-1) var(--spacer-4);
|
|
5594
|
+
box-sizing: border-box;
|
|
5547
5595
|
display: flex;
|
|
5596
|
+
flex-shrink: 0;
|
|
5548
5597
|
align-items: center;
|
|
5549
5598
|
gap: var(--spacer-2);
|
|
5550
|
-
|
|
5551
|
-
|
|
5599
|
+
width: 100%;
|
|
5600
|
+
padding: var(--fig-menu-item-padding);
|
|
5601
|
+
min-height: var(--spacer-4);
|
|
5602
|
+
height: auto;
|
|
5552
5603
|
cursor: default;
|
|
5553
5604
|
border-radius: var(--radius-medium);
|
|
5554
|
-
color:
|
|
5605
|
+
color: inherit;
|
|
5555
5606
|
font-weight: var(--body-medium-fontWeight);
|
|
5556
5607
|
position: relative;
|
|
5557
5608
|
|
|
@@ -5559,6 +5610,23 @@ fig-menu-item {
|
|
|
5559
5610
|
margin: 0 0 var(--spacer-1) 0;
|
|
5560
5611
|
}
|
|
5561
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
|
+
|
|
5562
5630
|
&::before {
|
|
5563
5631
|
content: "";
|
|
5564
5632
|
display: block;
|
|
@@ -5596,14 +5664,15 @@ fig-menu-item {
|
|
|
5596
5664
|
:is(fig-separator, fig-menu-separator) {
|
|
5597
5665
|
--fig-separator-color: var(--figma-color-border);
|
|
5598
5666
|
--fig-separator-label-color: var(--figma-color-text-secondary);
|
|
5599
|
-
--fig-
|
|
5667
|
+
--fig-separator-padding: 0 var(--spacer-3);
|
|
5600
5668
|
|
|
5601
5669
|
display: block;
|
|
5602
5670
|
box-sizing: border-box;
|
|
5671
|
+
flex-shrink: 0;
|
|
5603
5672
|
width: 100%;
|
|
5604
5673
|
height: 1px;
|
|
5605
5674
|
margin: var(--spacer-2) 0;
|
|
5606
|
-
padding:
|
|
5675
|
+
padding: var(--fig-separator-padding);
|
|
5607
5676
|
border: 0;
|
|
5608
5677
|
background: var(--fig-separator-color);
|
|
5609
5678
|
color: var(--fig-separator-label-color);
|
|
@@ -5635,7 +5704,6 @@ fig-menu-item {
|
|
|
5635
5704
|
height: auto;
|
|
5636
5705
|
margin: var(--spacer-2) 0;
|
|
5637
5706
|
margin-bottom: var(--spacer-1);
|
|
5638
|
-
padding: var(--fig-menu-inline-padding);
|
|
5639
5707
|
background: transparent;
|
|
5640
5708
|
|
|
5641
5709
|
&::before {
|
|
@@ -5663,10 +5731,17 @@ fig-menu-item {
|
|
|
5663
5731
|
&[sticky]:not([sticky="false"]) {
|
|
5664
5732
|
position: sticky;
|
|
5665
5733
|
top: 0;
|
|
5666
|
-
z-index:
|
|
5734
|
+
z-index: 9999;
|
|
5667
5735
|
background: var(--fig-popup-bg-color, var(--figma-color-bg));
|
|
5668
5736
|
|
|
5669
|
-
|
|
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 > & {
|
|
5670
5745
|
top: var(--fig-vertical-overflow-size, var(--spacer-4));
|
|
5671
5746
|
}
|
|
5672
5747
|
}
|
|
@@ -5680,18 +5755,8 @@ fig-select-options > :is(fig-separator, fig-menu-separator):first-child[label]:n
|
|
|
5680
5755
|
}
|
|
5681
5756
|
|
|
5682
5757
|
dialog[is="fig-popup"][theme="menu"] :is(fig-separator, fig-menu-separator),
|
|
5758
|
+
fig-menu > :is(fig-separator, fig-menu-separator),
|
|
5683
5759
|
fig-select-options > :is(fig-separator, fig-menu-separator) {
|
|
5684
5760
|
--fig-separator-color: var(--figma-color-border-menu);
|
|
5685
5761
|
--fig-separator-label-color: var(--figma-color-text-menu-secondary);
|
|
5686
5762
|
}
|
|
5687
|
-
|
|
5688
|
-
dialog[is="fig-popup"][theme="menu"] :is(fig-separator, fig-menu-separator) {
|
|
5689
|
-
--fig-menu-inline-padding: 0
|
|
5690
|
-
var(--fig-menu-item-padding, var(--spacer-4));
|
|
5691
|
-
|
|
5692
|
-
&[label]:not([label=""])::before {
|
|
5693
|
-
margin-inline: calc(
|
|
5694
|
-
-1 * var(--fig-menu-item-padding, var(--spacer-4))
|
|
5695
|
-
);
|
|
5696
|
-
}
|
|
5697
|
-
}
|