@reliability-design/web 1.4.6 → 1.4.7

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 (146) hide show
  1. package/CHANGELOG.md +211 -49
  2. package/dist/components/rel-accordion/README.md +132 -132
  3. package/dist/components/rel-alert/README.md +145 -141
  4. package/dist/components/rel-autocomplete/README.md +156 -61
  5. package/dist/components/rel-avatar/README.md +116 -130
  6. package/dist/components/rel-badge/README.md +98 -93
  7. package/dist/components/rel-breadcrumb/README.md +96 -102
  8. package/dist/components/rel-button/README.md +160 -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 +184 -166
  13. package/dist/components/rel-checkbox/README.md +93 -75
  14. package/dist/components/rel-chip/README.md +132 -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 +78 -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-menu/README.md +178 -151
  21. package/dist/components/rel-menubar/README.md +218 -329
  22. package/dist/components/rel-otp/README.md +170 -440
  23. package/dist/components/rel-pagination/README.md +100 -424
  24. package/dist/components/rel-popover/README.md +203 -568
  25. package/dist/components/rel-progress/README.md +88 -110
  26. package/dist/components/rel-radio/README.md +131 -138
  27. package/dist/components/rel-range-picker/README.md +162 -0
  28. package/dist/components/rel-resizable/README.md +199 -140
  29. package/dist/components/rel-segmented-button/README.md +155 -141
  30. package/dist/components/rel-select/README.md +154 -93
  31. package/dist/components/rel-sheet/README.md +168 -224
  32. package/dist/components/rel-skeleton/README.md +103 -0
  33. package/dist/components/rel-slider/README.md +130 -135
  34. package/dist/components/rel-snackbar/README.md +162 -134
  35. package/dist/components/rel-stepper/README.md +158 -160
  36. package/dist/components/rel-switch/README.md +108 -102
  37. package/dist/components/rel-tab/README.md +171 -163
  38. package/dist/components/rel-textfield/README.md +159 -69
  39. package/dist/components/rel-time-picker/README.md +122 -157
  40. package/dist/components/rel-toast/README.md +157 -148
  41. package/dist/components/rel-tooltip/README.md +108 -111
  42. package/dist/components/rel-upload/README.md +220 -0
  43. package/dist/index.d.mts +1147 -340
  44. package/dist/index.d.ts +1147 -340
  45. package/dist/index.js +3451 -3163
  46. package/dist/index.js.map +1 -1
  47. package/dist/index.mjs +3416 -3118
  48. package/dist/index.mjs.map +1 -1
  49. package/dist/styles/vars.css +877 -866
  50. package/dist/{vars-XNMTSRVN.css → vars-JHTUGNRG.css} +877 -866
  51. package/package.json +3 -2
  52. package/dist/components/rel-accordion/rel-accordion.ts +0 -336
  53. package/dist/components/rel-alert/rel-alert.ts +0 -305
  54. package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -720
  55. package/dist/components/rel-avatar/rel-avatar.ts +0 -142
  56. package/dist/components/rel-badge/rel-badge.ts +0 -175
  57. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
  58. package/dist/components/rel-button/rel-button.ts +0 -560
  59. package/dist/components/rel-calendar/index.ts +0 -1
  60. package/dist/components/rel-calendar/rel-calendar.ts +0 -565
  61. package/dist/components/rel-card/rel-card.ts +0 -88
  62. package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
  63. package/dist/components/rel-carousel/index.ts +0 -1
  64. package/dist/components/rel-carousel/rel-carousel.ts +0 -723
  65. package/dist/components/rel-chart/README.md +0 -286
  66. package/dist/components/rel-chart/index.ts +0 -1
  67. package/dist/components/rel-chart/rel-chart.ts +0 -754
  68. package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
  69. package/dist/components/rel-chip/rel-chip.ts +0 -483
  70. package/dist/components/rel-container/rel-container.ts +0 -95
  71. package/dist/components/rel-date-picker/date-format.test.ts +0 -43
  72. package/dist/components/rel-date-picker/date-format.ts +0 -47
  73. package/dist/components/rel-date-picker/date-parse.test.ts +0 -116
  74. package/dist/components/rel-date-picker/date-parse.ts +0 -178
  75. package/dist/components/rel-date-picker/index.ts +0 -1
  76. package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1676
  77. package/dist/components/rel-divider/rel-divider.ts +0 -54
  78. package/dist/components/rel-drawer/index.ts +0 -1
  79. package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
  80. package/dist/components/rel-drawer/rel-drawer.ts +0 -607
  81. package/dist/components/rel-kbd/rel-kbd.ts +0 -188
  82. package/dist/components/rel-layout/rel-layout.ts +0 -49
  83. package/dist/components/rel-menu/rel-menu.ts +0 -541
  84. package/dist/components/rel-menubar/index.ts +0 -2
  85. package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
  86. package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
  87. package/dist/components/rel-menubar/rel-menubar.ts +0 -777
  88. package/dist/components/rel-nav-menu/README.md +0 -289
  89. package/dist/components/rel-nav-menu/index.ts +0 -3
  90. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
  91. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
  92. package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
  93. package/dist/components/rel-navbar/README.md +0 -126
  94. package/dist/components/rel-navbar/rel-navbar.ts +0 -105
  95. package/dist/components/rel-otp/index.ts +0 -1
  96. package/dist/components/rel-otp/rel-otp.ts +0 -527
  97. package/dist/components/rel-pagination/index.ts +0 -1
  98. package/dist/components/rel-pagination/rel-pagination.ts +0 -522
  99. package/dist/components/rel-popover/index.ts +0 -2
  100. package/dist/components/rel-popover/rel-popover-content.ts +0 -81
  101. package/dist/components/rel-popover/rel-popover.ts +0 -475
  102. package/dist/components/rel-progress/rel-progress.ts +0 -234
  103. package/dist/components/rel-radio/rel-radio.ts +0 -481
  104. package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1688
  105. package/dist/components/rel-resizable/index.ts +0 -3
  106. package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
  107. package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
  108. package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
  109. package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -217
  110. package/dist/components/rel-select/rel-select.ts +0 -1416
  111. package/dist/components/rel-sheet/rel-sheet.ts +0 -610
  112. package/dist/components/rel-sidebar/README.md +0 -209
  113. package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1191
  114. package/dist/components/rel-skeleton/rel-skeleton.ts +0 -143
  115. package/dist/components/rel-slider/rel-slider.ts +0 -673
  116. package/dist/components/rel-snackbar/rel-snackbar.ts +0 -329
  117. package/dist/components/rel-stepper/rel-stepper.ts +0 -567
  118. package/dist/components/rel-switch/rel-switch.ts +0 -199
  119. package/dist/components/rel-tab/rel-tab.ts +0 -264
  120. package/dist/components/rel-table/README.md +0 -178
  121. package/dist/components/rel-table/rel-table-body.ts +0 -22
  122. package/dist/components/rel-table/rel-table-caption.ts +0 -30
  123. package/dist/components/rel-table/rel-table-cell.ts +0 -43
  124. package/dist/components/rel-table/rel-table-footer.ts +0 -24
  125. package/dist/components/rel-table/rel-table-head.ts +0 -47
  126. package/dist/components/rel-table/rel-table-header.ts +0 -22
  127. package/dist/components/rel-table/rel-table-row.ts +0 -37
  128. package/dist/components/rel-table/rel-table.ts +0 -62
  129. package/dist/components/rel-textfield/rel-textfield.ts +0 -1054
  130. package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1482
  131. package/dist/components/rel-time-picker/time-parse.test.ts +0 -67
  132. package/dist/components/rel-time-picker/time-parse.ts +0 -85
  133. package/dist/components/rel-toast/rel-toast.ts +0 -206
  134. package/dist/components/rel-tooltip/rel-tooltip.ts +0 -193
  135. package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
  136. package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
  137. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
  138. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
  139. package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
  140. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
  141. package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
  142. package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
  143. package/dist/components/rel-topbar/rel-topbar.ts +0 -446
  144. package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
  145. package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
  146. 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.