@rogieking/figui3 8.9.11 → 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.
@@ -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`, or `trigger="contextmenu"`
52
+ - Trigger: child with `fig-menu-trigger` (auto `slot="trigger"`), or `trigger="contextmenu"`
53
53
  - `position`, `offset`, `closedby="auto|any|none"`, `open`
54
- - Items: `fig-menu-item` (`value`, `disabled`, `subtle`)
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[padding]:not([padding="false"]):not(:empty) {
3615
- padding-inline: var(--spacer-3);
3616
- }
3617
-
3618
- fig-content:empty,
3619
- fig-content:not(:has(*)) {
3620
- padding: 0;
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
- & > fig-menu-item,
5540
- & > :is(fig-separator, fig-menu-separator) {
5541
- display: none;
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
- padding: 0 var(--fig-menu-item-padding);
5551
- height: var(--spacer-4);
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: var(--figma-color-text-menu);
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-menu-inline-padding: 0 var(--spacer-3);
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: 0;
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 {
@@ -5666,7 +5734,14 @@ fig-menu-item {
5666
5734
  z-index: 1;
5667
5735
  background: var(--fig-popup-bg-color, var(--figma-color-bg));
5668
5736
 
5669
- :is(fig-select-options, .fig-menu-options).overflow-start > & {
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
- }