@reliability-design/web 1.4.5 → 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.
- package/CHANGELOG.md +211 -49
- package/dist/components/rel-accordion/README.md +132 -132
- package/dist/components/rel-alert/README.md +145 -141
- package/dist/components/rel-autocomplete/README.md +156 -61
- package/dist/components/rel-avatar/README.md +116 -130
- package/dist/components/rel-badge/README.md +98 -93
- package/dist/components/rel-breadcrumb/README.md +96 -102
- package/dist/components/rel-button/README.md +160 -130
- package/dist/components/rel-calendar/README.md +109 -109
- package/dist/components/rel-card/README.md +75 -94
- package/dist/components/rel-cardmedia/README.md +100 -127
- package/dist/components/rel-carousel/README.md +184 -166
- package/dist/components/rel-checkbox/README.md +93 -75
- package/dist/components/rel-chip/README.md +132 -121
- package/dist/components/rel-container/README.md +107 -99
- package/dist/components/rel-date-picker/README.md +200 -289
- package/dist/components/rel-divider/README.md +78 -100
- package/dist/components/rel-drawer/README.md +187 -200
- package/dist/components/rel-kbd/README.md +141 -151
- package/dist/components/rel-menu/README.md +178 -151
- package/dist/components/rel-menubar/README.md +218 -329
- package/dist/components/rel-otp/README.md +170 -440
- package/dist/components/rel-pagination/README.md +100 -424
- package/dist/components/rel-popover/README.md +203 -568
- package/dist/components/rel-progress/README.md +88 -110
- package/dist/components/rel-radio/README.md +131 -135
- package/dist/components/rel-range-picker/README.md +162 -0
- package/dist/components/rel-resizable/README.md +199 -140
- package/dist/components/rel-segmented-button/README.md +155 -141
- package/dist/components/rel-select/README.md +154 -93
- package/dist/components/rel-sheet/README.md +168 -224
- package/dist/components/rel-skeleton/README.md +103 -0
- package/dist/components/rel-slider/README.md +130 -135
- package/dist/components/rel-snackbar/README.md +162 -134
- package/dist/components/rel-stepper/README.md +158 -160
- package/dist/components/rel-switch/README.md +108 -102
- package/dist/components/rel-tab/README.md +171 -163
- package/dist/components/rel-textfield/README.md +159 -69
- package/dist/components/rel-time-picker/README.md +122 -157
- package/dist/components/rel-toast/README.md +157 -148
- package/dist/components/rel-tooltip/README.md +108 -103
- package/dist/components/rel-upload/README.md +220 -0
- package/dist/index.d.mts +1387 -350
- package/dist/index.d.ts +1387 -350
- package/dist/index.js +5863 -3600
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5864 -3592
- package/dist/index.mjs.map +1 -1
- package/dist/styles/vars.css +877 -866
- package/dist/{vars-XNMTSRVN.css → vars-JHTUGNRG.css} +877 -866
- package/package.json +4 -3
- package/dist/components/rel-accordion/rel-accordion.ts +0 -336
- package/dist/components/rel-alert/rel-alert.ts +0 -305
- package/dist/components/rel-autocomplete/rel-autocomplete.ts +0 -689
- package/dist/components/rel-avatar/rel-avatar.ts +0 -142
- package/dist/components/rel-badge/rel-badge.ts +0 -170
- package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +0 -106
- package/dist/components/rel-button/rel-button.ts +0 -560
- package/dist/components/rel-calendar/index.ts +0 -1
- package/dist/components/rel-calendar/rel-calendar.ts +0 -519
- package/dist/components/rel-card/rel-card.ts +0 -88
- package/dist/components/rel-cardmedia/rel-cardmedia.ts +0 -88
- package/dist/components/rel-carousel/index.ts +0 -1
- package/dist/components/rel-carousel/rel-carousel.ts +0 -700
- package/dist/components/rel-chart/README.md +0 -286
- package/dist/components/rel-chart/index.ts +0 -1
- package/dist/components/rel-chart/rel-chart.ts +0 -754
- package/dist/components/rel-checkbox/rel-checkbox.ts +0 -382
- package/dist/components/rel-chip/rel-chip.ts +0 -472
- package/dist/components/rel-container/rel-container.ts +0 -95
- package/dist/components/rel-date-picker/date-format.test.ts +0 -43
- package/dist/components/rel-date-picker/date-format.ts +0 -47
- package/dist/components/rel-date-picker/index.ts +0 -1
- package/dist/components/rel-date-picker/rel-date-picker.ts +0 -1237
- package/dist/components/rel-divider/rel-divider.ts +0 -54
- package/dist/components/rel-drawer/index.ts +0 -1
- package/dist/components/rel-drawer/rel-drawer-footer.ts +0 -27
- package/dist/components/rel-drawer/rel-drawer.ts +0 -604
- package/dist/components/rel-kbd/rel-kbd.ts +0 -188
- package/dist/components/rel-layout/rel-layout.ts +0 -49
- package/dist/components/rel-menu/rel-menu.ts +0 -535
- package/dist/components/rel-menubar/index.ts +0 -2
- package/dist/components/rel-menubar/rel-menubar-menu.css +0 -3
- package/dist/components/rel-menubar/rel-menubar-menu.ts +0 -17
- package/dist/components/rel-menubar/rel-menubar.ts +0 -777
- package/dist/components/rel-nav-menu/README.md +0 -289
- package/dist/components/rel-nav-menu/index.ts +0 -3
- package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +0 -243
- package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +0 -72
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +0 -270
- package/dist/components/rel-navbar/README.md +0 -126
- package/dist/components/rel-navbar/rel-navbar.ts +0 -105
- package/dist/components/rel-otp/index.ts +0 -1
- package/dist/components/rel-otp/rel-otp.ts +0 -517
- package/dist/components/rel-pagination/index.ts +0 -1
- package/dist/components/rel-pagination/rel-pagination.ts +0 -509
- package/dist/components/rel-popover/index.ts +0 -2
- package/dist/components/rel-popover/rel-popover-content.ts +0 -81
- package/dist/components/rel-popover/rel-popover.ts +0 -444
- package/dist/components/rel-progress/rel-progress.ts +0 -225
- package/dist/components/rel-radio/rel-radio.ts +0 -305
- package/dist/components/rel-range-picker/rel-range-picker.ts +0 -1331
- package/dist/components/rel-resizable/index.ts +0 -3
- package/dist/components/rel-resizable/rel-resizable-group.ts +0 -405
- package/dist/components/rel-resizable/rel-resizable-handle.ts +0 -150
- package/dist/components/rel-resizable/rel-resizable-panel.ts +0 -58
- package/dist/components/rel-segmented-button/rel-segmented-button.ts +0 -213
- package/dist/components/rel-select/rel-select.ts +0 -1385
- package/dist/components/rel-sheet/rel-sheet.ts +0 -465
- package/dist/components/rel-sidebar/README.md +0 -209
- package/dist/components/rel-sidebar/rel-sidebar.ts +0 -1185
- package/dist/components/rel-skeleton/rel-skeleton.ts +0 -134
- package/dist/components/rel-slider/rel-slider.ts +0 -670
- package/dist/components/rel-snackbar/rel-snackbar.ts +0 -325
- package/dist/components/rel-stepper/rel-stepper.ts +0 -565
- package/dist/components/rel-switch/rel-switch.ts +0 -197
- package/dist/components/rel-tab/rel-tab.ts +0 -264
- package/dist/components/rel-table/README.md +0 -178
- package/dist/components/rel-table/rel-table-body.ts +0 -22
- package/dist/components/rel-table/rel-table-caption.ts +0 -30
- package/dist/components/rel-table/rel-table-cell.ts +0 -43
- package/dist/components/rel-table/rel-table-footer.ts +0 -24
- package/dist/components/rel-table/rel-table-head.ts +0 -47
- package/dist/components/rel-table/rel-table-header.ts +0 -22
- package/dist/components/rel-table/rel-table-row.ts +0 -37
- package/dist/components/rel-table/rel-table.ts +0 -62
- package/dist/components/rel-textfield/rel-textfield.ts +0 -866
- package/dist/components/rel-time-picker/rel-time-picker.ts +0 -1126
- package/dist/components/rel-toast/rel-toast.ts +0 -204
- package/dist/components/rel-tooltip/rel-tooltip.ts +0 -190
- package/dist/components/rel-topbar/rel-topbar-actions.ts +0 -27
- package/dist/components/rel-topbar/rel-topbar-brand.ts +0 -54
- package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +0 -69
- package/dist/components/rel-topbar/rel-topbar-dropdown.ts +0 -31
- package/dist/components/rel-topbar/rel-topbar-item.ts +0 -324
- package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -27
- package/dist/components/rel-topbar/rel-topbar-profile.ts +0 -243
- package/dist/components/rel-topbar/rel-topbar-utilities.ts +0 -27
- package/dist/components/rel-topbar/rel-topbar.ts +0 -446
- package/dist/components/rel-upload/UPLOAD-DESIGN-CHANGES.md +0 -134
- package/dist/components/rel-upload/rel-upload-actions.ts +0 -163
- package/dist/components/rel-upload/rel-upload.ts +0 -943
|
@@ -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";
|