@reliability-design/web 1.4.6 → 1.4.8

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 (148) hide show
  1. package/CHANGELOG.md +246 -49
  2. package/dist/components/rel-accordion/README.md +133 -132
  3. package/dist/components/rel-alert/README.md +145 -141
  4. package/dist/components/rel-autocomplete/README.md +172 -61
  5. package/dist/components/rel-avatar/README.md +118 -130
  6. package/dist/components/rel-badge/README.md +97 -93
  7. package/dist/components/rel-breadcrumb/README.md +96 -102
  8. package/dist/components/rel-button/README.md +190 -130
  9. package/dist/components/rel-calendar/README.md +109 -109
  10. package/dist/components/rel-card/README.md +75 -94
  11. package/dist/components/rel-cardmedia/README.md +100 -127
  12. package/dist/components/rel-carousel/README.md +186 -166
  13. package/dist/components/rel-checkbox/README.md +96 -75
  14. package/dist/components/rel-chip/README.md +138 -121
  15. package/dist/components/rel-container/README.md +107 -99
  16. package/dist/components/rel-date-picker/README.md +200 -289
  17. package/dist/components/rel-divider/README.md +88 -100
  18. package/dist/components/rel-drawer/README.md +187 -200
  19. package/dist/components/rel-kbd/README.md +141 -151
  20. package/dist/components/rel-layout/README.md +34 -0
  21. package/dist/components/rel-menu/README.md +180 -151
  22. package/dist/components/rel-menubar/README.md +218 -329
  23. package/dist/components/rel-otp/README.md +171 -440
  24. package/dist/components/rel-pagination/README.md +100 -424
  25. package/dist/components/rel-popover/README.md +203 -568
  26. package/dist/components/rel-progress/README.md +88 -110
  27. package/dist/components/rel-radio/README.md +131 -138
  28. package/dist/components/rel-range-picker/README.md +165 -0
  29. package/dist/components/rel-resizable/README.md +199 -140
  30. package/dist/components/rel-segmented-button/README.md +156 -141
  31. package/dist/components/rel-select/README.md +156 -93
  32. package/dist/components/rel-sheet/README.md +168 -224
  33. package/dist/components/rel-skeleton/README.md +104 -0
  34. package/dist/components/rel-slider/README.md +131 -135
  35. package/dist/components/rel-snackbar/README.md +168 -134
  36. package/dist/components/rel-stepper/README.md +162 -160
  37. package/dist/components/rel-switch/README.md +117 -102
  38. package/dist/components/rel-tab/README.md +173 -163
  39. package/dist/components/rel-textfield/README.md +176 -69
  40. package/dist/components/rel-time-picker/README.md +122 -157
  41. package/dist/components/rel-toast/README.md +165 -148
  42. package/dist/components/rel-tooltip/README.md +119 -111
  43. package/dist/components/rel-upload/README.md +220 -0
  44. package/dist/index.d.mts +1726 -447
  45. package/dist/index.d.ts +1726 -447
  46. package/dist/index.js +13715 -10870
  47. package/dist/index.js.map +1 -1
  48. package/dist/index.mjs +14018 -11163
  49. package/dist/index.mjs.map +1 -1
  50. package/dist/styles/focus-ring.ts +84 -0
  51. package/dist/styles/vars.css +892 -866
  52. package/dist/{vars-XNMTSRVN.css → vars-DXX6QOAG.css} +892 -866
  53. package/package.json +3 -2
  54. package/dist/components/rel-accordion/rel-accordion.ts +0 -336
  55. package/dist/components/rel-alert/rel-alert.ts +0 -305
  56. package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -720
  57. package/dist/components/rel-avatar/rel-avatar.ts +0 -142
  58. package/dist/components/rel-badge/rel-badge.ts +0 -175
  59. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
  60. package/dist/components/rel-button/rel-button.ts +0 -560
  61. package/dist/components/rel-calendar/index.ts +0 -1
  62. package/dist/components/rel-calendar/rel-calendar.ts +0 -565
  63. package/dist/components/rel-card/rel-card.ts +0 -88
  64. package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
  65. package/dist/components/rel-carousel/index.ts +0 -1
  66. package/dist/components/rel-carousel/rel-carousel.ts +0 -723
  67. package/dist/components/rel-chart/README.md +0 -286
  68. package/dist/components/rel-chart/index.ts +0 -1
  69. package/dist/components/rel-chart/rel-chart.ts +0 -754
  70. package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
  71. package/dist/components/rel-chip/rel-chip.ts +0 -483
  72. package/dist/components/rel-container/rel-container.ts +0 -95
  73. package/dist/components/rel-date-picker/date-format.test.ts +0 -43
  74. package/dist/components/rel-date-picker/date-format.ts +0 -47
  75. package/dist/components/rel-date-picker/date-parse.test.ts +0 -116
  76. package/dist/components/rel-date-picker/date-parse.ts +0 -178
  77. package/dist/components/rel-date-picker/index.ts +0 -1
  78. package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1676
  79. package/dist/components/rel-divider/rel-divider.ts +0 -54
  80. package/dist/components/rel-drawer/index.ts +0 -1
  81. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
  82. package/dist/components/rel-drawer/rel-drawer.ts +0 -607
  83. package/dist/components/rel-kbd/rel-kbd.ts +0 -188
  84. package/dist/components/rel-layout/rel-layout.ts +0 -49
  85. package/dist/components/rel-menu/rel-menu.ts +0 -541
  86. package/dist/components/rel-menubar/index.ts +0 -2
  87. package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
  88. package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
  89. package/dist/components/rel-menubar/rel-menubar.ts +0 -777
  90. package/dist/components/rel-nav-menu/README.md +0 -289
  91. package/dist/components/rel-nav-menu/index.ts +0 -3
  92. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
  93. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
  94. package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
  95. package/dist/components/rel-navbar/README.md +0 -126
  96. package/dist/components/rel-navbar/rel-navbar.ts +0 -105
  97. package/dist/components/rel-otp/index.ts +0 -1
  98. package/dist/components/rel-otp/rel-otp.ts +0 -527
  99. package/dist/components/rel-pagination/index.ts +0 -1
  100. package/dist/components/rel-pagination/rel-pagination.ts +0 -522
  101. package/dist/components/rel-popover/index.ts +0 -2
  102. package/dist/components/rel-popover/rel-popover-content.ts +0 -81
  103. package/dist/components/rel-popover/rel-popover.ts +0 -475
  104. package/dist/components/rel-progress/rel-progress.ts +0 -234
  105. package/dist/components/rel-radio/rel-radio.ts +0 -481
  106. package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1688
  107. package/dist/components/rel-resizable/index.ts +0 -3
  108. package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
  109. package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
  110. package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
  111. package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -217
  112. package/dist/components/rel-select/rel-select.ts +0 -1416
  113. package/dist/components/rel-sheet/rel-sheet.ts +0 -610
  114. package/dist/components/rel-sidebar/README.md +0 -209
  115. package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1191
  116. package/dist/components/rel-skeleton/rel-skeleton.ts +0 -143
  117. package/dist/components/rel-slider/rel-slider.ts +0 -673
  118. package/dist/components/rel-snackbar/rel-snackbar.ts +0 -329
  119. package/dist/components/rel-stepper/rel-stepper.ts +0 -567
  120. package/dist/components/rel-switch/rel-switch.ts +0 -199
  121. package/dist/components/rel-tab/rel-tab.ts +0 -264
  122. package/dist/components/rel-table/README.md +0 -178
  123. package/dist/components/rel-table/rel-table-body.ts +0 -22
  124. package/dist/components/rel-table/rel-table-caption.ts +0 -30
  125. package/dist/components/rel-table/rel-table-cell.ts +0 -43
  126. package/dist/components/rel-table/rel-table-footer.ts +0 -24
  127. package/dist/components/rel-table/rel-table-head.ts +0 -47
  128. package/dist/components/rel-table/rel-table-header.ts +0 -22
  129. package/dist/components/rel-table/rel-table-row.ts +0 -37
  130. package/dist/components/rel-table/rel-table.ts +0 -62
  131. package/dist/components/rel-textfield/rel-textfield.ts +0 -1054
  132. package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1482
  133. package/dist/components/rel-time-picker/time-parse.test.ts +0 -67
  134. package/dist/components/rel-time-picker/time-parse.ts +0 -85
  135. package/dist/components/rel-toast/rel-toast.ts +0 -206
  136. package/dist/components/rel-tooltip/rel-tooltip.ts +0 -193
  137. package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
  138. package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
  139. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
  140. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
  141. package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
  142. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
  143. package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
  144. package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
  145. package/dist/components/rel-topbar/rel-topbar.ts +0 -446
  146. package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
  147. package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
  148. package/dist/components/rel-upload/rel-upload.ts +0 -968
