@reliability-design/web 1.3.1 → 1.4.1

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.
Files changed (124) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/components/rel-accordion/README.md +13 -13
  3. package/dist/components/rel-accordion/rel-accordion.ts +80 -45
  4. package/dist/components/rel-alert/README.md +15 -15
  5. package/dist/components/rel-alert/rel-alert.ts +149 -158
  6. package/dist/components/rel-avatar/README.md +15 -15
  7. package/dist/components/rel-avatar/rel-avatar.ts +42 -38
  8. package/dist/components/rel-badge/README.md +5 -5
  9. package/dist/components/rel-badge/rel-badge.ts +46 -11
  10. package/dist/components/rel-breadcrumb/README.md +7 -7
  11. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +16 -13
  12. package/dist/components/rel-button/README.md +20 -20
  13. package/dist/components/rel-button/rel-button.ts +292 -105
  14. package/dist/components/rel-calendar/README.md +5 -5
  15. package/dist/components/rel-calendar/rel-calendar.ts +68 -45
  16. package/dist/components/rel-card/README.md +6 -6
  17. package/dist/components/rel-card/rel-card.ts +88 -35
  18. package/dist/components/rel-cardmedia/README.md +9 -9
  19. package/dist/components/rel-cardmedia/rel-cardmedia.ts +27 -20
  20. package/dist/components/rel-carousel/README.md +3 -3
  21. package/dist/components/rel-carousel/rel-carousel.ts +174 -90
  22. package/dist/components/rel-chart/README.md +8 -8
  23. package/dist/components/rel-chart/rel-chart.ts +59 -41
  24. package/dist/components/rel-checkbox/README.md +7 -7
  25. package/dist/components/rel-checkbox/rel-checkbox.ts +61 -61
  26. package/dist/components/rel-chip/README.md +16 -16
  27. package/dist/components/rel-chip/rel-chip.ts +143 -111
  28. package/dist/components/rel-container/README.md +11 -11
  29. package/dist/components/rel-container/rel-container.ts +35 -9
  30. package/dist/components/rel-date-picker/README.md +43 -7
  31. package/dist/components/rel-date-picker/date-format.test.ts +43 -0
  32. package/dist/components/rel-date-picker/date-format.ts +47 -0
  33. package/dist/components/rel-date-picker/rel-date-picker.ts +154 -82
  34. package/dist/components/rel-divider/README.md +9 -9
  35. package/dist/components/rel-divider/rel-divider.ts +10 -9
  36. package/dist/components/rel-drawer/README.md +1 -1
  37. package/dist/components/rel-drawer/rel-drawer-footer.ts +1 -1
  38. package/dist/components/rel-drawer/rel-drawer.ts +88 -25
  39. package/dist/components/rel-kbd/README.md +20 -20
  40. package/dist/components/rel-kbd/rel-kbd.ts +119 -25
  41. package/dist/components/rel-layout/README.md +1 -1
  42. package/dist/components/rel-layout/rel-layout.ts +2 -2
  43. package/dist/components/rel-menu/README.md +15 -15
  44. package/dist/components/rel-menu/rel-menu.ts +155 -88
  45. package/dist/components/rel-menubar/README.md +20 -20
  46. package/dist/components/rel-menubar/rel-menubar.ts +47 -31
  47. package/dist/components/rel-nav-menu/README.md +21 -21
  48. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +17 -14
  49. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +33 -14
  50. package/dist/components/rel-nav-menu/rel-nav-menu.ts +14 -7
  51. package/dist/components/rel-navbar/README.md +7 -7
  52. package/dist/components/rel-navbar/rel-navbar.ts +12 -11
  53. package/dist/components/rel-otp/README.md +3 -3
  54. package/dist/components/rel-otp/rel-otp.ts +46 -30
  55. package/dist/components/rel-pagination/README.md +18 -18
  56. package/dist/components/rel-pagination/rel-pagination.ts +141 -90
  57. package/dist/components/rel-popover/README.md +13 -13
  58. package/dist/components/rel-popover/rel-popover-content.ts +39 -14
  59. package/dist/components/rel-popover/rel-popover.ts +18 -7
  60. package/dist/components/rel-progress/README.md +5 -5
  61. package/dist/components/rel-progress/rel-progress.ts +16 -12
  62. package/dist/components/rel-radio/README.md +9 -9
  63. package/dist/components/rel-radio/rel-radio.ts +64 -50
  64. package/dist/components/rel-range-picker/rel-range-picker.ts +120 -117
  65. package/dist/components/rel-resizable/README.md +4 -4
  66. package/dist/components/rel-resizable/rel-resizable-group.ts +15 -4
  67. package/dist/components/rel-resizable/rel-resizable-handle.ts +13 -9
  68. package/dist/components/rel-resizable/rel-resizable-panel.ts +11 -0
  69. package/dist/components/rel-segmented-button/README.md +10 -10
  70. package/dist/components/rel-segmented-button/rel-segmented-button.ts +39 -40
  71. package/dist/components/rel-select/README.md +14 -14
  72. package/dist/components/rel-select/rel-select.ts +120 -188
  73. package/dist/components/rel-sheet/README.md +10 -10
  74. package/dist/components/rel-sheet/rel-sheet.ts +82 -23
  75. package/dist/components/rel-sidebar/README.md +5 -5
  76. package/dist/components/rel-sidebar/rel-sidebar.ts +75 -60
  77. package/dist/components/rel-skeleton/rel-skeleton.ts +8 -13
  78. package/dist/components/rel-slider/README.md +10 -10
  79. package/dist/components/rel-slider/rel-slider.ts +230 -57
  80. package/dist/components/rel-snackbar/README.md +9 -9
  81. package/dist/components/rel-snackbar/rel-snackbar.ts +44 -32
  82. package/dist/components/rel-stepper/README.md +10 -10
  83. package/dist/components/rel-stepper/rel-stepper.ts +110 -46
  84. package/dist/components/rel-switch/README.md +5 -5
  85. package/dist/components/rel-switch/rel-switch.ts +23 -12
  86. package/dist/components/rel-tab/README.md +13 -13
  87. package/dist/components/rel-tab/rel-tab.ts +63 -19
  88. package/dist/components/rel-table/README.md +7 -7
  89. package/dist/components/rel-table/rel-table-body.ts +2 -1
  90. package/dist/components/rel-table/rel-table-caption.ts +10 -7
  91. package/dist/components/rel-table/rel-table-cell.ts +7 -6
  92. package/dist/components/rel-table/rel-table-footer.ts +4 -3
  93. package/dist/components/rel-table/rel-table-head.ts +11 -9
  94. package/dist/components/rel-table/rel-table-header.ts +2 -1
  95. package/dist/components/rel-table/rel-table-row.ts +5 -4
  96. package/dist/components/rel-table/rel-table.ts +16 -7
  97. package/dist/components/rel-textfield/README.md +11 -11
  98. package/dist/components/rel-textfield/rel-textfield.ts +218 -92
  99. package/dist/components/rel-toast/README.md +11 -11
  100. package/dist/components/rel-toast/rel-toast.ts +33 -30
  101. package/dist/components/rel-tooltip/README.md +7 -7
  102. package/dist/components/rel-tooltip/rel-tooltip.ts +160 -123
  103. package/dist/components/rel-topbar/rel-topbar-actions.ts +1 -2
  104. package/dist/components/rel-topbar/rel-topbar-brand.ts +8 -5
  105. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +10 -8
  106. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +4 -5
  107. package/dist/components/rel-topbar/rel-topbar-item.ts +23 -22
  108. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -1
  109. package/dist/components/rel-topbar/rel-topbar-profile.ts +68 -50
  110. package/dist/components/rel-topbar/rel-topbar-utilities.ts +1 -2
  111. package/dist/components/rel-topbar/rel-topbar.ts +31 -29
  112. package/dist/index.d.mts +159 -39
  113. package/dist/index.d.ts +159 -39
  114. package/dist/index.js +3600 -2269
  115. package/dist/index.js.map +1 -1
  116. package/dist/index.mjs +3930 -2600
  117. package/dist/index.mjs.map +1 -1
  118. package/dist/styles/vars.css +866 -363
  119. package/dist/vars-BYCEEKWJ.css +866 -0
  120. package/package.json +5 -3
  121. package/dist/components/rel-range-picker/rel-range-picker.css +0 -349
  122. package/dist/styles/vars-v2.css +0 -525
  123. package/dist/vars-F54UGCHM.css +0 -363
  124. package/dist/vars-v2-VMGWTFJZ.css +0 -525
