@reliability-design/web 1.3.1 → 1.4.0

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 (123) hide show
  1. package/dist/components/rel-accordion/README.md +13 -13
  2. package/dist/components/rel-accordion/rel-accordion.ts +80 -45
  3. package/dist/components/rel-alert/README.md +15 -15
  4. package/dist/components/rel-alert/rel-alert.ts +153 -158
  5. package/dist/components/rel-avatar/README.md +15 -15
  6. package/dist/components/rel-avatar/rel-avatar.ts +42 -38
  7. package/dist/components/rel-badge/README.md +5 -5
  8. package/dist/components/rel-badge/rel-badge.ts +46 -11
  9. package/dist/components/rel-breadcrumb/README.md +7 -7
  10. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +16 -13
  11. package/dist/components/rel-button/README.md +20 -20
  12. package/dist/components/rel-button/rel-button.ts +292 -105
  13. package/dist/components/rel-calendar/README.md +5 -5
  14. package/dist/components/rel-calendar/rel-calendar.ts +61 -45
  15. package/dist/components/rel-card/README.md +6 -6
  16. package/dist/components/rel-card/rel-card.ts +88 -35
  17. package/dist/components/rel-cardmedia/README.md +9 -9
  18. package/dist/components/rel-cardmedia/rel-cardmedia.ts +27 -20
  19. package/dist/components/rel-carousel/README.md +3 -3
  20. package/dist/components/rel-carousel/rel-carousel.ts +174 -90
  21. package/dist/components/rel-chart/README.md +8 -8
  22. package/dist/components/rel-chart/rel-chart.ts +59 -41
  23. package/dist/components/rel-checkbox/README.md +7 -7
  24. package/dist/components/rel-checkbox/rel-checkbox.ts +61 -61
  25. package/dist/components/rel-chip/README.md +16 -16
  26. package/dist/components/rel-chip/rel-chip.ts +143 -111
  27. package/dist/components/rel-container/README.md +11 -11
  28. package/dist/components/rel-container/rel-container.ts +35 -9
  29. package/dist/components/rel-date-picker/README.md +43 -7
  30. package/dist/components/rel-date-picker/date-format.test.ts +43 -0
  31. package/dist/components/rel-date-picker/date-format.ts +47 -0
  32. package/dist/components/rel-date-picker/rel-date-picker.ts +154 -82
  33. package/dist/components/rel-divider/README.md +9 -9
  34. package/dist/components/rel-divider/rel-divider.ts +10 -9
  35. package/dist/components/rel-drawer/README.md +1 -1
  36. package/dist/components/rel-drawer/rel-drawer-footer.ts +1 -1
  37. package/dist/components/rel-drawer/rel-drawer.ts +56 -28
  38. package/dist/components/rel-kbd/README.md +20 -20
  39. package/dist/components/rel-kbd/rel-kbd.ts +119 -25
  40. package/dist/components/rel-layout/README.md +1 -1
  41. package/dist/components/rel-layout/rel-layout.ts +2 -2
  42. package/dist/components/rel-menu/README.md +15 -15
  43. package/dist/components/rel-menu/rel-menu.ts +155 -88
  44. package/dist/components/rel-menubar/README.md +20 -20
  45. package/dist/components/rel-menubar/rel-menubar.ts +47 -31
  46. package/dist/components/rel-nav-menu/README.md +21 -21
  47. package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +17 -14
  48. package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +33 -14
  49. package/dist/components/rel-nav-menu/rel-nav-menu.ts +14 -7
  50. package/dist/components/rel-navbar/README.md +7 -7
  51. package/dist/components/rel-navbar/rel-navbar.ts +12 -11
  52. package/dist/components/rel-otp/README.md +3 -3
  53. package/dist/components/rel-otp/rel-otp.ts +46 -30
  54. package/dist/components/rel-pagination/README.md +18 -18
  55. package/dist/components/rel-pagination/rel-pagination.ts +141 -90
  56. package/dist/components/rel-popover/README.md +13 -13
  57. package/dist/components/rel-popover/rel-popover-content.ts +39 -14
  58. package/dist/components/rel-popover/rel-popover.ts +18 -7
  59. package/dist/components/rel-progress/README.md +5 -5
  60. package/dist/components/rel-progress/rel-progress.ts +16 -12
  61. package/dist/components/rel-radio/README.md +9 -9
  62. package/dist/components/rel-radio/rel-radio.ts +64 -50
  63. package/dist/components/rel-range-picker/rel-range-picker.ts +120 -117
  64. package/dist/components/rel-resizable/README.md +4 -4
  65. package/dist/components/rel-resizable/rel-resizable-group.ts +15 -4
  66. package/dist/components/rel-resizable/rel-resizable-handle.ts +13 -9
  67. package/dist/components/rel-resizable/rel-resizable-panel.ts +11 -0
  68. package/dist/components/rel-segmented-button/README.md +10 -10
  69. package/dist/components/rel-segmented-button/rel-segmented-button.ts +39 -40
  70. package/dist/components/rel-select/README.md +14 -14
  71. package/dist/components/rel-select/rel-select.ts +120 -188
  72. package/dist/components/rel-sheet/README.md +10 -10
  73. package/dist/components/rel-sheet/rel-sheet.ts +82 -23
  74. package/dist/components/rel-sidebar/README.md +5 -5
  75. package/dist/components/rel-sidebar/rel-sidebar.ts +75 -60
  76. package/dist/components/rel-skeleton/rel-skeleton.ts +8 -13
  77. package/dist/components/rel-slider/README.md +10 -10
  78. package/dist/components/rel-slider/rel-slider.ts +230 -57
  79. package/dist/components/rel-snackbar/README.md +9 -9
  80. package/dist/components/rel-snackbar/rel-snackbar.ts +44 -32
  81. package/dist/components/rel-stepper/README.md +10 -10
  82. package/dist/components/rel-stepper/rel-stepper.ts +110 -46
  83. package/dist/components/rel-switch/README.md +5 -5
  84. package/dist/components/rel-switch/rel-switch.ts +23 -12
  85. package/dist/components/rel-tab/README.md +13 -13
  86. package/dist/components/rel-tab/rel-tab.ts +63 -19
  87. package/dist/components/rel-table/README.md +7 -7
  88. package/dist/components/rel-table/rel-table-body.ts +2 -1
  89. package/dist/components/rel-table/rel-table-caption.ts +10 -7
  90. package/dist/components/rel-table/rel-table-cell.ts +7 -6
  91. package/dist/components/rel-table/rel-table-footer.ts +4 -3
  92. package/dist/components/rel-table/rel-table-head.ts +11 -9
  93. package/dist/components/rel-table/rel-table-header.ts +2 -1
  94. package/dist/components/rel-table/rel-table-row.ts +5 -4
  95. package/dist/components/rel-table/rel-table.ts +16 -7
  96. package/dist/components/rel-textfield/README.md +11 -11
  97. package/dist/components/rel-textfield/rel-textfield.ts +172 -91
  98. package/dist/components/rel-toast/README.md +11 -11
  99. package/dist/components/rel-toast/rel-toast.ts +33 -30
  100. package/dist/components/rel-tooltip/README.md +7 -7
  101. package/dist/components/rel-tooltip/rel-tooltip.ts +160 -123
  102. package/dist/components/rel-topbar/rel-topbar-actions.ts +1 -2
  103. package/dist/components/rel-topbar/rel-topbar-brand.ts +8 -5
  104. package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +10 -8
  105. package/dist/components/rel-topbar/rel-topbar-dropdown.ts +4 -5
  106. package/dist/components/rel-topbar/rel-topbar-item.ts +23 -22
  107. package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -1
  108. package/dist/components/rel-topbar/rel-topbar-profile.ts +68 -50
  109. package/dist/components/rel-topbar/rel-topbar-utilities.ts +1 -2
  110. package/dist/components/rel-topbar/rel-topbar.ts +31 -29
  111. package/dist/index.d.mts +151 -39
  112. package/dist/index.d.ts +151 -39
  113. package/dist/index.js +3544 -2289
  114. package/dist/index.js.map +1 -1
  115. package/dist/index.mjs +3876 -2622
  116. package/dist/index.mjs.map +1 -1
  117. package/dist/styles/vars.css +653 -363
  118. package/dist/vars-WY2XCF5G.css +653 -0
  119. package/package.json +3 -2
  120. package/dist/components/rel-range-picker/rel-range-picker.css +0 -349
  121. package/dist/styles/vars-v2.css +0 -525
  122. package/dist/vars-F54UGCHM.css +0 -363
  123. package/dist/vars-v2-VMGWTFJZ.css +0 -525
