@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,560 +0,0 @@
1
- import "../rel-badge/rel-badge";
2
- import { LitElement, html, css } from "lit";
3
- import { customElement, property } from "lit/decorators.js";
4
- import { ifDefined } from "lit/directives/if-defined.js";
5
-
6
- /**
7
- * Style axis. Canonical values are the historical ones; "outline"/"ghost" are
8
- * accepted Figma-vocabulary aliases for "secondary"/"text". "static" is the
9
- * Figma Style=Static treatment (transparent, static-light label — for use on
10
- * colored/dark surfaces). Legacy "icon" is the pre-2.0 collapsed icon-button
11
- * variant and keeps its exact behavior; prefer icon-only + a style instead.
12
- */
13
- export type ButtonVariant =
14
- | "default" | "primary" | "secondary" | "text" | "link" | "icon" | "static"
15
- | "outline" | "ghost";
16
-
17
- /** Tone axis. "neutral"/"brand"/"danger" are Figma-vocabulary aliases. */
18
- export type ButtonTone =
19
- | "default" | "primary" | "secondary" | "critical" | "success"
20
- | "neutral" | "brand" | "danger";
21
-
22
- @customElement("rel-button")
23
- export class RelButton extends LitElement {
24
- @property({ type: String })
25
- variant: ButtonVariant = "default";
26
-
27
- @property({ type: String })
28
- tone: ButtonTone = "default";
29
-
30
- /**
31
- * Figma "Type=Icon Only" as an independent axis: a square button of the
32
- * current style/tone. Unlike legacy variant="icon", this composes with
33
- * every style (outline/ghost/link included).
34
- */
35
- @property({ type: Boolean, reflect: true, attribute: "icon-only" })
36
- iconOnly = false;
37
-
38
- /** Figma "State=Loading": shows a spinner, blocks interaction. */
39
- @property({ type: Boolean, reflect: true }) loading = false;
40
-
41
- @property({ type: String })
42
- size: "small" | "medium" | "large" = "medium";
43
-
44
- @property({ type: String })
45
- border: "corner-radius" | "small-corner-radius" | "no-corner-radius" =
46
- "corner-radius";
47
-
48
- @property({ type: Boolean, reflect: true }) disabled = false;
49
-
50
- @property({ type: String, attribute: "aria-label" }) ariaLabel: string | null = null;
51
-
52
- /** Numeric/overflow text shown in a corner badge (e.g. "3", "999+"). Ignored when badgeDot is set. */
53
- @property({ type: String }) badge = "";
54
-
55
- /** Shows a small corner dot instead of a numbered badge — takes precedence over `badge`. */
56
- @property({ type: Boolean, attribute: "badge-dot" }) badgeDot = false;
57
-
58
- static styles = css`
59
- :host {
60
- color: var(--rel-text-default);
61
- display: inline-flex;
62
- position: relative;
63
- }
64
-
65
- .btn-badge {
66
- position: absolute;
67
- top: -6px;
68
- right: -6px;
69
- pointer-events: none;
70
- }
71
-
72
- /* Icon buttons are a 40px circle (radius 20). An 8px dot's center needs to
73
- land ON that circle's edge at the 45° corner point — which works out to
74
- roughly 2px inward from each edge (R - R/√2 - dot-radius ≈ 1.9px), not
75
- outside it like the numbered badge. Negative offsets here would float
76
- the dot in the empty bounding-box corner outside the visible circle. */
77
- .btn-badge[variant="dot"] {
78
- top: 2px;
79
- right: 2px;
80
- }
81
-
82
- button {
83
- font-family: var(--rel-action-md-font-family);
84
- font-weight: var(--rel-action-md-font-weight);
85
- cursor: pointer;
86
- border: none;
87
- transition: all 0.15s ease;
88
- display: inline-flex;
89
- align-items: center;
90
- justify-content: center;
91
- gap: var(--rel-spacing-2xs);
92
- outline: none;
93
- position: relative;
94
- }
95
-
96
- /* Content wrapper mirrors the button's flex so slotted icon+label keep
97
- their gap; it exists so loading can hide the label without collapsing
98
- the button's width. */
99
- .content {
100
- display: inline-flex;
101
- align-items: center;
102
- justify-content: center;
103
- gap: inherit;
104
- }
105
-
106
- /* Loading (Figma State=Loading): spinner replaces the label in place */
107
- :host([loading]) {
108
- pointer-events: none;
109
- }
110
-
111
- :host([loading]) .content {
112
- visibility: hidden;
113
- }
114
-
115
- .spinner {
116
- position: absolute;
117
- left: 50%;
118
- top: 50%;
119
- width: 16px;
120
- height: 16px;
121
- margin: -8px 0 0 -8px;
122
- border-radius: 50%;
123
- border: 2px solid currentColor;
124
- border-top-color: transparent;
125
- animation: rel-btn-spin 0.8s linear infinite;
126
- }
127
-
128
- @keyframes rel-btn-spin {
129
- to {
130
- transform: rotate(360deg);
131
- }
132
- }
133
-
134
- @media (prefers-reduced-motion: reduce) {
135
- .spinner {
136
- animation-duration: 2s;
137
- }
138
- }
139
-
140
- /* Sizes. Min-widths come from the Figma Button master (S/M/L =
141
- 80/100/120) so short labels like "OK" keep a proper pill shape;
142
- --rel-button-min-width lets a consumer override per instance. */
143
- .size-small {
144
- height: 32px;
145
- min-width: var(--rel-button-min-width, 80px);
146
- padding: 0 var(--rel-spacing-sm);
147
- font-family: var(--rel-action-md-font-family);
148
- font-weight: var(--rel-action-md-font-weight);
149
- font-size: var(--rel-action-md-font-size);
150
- line-height: var(--rel-action-md-line-height);
151
- letter-spacing: var(--rel-action-md-letter-spacing);
152
- }
153
-
154
- .size-medium {
155
- height: 40px;
156
- min-width: var(--rel-button-min-width, 100px);
157
- gap: var(--rel-spacing-xs);
158
- padding: 0 var(--rel-spacing-md);
159
- font-family: var(--rel-action-lg-font-family);
160
- font-weight: var(--rel-action-lg-font-weight);
161
- font-size: var(--rel-action-lg-font-size);
162
- line-height: var(--rel-action-lg-line-height);
163
- letter-spacing: var(--rel-action-lg-letter-spacing);
164
- }
165
-
166
- .size-large {
167
- height: 48px;
168
- min-width: var(--rel-button-min-width, 120px);
169
- padding: 0 var(--rel-spacing-lg);
170
- font-family: var(--rel-action-xl-font-family);
171
- font-weight: var(--rel-action-xl-font-weight);
172
- font-size: var(--rel-action-xl-font-size);
173
- line-height: var(--rel-action-xl-line-height);
174
- letter-spacing: var(--rel-action-xl-letter-spacing);
175
- }
176
-
177
- /* Borders */
178
- .border-corner-radius {
179
- border-radius: var(--rel-border-radius-full-rounded);
180
- }
181
-
182
- .border-small-corner-radius {
183
- border-radius: var(--rel-border-radius-sm);
184
- }
185
-
186
- .border-no-corner-radius {
187
- border-radius: var(--rel-border-radius-square);
188
- }
189
-
190
- /* Disabled */
191
- :host([disabled]) {
192
- /* not-allowed must stay VISIBLE: the host keeps pointer-events so the
193
- cursor renders. Clicks are neutralised two ways — the inner button's
194
- pointer-events:none (below) and the capture-phase handleHostClick
195
- guard (which also stops programmatic .click()). Putting
196
- pointer-events:none on the HOST would make it transparent to the
197
- pointer and suppress the cursor entirely. */
198
- cursor: not-allowed;
199
- /* The disabled tokens are full-contrast pairs (light: near-black on
200
- gray-300, dark: white on gray-700), so on their own they render a
201
- crisp button that reads as enabled. Dulling happens here instead —
202
- same opacity mechanism the rest of the library uses. */
203
- opacity: 0.5;
204
- }
205
-
206
- /* Only styles that actually carry a fill flatten to the neutral disabled
207
- fill. Ghost/link/outline/static stay transparent so a disabled one
208
- keeps its own shape rather than becoming a solid pill — which is what
209
- made them look like ordinary enabled buttons. */
210
- :host([disabled]) button.btn-primary,
211
- :host([disabled]) button.btn-default,
212
- :host([disabled]) button.btn-icon {
213
- background-color: var(--rel-action-brand-fill-disabled) !important;
214
- color: var(--rel-action-brand-label-icons-primary-disabled) !important;
215
- border: none !important;
216
- }
217
-
218
- :host([disabled]) button {
219
- cursor: not-allowed;
220
- pointer-events: none;
221
- filter: none !important;
222
- }
223
-
224
- /* Variants & tones — V2 action tokens (auto light/dark) */
225
- .btn-default.tone-default {
226
- background: var(--rel-action-neutral-fill);
227
- color: var(--rel-action-neutral-label-icons-fill-inverse);
228
- }
229
- .btn-default.tone-critical {
230
- background: var(--rel-action-danger-fill);
231
- color: var(--rel-action-danger-label-icons-fill);
232
- }
233
- .btn-default.tone-success {
234
- background: var(--rel-action-success-fill);
235
- color: var(--rel-text-static-light);
236
- }
237
-
238
- .btn-primary.tone-default {
239
- background: var(--rel-action-brand-fill);
240
- color: var(--rel-action-brand-label-icons-primary);
241
- }
242
- .btn-primary.tone-critical {
243
- background: var(--rel-action-danger-fill);
244
- color: var(--rel-action-danger-label-icons-fill);
245
- }
246
- .btn-primary.tone-success {
247
- background: var(--rel-action-success-fill);
248
- color: var(--rel-action-success-label-icons-fill);
249
- }
250
-
251
- .btn-secondary {
252
- background: var(--rel-bg-surface);
253
- border: 1px solid var(--rel-action-neutral-border-outline);
254
- color: var(--rel-action-neutral-label-icons-outline);
255
- }
256
-
257
- /* Secondary critical / success tones. The danger outline border is
258
- deliberately the SUBTLE red at rest (red-100) and only goes solid
259
- (red-600) on hover/pressed — verified against the Figma design nodes,
260
- not just the variable names. Do not "fix" it to a solid red. */
261
- .btn-secondary.tone-critical {
262
- border-color: var(--rel-action-danger-border-outline);
263
- color: var(--rel-action-danger-label-icons-outline);
264
- }
265
- .btn-secondary.tone-success {
266
- border-color: var(--rel-border-success);
267
- color: var(--rel-text-success);
268
- }
269
-
270
- .btn-text {
271
- background: transparent;
272
- color: var(--rel-action-neutral-label-icons-ghost);
273
- }
274
-
275
- /* Figma master: ghost S has a narrower 56px min than primary/outline. */
276
- .btn-text.size-small {
277
- min-width: var(--rel-button-min-width, 56px);
278
- }
279
-
280
- .btn-text.tone-critical {
281
- color: var(--rel-action-danger-label-icons-ghost);
282
- }
283
-
284
- .btn-link {
285
- background: transparent;
286
- color: var(--rel-action-neutral-label-icons-link);
287
- text-decoration: underline;
288
- /* Links are inline actions — no minimum footprint in the Figma master. */
289
- min-width: 0;
290
- }
291
-
292
- button.btn-link:hover:not(:disabled) {
293
- color: var(--rel-action-neutral-label-icons-link-hover);
294
- }
295
-
296
- .btn-link.tone-critical {
297
- color: var(--rel-action-danger-label-icons-link);
298
- }
299
-
300
- button.btn-link.tone-critical:hover:not(:disabled) {
301
- color: var(--rel-action-danger-label-icons-link-hover);
302
- }
303
-
304
- /* Static style (Figma Style=Static, Tone=Neutral only): transparent with a
305
- static-light label, for placement on colored or dark surfaces */
306
- .btn-static {
307
- background: transparent;
308
- color: var(--rel-text-static-light);
309
- }
310
-
311
- button.btn-static:hover:not(:disabled) {
312
- background: var(--rel-action-neutral-static-hover);
313
- }
314
-
315
- /* Icon Only as an independent axis: square button of the current style */
316
- .btn-icon-only {
317
- padding: 0;
318
- min-width: 0;
319
- }
320
-
321
- .btn-icon-only.size-small {
322
- width: 32px;
323
- }
324
-
325
- .btn-icon-only.size-medium {
326
- width: 40px;
327
- }
328
-
329
- .btn-icon-only.size-large {
330
- width: 48px;
331
- }
332
-
333
- .btn-icon {
334
- width: 40px;
335
- height: 40px;
336
- padding: 0;
337
- min-width: 0;
338
- border-radius: 50%;
339
- }
340
- .btn-icon.tone-default {
341
- background: var(--rel-action-neutral-subtle);
342
- color: var(--rel-action-neutral-label-icons-ghost);
343
- border: none;
344
- }
345
- .btn-icon.tone-primary {
346
- background: var(--rel-action-brand-fill);
347
- color: var(--rel-action-brand-label-icons-primary);
348
- border: none;
349
- }
350
- .btn-icon.tone-secondary {
351
- background: var(--rel-bg-surface);
352
- color: var(--rel-action-neutral-label-icons-outline);
353
- border: 1px solid var(--rel-action-neutral-border-outline);
354
- }
355
- .btn-icon.tone-success {
356
- background: var(--rel-action-success-fill);
357
- color: var(--rel-action-success-label-icons-fill);
358
- border: none;
359
- }
360
- .btn-icon.tone-critical {
361
- background: var(--rel-action-danger-fill);
362
- color: var(--rel-action-danger-label-icons-fill);
363
- border: none;
364
- }
365
-
366
- button.btn-primary.tone-default:hover:not(:disabled) {
367
- background: var(--rel-action-brand-fill-hover);
368
- }
369
-
370
- button.btn-primary.tone-critical:hover:not(:disabled) {
371
- background: var(--rel-action-danger-fill-hover);
372
- }
373
-
374
- button.btn-primary.tone-success:hover:not(:disabled) {
375
- background: var(--rel-action-success-fill-hover);
376
- }
377
-
378
- /* Generic hover — excludes styles handled per-style. Now also excludes
379
- .btn-text: ghost gets tone-accent hovers below (this generic grey overlay
380
- has higher specificity than the per-tone ghost rules and was overriding
381
- them, which is why every ghost hover looked grey). */
382
- /* Link buttons change label color only (rule above) — the overlay fill
383
- here would make a hovered link read as a ghost button. */
384
- button:hover:not(:disabled):not(.btn-primary):not(.btn-default):not(.btn-icon):not(.btn-secondary):not(.btn-static):not(.btn-text):not(.btn-link) {
385
- background: var(--rel-action-overlay-hover);
386
- }
387
-
388
- /* Outline (secondary): fill stays on the surface (white) on hover AND
389
- pressed — only the border reacts. Matches Figma (transparent outline fill). */
390
- button.btn-secondary:hover:not(:disabled),
391
- button.btn-secondary:active:not(:disabled) {
392
- background: var(--rel-bg-surface);
393
- border-color: var(--rel-action-neutral-border-outline-hover);
394
- }
395
- button.btn-secondary.tone-critical:hover:not(:disabled),
396
- button.btn-secondary.tone-critical:active:not(:disabled) {
397
- background: var(--rel-bg-surface);
398
- border-color: var(--rel-action-danger-border-outline-hover);
399
- }
400
- button.btn-secondary.tone-success:hover:not(:disabled),
401
- button.btn-secondary.tone-success:active:not(:disabled) {
402
- background: var(--rel-bg-surface);
403
- border-color: var(--rel-border-success-strong);
404
- }
405
-
406
- /* Ghost (text): tone-accent fill on hover AND pressed — the same tint the
407
- outline used to carry (neutral = subtle overlay, danger = red-50,
408
- success = green-50). Matches Figma. */
409
- button.btn-text:hover:not(:disabled),
410
- button.btn-text:active:not(:disabled) {
411
- background: var(--rel-action-neutral-ghost-hover);
412
- }
413
- button.btn-text.tone-critical:hover:not(:disabled),
414
- button.btn-text.tone-critical:active:not(:disabled) {
415
- background: var(--rel-action-danger-ghost-hover);
416
- }
417
- button.btn-text.tone-success:hover:not(:disabled),
418
- button.btn-text.tone-success:active:not(:disabled) {
419
- background: var(--rel-action-success-ghost-hover);
420
- }
421
-
422
- button.btn-default.tone-default:hover:not(:disabled) {
423
- background: var(--rel-action-neutral-fill-hover);
424
- }
425
- button.btn-default.tone-critical:hover:not(:disabled) {
426
- background: var(--rel-action-danger-fill-hover);
427
- }
428
- button.btn-default.tone-success:hover:not(:disabled) {
429
- background: var(--rel-action-success-fill-hover);
430
- }
431
-
432
- /* Icon hover — each tone matches its corresponding button variant */
433
- button.btn-icon.tone-default:hover:not(:disabled) {
434
- background: var(--rel-action-neutral-subtle-hover);
435
- }
436
- button.btn-icon.tone-primary:hover:not(:disabled) {
437
- background: var(--rel-action-brand-fill-hover);
438
- }
439
- button.btn-icon.tone-secondary:hover:not(:disabled) {
440
- background: var(--rel-action-neutral-ghost-hover);
441
- border-color: var(--rel-action-neutral-border-outline-hover);
442
- }
443
- button.btn-icon.tone-success:hover:not(:disabled) {
444
- background: var(--rel-action-success-fill-hover);
445
- }
446
- button.btn-icon.tone-critical:hover:not(:disabled) {
447
- background: var(--rel-action-danger-fill-hover);
448
- }
449
-
450
- button.btn-default.tone-default:active:not(:disabled) {
451
- background-image: linear-gradient(
452
- var(--rel-action-neutral-static-hover),
453
- var(--rel-action-neutral-static-hover)
454
- );
455
- }
456
-
457
- button:active:not(:disabled) {
458
- transform: scale(0.97);
459
- }
460
-
461
- button:focus-visible {
462
- outline: 2px solid var(--rel-border-focus);
463
- outline-offset: 2px;
464
- box-shadow:
465
- 0 0 0 2px var(--rel-border-focus-inner),
466
- 0 0 0 8px var(--rel-border-focus-subtle);
467
- }
468
- `;
469
-
470
- connectedCallback() {
471
- super.connectedCallback();
472
- // Capture-phase, on the host itself: blocks programmatic el.click() calls
473
- // and any other path that bypasses the inner button's pointer-events:none
474
- // (e.g. CSS pointer-events doesn't stop .click(), which targets the host
475
- // directly regardless of hit-testing).
476
- this.addEventListener("click", this.handleHostClick, { capture: true });
477
- }
478
-
479
- disconnectedCallback() {
480
- this.removeEventListener("click", this.handleHostClick, { capture: true });
481
- super.disconnectedCallback();
482
- }
483
-
484
- private handleHostClick = (e: MouseEvent) => {
485
- if (this.disabled || this.loading) {
486
- e.preventDefault();
487
- e.stopImmediatePropagation();
488
- }
489
- };
490
-
491
- /** Style axis with Figma-vocabulary aliases folded onto canonical values. */
492
- private get styleVariant(): string {
493
- if (this.variant === "outline") return "secondary";
494
- if (this.variant === "ghost") return "text";
495
- return this.variant;
496
- }
497
-
498
- /** Tone axis with Figma-vocabulary aliases folded onto canonical values. */
499
- private get effectiveTone(): string {
500
- if (this.tone === "neutral") return "default";
501
- if (this.tone === "brand") return "primary";
502
- if (this.tone === "danger") return "critical";
503
- return this.tone;
504
- }
505
-
506
- private get classes() {
507
- const style = this.styleVariant;
508
- return [
509
- `btn-${style}`,
510
- this.iconOnly && style !== "icon" ? "btn-icon-only" : "",
511
- `tone-${this.effectiveTone}`,
512
- `size-${this.size}`,
513
- `border-${this.border}`,
514
- ]
515
- .filter(Boolean)
516
- .join(" ");
517
- }
518
-
519
- private handleClick(e: MouseEvent) {
520
- if (this.disabled || this.loading) {
521
- e.preventDefault();
522
- e.stopImmediatePropagation();
523
- return;
524
- }
525
-
526
- // Native click events are already composed: true, so this bubbles
527
- // through the shadow boundary to the host on its own — no re-dispatch
528
- // needed. (A manual re-dispatch here previously caused every @click
529
- // listener on <rel-button> to fire twice per real click.)
530
- }
531
-
532
- render() {
533
- const showBadge = this.badgeDot || !!this.badge;
534
-
535
- return html`
536
- <button
537
- part="base"
538
- class="${this.classes}"
539
- ?disabled=${this.disabled}
540
- @click=${this.handleClick}
541
- aria-disabled=${this.disabled}
542
- aria-busy=${this.loading}
543
- aria-label=${ifDefined(this.ariaLabel ?? undefined)}
544
- >
545
- ${this.loading
546
- ? html`<span part="indicator" class="spinner" aria-hidden="true"></span>`
547
- : null}
548
- <span class="content"><slot></slot></span>
549
- </button>
550
- ${showBadge
551
- ? html`<rel-badge
552
- class="btn-badge"
553
- variant=${this.badgeDot ? "dot" : "number"}
554
- number=${this.badge}
555
- size="sm"
556
- ></rel-badge>`
557
- : null}
558
- `;
559
- }
560
- }
@@ -1 +0,0 @@
1
- export * from "./rel-calendar";