@@ -1,8 +1,10 @@
1
1
  import { LitElement, html } from "lit";
2
2
  import { customElement, property, state } from "lit/decorators.js";
3
- import "../../styles/vars-v2.css";
3
+ import "@reliability-design/icons/rel-icon";
4
+ import "../rel-checkbox/rel-checkbox.js";
5
+ import "../rel-radio/rel-radio.js";
4
6
 
5
- export type MenuType = "default" | "checkbox" | "arrow";
7
+ export type MenuType = "default" | "checkbox" | "arrow" | "select-option" | "fixed" | "fit";
6
8
 
7
9
  export interface MenuItem {
8
10
  label: string;
@@ -40,27 +42,36 @@ export class RelMenu extends LitElement {
40
42
 
41
43
  public static readonly _css = `
42
44
  :host {
45
+ color: var(--rel-text-default);
43
46
  display: inline-block;
44
47
  }
45
48
 
46
49
  .rel-menu {
47
50
  position: relative;
48
51
  display: inline-block;
49
- background: var(--rel-color-bg-primary);
50
- color: var(--rel-color-text-primary);
51
- border-radius: var(--rel-borders-radius-border-radius-300);
52
+ background: var(--rel-bg-neutral);
53
+ color: var(--rel-text-default);
54
+ border-radius: var(--rel-border-radius-md);
52
55
  box-shadow: var(--rel-shadows-shadow-300);
53
56
  min-width: 220px;
54
- font-family: var(--rel-fontFamilies-primary, sans-serif);
55
- font-size: var(--rel-fontSizes-sm);
56
- padding: var(--rel-spacing-space-3xs) 0;
57
+ /* No vertical padding: items fill edge-to-edge so a first/last-item hover
58
+ reaches the menu's top/bottom. NOT overflow:hidden — that clips the
59
+ type=arrow flyout (position:absolute at left:100%). Instead the first
60
+ and last items carry the outer corner radius so hover backgrounds clip
61
+ to the rounded shape without any overflow. */
62
+ padding: 0;
57
63
  outline: none;
58
- overflow: hidden;
64
+ overflow: visible;
65
+ font-family: var(--rel-body-lg-regular-font-family);
66
+ font-weight: var(--rel-body-lg-regular-font-weight);
67
+ font-size: var(--rel-body-lg-regular-font-size);
68
+ line-height: var(--rel-body-lg-regular-line-height);
69
+ letter-spacing: var(--rel-body-lg-regular-letter-spacing);
59
70
  }
60
71
 
61
72
  .rel-menu:focus-visible,
62
73
  :host(:focus-visible) .rel-menu {
63
- outline: 2px solid var(--rel-color-border-focus);
74
+ outline: 2px solid var(--rel-border-focus);
64
75
  outline-offset: 2px;
65
76
  }
66
77
 
@@ -68,75 +79,117 @@ export class RelMenu extends LitElement {
68
79
  position: relative;
69
80
  display: flex;
70
81
  align-items: center;
71
- gap: var(--rel-spacing-space-xs);
72
- padding: var(--rel-spacing-space-xs) var(--rel-spacing-space-sm);
73
- line-height: var(--rel-lineHeights-sm);
82
+ gap: var(--rel-spacing-xs);
83
+ padding: var(--rel-spacing-xs) var(--rel-spacing-sm);
84
+ line-height: var(--rel-body-sm-regular-line-height);
74
85
  cursor: pointer;
75
86
  transition: background 0.15s ease, color 0.15s ease;
76
87
  user-select: none;
77
88
  }
78
89
 
90
+ /* First/last items carry the menu's outer radius so their hover fill clips
91
+ to the rounded corners (replaces the removed overflow:hidden). */
92
+ .rel-menu > .rel-menu-item:first-child {
93
+ border-top-left-radius: var(--rel-border-radius-md);
94
+ border-top-right-radius: var(--rel-border-radius-md);
95
+ }
96
+
97
+ .rel-menu > .rel-menu-item:last-child {
98
+ border-bottom-left-radius: var(--rel-border-radius-md);
99
+ border-bottom-right-radius: var(--rel-border-radius-md);
100
+ }
101
+
79
102
  .rel-menu-item:hover {
80
- background: var(--rel-color-bg-hover);
103
+ background: var(--rel-bg-neutral-elevated-1);
81
104
  }
82
105
 
83
106
  .rel-menu-item:active {
84
- background: var(--rel-color-bg-hover);
107
+ background: var(--rel-bg-neutral-elevated-1);
85
108
  }
86
109
 
87
110
  .rel-menu-item.active {
88
- background: var(--rel-color-bg-hover);
89
- font-weight: var(--rel-fontWeights-medium);
111
+ background: var(--rel-bg-neutral-elevated-2);
112
+ font-weight: var(--rel-body-lg-medium-font-weight);
90
113
  }
91
114
 
92
115
  .rel-menu-item.disabled {
93
116
  background-color: transparent;
94
- color: var(--rel-color-text-disabled);
117
+ color: var(--rel-text-disabled);
118
+ /* Keep the item pointer-interactive so cursor:not-allowed actually
119
+ renders; clicks are already neutralised by the item.disabled guard in
120
+ onItemClick/onItemEnter. pointer-events:none here would make the row
121
+ transparent to the pointer and suppress the cursor. */
95
122
  cursor: not-allowed;
96
123
  }
97
124
 
98
125
  .rel-menu-item[data-focused="true"] {
99
- outline: 2px solid var(--rel-color-border-focus);
126
+ outline: 2px solid var(--rel-border-focus);
100
127
  outline-offset: -2px;
101
128
  }
102
129
 
103
- .menu-check-row {
104
- display: inline-flex;
105
- align-items: center;
106
- gap: var(--rel-spacing-space-2xs);
107
- width: 100%;
130
+ /* Width modes (Figma Type=Fixed / Type=Fit) */
131
+ .rel-menu.fixed {
132
+ width: var(--rel-menu-width, 300px);
133
+ min-width: 0;
134
+ box-sizing: border-box;
108
135
  }
109
136
 
110
- .menu-checkbox {
111
- width: 16px;
112
- height: 16px;
113
- cursor: pointer;
137
+ .rel-menu.fit {
138
+ min-width: 0;
139
+ width: max-content;
114
140
  }
115
141
 
116
- .menu-checkbox:disabled {
117
- cursor: not-allowed;
142
+ /* Checkbox (Type=Checkbox) / radio (Type=Select Option) rows render the real
143
+ rel-checkbox / rel-radio. pointer-events:none keeps the whole row the click
144
+ target so onItemClick stays the single source of truth. */
145
+ .menu-control {
146
+ pointer-events: none;
147
+ width: 100%;
118
148
  }
119
149
 
120
150
  .menu-label {
121
151
  flex: 1 1 auto;
122
152
  }
123
153
 
124
- .chevron {
125
- font-size: var(--rel-fontSizes-lg);
126
- opacity: 0.75;
127
- transform: translateY(1px);
154
+ /* Submenu affordance — same rel-icon chevron the menubar uses. */
155
+ .submenu-chevron {
156
+ margin-left: auto;
157
+ display: inline-flex;
158
+ align-items: center;
159
+ opacity: 0.6;
160
+ pointer-events: none;
161
+ }
162
+
163
+ /* Shortcut / hint text (Figma body-sm-regular, caption tone) */
164
+ .menu-shortcut,
165
+ ::slotted([slot="shortcut"]) {
166
+ font-family: var(--rel-body-sm-regular-font-family);
167
+ font-weight: var(--rel-body-sm-regular-font-weight);
168
+ font-size: var(--rel-body-sm-regular-font-size);
169
+ line-height: var(--rel-body-sm-regular-line-height);
170
+ letter-spacing: var(--rel-body-sm-regular-letter-spacing);
171
+ color: var(--rel-text-caption);
172
+ margin-left: auto;
173
+ padding-left: var(--rel-spacing-md);
128
174
  }
129
175
 
176
+
130
177
  .rel-submenu {
178
+ border: 1px solid var(--rel-border-subtle);
131
179
  position: absolute;
132
- top: calc(-1 * var(--rel-spacing-space-3xs));
133
- left: calc(100% + var(--rel-spacing-space-2xs));
180
+ top: calc(-1 * var(--rel-spacing-3xs));
181
+ /* flush against the trigger (no gap) so moving the pointer into the flyout
182
+ never crosses a dead zone that would fire the trigger's mouseleave */
183
+ left: 100%;
134
184
  min-width: 200px;
135
- background: var(--rel-color-bg-primary);
136
- color: var(--rel-color-text-primary);
137
- border-radius: var(--rel-borders-radius-border-radius-300);
185
+ background: var(--rel-bg-neutral);
186
+ color: var(--rel-text-default);
187
+ border-radius: var(--rel-border-radius-md);
138
188
  box-shadow: var(--rel-shadows-shadow-300);
139
- padding: var(--rel-spacing-space-3xs) 0;
189
+ /* padding:0 + overflow:hidden — safe here (a submenu has no further nested
190
+ flyout to clip) — so sub-item hovers fill fully and clip to the corners */
191
+ padding: 0;
192
+ overflow: hidden;
140
193
  visibility: hidden;
141
194
  pointer-events: none;
142
195
  z-index: 20;
@@ -148,7 +201,7 @@ export class RelMenu extends LitElement {
148
201
  }
149
202
 
150
203
  .rel-submenu .rel-menu-item {
151
- padding: var(--rel-spacing-space-xs) var(--rel-spacing-space-sm);
204
+ padding: var(--rel-spacing-xs) var(--rel-spacing-sm);
152
205
  }
153
206
  `;
154
207
 
@@ -172,20 +225,35 @@ export class RelMenu extends LitElement {
172
225
  }
173
226
  }
174
227
 
228
+ /** Index of a submenu locked open by a click (survives hover-out). -1 = none. */
229
+ private lockedIndex = -1;
230
+
175
231
  private handleDocumentClick = (e: MouseEvent) => {
176
- if (!this.contains(e.target as Node)) {
232
+ // composedPath (NOT e.target): document-level e.target is retargeted to the
233
+ // shadow host when this menu lives inside a shadow root, so this.contains()
234
+ // wrongly reads an inside-click as outside and closes an open submenu on the
235
+ // click that opened it. composedPath pierces shadow boundaries.
236
+ if (!e.composedPath().includes(this)) {
237
+ this.lockedIndex = -1;
177
238
  this._items = this._items.map(i => ({ ...i, open: false }));
178
239
  }
179
240
  };
180
241
 
181
242
  private onItemEnter(item: MenuItem, index: number) {
243
+ if (item.disabled) return;
182
244
  if (this.type === "arrow" && item.submenu?.length) {
183
- this._items = this._items.map((i, idx) => ({ ...i, open: idx === index }));
245
+ // Hover opens this submenu; a click-locked one also stays open.
246
+ this._items = this._items.map((i, idx) => ({ ...i, open: idx === index || idx === this.lockedIndex }));
184
247
  }
185
248
  }
186
249
 
187
- private onItemLeave(_item: MenuItem) {
188
- // Keep open so user can move into submenu
250
+ private onItemLeave(_item: MenuItem, index: number) {
251
+ if (this.type !== "arrow") return;
252
+ // Hover-out closes the submenu UNLESS it was locked open by a click. Moving
253
+ // into the flyout doesn't fire this (the flyout is a DOM descendant of the
254
+ // item and sits flush at left:100%, so there's no dead-zone gap).
255
+ if (this.lockedIndex === index) return;
256
+ this._items = this._items.map((i, idx) => ({ ...i, open: idx === this.lockedIndex }));
189
257
  }
190
258
 
191
259
  private onItemClick(item: MenuItem, index: number, ev?: Event) {
@@ -208,11 +276,10 @@ export class RelMenu extends LitElement {
208
276
  }
209
277
 
210
278
  if (this.type === "arrow" && item.submenu?.length) {
211
- const next = !item.open;
212
- this._items = this._items.map((i, idx) => ({
213
- ...i,
214
- open: idx === index ? next : false,
215
- }));
279
+ // Click LOCKS the submenu open (click again to unlock/close).
280
+ const lock = this.lockedIndex !== index;
281
+ this.lockedIndex = lock ? index : -1;
282
+ this._items = this._items.map((i, idx) => ({ ...i, open: idx === index ? lock : false }));
216
283
  return;
217
284
  }
218
285
 
@@ -230,6 +297,7 @@ export class RelMenu extends LitElement {
230
297
 
231
298
  private onSubClick(parentIndex: number, subIndex: number, ev?: Event) {
232
299
  ev?.stopPropagation();
300
+ this.lockedIndex = -1;
233
301
  this._items = this._items.map((i, idx) => ({
234
302
  ...i,
235
303
  active: false,
@@ -251,21 +319,6 @@ export class RelMenu extends LitElement {
251
319
  );
252
320
  }
253
321
 
254
- private toggleCheckbox(item: MenuItem, ev: Event) {
255
- ev.stopPropagation();
256
- if (!item.disabled) {
257
- item.checked = !item.checked;
258
- this.dispatchEvent(
259
- new CustomEvent("rel-check", {
260
- detail: { item, checked: item.checked },
261
- bubbles: true,
262
- composed: true,
263
- })
264
- );
265
- this.requestUpdate();
266
- }
267
- }
268
-
269
322
  private onKeydown(ev: KeyboardEvent) {
270
323
  const last = this._items.length - 1;
271
324
 
@@ -278,12 +331,15 @@ export class RelMenu extends LitElement {
278
331
  } else if (ev.key === "ArrowRight") {
279
332
  const item = this._items[this.focusedIndex];
280
333
  if (this.type === "arrow" && item?.submenu?.length) {
334
+ // keyboard-open locks (no hover-out to close it)
335
+ this.lockedIndex = this.focusedIndex;
281
336
  this._items = this._items.map((i, idx) => ({
282
337
  ...i,
283
338
  open: idx === this.focusedIndex ? true : i.open,
284
339
  }));
285
340
  }
286
341
  } else if (ev.key === "ArrowLeft" || ev.key === "Escape") {
342
+ this.lockedIndex = -1;
287
343
  this._items = this._items.map(i => ({ ...i, open: false }));
288
344
  } else if (ev.key === "Enter" || ev.key === " ") {
289
345
  const item = this._items[this.focusedIndex];
@@ -294,6 +350,34 @@ export class RelMenu extends LitElement {
294
350
 
295
351
  private renderMenuItem(item: MenuItem, index: number) {
296
352
  const isFocused = this.focusedIndex === index;
353
+ const hasSubmenu = this.type === "arrow" && !!item.submenu?.length;
354
+
355
+ // Checkbox / select-option rows delegate their control to the real
356
+ // rel-checkbox / rel-radio components (pointer-events:none so the whole ROW
357
+ // stays the click target — onItemClick is the single source of truth and
358
+ // the control just reflects item state).
359
+ const control =
360
+ this.type === "checkbox"
361
+ ? html`<rel-checkbox
362
+ class="menu-control"
363
+ label="${item.label}"
364
+ status="${item.checked ? "checked" : "unchecked"}"
365
+ state="${item.disabled ? "disabled" : "default"}"
366
+ ></rel-checkbox>`
367
+ : this.type === "select-option"
368
+ ? html`<rel-radio
369
+ class="menu-control"
370
+ .options="${[{ value: item.label, label: item.label, disabled: !!item.disabled }]}"
371
+ .selectedValue="${item.active ? item.label : null}"
372
+ ></rel-radio>`
373
+ : html`
374
+ <span class="menu-label">${item.label}</span>
375
+ ${hasSubmenu
376
+ ? html`<span class="submenu-chevron"
377
+ ><rel-icon name="chevron-right" size="sm"></rel-icon
378
+ ></span>`
379
+ : ""}
380
+ `;
297
381
 
298
382
  return html`
299
383
  <div
@@ -301,32 +385,15 @@ export class RelMenu extends LitElement {
301
385
  role="menuitem"
302
386
  ?data-focused="${isFocused}"
303
387
  @mouseenter="${() => this.onItemEnter(item, index)}"
304
- @mouseleave="${() => this.onItemLeave(item)}"
388
+ @mouseleave="${() => this.onItemLeave(item, index)}"
305
389
  @click="${(e: Event) => this.onItemClick(item, index, e)}">
306
- ${this.type === "checkbox"
307
- ? html`
308
- <div class="menu-check-row">
309
- <input
310
- class="menu-checkbox"
311
- type="checkbox"
312
- .checked="${item.checked || false}"
313
- ?disabled="${item.disabled}"
314
- @click="${(e: Event) => this.toggleCheckbox(item, e)}" />
315
- <span class="menu-label">${item.label}</span>
316
- </div>
317
- `
318
- : html`
319
- <span class="menu-label">${item.label}</span>
320
- ${this.type === "arrow" && item.submenu?.length
321
- ? html`<span class="chevron">▸</span>`
322
- : ""}
323
- `}
324
- ${this.type === "arrow" && item.submenu?.length
390
+ ${control}
391
+ ${hasSubmenu
325
392
  ? html`
326
393
  <div
327
- class="rel-submenu ${item.open ? "visible" : ""}"
394
+ part="panel" class="rel-submenu ${item.open ? "visible" : ""}"
328
395
  @click="${(e: Event) => e.stopPropagation()}">
329
- ${item.submenu.map(
396
+ ${item.submenu!.map(
330
397
  (sub, subIndex) => html`
331
398
  <div
332
399
  class="rel-menu-item ${sub.active ? "active" : ""}"
@@ -347,7 +414,7 @@ export class RelMenu extends LitElement {
347
414
  return html`
348
415
  <style>${RelMenu._css}</style>
349
416
  <div
350
- class="rel-menu"
417
+ part="list" class="rel-menu ${this.type === "fixed" || this.type === "fit" ? this.type : ""}"
351
418
  role="menu"
352
419
  tabindex="0"
353
420
  @keydown="${this.onKeydown}"
@@ -224,32 +224,32 @@ The menubar provides comprehensive keyboard support:
224
224
  The component uses design tokens from the Reliability Design System:
225
225
 
226
226
  ### Colors
227
- - `--rel-colors-gray-light` - Menubar background
228
- - `--rel-colors-gray-background` - Trigger and item hover state
229
- - `--rel-colors-gray-border` - Borders
230
- - `--rel-colors-gray-divider` - Separators
231
- - `--rel-colors-gray-heading` - Text color
232
- - `--rel-colors-white` - Panel background
233
- - `--rel-colors-blue-primary` - Focus outline and radio selected state
227
+ - `--rel-bg-surface-elevated` - Menubar background
228
+ - `--rel-bg-neutral-elevated-1` - Trigger and item hover state
229
+ - `--rel-border-default` - Borders
230
+ - `--rel-border-default` - Separators
231
+ - `--rel-text-label` - Text color
232
+ - `--rel-bg-surface` - Panel background
233
+ - `--rel-bg-info` - Focus outline and radio selected state
234
234
 
235
235
  ### Typography
236
- - `--rel-fontFamilies-primary` - Font family (Poppins)
237
- - `--rel-fontSizes-sm` - Default text size
238
- - `--rel-fontSizes-xs` - Shortcut text size
239
- - `--rel-lineHeights-sm` - Line height
236
+ - `--rel-fonts-font-family` - Font family (Poppins)
237
+ - `--rel-body-sm-regular-font-size` - Default text size
238
+ - `--rel-body-xs-regular-font-size` - Shortcut text size
239
+ - `--rel-body-sm-regular-line-height` - Line height
240
240
 
241
241
  ### Spacing
242
- - `--rel-spacing-space-3xs` (2px) - Menubar gap and panel padding
243
- - `--rel-spacing-space-2xs` (4px) - Trigger padding vertical
244
- - `--rel-spacing-space-xs` (8px) - Item padding vertical
245
- - `--rel-spacing-space-sm` (12px) - Trigger and item horizontal padding
246
- - `--rel-spacing-space-md` (16px) - Item padding and radio offset
242
+ - `--rel-spacing-3xs` (2px) - Menubar gap and panel padding
243
+ - `--rel-spacing-2xs` (4px) - Trigger padding vertical
244
+ - `--rel-spacing-xs` (8px) - Item padding vertical
245
+ - `--rel-spacing-sm` (12px) - Trigger and item horizontal padding
246
+ - `--rel-spacing-md` (16px) - Item padding and radio offset
247
247
 
248
248
  ### Borders
249
- - `--rel-borders-width-stroke-100` (1px) - Border width
250
- - `--rel-borders-width-stroke-150` (1.5px) - Radio border
251
- - `--rel-borders-radius-border-radius-100` (2px) - Trigger border radius
252
- - `--rel-borders-radius-border-radius-200` (8px) - Panel border radius
249
+ - `--rel-stroke-sm` (1px) - Border width
250
+ - `--rel-stroke-md` (1.5px) - Radio border
251
+ - `--rel-border-radius-xs` (2px) - Trigger border radius
252
+ - `--rel-border-radius-sm` (8px) - Panel border radius
253
253
 
254
254
  ### Shadows
255
255
  - `--rel-shadows-shadow-300` - Panel drop shadow
@@ -4,7 +4,6 @@ import { RelMenubarMenu } from "./rel-menubar-menu";
4
4
  import "@reliability-design/icons/rel-icon";
5
5
  import "../rel-checkbox/rel-checkbox.js";
6
6
  import "../rel-radio/rel-radio.js";
7
- import "../../styles/vars-v2.css";
8
7
 
9
8
  interface MenuState {
10
9
  menu: RelMenubarMenu;
@@ -24,27 +23,36 @@ export class RelMenubar extends LitElement {
24
23
  private _onScroll?: () => void;
25
24
 
26
25
  public static readonly _css = `
27
- :host {
26
+ /* This component renders into LIGHT DOM (createRenderRoot returns this), so
27
+ :host / <slot> don't work here — the raw <rel-menubar-menu> definitions
28
+ are data holders that must be hidden with a plain selector. Relying on the
29
+ global rel-menubar-menu.css is not enough: it can't cross a consumer's
30
+ shadow boundary AND it's a no-op in the tsup-built package (css:"copy"). */
31
+ rel-menubar {
32
+ color: var(--rel-text-default);
28
33
  display: block;
29
34
  }
30
35
 
31
- :host > slot {
36
+ rel-menubar-menu {
32
37
  display: none;
33
38
  }
34
39
 
35
40
  .menubar-container {
36
41
  display: flex;
37
42
  align-items: stretch;
38
- background: var(--rel-color-bg-secondary);
39
- border: var(--rel-borders-width-stroke-100) solid var(--rel-color-border-default);
40
- border-radius: var(--rel-borders-radius-border-radius-200);
43
+ background: var(--rel-bg-surface-elevated);
44
+ border: var(--rel-stroke-sm) solid var(--rel-border-default);
45
+ border-radius: var(--rel-border-radius-sm);
41
46
  overflow: hidden;
42
47
  padding: 0;
43
- gap: var(--rel-spacing-space-3xs);
44
- font-family: var(--rel-fontFamilies-primary);
45
- font-size: var(--rel-fontSizes-sm);
48
+ gap: var(--rel-spacing-3xs);
46
49
  height: 40px;
47
50
  width: fit-content;
51
+ font-family: var(--rel-body-sm-regular-font-family);
52
+ font-weight: var(--rel-body-sm-regular-font-weight);
53
+ font-size: var(--rel-body-sm-regular-font-size);
54
+ line-height: var(--rel-body-sm-regular-line-height);
55
+ letter-spacing: var(--rel-body-sm-regular-letter-spacing);
48
56
  }
49
57
 
50
58
  .menubar-trigger {
@@ -52,13 +60,13 @@ export class RelMenubar extends LitElement {
52
60
  align-items: center;
53
61
  align-self: stretch;
54
62
  height: 100%;
55
- padding: 0 var(--rel-spacing-space-sm);
63
+ padding: 0 var(--rel-spacing-sm);
56
64
  background: transparent;
57
65
  border: none;
58
66
  cursor: pointer;
59
67
  font-family: inherit;
60
68
  font-size: inherit;
61
- color: var(--rel-color-text-heading);
69
+ color: var(--rel-text-label);
62
70
  transition: background 0.15s ease;
63
71
  user-select: none;
64
72
  outline: none;
@@ -66,20 +74,20 @@ export class RelMenubar extends LitElement {
66
74
 
67
75
  .menubar-trigger:hover,
68
76
  .menubar-trigger.active {
69
- background: var(--rel-color-bg-selected);
77
+ background: var(--rel-bg-neutral-elevated-2);
70
78
  }
71
79
 
72
80
  .menubar-trigger:focus-visible {
73
- outline: 2px solid var(--rel-color-border-focus);
81
+ outline: 2px solid var(--rel-border-focus);
74
82
  outline-offset: 2px;
75
83
  }
76
84
 
77
85
  .menubar-panel {
78
86
  position: fixed;
79
87
  min-width: 220px;
80
- background: var(--rel-color-bg-primary);
81
- border: var(--rel-borders-width-stroke-100) solid var(--rel-color-border-default);
82
- border-radius: var(--rel-borders-radius-border-radius-200);
88
+ background: var(--rel-bg-surface);
89
+ border: var(--rel-stroke-sm) solid var(--rel-border-default);
90
+ border-radius: var(--rel-border-radius-sm);
83
91
  box-shadow: var(--rel-shadows-shadow-300);
84
92
  padding: 0;
85
93
  overflow: hidden;
@@ -96,18 +104,18 @@ export class RelMenubar extends LitElement {
96
104
  display: flex;
97
105
  align-items: center;
98
106
  justify-content: space-between;
99
- gap: var(--rel-spacing-space-sm);
100
- padding: var(--rel-spacing-space-xs) var(--rel-spacing-space-md);
107
+ gap: var(--rel-spacing-sm);
108
+ padding: var(--rel-spacing-xs) var(--rel-spacing-md);
101
109
  cursor: pointer;
102
110
  transition: background 0.15s ease;
103
111
  user-select: none;
104
- color: var(--rel-color-text-heading);
105
- line-height: var(--rel-lineHeights-sm);
112
+ color: var(--rel-text-label);
113
+ line-height: var(--rel-body-sm-regular-line-height);
106
114
  }
107
115
 
108
116
  .menu-item:hover,
109
117
  .menu-item.focused {
110
- background: var(--rel-color-bg-selected);
118
+ background: var(--rel-bg-neutral-elevated-2);
111
119
  }
112
120
 
113
121
  .menu-item[data-disabled="true"] {
@@ -116,8 +124,8 @@ export class RelMenubar extends LitElement {
116
124
  }
117
125
 
118
126
  .menu-separator {
119
- height: var(--rel-borders-width-stroke-100);
120
- background: var(--rel-color-border-divider);
127
+ height: var(--rel-stroke-sm);
128
+ background: var(--rel-border-default);
121
129
  margin: 0;
122
130
  padding: 0;
123
131
  }
@@ -138,9 +146,9 @@ export class RelMenubar extends LitElement {
138
146
  .submenu-panel {
139
147
  position: fixed;
140
148
  min-width: 200px;
141
- background: var(--rel-color-bg-primary);
142
- border: var(--rel-borders-width-stroke-100) solid var(--rel-color-border-default);
143
- border-radius: var(--rel-borders-radius-border-radius-200);
149
+ background: var(--rel-bg-surface);
150
+ border: var(--rel-stroke-sm) solid var(--rel-border-default);
151
+ border-radius: var(--rel-border-radius-sm);
144
152
  box-shadow: var(--rel-shadows-shadow-300);
145
153
  padding: 0;
146
154
  overflow: hidden;
@@ -154,17 +162,21 @@ export class RelMenubar extends LitElement {
154
162
 
155
163
  .menu-shortcut {
156
164
  opacity: 0.6;
157
- font-size: var(--rel-fontSizes-xs);
158
165
  margin-left: auto;
159
166
  display: flex;
160
167
  align-items: center;
161
- gap: var(--rel-spacing-space-3xs);
168
+ gap: var(--rel-spacing-3xs);
169
+ font-family: var(--rel-body-xs-regular-font-family);
170
+ font-weight: var(--rel-body-xs-regular-font-weight);
171
+ font-size: var(--rel-body-xs-regular-font-size);
172
+ line-height: var(--rel-body-xs-regular-line-height);
173
+ letter-spacing: var(--rel-body-xs-regular-letter-spacing);
162
174
  }
163
175
 
164
176
  .menubar-panel rel-checkbox {
165
177
  display: block;
166
178
  width: 100%;
167
- padding: var(--rel-spacing-space-xs, 8px) var(--rel-spacing-space-md, 16px);
179
+ padding: var(--rel-spacing-xs, 8px) var(--rel-spacing-md, 16px);
168
180
  box-sizing: border-box;
169
181
  cursor: pointer;
170
182
  }
@@ -224,7 +236,12 @@ export class RelMenubar extends LitElement {
224
236
  }
225
237
 
226
238
  private handleOutsideClick = (e: MouseEvent) => {
227
- if (!this.contains(e.target as Node)) {
239
+ // composedPath (NOT e.target): a document-level listener sees e.target
240
+ // retargeted to the shadow host when the menubar lives inside a shadow root
241
+ // (e.g. any consumer component), so this.contains(e.target) is false for a
242
+ // click that is actually INSIDE the menubar — closing the menu on the very
243
+ // click that opened it. composedPath pierces shadow boundaries.
244
+ if (!e.composedPath().includes(this)) {
228
245
  this.closeAllMenus();
229
246
  }
230
247
  };
@@ -581,7 +598,6 @@ export class RelMenubar extends LitElement {
581
598
  render() {
582
599
  return html`
583
600
  <style>${RelMenubar._css}</style>
584
- <slot></slot>
585
601
  <div class="menubar-container">
586
602
  ${this.menus.map(
587
603
  (menuState, index) => html`