@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,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";