@@ -1,446 +0,0 @@
1
- import { LitElement, html, css } from "lit";
2
- import { customElement, property, state } from "lit/decorators.js";
3
- import "@reliability-design/icons/rel-icon";
4
- import "./rel-topbar-brand";
5
- import "./rel-topbar-actions";
6
- import "./rel-topbar-utilities";
7
- import "./rel-topbar-mobile-panel";
8
- import "./rel-topbar-dropdown";
9
- import "./rel-topbar-dropdown-item";
10
-
11
- export type TopbarVariant = "customer" | "carrier";
12
- export type TopbarForceLayout = "auto" | "desktop" | "tablet" | "mobile";
13
-
14
- /**
15
- * `rel-topbar` — Application-level top navigation / header shell.
16
- *
17
- * @deprecated This component's fixed slot structure, hardcoded breakpoint,
18
- * and built-in overflow logic are too rigid for most real layouts. Use the
19
- * "Navbar" recipe at /docs/recipes/navbar instead, composed from
20
- * rel-menu/rel-popover, rel-drawer, and rel-button. Still supported, but
21
- * will be removed in a future major version.
22
- *
23
- * Slots:
24
- * brand — Logo / wordmark region (left)
25
- * (default) — Primary nav items (rel-topbar-item elements)
26
- * actions — CTA / action buttons (customer) or dark-pill area (carrier)
27
- * utilities — Utility icon buttons (notifications, search, etc.)
28
- * profile — rel-topbar-profile tile
29
- * mobile-panel — Collapsed nav panel shown when mobileOpen is true
30
- *
31
- * Events:
32
- * rel-menu-toggle — fired when hamburger is clicked { open: boolean }
33
- * rel-mobile-open-change — alias of rel-menu-toggle for convenience
34
- * rel-nav-select — bubbled from child rel-topbar-item { value, label }
35
- */
36
- @customElement("rel-topbar")
37
- export class RelTopbar extends LitElement {
38
- /** Visual variant — affects action/profile region styling */
39
- @property({ type: String, reflect: true }) variant: TopbarVariant = "customer";
40
-
41
- /** Whether the mobile nav panel is currently open */
42
- @property({ type: Boolean, reflect: true }) mobileOpen = false;
43
-
44
- /** Whether to render the hamburger / close toggle button */
45
- @property({ type: Boolean, reflect: true }) showMenuToggle = false;
46
-
47
- /** Reduce height to compact (48 px) mode */
48
- @property({ type: Boolean, reflect: true }) compact = false;
49
-
50
- /** Stick topbar to top of viewport while scrolling */
51
- @property({ type: Boolean, reflect: true }) sticky = false;
52
-
53
- /**
54
- * Override the responsive layout for demos / storybook.
55
- * 'auto' respects CSS media queries.
56
- */
57
- @property({ type: String }) forceLayout: TopbarForceLayout = "auto";
58
-
59
- private _overflowItems: Element[] = [];
60
- @state() private _overflowCount: number = 0;
61
- @state() private _overflowItemsData: Array<{ label: string; href: string; disabled: boolean }> = [];
62
- private _resizeObserver?: ResizeObserver;
63
- private _resizeTimer?: number;
64
- private _measuring = false;
65
- private _lastNavWidth = 0;
66
-
67
- static styles = css`
68
- /* ─── Component tokens ─────────────────────────────────────────── */
69
- :host {
70
- color: var(--rel-text-default);
71
- --rel-topbar-height: 64px;
72
- --rel-topbar-height-compact: 48px;
73
- --rel-topbar-bg: var(--rel-bg-surface);
74
- --rel-topbar-border-color: var(--rel-border-default);
75
- --rel-topbar-item-color: var(--rel-text-default);
76
- --rel-topbar-item-active-color: var(--rel-text-success);
77
- --rel-topbar-item-active-indicator: var(--rel-bg-brand);
78
- --rel-topbar-item-hover-bg: var(--rel-bg-surface-elevated);
79
- --rel-topbar-divider-color: var(--rel-border-default);
80
- --rel-topbar-carrier-action-bg: var(--rel-text-default);
81
- --rel-topbar-carrier-action-color: var(--rel-bg-surface);
82
- --rel-topbar-z: 100;
83
-
84
- display: block;
85
- width: 100%;
86
- font-family: var(--rel-fonts-font-family, Poppins, sans-serif);
87
- }
88
-
89
- /* ─── Sticky host ───────────────────────────────────────────────── */
90
- :host([sticky]) {
91
- position: sticky;
92
- top: 0;
93
- z-index: var(--rel-topbar-z);
94
- }
95
-
96
- /* ─── Bar shell ─────────────────────────────────────────────────── */
97
- .topbar {
98
- display: flex;
99
- align-items: center;
100
- height: var(--rel-topbar-height);
101
- background: var(--rel-topbar-bg);
102
- border-bottom: var(--rel-stroke-sm, 1px) solid var(--rel-topbar-border-color);
103
- padding: 0 var(--rel-spacing-xl, 24px);
104
- box-sizing: border-box;
105
- gap: var(--rel-spacing-md, 16px);
106
- }
107
-
108
- :host([compact]) .topbar {
109
- height: var(--rel-topbar-height-compact);
110
- }
111
-
112
- /* ─── Brand ─────────────────────────────────────────────────────── */
113
- .brand-region {
114
- display: flex;
115
- align-items: center;
116
- flex-shrink: 0;
117
- }
118
-
119
- /* ─── Nav (primary, desktop) ────────────────────────────────────── */
120
- .nav-region {
121
- display: flex;
122
- align-items: center;
123
- flex: 1 1 0;
124
- gap: var(--rel-spacing-2xs, 4px);
125
- min-width: 0;
126
- }
127
-
128
- /* ─── Right side cluster ────────────────────────────────────────── */
129
- .right-region {
130
- display: flex;
131
- align-items: center;
132
- flex-shrink: 0;
133
- margin-left: auto;
134
- gap: var(--rel-spacing-xs, 8px);
135
- }
136
-
137
- .actions-region,
138
- .utilities-region,
139
- .profile-region {
140
- display: contents;
141
- }
142
-
143
- /* Vertical divider between util/profile when both are populated */
144
- .divider {
145
- width: var(--rel-stroke-sm, 1px);
146
- height: 24px;
147
- background: var(--rel-topbar-divider-color);
148
- flex-shrink: 0;
149
- }
150
-
151
- /* ─── Carrier action pill wrapper ───────────────────────────────── */
152
- :host([variant="carrier"]) .actions-region ::slotted(*) {
153
- display: flex;
154
- align-items: center;
155
- gap: var(--rel-spacing-xs, 8px);
156
- background: var(--rel-topbar-carrier-action-bg);
157
- color: var(--rel-topbar-carrier-action-color);
158
- padding: var(--rel-spacing-2xs, 4px) var(--rel-spacing-sm, 12px);
159
- border-radius: var(--rel-border-radius-full-rounded, 1000px);
160
- }
161
-
162
- /* ─── Mobile toggle button ──────────────────────────────────────── */
163
- .menu-toggle {
164
- display: none;
165
- align-items: center;
166
- justify-content: center;
167
- width: 40px;
168
- height: 40px;
169
- padding: 0;
170
- border: none;
171
- background: transparent;
172
- border-radius: var(--rel-border-radius-sm, 8px);
173
- cursor: pointer;
174
- color: var(--rel-topbar-item-color);
175
- flex-shrink: 0;
176
- }
177
-
178
- .menu-toggle:focus-visible {
179
- outline: 2px solid var(--rel-topbar-item-active-indicator);
180
- outline-offset: 2px;
181
- }
182
-
183
- .menu-toggle:hover {
184
- background: var(--rel-topbar-item-hover-bg);
185
- }
186
-
187
- .menu-toggle svg {
188
- display: block;
189
- }
190
-
191
- /* ─── Mobile panel ──────────────────────────────────────────────── */
192
- .mobile-panel {
193
- display: none;
194
- background: var(--rel-topbar-bg);
195
- border-bottom: var(--rel-stroke-sm, 1px) solid var(--rel-topbar-border-color);
196
- }
197
-
198
- .mobile-panel.open {
199
- display: block;
200
- }
201
-
202
- .mobile-overflow-nav {
203
- display: flex;
204
- flex-direction: column;
205
- padding: var(--rel-spacing-xs, 8px) 0;
206
- border-top: 1px solid var(--rel-topbar-border-color);
207
- }
208
-
209
- .mobile-overflow-item {
210
- display: block;
211
- padding: var(--rel-spacing-xs, 8px) var(--rel-spacing-xl, 24px);
212
- color: var(--rel-topbar-item-color);
213
- text-decoration: none;
214
- font-family: var(--rel-body-md-medium-font-family);
215
- font-weight: var(--rel-body-md-medium-font-weight);
216
- font-size: var(--rel-body-md-medium-font-size);
217
- line-height: var(--rel-body-md-medium-line-height);
218
- letter-spacing: var(--rel-body-md-medium-letter-spacing);
219
- }
220
-
221
- .mobile-overflow-item:hover {
222
- background: var(--rel-topbar-item-hover-bg);
223
- }
224
-
225
- .mobile-overflow-item.disabled {
226
- opacity: 0.45;
227
- pointer-events: none;
228
- }
229
-
230
- /* ─── Responsive: auto mode (CSS media queries) ─────────────────── */
231
- @media (max-width: 768px) {
232
- .topbar:not(.force-desktop) .nav-region {
233
- display: none;
234
- }
235
-
236
- .topbar:not(.force-desktop) .menu-toggle {
237
- display: flex;
238
- }
239
- }
240
-
241
- @media (min-width: 769px) {
242
- .topbar:not(.force-mobile):not(.force-tablet) .menu-toggle {
243
- display: none;
244
- }
245
- }
246
-
247
- /* ─── forceLayout overrides ─────────────────────────────────────── */
248
- .topbar.force-mobile .nav-region,
249
- .topbar.force-tablet .nav-region {
250
- display: none;
251
- }
252
-
253
- .topbar.force-mobile .menu-toggle,
254
- .topbar.force-tablet .menu-toggle {
255
- display: flex;
256
- }
257
-
258
- .topbar.force-desktop .nav-region {
259
- display: flex;
260
- }
261
-
262
- .topbar.force-desktop .menu-toggle {
263
- display: none;
264
- }
265
- `;
266
-
267
- override firstUpdated() {
268
- const navRegion = this.renderRoot.querySelector(".nav-region") as HTMLElement;
269
- if (navRegion) {
270
- this._resizeObserver = new ResizeObserver(() => {
271
- clearTimeout(this._resizeTimer);
272
- this._resizeTimer = window.setTimeout(() => this._measureNav(), 150);
273
- });
274
- this._resizeObserver.observe(navRegion);
275
- }
276
- requestAnimationFrame(() => this._measureNav());
277
- }
278
-
279
- override disconnectedCallback() {
280
- super.disconnectedCallback();
281
- this._resizeObserver?.disconnect();
282
- clearTimeout(this._resizeTimer);
283
- }
284
-
285
- private _measureNav() {
286
- if (this._measuring) return;
287
- this._measuring = true;
288
- const navRegion = this.renderRoot.querySelector(".nav-region") as HTMLElement;
289
- if (!navRegion) return;
290
- const navSlot = this.renderRoot.querySelector('slot[name="nav"]') as HTMLSlotElement;
291
- if (!navSlot) return;
292
- const items = Array.from(navSlot.assignedElements()).filter(
293
- el => el.tagName.toLowerCase() !== "rel-topbar-item" || !el.hasAttribute("data-more")
294
- );
295
- const navWidth = navRegion.getBoundingClientRect().width;
296
- if (Math.abs(navWidth - this._lastNavWidth) < 2) return;
297
- this._lastNavWidth = navWidth;
298
- const available = navWidth - 120;
299
-
300
- let accumulated = 0;
301
- let firstOverflowIndex = items.length;
302
- for (let i = 0; i < items.length; i++) {
303
- accumulated += items[i].getBoundingClientRect().width;
304
- if (accumulated > available) {
305
- firstOverflowIndex = i;
306
- break;
307
- }
308
- }
309
-
310
- const overflowingElements = Array.from(items).slice(firstOverflowIndex);
311
- this._overflowItems = overflowingElements;
312
- this._overflowCount = overflowingElements.length;
313
- this._overflowItemsData = overflowingElements.map(el => ({
314
- label: el.getAttribute("label") ?? "",
315
- href: el.getAttribute("href") ?? "",
316
- disabled: el.hasAttribute("disabled"),
317
- }));
318
-
319
- for (const el of items) {
320
- if (overflowingElements.includes(el)) {
321
- el.setAttribute("hidden", "");
322
- } else {
323
- el.removeAttribute("hidden");
324
- }
325
- }
326
-
327
- this._measuring = false;
328
- }
329
-
330
- private onRouterSlotChange(e: Event) {
331
- const slot = e.target as HTMLSlotElement;
332
- for (const el of slot.assignedElements({ flatten: true })) {
333
- const tag = (el as HTMLElement).tagName.toLowerCase();
334
- const targetSlot =
335
- tag === "rel-topbar-brand" ? "brand" :
336
- tag === "rel-topbar-actions" ? "actions" :
337
- tag === "rel-topbar-utilities" ? "utilities" :
338
- tag === "rel-topbar-profile" ? "profile" :
339
- tag === "rel-topbar-mobile-panel" ? "mobile-panel" :
340
- "nav";
341
- if ((el as HTMLElement).slot !== targetSlot) {
342
- (el as HTMLElement).slot = targetSlot;
343
- }
344
- }
345
- }
346
-
347
- private get layoutClass(): string {
348
- if (this.forceLayout === "auto") return "";
349
- return `force-${this.forceLayout}`;
350
- }
351
-
352
- private handleMenuToggle() {
353
- this.mobileOpen = !this.mobileOpen;
354
- const detail = { open: this.mobileOpen };
355
- this.dispatchEvent(
356
- new CustomEvent("rel-menu-toggle", { detail, bubbles: true, composed: true })
357
- );
358
- this.dispatchEvent(
359
- new CustomEvent("rel-mobile-open-change", { detail, bubbles: true, composed: true })
360
- );
361
- }
362
-
363
- render() {
364
- const topbarClass = ["topbar", this.layoutClass].filter(Boolean).join(" ");
365
- const menuLabel = this.mobileOpen ? "Close menu" : "Open menu";
366
-
367
- return html`
368
- <!-- router: auto-assigns slot names by tagName; stays empty after redistribution -->
369
- <slot style="display:none" @slotchange=${this.onRouterSlotChange}></slot>
370
-
371
- <header>
372
- <div class=${topbarClass} role="banner">
373
- <div class="brand-region">
374
- <slot name="brand"></slot>
375
- </div>
376
-
377
- <nav class="nav-region" aria-label="Primary">
378
- <slot name="nav" @slotchange=${this._measureNav}></slot>
379
- ${this._overflowCount > 0 ? html`
380
- <rel-topbar-item label="More" expandable data-more>
381
- <rel-topbar-dropdown slot="dropdown">
382
- ${this._overflowItemsData.map(item => html`
383
- <rel-topbar-dropdown-item
384
- label=${item.label}
385
- href=${item.href}
386
- ?disabled=${item.disabled}
387
- ></rel-topbar-dropdown-item>
388
- `)}
389
- </rel-topbar-dropdown>
390
- </rel-topbar-item>
391
- ` : null}
392
- </nav>
393
-
394
- <div class="right-region">
395
- <div class="actions-region">
396
- <slot name="actions"></slot>
397
- </div>
398
- <div class="utilities-region">
399
- <slot name="utilities"></slot>
400
- </div>
401
- <div class="profile-region">
402
- <slot name="profile"></slot>
403
- </div>
404
-
405
- ${this.showMenuToggle
406
- ? html`
407
- <button
408
- class="menu-toggle"
409
- aria-label=${menuLabel}
410
- aria-expanded=${this.mobileOpen}
411
- aria-controls="mobile-panel"
412
- @click=${this.handleMenuToggle}
413
- >
414
- ${this.mobileOpen
415
- ? html`<rel-icon name="close" size="md"></rel-icon>`
416
- : html`<rel-icon name="hamburger" size="md"></rel-icon>`}
417
- </button>
418
- `
419
- : null}
420
- </div>
421
- </div>
422
- </header>
423
-
424
- <div
425
- class="mobile-panel ${this.mobileOpen ? "open" : ""}"
426
- id="mobile-panel"
427
- role="region"
428
- aria-label="Mobile navigation"
429
- >
430
- <slot name="mobile-panel"></slot>
431
- ${this._overflowItemsData.length > 0 ? html`
432
- <div class="mobile-overflow-nav">
433
- ${this._overflowItemsData.map(item => html`
434
- <a
435
- class="mobile-overflow-item ${item.disabled ? "disabled" : ""}"
436
- href=${item.href}
437
- ?aria-disabled=${item.disabled}
438
- >${item.label}</a>
439
- `)}
440
- </div>
441
- ` : null}
442
- </div>
443
- `;
444
- }
445
- }
446
-
@@ -1,134 +0,0 @@
1
- # rel-upload — implementation vs. Figma, and what to update in the design
2
-
3
- **For:** design team · **From:** engineering · **Date:** 2026-08-09
4
- **Figma reference:** Reliability DS V1.0 → "↳ File Upload" page (node `8160:917`)
5
- — `Upload Dropzone` (`8160:2615`), `Upload File Row` (`8160:2566`), `Upload Modal` (`8160:2622`)
6
- **Shipped:** `<rel-upload>` + `<rel-upload-actions>` in `packages/web/src/components/rel-upload/`
7
-
8
- This replaces the previous version of this doc, which was written against the
9
- outdated sandbox design. Per our agreement, engineering adapted the design where
10
- it had gaps or conflicted with how the design system already works — everything
11
- that differs is listed here so the Figma file can be brought in line.
12
-
13
- ---
14
-
15
- ## What matches the Figma exactly
16
-
17
- - **Dropzone**: 16px radius, dashed border, 32/24 padding, circular icon badge,
18
- "Drag and drop your files here" (16/24 medium) over the format/size hint
19
- (12/20), pill "Browse files" button.
20
- - **File rows**: 8px radius, 12/16 padding, document icon → name (14/22) + meta
21
- (12/20) → trailing status icon + remove. All four states (Ready, Uploading,
22
- Success, Error) including the tinted error row.
23
- - **Progress bar**: 4px, fully rounded, green fill, with the
24
- `45% - 2.4 MB of 5.1 MB` meta line.
25
- - **Modal**: composed from the existing `<rel-modal>` — title, supporting text,
26
- close ×, scrolling body, pinned footer. All six variants are reproduced.
27
- - **Copy**: every string in the design ships verbatim, including
28
- `Selected files (3)`, `Uploading 3 files`, `3 files - 2 failed`,
29
- `Upload 3 files`, `Retry upload`, and both alert messages.
30
-
31
- The hint row is driven by real props: formats come from `accept`, the size limit
32
- from `max-size`. The design's "PDF, PNG or JPG" / "10 MB" are example values.
33
-
34
- ## Colour changes the design should adopt
35
-
36
- All three are contrast or token-layer corrections. Every hex in the design
37
- already exists in `vars.css`, but three of them are used at the wrong layer.
38
-
39
- ### 1. Secondary text `#7D7D7D` → `--rel-text-caption` `#525252`
40
-
41
- `#7D7D7D` (gray-400) scores **4.12:1 on white — below the 4.5:1 gate**, and in
42
- the token system gray-400 is not a body-text token at all. Used for the dropzone
43
- hint and the file-row meta line. `--rel-text-caption` (`#525252`) scores
44
- **7.81:1**.
45
-
46
- ### 2. Success text `#008655` → `--rel-text-success` `#006440`
47
-
48
- Green-600 passes (4.62:1) but the system's success-text token is green-700
49
- (**7.25:1**), and the green rule fixes success text/icons at `#006440`. Affects
50
- the "…- Uploaded" meta line and the success check icon.
51
-
52
- ### 3. Dashed dropzone border `#C2C2C2` → `--rel-border-accent` `#8C8C8C`
53
-
54
- `#C2C2C2` scores **1.78:1**, under the 3:1 gate for UI boundaries, and gray-200
55
- has no border token. `--rel-border-accent` (gray-350) scores **3.36:1**.
56
-
57
- Also note: the error row border uses `--rel-border-danger` (`#CE3131`, 5.11:1)
58
- rather than the design's `#FF5252` (3.19:1) — red-500 has no border token.
59
- And the error meta on the tinted row (`#CE3131` on `#FFEBEB`) lands at
60
- **4.46:1**, marginally under. That pairing is inherited from the existing
61
- `rel-alert` subtle-error style, so it is a system-wide question rather than
62
- something this component introduced — flagging it, not changing it here.
63
-
64
- ## Structural changes
65
-
66
- ### 4. The footer is a separate element, not part of the upload body
67
-
68
- In the Figma the Cancel/Upload row sits in the Modal Sheet, outside the slot
69
- holding the upload body. We matched that: `<rel-upload>` renders the alert,
70
- dropzone and file list; `<rel-upload-actions>` renders the buttons and goes in
71
- `<rel-modal>`'s footer slot. This is what keeps the footer **pinned while the
72
- file list scrolls** — with five files the body scrolls and the buttons stay put.
73
- The actions element finds its `<rel-upload>` sibling automatically.
74
-
75
- ### 5. The dropzone is not itself a button
76
-
77
- The design puts a "Browse files" button inside the dropzone. Making the whole
78
- dropzone clickable *and* focusable would nest interactive controls inside each
79
- other, which is an accessibility violation. The dropzone stays a drag-and-drop
80
- target and a mouse click affordance, but "Browse files" is the only focusable
81
- control — so keyboard and screen-reader users get exactly one clear action.
82
-
83
- ### 6. Buttons and the alert are the standard DS components
84
-
85
- Cancel/Upload are `rel-button`, and both alerts are `rel-alert` (warning and
86
- error, subtle). They inherit the system's colours, states and focus rings rather
87
- than the frame's local styles. One visible consequence: the design draws the
88
- buttons as pills, which `rel-button` supports, but the disabled Upload uses the
89
- system's disabled fill rather than the frame's dimmed grey. If design prefers the
90
- dimmed look, that is a change to the *button* spec, not to upload.
91
-
92
- ## Behaviour the design doesn't specify
93
-
94
- ### 7. Validation failures render as error rows
95
-
96
- The Figma Failure state shows rows reading "Upload failed - file exceeds the
97
- 10 MB limit" and "Upload failed - unsupported file type". Both are knowable
98
- *before* any transfer, so we surface rejected files as error rows immediately on
99
- drop rather than silently discarding them. The wording is unchanged.
100
-
101
- ### 8. Retry only re-sends what can succeed
102
-
103
- A file rejected for format or size will fail identically every time, so
104
- "Retry upload" skips it — and if *every* failure is of that kind the button is
105
- disabled. Removing the offending row returns the card to the normal Upload state.
106
-
107
- ### 9. "Upload" wins over "Retry" while anything is still pending
108
-
109
- If some rows failed but others are still ready, the primary action stays
110
- "Upload N files". Retry only appears once there is nothing left to send. The
111
- Figma Multi-upload and Failure frames are both consistent with this.
112
-
113
- ## States still missing from the Figma
114
-
115
- These are implemented, but no frame specs them:
116
-
117
- | State | What ships today |
118
- | ----- | ---------------- |
119
- | **Drag-over** | Dashed border becomes solid brand green, surface takes an 8% brand tint |
120
- | **Disabled** | Whole card at 60% opacity, nothing interactive |
121
- | **Dark mode** | Works automatically — every colour is a semantic token |
122
-
123
- A frame for each (drag-over especially) would make the set complete.
124
-
125
- ## Suggested Figma additions
126
-
127
- 1. Drag-over variant
128
- 2. Disabled variant
129
- 3. Dark reference frame
130
- 4. Apply the three colour corrections above to the shared styles
131
-
132
- Questions and pushback welcome — none of this is precious, but items 1–3 and 5
133
- fix measurable contrast or accessibility problems, so if the design goes a
134
- different way it needs to solve those rather than reverting.