@@ -1,6 +1,5 @@
1
1
  import { LitElement, html, css, nothing } from "lit";
2
2
  import { customElement, property, state, query } from "lit/decorators.js";
3
- import "../../styles/vars-v2.css";
4
3
 
5
4
  @customElement("rel-carousel")
6
5
  export class RelCarousel extends LitElement {
@@ -11,7 +10,17 @@ export class RelCarousel extends LitElement {
11
10
  @property({ type: Boolean, attribute: "pause-on-hover" }) pauseOnHover = true;
12
11
  @property({ type: Boolean, attribute: "show-buttons" }) showButtons = false;
13
12
  @property({ type: Boolean, attribute: "show-dots" }) showDots = false;
14
- @property({ type: String }) orientation: "horizontal" | "vertical" = "horizontal";
13
+
14
+ /**
15
+ * Figma "Type" axis. When set it drives which controls render:
16
+ * default = content only · only-indicators = dots · side-arrows = overlaid
17
+ * arrows + dots · bottom-arrows = dots flanked by arrows below the slide.
18
+ * When unset, the legacy show-buttons/show-dots booleans apply unchanged.
19
+ */
20
+ @property({ type: String, reflect: true })
21
+ type?: "default" | "only-indicators" | "side-arrows" | "bottom-arrows";
22
+
23
+ @property({ type: String, reflect: true }) orientation: "horizontal" | "vertical" = "horizontal";
15
24
  @property({ type: String }) label: string = "Carousel";
16
25
  @property({ type: Number }) gap?: number;
17
26
  @property({ type: Boolean, reflect: true }) disabled = false;
@@ -36,8 +45,9 @@ export class RelCarousel extends LitElement {
36
45
 
37
46
  static styles = css`
38
47
  :host {
48
+ color: var(--rel-text-default);
39
49
  display: block;
40
- font-family: var(--rel-fontFamilies-primary);
50
+ font-family: var(--rel-fonts-font-family);
41
51
  position: relative;
42
52
  user-select: none;
43
53
  }
@@ -62,6 +72,21 @@ export class RelCarousel extends LitElement {
62
72
  will-change: transform;
63
73
  }
64
74
 
75
+ /* Vertical orientation: column track inside a fixed-height viewport */
76
+ :host([orientation="vertical"]) .viewport {
77
+ height: var(--rel-carousel-height, 320px);
78
+ }
79
+
80
+ :host([orientation="vertical"]) .track {
81
+ flex-direction: column;
82
+ height: 100%;
83
+ }
84
+
85
+ :host([orientation="vertical"]) ::slotted(*) {
86
+ width: 100% !important;
87
+ height: var(--rel-carousel-slide-height, 100%) !important;
88
+ }
89
+
65
90
  .track.dragging {
66
91
  transition: none;
67
92
  }
@@ -85,9 +110,30 @@ export class RelCarousel extends LitElement {
85
110
  display: flex;
86
111
  align-items: center;
87
112
  justify-content: center;
88
- background: var(--rel-color-bg-tertiary, #f0f0f0);
113
+ background: var(--rel-bg-neutral-elevated-1);
89
114
  }
90
115
 
116
+ /* Default typography for slotted slide captions (Figma roles) */
117
+ ::slotted(h1), ::slotted(h2), ::slotted(h3), ::slotted(h4) {
118
+ font-family: var(--rel-headings-sm-font-family);
119
+ font-weight: var(--rel-headings-sm-font-weight);
120
+ font-size: var(--rel-headings-xs-font-size);
121
+ line-height: var(--rel-headings-xs-line-height);
122
+ letter-spacing: var(--rel-headings-xs-letter-spacing);
123
+ margin: 0 0 var(--rel-spacing-2xs) 0;
124
+ }
125
+
126
+ ::slotted(p) {
127
+ font-family: var(--rel-body-sm-regular-font-family);
128
+ font-weight: var(--rel-body-sm-regular-font-weight);
129
+ font-size: var(--rel-body-sm-regular-font-size);
130
+ line-height: var(--rel-body-sm-regular-line-height);
131
+ letter-spacing: var(--rel-body-sm-regular-letter-spacing);
132
+ color: var(--rel-text-body);
133
+ margin: 0 0 var(--rel-spacing-lg) 0;
134
+ }
135
+
136
+
91
137
  /* Navigation buttons */
92
138
  .nav-button {
93
139
  position: absolute;
@@ -97,9 +143,9 @@ export class RelCarousel extends LitElement {
97
143
  width: var(--rel-carousel-nav-size, 40px);
98
144
  height: var(--rel-carousel-nav-size, 40px);
99
145
  border-radius: 50%;
100
- border: 1px solid var(--rel-color-border-default);
101
- background: var(--rel-color-bg-primary);
102
- color: var(--rel-color-text-primary);
146
+ border: 1px solid var(--rel-border-default);
147
+ background: var(--rel-bg-neutral);
148
+ color: var(--rel-action-neutral-label-icons-ghost);
103
149
  cursor: pointer;
104
150
  display: flex;
105
151
  align-items: center;
@@ -109,11 +155,11 @@ export class RelCarousel extends LitElement {
109
155
  }
110
156
 
111
157
  .nav-button:hover:not(:disabled) {
112
- background: var(--rel-color-bg-secondary);
158
+ background: var(--rel-bg-surface-elevated);
113
159
  }
114
160
 
115
161
  .nav-button:focus-visible {
116
- outline: 2px solid var(--rel-color-border-focus);
162
+ outline: 2px solid var(--rel-border-focus);
117
163
  outline-offset: 2px;
118
164
  }
119
165
 
@@ -123,11 +169,20 @@ export class RelCarousel extends LitElement {
123
169
  }
124
170
 
125
171
  .nav-button.prev {
126
- left: 12px;
172
+ left: var(--rel-spacing-sm);
127
173
  }
128
174
 
129
175
  .nav-button.next {
130
- right: 12px;
176
+ right: var(--rel-spacing-sm);
177
+ }
178
+
179
+ /* Bottom arrows (Figma Type=Bottom Arrows): inline with the dots row */
180
+ .dots .nav-button {
181
+ position: static;
182
+ transform: none;
183
+ width: 32px;
184
+ height: 32px;
185
+ box-shadow: none;
131
186
  }
132
187
 
133
188
  .nav-button svg {
@@ -138,9 +193,10 @@ export class RelCarousel extends LitElement {
138
193
  /* Dots indicator */
139
194
  .dots {
140
195
  display: flex;
196
+ align-items: center;
141
197
  justify-content: center;
142
- gap: var(--rel-spacing-space-xs);
143
- margin-top: var(--rel-spacing-space-md);
198
+ gap: var(--rel-spacing-xs);
199
+ margin-top: var(--rel-spacing-md);
144
200
  }
145
201
 
146
202
  .dot {
@@ -148,24 +204,24 @@ export class RelCarousel extends LitElement {
148
204
  height: var(--rel-carousel-dot-size, 8px);
149
205
  border-radius: 50%;
150
206
  border: none;
151
- background: var(--rel-color-border-default);
207
+ background: var(--rel-bg-neutral-elevated-2);
152
208
  cursor: pointer;
153
209
  padding: 0;
154
210
  transition: all 0.15s ease;
155
211
  }
156
212
 
157
213
  .dot:hover {
158
- background: var(--rel-color-border-divider);
214
+ background: var(--rel-bg-neutral-elevated-2);
159
215
  }
160
216
 
161
217
  .dot.active {
162
- background: var(--rel-color-brand);
218
+ background: var(--rel-bg-surface-brand);
163
219
  width: var(--rel-carousel-dot-size-active, 24px);
164
- border-radius: 4px;
220
+ border-radius: var(--rel-border-radius-xs);
165
221
  }
166
222
 
167
223
  .dot:focus-visible {
168
- outline: 2px solid var(--rel-color-border-focus);
224
+ outline: 2px solid var(--rel-border-focus);
169
225
  outline-offset: 2px;
170
226
  }
171
227
 
@@ -178,14 +234,14 @@ export class RelCarousel extends LitElement {
178
234
 
179
235
  :host-context([data-theme="dark"]) .nav-button,
180
236
  :host-context(.dark) .nav-button {
181
- background: var(--rel-color-bg-secondary);
182
- border-color: var(--rel-color-border-default);
183
- color: var(--rel-color-text-primary);
237
+ background: var(--rel-bg-surface-elevated);
238
+ border-color: var(--rel-border-default);
239
+ color: var(--rel-text-default);
184
240
  }
185
241
 
186
242
  :host-context([data-theme="dark"]) .nav-button:hover,
187
243
  :host-context(.dark) .nav-button:hover {
188
- background: var(--rel-color-bg-tertiary);
244
+ background: var(--rel-bg-neutral-elevated-1);
189
245
  }
190
246
  `;
191
247
 
@@ -203,10 +259,13 @@ export class RelCarousel extends LitElement {
203
259
  firstUpdated() {
204
260
  const ro = new ResizeObserver(() => {
205
261
  const w = this.viewport?.offsetWidth ?? 0;
206
- if (w === 0) return;
262
+ const h = this.viewport?.offsetHeight ?? 0;
263
+ if (w === 0 && h === 0) return;
207
264
  this.style.setProperty('--rel-carousel-slide-width', `${w}px`);
265
+ this.style.setProperty('--rel-carousel-slide-height', `${h}px`);
208
266
  this.renderRoot.querySelectorAll('.carousel-clone').forEach(el => {
209
- (el as HTMLElement).style.width = `${w}px`;
267
+ if (this.vertical) (el as HTMLElement).style.height = `${h}px`;
268
+ else (el as HTMLElement).style.width = `${w}px`;
210
269
  });
211
270
  this.updateTransform();
212
271
  });
@@ -215,8 +274,10 @@ export class RelCarousel extends LitElement {
215
274
 
216
275
  requestAnimationFrame(() => {
217
276
  const w = this.viewport?.offsetWidth ?? 0;
218
- if (w > 0) {
277
+ const h = this.viewport?.offsetHeight ?? 0;
278
+ if (w > 0 || h > 0) {
219
279
  this.style.setProperty('--rel-carousel-slide-width', `${w}px`);
280
+ this.style.setProperty('--rel-carousel-slide-height', `${h}px`);
220
281
  this.updateTransform();
221
282
  }
222
283
  });
@@ -251,13 +312,18 @@ export class RelCarousel extends LitElement {
251
312
  if (useClones) {
252
313
  this.clonesBefore = 1;
253
314
 
315
+ const sizeClone = (clone: HTMLElement) => {
316
+ if (this.vertical) clone.style.height = `${this.viewport?.offsetHeight ?? 0}px`;
317
+ else clone.style.width = `${this.viewport?.offsetWidth ?? 0}px`;
318
+ clone.style.boxSizing = 'border-box';
319
+ };
320
+
254
321
  // Clone last slide → prepend before <slot>
255
322
  const lastEl = elements[this.slideCount - 1] as HTMLElement;
256
323
  const lastClone = lastEl.cloneNode(true) as HTMLElement;
257
324
  lastClone.classList.add("carousel-clone");
258
325
  lastClone.setAttribute("aria-hidden", "true");
259
- lastClone.style.width = `${this.viewport?.offsetWidth ?? 0}px`;
260
- lastClone.style.boxSizing = 'border-box';
326
+ sizeClone(lastClone);
261
327
  track.insertBefore(lastClone, slot);
262
328
 
263
329
  // Clone first slide → append after <slot>
@@ -265,8 +331,7 @@ export class RelCarousel extends LitElement {
265
331
  const firstClone = firstEl.cloneNode(true) as HTMLElement;
266
332
  firstClone.classList.add("carousel-clone");
267
333
  firstClone.setAttribute("aria-hidden", "true");
268
- firstClone.style.width = `${this.viewport?.offsetWidth ?? 0}px`;
269
- firstClone.style.boxSizing = 'border-box';
334
+ sizeClone(firstClone);
270
335
  track.appendChild(firstClone);
271
336
  } else {
272
337
  this.clonesBefore = 0;
@@ -285,10 +350,19 @@ export class RelCarousel extends LitElement {
285
350
  }
286
351
  }
287
352
 
353
+ private get vertical(): boolean {
354
+ return this.orientation === "vertical";
355
+ }
356
+
357
+ /** Translate along the orientation axis. */
358
+ private axisTransform(px: number): string {
359
+ return this.vertical ? `translateY(${px}px)` : `translateX(${px}px)`;
360
+ }
361
+
288
362
  private unit(): number {
289
- const slideWidth = this.viewport?.offsetWidth ?? 0;
363
+ const slideSize = (this.vertical ? this.viewport?.offsetHeight : this.viewport?.offsetWidth) ?? 0;
290
364
  const gapValue = this.gap && this.gap > 0 ? this.gap : 0;
291
- return slideWidth + gapValue;
365
+ return slideSize + gapValue;
292
366
  }
293
367
 
294
368
  private updateTransform() {
@@ -299,7 +373,7 @@ export class RelCarousel extends LitElement {
299
373
 
300
374
  this.currentTranslate = translate;
301
375
  this.prevTranslate = translate;
302
- this.track.style.transform = `translateX(${translate}px)`;
376
+ this.track.style.transform = this.axisTransform(translate);
303
377
  this.track.style.gap = this.gap && this.gap > 0 ? `${this.gap}px` : "";
304
378
  }
305
379
 
@@ -315,7 +389,7 @@ export class RelCarousel extends LitElement {
315
389
  const translate = 0;
316
390
  this.currentTranslate = translate;
317
391
  this.prevTranslate = translate;
318
- this.track!.style.transform = `translateX(${translate}px)`;
392
+ this.track!.style.transform = this.axisTransform(translate);
319
393
  this.track!.addEventListener(
320
394
  "transitionend",
321
395
  () => this.silentJump(this.slideCount - 1),
@@ -337,7 +411,7 @@ export class RelCarousel extends LitElement {
337
411
  const translate = -(cloneFirstPos * this.unit());
338
412
  this.currentTranslate = translate;
339
413
  this.prevTranslate = translate;
340
- this.track!.style.transform = `translateX(${translate}px)`;
414
+ this.track!.style.transform = this.axisTransform(translate);
341
415
  this.track!.addEventListener(
342
416
  "transitionend",
343
417
  () => this.silentJump(0),
@@ -400,7 +474,7 @@ export class RelCarousel extends LitElement {
400
474
  if (this.disabled || !this.track) return;
401
475
 
402
476
  this.isDragging = true;
403
- this.dragStartX = e.clientX;
477
+ this.dragStartX = this.vertical ? e.clientY : e.clientX;
404
478
  this.prevTranslate = this.currentTranslate;
405
479
 
406
480
  this.track.classList.add("dragging");
@@ -415,9 +489,9 @@ export class RelCarousel extends LitElement {
415
489
  private handlePointerMove(e: PointerEvent) {
416
490
  if (!this.isDragging || !this.track) return;
417
491
 
418
- const currentX = e.clientX;
419
- const deltaX = currentX - this.dragStartX;
420
- this.currentTranslate = this.prevTranslate + deltaX;
492
+ const current = this.vertical ? e.clientY : e.clientX;
493
+ const delta = current - this.dragStartX;
494
+ this.currentTranslate = this.prevTranslate + delta;
421
495
 
422
496
  if (!this.prefersReducedMotion && this.animationId) {
423
497
  cancelAnimationFrame(this.animationId);
@@ -425,7 +499,7 @@ export class RelCarousel extends LitElement {
425
499
 
426
500
  this.animationId = requestAnimationFrame(() => {
427
501
  if (this.track) {
428
- this.track.style.transform = `translateX(${this.currentTranslate}px)`;
502
+ this.track.style.transform = this.axisTransform(this.currentTranslate);
429
503
  }
430
504
  });
431
505
  }
@@ -436,14 +510,14 @@ export class RelCarousel extends LitElement {
436
510
  this.isDragging = false;
437
511
  this.track.classList.remove("dragging");
438
512
 
439
- const deltaX = e.clientX - this.dragStartX;
440
- const slideWidth = this.viewport.offsetWidth;
441
- const threshold = slideWidth * 0.2; // 20% threshold
513
+ const delta = (this.vertical ? e.clientY : e.clientX) - this.dragStartX;
514
+ const slideSize = this.vertical ? this.viewport.offsetHeight : this.viewport.offsetWidth;
515
+ const threshold = slideSize * 0.2; // 20% threshold
442
516
 
443
- if (Math.abs(deltaX) > threshold) {
444
- if (deltaX > 0 && (this.value > 0 || this.clonesBefore > 0)) {
517
+ if (Math.abs(delta) > threshold) {
518
+ if (delta > 0 && (this.value > 0 || this.clonesBefore > 0)) {
445
519
  this.goToPrevious();
446
- } else if (deltaX < 0 && (this.value < this.slideCount - 1 || this.clonesBefore > 0)) {
520
+ } else if (delta < 0 && (this.value < this.slideCount - 1 || this.clonesBefore > 0)) {
447
521
  this.goToNext();
448
522
  } else {
449
523
  this.updateTransform();
@@ -462,10 +536,12 @@ export class RelCarousel extends LitElement {
462
536
 
463
537
  switch (e.key) {
464
538
  case "ArrowLeft":
539
+ case "ArrowUp":
465
540
  e.preventDefault();
466
541
  this.goToPrevious();
467
542
  break;
468
543
  case "ArrowRight":
544
+ case "ArrowDown":
469
545
  e.preventDefault();
470
546
  this.goToNext();
471
547
  break;
@@ -509,6 +585,43 @@ export class RelCarousel extends LitElement {
509
585
  }
510
586
  }
511
587
 
588
+ /** Where nav arrows render, honouring the Figma type when set. */
589
+ private get buttonPlacement(): "side" | "bottom" | "none" {
590
+ if (this.type === "side-arrows") return "side";
591
+ if (this.type === "bottom-arrows") return "bottom";
592
+ if (this.type === "only-indicators" || this.type === "default") return "none";
593
+ return this.showButtons ? "side" : "none";
594
+ }
595
+
596
+ private get dotsVisible(): boolean {
597
+ if (this.type === "default") return false;
598
+ if (this.type) return true;
599
+ return this.showDots;
600
+ }
601
+
602
+ private renderNavButton(direction: "prev" | "next", enabled: boolean) {
603
+ const path = direction === "prev" ? "M10 12L6 8L10 4" : "M6 4L10 8L6 12";
604
+ return html`
605
+ <button
606
+ class="nav-button ${direction}"
607
+ part="${direction}-button"
608
+ aria-label="${direction === "prev" ? "Previous" : "Next"} slide"
609
+ ?disabled=${!enabled || this.disabled}
610
+ @click=${direction === "prev" ? this.goToPrevious : this.goToNext}
611
+ >
612
+ <svg viewBox="0 0 16 16" fill="none">
613
+ <path
614
+ d="${path}"
615
+ stroke="currentColor"
616
+ stroke-width="2"
617
+ stroke-linecap="round"
618
+ stroke-linejoin="round"
619
+ />
620
+ </svg>
621
+ </button>
622
+ `;
623
+ }
624
+
512
625
  render() {
513
626
  const canGoPrev = this.clonesBefore > 0 || this.value > 0;
514
627
  const canGoNext = this.clonesBefore > 0 || this.value < this.slideCount - 1;
@@ -536,58 +649,29 @@ export class RelCarousel extends LitElement {
536
649
  <slot @slotchange=${this.handleSlotChange}></slot>
537
650
  </div>
538
651
 
539
- ${this.showButtons
652
+ ${this.buttonPlacement === "side"
540
653
  ? html`
541
- <button
542
- class="nav-button prev"
543
- part="prev-button"
544
- aria-label="Previous slide"
545
- ?disabled=${!canGoPrev || this.disabled}
546
- @click=${this.goToPrevious}
547
- >
548
- <svg viewBox="0 0 16 16" fill="none">
549
- <path
550
- d="M10 12L6 8L10 4"
551
- stroke="currentColor"
552
- stroke-width="2"
553
- stroke-linecap="round"
554
- stroke-linejoin="round"
555
- />
556
- </svg>
557
- </button>
558
-
559
- <button
560
- class="nav-button next"
561
- part="next-button"
562
- aria-label="Next slide"
563
- ?disabled=${!canGoNext || this.disabled}
564
- @click=${this.goToNext}
565
- >
566
- <svg viewBox="0 0 16 16" fill="none">
567
- <path
568
- d="M6 4L10 8L6 12"
569
- stroke="currentColor"
570
- stroke-width="2"
571
- stroke-linecap="round"
572
- stroke-linejoin="round"
573
- />
574
- </svg>
575
- </button>
654
+ ${this.renderNavButton("prev", canGoPrev)}
655
+ ${this.renderNavButton("next", canGoNext)}
576
656
  `
577
657
  : nothing}
578
658
  </div>
579
659
 
580
- ${this.showDots && this.slideCount > 1
660
+ ${(this.dotsVisible || this.buttonPlacement === "bottom") && this.slideCount > 1
581
661
  ? html`
582
662
  <div class="dots" part="dots">
583
- ${Array.from({ length: this.slideCount }, (_, i) => html`
584
- <button
585
- class="dot ${i === this.value ? "active" : ""}"
586
- part="dot"
587
- aria-label="Go to slide ${i + 1}"
588
- @click=${() => this.goToSlide(i)}
589
- ></button>
590
- `)}
663
+ ${this.buttonPlacement === "bottom" ? this.renderNavButton("prev", canGoPrev) : nothing}
664
+ ${this.dotsVisible
665
+ ? Array.from({ length: this.slideCount }, (_, i) => html`
666
+ <button
667
+ class="dot ${i === this.value ? "active" : ""}"
668
+ part="dot"
669
+ aria-label="Go to slide ${i + 1}"
670
+ @click=${() => this.goToSlide(i)}
671
+ ></button>
672
+ `)
673
+ : nothing}
674
+ ${this.buttonPlacement === "bottom" ? this.renderNavButton("next", canGoNext) : nothing}
591
675
  </div>
592
676
  `
593
677
  : nothing}
@@ -90,12 +90,12 @@ chart.series = [
90
90
  {
91
91
  key: 'desktop',
92
92
  label: 'Desktop',
93
- colorToken: 'var(--rel-colors-blue-primary)'
93
+ colorToken: 'var(--rel-bg-info)'
94
94
  },
95
95
  {
96
96
  key: 'mobile',
97
97
  label: 'Mobile',
98
- colorToken: 'var(--rel-colors-green-primary)'
98
+ colorToken: 'var(--rel-bg-brand)'
99
99
  }
100
100
  ];
101
101
  ```
@@ -223,8 +223,8 @@ Available parts:
223
223
  ];
224
224
 
225
225
  chart.series = [
226
- { key: 'desktop', label: 'Desktop', colorToken: 'var(--rel-colors-blue-primary)' },
227
- { key: 'mobile', label: 'Mobile', colorToken: 'var(--rel-colors-green-primary)' }
226
+ { key: 'desktop', label: 'Desktop', colorToken: 'var(--rel-bg-info)' },
227
+ { key: 'mobile', label: 'Mobile', colorToken: 'var(--rel-bg-brand)' }
228
228
  ];
229
229
 
230
230
  chart.addEventListener('rel-hover', (e) => {
@@ -256,7 +256,7 @@ Available parts:
256
256
  ];
257
257
 
258
258
  chart.series = [
259
- { key: 'revenue', label: 'Revenue', colorToken: 'var(--rel-colors-green-success)' }
259
+ { key: 'revenue', label: 'Revenue', colorToken: 'var(--rel-text-success)' }
260
260
  ];
261
261
  </script>
262
262
  ```
@@ -274,10 +274,10 @@ Available parts:
274
274
  The component uses design tokens from the Reliability Design System. All colors, spacing, and typography are inherited from CSS variables.
275
275
 
276
276
  Key tokens used:
277
- - `--rel-colors-*` for all colors
277
+ - `--rel-bg-*` / `--rel-text-*` semantic colors
278
278
  - `--rel-spacing-*` for spacing
279
- - `--rel-fontSizes-*`, `--rel-fontWeights-*` for typography
280
- - `--rel-borders-*` for borders and radius
279
+ - `--rel-body-*` composite typography roles
280
+ - `--rel-border-*` / `--rel-border-radius-*` tokens
281
281
  - `--rel-shadows-*` for shadows
282
282
  - Default series colors use system color tokens
283
283