@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.
- package/dist/components/rel-accordion/README.md +13 -13
- package/dist/components/rel-accordion/rel-accordion.ts +80 -45
- package/dist/components/rel-alert/README.md +15 -15
- package/dist/components/rel-alert/rel-alert.ts +153 -158
- package/dist/components/rel-avatar/README.md +15 -15
- package/dist/components/rel-avatar/rel-avatar.ts +42 -38
- package/dist/components/rel-badge/README.md +5 -5
- package/dist/components/rel-badge/rel-badge.ts +46 -11
- package/dist/components/rel-breadcrumb/README.md +7 -7
- package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +16 -13
- package/dist/components/rel-button/README.md +20 -20
- package/dist/components/rel-button/rel-button.ts +292 -105
- package/dist/components/rel-calendar/README.md +5 -5
- package/dist/components/rel-calendar/rel-calendar.ts +61 -45
- package/dist/components/rel-card/README.md +6 -6
- package/dist/components/rel-card/rel-card.ts +88 -35
- package/dist/components/rel-cardmedia/README.md +9 -9
- package/dist/components/rel-cardmedia/rel-cardmedia.ts +27 -20
- package/dist/components/rel-carousel/README.md +3 -3
- package/dist/components/rel-carousel/rel-carousel.ts +174 -90
- package/dist/components/rel-chart/README.md +8 -8
- package/dist/components/rel-chart/rel-chart.ts +59 -41
- package/dist/components/rel-checkbox/README.md +7 -7
- package/dist/components/rel-checkbox/rel-checkbox.ts +61 -61
- package/dist/components/rel-chip/README.md +16 -16
- package/dist/components/rel-chip/rel-chip.ts +143 -111
- package/dist/components/rel-container/README.md +11 -11
- package/dist/components/rel-container/rel-container.ts +35 -9
- package/dist/components/rel-date-picker/README.md +43 -7
- package/dist/components/rel-date-picker/date-format.test.ts +43 -0
- package/dist/components/rel-date-picker/date-format.ts +47 -0
- package/dist/components/rel-date-picker/rel-date-picker.ts +154 -82
- package/dist/components/rel-divider/README.md +9 -9
- package/dist/components/rel-divider/rel-divider.ts +10 -9
- package/dist/components/rel-drawer/README.md +1 -1
- package/dist/components/rel-drawer/rel-drawer-footer.ts +1 -1
- package/dist/components/rel-drawer/rel-drawer.ts +56 -28
- package/dist/components/rel-kbd/README.md +20 -20
- package/dist/components/rel-kbd/rel-kbd.ts +119 -25
- package/dist/components/rel-layout/README.md +1 -1
- package/dist/components/rel-layout/rel-layout.ts +2 -2
- package/dist/components/rel-menu/README.md +15 -15
- package/dist/components/rel-menu/rel-menu.ts +155 -88
- package/dist/components/rel-menubar/README.md +20 -20
- package/dist/components/rel-menubar/rel-menubar.ts +47 -31
- package/dist/components/rel-nav-menu/README.md +21 -21
- package/dist/components/rel-nav-menu/rel-nav-menu-item.ts +17 -14
- package/dist/components/rel-nav-menu/rel-nav-menu-panel.ts +33 -14
- package/dist/components/rel-nav-menu/rel-nav-menu.ts +14 -7
- package/dist/components/rel-navbar/README.md +7 -7
- package/dist/components/rel-navbar/rel-navbar.ts +12 -11
- package/dist/components/rel-otp/README.md +3 -3
- package/dist/components/rel-otp/rel-otp.ts +46 -30
- package/dist/components/rel-pagination/README.md +18 -18
- package/dist/components/rel-pagination/rel-pagination.ts +141 -90
- package/dist/components/rel-popover/README.md +13 -13
- package/dist/components/rel-popover/rel-popover-content.ts +39 -14
- package/dist/components/rel-popover/rel-popover.ts +18 -7
- package/dist/components/rel-progress/README.md +5 -5
- package/dist/components/rel-progress/rel-progress.ts +16 -12
- package/dist/components/rel-radio/README.md +9 -9
- package/dist/components/rel-radio/rel-radio.ts +64 -50
- package/dist/components/rel-range-picker/rel-range-picker.ts +120 -117
- package/dist/components/rel-resizable/README.md +4 -4
- package/dist/components/rel-resizable/rel-resizable-group.ts +15 -4
- package/dist/components/rel-resizable/rel-resizable-handle.ts +13 -9
- package/dist/components/rel-resizable/rel-resizable-panel.ts +11 -0
- package/dist/components/rel-segmented-button/README.md +10 -10
- package/dist/components/rel-segmented-button/rel-segmented-button.ts +39 -40
- package/dist/components/rel-select/README.md +14 -14
- package/dist/components/rel-select/rel-select.ts +120 -188
- package/dist/components/rel-sheet/README.md +10 -10
- package/dist/components/rel-sheet/rel-sheet.ts +82 -23
- package/dist/components/rel-sidebar/README.md +5 -5
- package/dist/components/rel-sidebar/rel-sidebar.ts +75 -60
- package/dist/components/rel-skeleton/rel-skeleton.ts +8 -13
- package/dist/components/rel-slider/README.md +10 -10
- package/dist/components/rel-slider/rel-slider.ts +230 -57
- package/dist/components/rel-snackbar/README.md +9 -9
- package/dist/components/rel-snackbar/rel-snackbar.ts +44 -32
- package/dist/components/rel-stepper/README.md +10 -10
- package/dist/components/rel-stepper/rel-stepper.ts +110 -46
- package/dist/components/rel-switch/README.md +5 -5
- package/dist/components/rel-switch/rel-switch.ts +23 -12
- package/dist/components/rel-tab/README.md +13 -13
- package/dist/components/rel-tab/rel-tab.ts +63 -19
- package/dist/components/rel-table/README.md +7 -7
- package/dist/components/rel-table/rel-table-body.ts +2 -1
- package/dist/components/rel-table/rel-table-caption.ts +10 -7
- package/dist/components/rel-table/rel-table-cell.ts +7 -6
- package/dist/components/rel-table/rel-table-footer.ts +4 -3
- package/dist/components/rel-table/rel-table-head.ts +11 -9
- package/dist/components/rel-table/rel-table-header.ts +2 -1
- package/dist/components/rel-table/rel-table-row.ts +5 -4
- package/dist/components/rel-table/rel-table.ts +16 -7
- package/dist/components/rel-textfield/README.md +11 -11
- package/dist/components/rel-textfield/rel-textfield.ts +172 -91
- package/dist/components/rel-toast/README.md +11 -11
- package/dist/components/rel-toast/rel-toast.ts +33 -30
- package/dist/components/rel-tooltip/README.md +7 -7
- package/dist/components/rel-tooltip/rel-tooltip.ts +160 -123
- package/dist/components/rel-topbar/rel-topbar-actions.ts +1 -2
- package/dist/components/rel-topbar/rel-topbar-brand.ts +8 -5
- package/dist/components/rel-topbar/rel-topbar-dropdown-item.ts +10 -8
- package/dist/components/rel-topbar/rel-topbar-dropdown.ts +4 -5
- package/dist/components/rel-topbar/rel-topbar-item.ts +23 -22
- package/dist/components/rel-topbar/rel-topbar-mobile-panel.ts +0 -1
- package/dist/components/rel-topbar/rel-topbar-profile.ts +68 -50
- package/dist/components/rel-topbar/rel-topbar-utilities.ts +1 -2
- package/dist/components/rel-topbar/rel-topbar.ts +31 -29
- package/dist/index.d.mts +151 -39
- package/dist/index.d.ts +151 -39
- package/dist/index.js +3544 -2289
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3876 -2622
- package/dist/index.mjs.map +1 -1
- package/dist/styles/vars.css +653 -363
- package/dist/vars-WY2XCF5G.css +653 -0
- package/package.json +3 -2
- package/dist/components/rel-range-picker/rel-range-picker.css +0 -349
- package/dist/styles/vars-v2.css +0 -525
- package/dist/vars-F54UGCHM.css +0 -363
- package/dist/vars-v2-VMGWTFJZ.css +0 -525
|
@@ -1,17 +1,42 @@
|
|
|
1
|
-
import "../../styles/vars-v2.css";
|
|
2
1
|
import "../rel-badge/rel-badge";
|
|
3
2
|
import { LitElement, html, css } from "lit";
|
|
4
3
|
import { customElement, property } from "lit/decorators.js";
|
|
5
4
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
6
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
|
+
|
|
7
22
|
@customElement("rel-button")
|
|
8
23
|
export class RelButton extends LitElement {
|
|
9
24
|
@property({ type: String })
|
|
10
|
-
variant:
|
|
11
|
-
"default";
|
|
25
|
+
variant: ButtonVariant = "default";
|
|
12
26
|
|
|
13
27
|
@property({ type: String })
|
|
14
|
-
tone:
|
|
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;
|
|
15
40
|
|
|
16
41
|
@property({ type: String })
|
|
17
42
|
size: "small" | "medium" | "large" = "medium";
|
|
@@ -32,6 +57,7 @@ export class RelButton extends LitElement {
|
|
|
32
57
|
|
|
33
58
|
static styles = css`
|
|
34
59
|
:host {
|
|
60
|
+
color: var(--rel-text-default);
|
|
35
61
|
display: inline-flex;
|
|
36
62
|
position: relative;
|
|
37
63
|
}
|
|
@@ -54,118 +80,229 @@ export class RelButton extends LitElement {
|
|
|
54
80
|
}
|
|
55
81
|
|
|
56
82
|
button {
|
|
57
|
-
font-family: var(--rel-
|
|
58
|
-
font-weight: var(--rel-
|
|
83
|
+
font-family: var(--rel-action-md-font-family);
|
|
84
|
+
font-weight: var(--rel-action-md-font-weight);
|
|
59
85
|
cursor: pointer;
|
|
60
86
|
border: none;
|
|
61
87
|
transition: all 0.15s ease;
|
|
62
88
|
display: inline-flex;
|
|
63
89
|
align-items: center;
|
|
64
90
|
justify-content: center;
|
|
65
|
-
gap: var(--rel-spacing-
|
|
91
|
+
gap: var(--rel-spacing-2xs);
|
|
66
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
|
+
}
|
|
67
138
|
}
|
|
68
139
|
|
|
69
140
|
/* Sizes */
|
|
70
141
|
.size-small {
|
|
71
142
|
height: 32px;
|
|
72
|
-
padding: 0 var(--rel-spacing-
|
|
73
|
-
font-
|
|
143
|
+
padding: 0 var(--rel-spacing-sm);
|
|
144
|
+
font-family: var(--rel-action-md-font-family);
|
|
145
|
+
font-weight: var(--rel-action-md-font-weight);
|
|
146
|
+
font-size: var(--rel-action-md-font-size);
|
|
147
|
+
line-height: var(--rel-action-md-line-height);
|
|
148
|
+
letter-spacing: var(--rel-action-md-letter-spacing);
|
|
74
149
|
}
|
|
75
150
|
|
|
76
151
|
.size-medium {
|
|
77
152
|
height: 40px;
|
|
78
|
-
|
|
79
|
-
|
|
153
|
+
gap: var(--rel-spacing-xs);
|
|
154
|
+
padding: 0 var(--rel-spacing-md);
|
|
155
|
+
font-family: var(--rel-action-lg-font-family);
|
|
156
|
+
font-weight: var(--rel-action-lg-font-weight);
|
|
157
|
+
font-size: var(--rel-action-lg-font-size);
|
|
158
|
+
line-height: var(--rel-action-lg-line-height);
|
|
159
|
+
letter-spacing: var(--rel-action-lg-letter-spacing);
|
|
80
160
|
}
|
|
81
161
|
|
|
82
162
|
.size-large {
|
|
83
163
|
height: 48px;
|
|
84
|
-
padding: 0 var(--rel-spacing-
|
|
85
|
-
font-
|
|
164
|
+
padding: 0 var(--rel-spacing-lg);
|
|
165
|
+
font-family: var(--rel-action-xl-font-family);
|
|
166
|
+
font-weight: var(--rel-action-xl-font-weight);
|
|
167
|
+
font-size: var(--rel-action-xl-font-size);
|
|
168
|
+
line-height: var(--rel-action-xl-line-height);
|
|
169
|
+
letter-spacing: var(--rel-action-xl-letter-spacing);
|
|
86
170
|
}
|
|
87
171
|
|
|
88
172
|
/* Borders */
|
|
89
173
|
.border-corner-radius {
|
|
90
|
-
border-radius: var(--rel-
|
|
174
|
+
border-radius: var(--rel-border-radius-full-rounded);
|
|
91
175
|
}
|
|
92
176
|
|
|
93
177
|
.border-small-corner-radius {
|
|
94
|
-
border-radius: var(--rel-
|
|
178
|
+
border-radius: var(--rel-border-radius-sm);
|
|
95
179
|
}
|
|
96
180
|
|
|
97
181
|
.border-no-corner-radius {
|
|
98
|
-
border-radius: var(--rel-
|
|
182
|
+
border-radius: var(--rel-border-radius-square);
|
|
99
183
|
}
|
|
100
184
|
|
|
101
185
|
/* Disabled */
|
|
102
186
|
:host([disabled]) {
|
|
187
|
+
/* not-allowed must stay VISIBLE: the host keeps pointer-events so the
|
|
188
|
+
cursor renders. Clicks are neutralised two ways — the inner button's
|
|
189
|
+
pointer-events:none (below) and the capture-phase handleHostClick
|
|
190
|
+
guard (which also stops programmatic .click()). Putting
|
|
191
|
+
pointer-events:none on the HOST would make it transparent to the
|
|
192
|
+
pointer and suppress the cursor entirely. */
|
|
103
193
|
cursor: not-allowed;
|
|
104
|
-
/* Without this, clicks on a disabled button hit-test fall through the
|
|
105
|
-
inner button's pointer-events:none straight onto the host, which still
|
|
106
|
-
dispatches a normal click to any externally-attached listener. */
|
|
107
|
-
pointer-events: none;
|
|
108
194
|
}
|
|
109
195
|
|
|
110
196
|
:host([disabled]) button {
|
|
111
197
|
cursor: not-allowed;
|
|
112
|
-
background-color: var(--rel-
|
|
113
|
-
color: var(--rel-
|
|
198
|
+
background-color: var(--rel-action-brand-fill-disabled) !important;
|
|
199
|
+
color: var(--rel-action-brand-label-icons-primary-disabled) !important;
|
|
114
200
|
border: none !important;
|
|
115
201
|
pointer-events: none;
|
|
116
202
|
filter: none !important;
|
|
117
203
|
}
|
|
118
204
|
|
|
119
|
-
|
|
120
|
-
:host([disabled]):host-context(.dark) button {
|
|
121
|
-
background-color: var(--rel-color-bg-tertiary) !important;
|
|
122
|
-
color: var(--rel-color-text-disabled) !important;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/* Variants & tones (unchanged) */
|
|
205
|
+
/* Variants & tones — V2 action tokens (auto light/dark) */
|
|
126
206
|
.btn-default.tone-default {
|
|
127
|
-
background: var(--rel-
|
|
128
|
-
color: var(--rel-
|
|
207
|
+
background: var(--rel-action-neutral-fill);
|
|
208
|
+
color: var(--rel-action-neutral-label-icons-fill-inverse);
|
|
129
209
|
}
|
|
130
210
|
.btn-default.tone-critical {
|
|
131
|
-
background: var(--rel-
|
|
132
|
-
color:
|
|
211
|
+
background: var(--rel-action-danger-fill);
|
|
212
|
+
color: var(--rel-action-danger-label-icons-fill);
|
|
133
213
|
}
|
|
134
214
|
.btn-default.tone-success {
|
|
135
|
-
background: var(--rel-
|
|
136
|
-
color:
|
|
215
|
+
background: var(--rel-action-success-fill);
|
|
216
|
+
color: var(--rel-text-static-light);
|
|
137
217
|
}
|
|
138
218
|
|
|
139
219
|
.btn-primary.tone-default {
|
|
140
|
-
background: var(--rel-
|
|
141
|
-
color:
|
|
220
|
+
background: var(--rel-action-brand-fill);
|
|
221
|
+
color: var(--rel-action-brand-label-icons-primary);
|
|
142
222
|
}
|
|
143
223
|
.btn-primary.tone-critical {
|
|
144
|
-
background: var(--rel-
|
|
145
|
-
color:
|
|
224
|
+
background: var(--rel-action-danger-fill);
|
|
225
|
+
color: var(--rel-action-danger-label-icons-fill);
|
|
146
226
|
}
|
|
147
227
|
.btn-primary.tone-success {
|
|
148
|
-
background: var(--rel-
|
|
149
|
-
color:
|
|
228
|
+
background: var(--rel-action-success-fill);
|
|
229
|
+
color: var(--rel-action-success-label-icons-fill);
|
|
150
230
|
}
|
|
151
231
|
|
|
152
232
|
.btn-secondary {
|
|
153
|
-
background: var(--rel-
|
|
154
|
-
border: 1px solid var(--rel-
|
|
155
|
-
color: var(--rel-
|
|
233
|
+
background: var(--rel-bg-surface);
|
|
234
|
+
border: 1px solid var(--rel-action-neutral-border-outline);
|
|
235
|
+
color: var(--rel-action-neutral-label-icons-outline);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Secondary critical / success tones. The danger outline border is
|
|
239
|
+
deliberately the SUBTLE red at rest (red-100) and only goes solid
|
|
240
|
+
(red-600) on hover/pressed — verified against the Figma design nodes,
|
|
241
|
+
not just the variable names. Do not "fix" it to a solid red. */
|
|
242
|
+
.btn-secondary.tone-critical {
|
|
243
|
+
border-color: var(--rel-action-danger-border-outline);
|
|
244
|
+
color: var(--rel-action-danger-label-icons-outline);
|
|
245
|
+
}
|
|
246
|
+
.btn-secondary.tone-success {
|
|
247
|
+
border-color: var(--rel-border-success);
|
|
248
|
+
color: var(--rel-text-success);
|
|
156
249
|
}
|
|
157
250
|
|
|
158
251
|
.btn-text {
|
|
159
252
|
background: transparent;
|
|
160
|
-
color: var(--rel-
|
|
253
|
+
color: var(--rel-action-neutral-label-icons-ghost);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.btn-text.tone-critical {
|
|
257
|
+
color: var(--rel-action-danger-label-icons-ghost);
|
|
161
258
|
}
|
|
162
259
|
|
|
163
260
|
.btn-link {
|
|
164
261
|
background: transparent;
|
|
165
|
-
color: var(--rel-
|
|
262
|
+
color: var(--rel-action-neutral-label-icons-link);
|
|
166
263
|
text-decoration: underline;
|
|
167
264
|
}
|
|
168
265
|
|
|
266
|
+
button.btn-link:hover:not(:disabled) {
|
|
267
|
+
color: var(--rel-action-neutral-label-icons-link-hover);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.btn-link.tone-critical {
|
|
271
|
+
color: var(--rel-action-danger-label-icons-link);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
button.btn-link.tone-critical:hover:not(:disabled) {
|
|
275
|
+
color: var(--rel-action-danger-label-icons-link-hover);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* Static style (Figma Style=Static, Tone=Neutral only): transparent with a
|
|
279
|
+
static-light label, for placement on colored or dark surfaces */
|
|
280
|
+
.btn-static {
|
|
281
|
+
background: transparent;
|
|
282
|
+
color: var(--rel-text-static-light);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
button.btn-static:hover:not(:disabled) {
|
|
286
|
+
background: var(--rel-action-neutral-static-hover);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* Icon Only as an independent axis: square button of the current style */
|
|
290
|
+
.btn-icon-only {
|
|
291
|
+
padding: 0;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.btn-icon-only.size-small {
|
|
295
|
+
width: 32px;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.btn-icon-only.size-medium {
|
|
299
|
+
width: 40px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.btn-icon-only.size-large {
|
|
303
|
+
width: 48px;
|
|
304
|
+
}
|
|
305
|
+
|
|
169
306
|
.btn-icon {
|
|
170
307
|
width: 40px;
|
|
171
308
|
height: 40px;
|
|
@@ -173,67 +310,118 @@ export class RelButton extends LitElement {
|
|
|
173
310
|
border-radius: 50%;
|
|
174
311
|
}
|
|
175
312
|
.btn-icon.tone-default {
|
|
176
|
-
background: var(--rel-
|
|
177
|
-
color: var(--rel-
|
|
313
|
+
background: var(--rel-action-neutral-subtle);
|
|
314
|
+
color: var(--rel-action-neutral-label-icons-ghost);
|
|
178
315
|
border: none;
|
|
179
316
|
}
|
|
180
317
|
.btn-icon.tone-primary {
|
|
181
|
-
background: var(--rel-
|
|
182
|
-
color:
|
|
318
|
+
background: var(--rel-action-brand-fill);
|
|
319
|
+
color: var(--rel-action-brand-label-icons-primary);
|
|
183
320
|
border: none;
|
|
184
321
|
}
|
|
185
322
|
.btn-icon.tone-secondary {
|
|
186
|
-
background: var(--rel-
|
|
187
|
-
color: var(--rel-
|
|
188
|
-
border: 1px solid var(--rel-
|
|
323
|
+
background: var(--rel-bg-surface);
|
|
324
|
+
color: var(--rel-action-neutral-label-icons-outline);
|
|
325
|
+
border: 1px solid var(--rel-action-neutral-border-outline);
|
|
189
326
|
}
|
|
190
327
|
.btn-icon.tone-success {
|
|
191
|
-
background: var(--rel-
|
|
192
|
-
color:
|
|
328
|
+
background: var(--rel-action-success-fill);
|
|
329
|
+
color: var(--rel-action-success-label-icons-fill);
|
|
193
330
|
border: none;
|
|
194
331
|
}
|
|
195
332
|
.btn-icon.tone-critical {
|
|
196
|
-
background: var(--rel-
|
|
197
|
-
color:
|
|
333
|
+
background: var(--rel-action-danger-fill);
|
|
334
|
+
color: var(--rel-action-danger-label-icons-fill);
|
|
198
335
|
border: none;
|
|
199
336
|
}
|
|
200
337
|
|
|
201
338
|
button.btn-primary.tone-default:hover:not(:disabled) {
|
|
202
|
-
background: var(--rel-
|
|
339
|
+
background: var(--rel-action-brand-fill-hover);
|
|
203
340
|
}
|
|
204
341
|
|
|
205
342
|
button.btn-primary.tone-critical:hover:not(:disabled) {
|
|
206
|
-
|
|
343
|
+
background: var(--rel-action-danger-fill-hover);
|
|
207
344
|
}
|
|
208
345
|
|
|
209
346
|
button.btn-primary.tone-success:hover:not(:disabled) {
|
|
210
|
-
|
|
347
|
+
background: var(--rel-action-success-fill-hover);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* Generic hover — excludes styles handled per-style. Now also excludes
|
|
351
|
+
.btn-text: ghost gets tone-accent hovers below (this generic grey overlay
|
|
352
|
+
has higher specificity than the per-tone ghost rules and was overriding
|
|
353
|
+
them, which is why every ghost hover looked grey). */
|
|
354
|
+
button:hover:not(:disabled):not(.btn-primary):not(.btn-default):not(.btn-icon):not(.btn-secondary):not(.btn-static):not(.btn-text) {
|
|
355
|
+
background: var(--rel-action-overlay-hover);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* Outline (secondary): fill stays on the surface (white) on hover AND
|
|
359
|
+
pressed — only the border reacts. Matches Figma (transparent outline fill). */
|
|
360
|
+
button.btn-secondary:hover:not(:disabled),
|
|
361
|
+
button.btn-secondary:active:not(:disabled) {
|
|
362
|
+
background: var(--rel-bg-surface);
|
|
363
|
+
border-color: var(--rel-action-neutral-border-outline-hover);
|
|
364
|
+
}
|
|
365
|
+
button.btn-secondary.tone-critical:hover:not(:disabled),
|
|
366
|
+
button.btn-secondary.tone-critical:active:not(:disabled) {
|
|
367
|
+
background: var(--rel-bg-surface);
|
|
368
|
+
border-color: var(--rel-action-danger-border-outline-hover);
|
|
369
|
+
}
|
|
370
|
+
button.btn-secondary.tone-success:hover:not(:disabled),
|
|
371
|
+
button.btn-secondary.tone-success:active:not(:disabled) {
|
|
372
|
+
background: var(--rel-bg-surface);
|
|
373
|
+
border-color: var(--rel-border-success-strong);
|
|
211
374
|
}
|
|
212
375
|
|
|
213
|
-
/*
|
|
214
|
-
|
|
215
|
-
|
|
376
|
+
/* Ghost (text): tone-accent fill on hover AND pressed — the same tint the
|
|
377
|
+
outline used to carry (neutral = subtle overlay, danger = red-50,
|
|
378
|
+
success = green-50). Matches Figma. */
|
|
379
|
+
button.btn-text:hover:not(:disabled),
|
|
380
|
+
button.btn-text:active:not(:disabled) {
|
|
381
|
+
background: var(--rel-action-neutral-ghost-hover);
|
|
382
|
+
}
|
|
383
|
+
button.btn-text.tone-critical:hover:not(:disabled),
|
|
384
|
+
button.btn-text.tone-critical:active:not(:disabled) {
|
|
385
|
+
background: var(--rel-action-danger-ghost-hover);
|
|
386
|
+
}
|
|
387
|
+
button.btn-text.tone-success:hover:not(:disabled),
|
|
388
|
+
button.btn-text.tone-success:active:not(:disabled) {
|
|
389
|
+
background: var(--rel-action-success-ghost-hover);
|
|
216
390
|
}
|
|
217
391
|
|
|
218
|
-
button.btn-default:hover:not(:disabled) {
|
|
219
|
-
|
|
392
|
+
button.btn-default.tone-default:hover:not(:disabled) {
|
|
393
|
+
background: var(--rel-action-neutral-fill-hover);
|
|
394
|
+
}
|
|
395
|
+
button.btn-default.tone-critical:hover:not(:disabled) {
|
|
396
|
+
background: var(--rel-action-danger-fill-hover);
|
|
397
|
+
}
|
|
398
|
+
button.btn-default.tone-success:hover:not(:disabled) {
|
|
399
|
+
background: var(--rel-action-success-fill-hover);
|
|
220
400
|
}
|
|
221
401
|
|
|
222
402
|
/* Icon hover — each tone matches its corresponding button variant */
|
|
223
403
|
button.btn-icon.tone-default:hover:not(:disabled) {
|
|
224
|
-
|
|
404
|
+
background: var(--rel-action-neutral-subtle-hover);
|
|
225
405
|
}
|
|
226
406
|
button.btn-icon.tone-primary:hover:not(:disabled) {
|
|
227
|
-
background: var(--rel-
|
|
407
|
+
background: var(--rel-action-brand-fill-hover);
|
|
228
408
|
}
|
|
229
409
|
button.btn-icon.tone-secondary:hover:not(:disabled) {
|
|
230
|
-
background: var(--rel-
|
|
410
|
+
background: var(--rel-action-neutral-ghost-hover);
|
|
411
|
+
border-color: var(--rel-action-neutral-border-outline-hover);
|
|
231
412
|
}
|
|
232
413
|
button.btn-icon.tone-success:hover:not(:disabled) {
|
|
233
|
-
|
|
414
|
+
background: var(--rel-action-success-fill-hover);
|
|
234
415
|
}
|
|
235
416
|
button.btn-icon.tone-critical:hover:not(:disabled) {
|
|
236
|
-
|
|
417
|
+
background: var(--rel-action-danger-fill-hover);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
button.btn-default.tone-default:active:not(:disabled) {
|
|
421
|
+
background-image: linear-gradient(
|
|
422
|
+
var(--rel-action-neutral-static-hover),
|
|
423
|
+
var(--rel-action-neutral-static-hover)
|
|
424
|
+
);
|
|
237
425
|
}
|
|
238
426
|
|
|
239
427
|
button:active:not(:disabled) {
|
|
@@ -241,37 +429,12 @@ export class RelButton extends LitElement {
|
|
|
241
429
|
}
|
|
242
430
|
|
|
243
431
|
button:focus-visible {
|
|
244
|
-
outline: 2px solid var(--rel-
|
|
432
|
+
outline: 2px solid var(--rel-border-focus);
|
|
245
433
|
outline-offset: 2px;
|
|
434
|
+
box-shadow:
|
|
435
|
+
0 0 0 2px var(--rel-border-focus-inner),
|
|
436
|
+
0 0 0 8px var(--rel-border-focus-subtle);
|
|
246
437
|
}
|
|
247
|
-
|
|
248
|
-
/* Dark mode overrides */
|
|
249
|
-
:host-context([data-theme="dark"]) button.btn-default.tone-default,
|
|
250
|
-
:host-context(.dark) button.btn-default.tone-default {
|
|
251
|
-
background: var(--rel-gray-text-primary);
|
|
252
|
-
color: #000000;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
:host-context([data-theme="dark"]) button:focus-visible,
|
|
256
|
-
:host-context(.dark) button:focus-visible {
|
|
257
|
-
outline-color: var(--rel-color-text-primary);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
:host-context([data-theme="dark"]) button.btn-secondary,
|
|
261
|
-
:host-context(.dark) button.btn-secondary,
|
|
262
|
-
:host-context([data-theme="dark"]) button.btn-icon.tone-secondary,
|
|
263
|
-
:host-context(.dark) button.btn-icon.tone-secondary {
|
|
264
|
-
background: var(--rel-color-bg-primary);
|
|
265
|
-
border-color: var(--rel-color-border-strong);
|
|
266
|
-
color: var(--rel-color-text-primary);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
:host-context([data-theme="dark"]) button.btn-text,
|
|
270
|
-
:host-context(.dark) button.btn-text {
|
|
271
|
-
color: var(--rel-color-text-primary);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/* btn-link: blue in both modes — no dark override */
|
|
275
438
|
`;
|
|
276
439
|
|
|
277
440
|
connectedCallback() {
|
|
@@ -289,23 +452,42 @@ export class RelButton extends LitElement {
|
|
|
289
452
|
}
|
|
290
453
|
|
|
291
454
|
private handleHostClick = (e: MouseEvent) => {
|
|
292
|
-
if (this.disabled) {
|
|
455
|
+
if (this.disabled || this.loading) {
|
|
293
456
|
e.preventDefault();
|
|
294
457
|
e.stopImmediatePropagation();
|
|
295
458
|
}
|
|
296
459
|
};
|
|
297
460
|
|
|
461
|
+
/** Style axis with Figma-vocabulary aliases folded onto canonical values. */
|
|
462
|
+
private get styleVariant(): string {
|
|
463
|
+
if (this.variant === "outline") return "secondary";
|
|
464
|
+
if (this.variant === "ghost") return "text";
|
|
465
|
+
return this.variant;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/** Tone axis with Figma-vocabulary aliases folded onto canonical values. */
|
|
469
|
+
private get effectiveTone(): string {
|
|
470
|
+
if (this.tone === "neutral") return "default";
|
|
471
|
+
if (this.tone === "brand") return "primary";
|
|
472
|
+
if (this.tone === "danger") return "critical";
|
|
473
|
+
return this.tone;
|
|
474
|
+
}
|
|
475
|
+
|
|
298
476
|
private get classes() {
|
|
477
|
+
const style = this.styleVariant;
|
|
299
478
|
return [
|
|
300
|
-
`btn-${
|
|
301
|
-
|
|
479
|
+
`btn-${style}`,
|
|
480
|
+
this.iconOnly && style !== "icon" ? "btn-icon-only" : "",
|
|
481
|
+
`tone-${this.effectiveTone}`,
|
|
302
482
|
`size-${this.size}`,
|
|
303
483
|
`border-${this.border}`,
|
|
304
|
-
]
|
|
484
|
+
]
|
|
485
|
+
.filter(Boolean)
|
|
486
|
+
.join(" ");
|
|
305
487
|
}
|
|
306
488
|
|
|
307
489
|
private handleClick(e: MouseEvent) {
|
|
308
|
-
if (this.disabled) {
|
|
490
|
+
if (this.disabled || this.loading) {
|
|
309
491
|
e.preventDefault();
|
|
310
492
|
e.stopImmediatePropagation();
|
|
311
493
|
return;
|
|
@@ -322,13 +504,18 @@ export class RelButton extends LitElement {
|
|
|
322
504
|
|
|
323
505
|
return html`
|
|
324
506
|
<button
|
|
507
|
+
part="base"
|
|
325
508
|
class="${this.classes}"
|
|
326
509
|
?disabled=${this.disabled}
|
|
327
510
|
@click=${this.handleClick}
|
|
328
511
|
aria-disabled=${this.disabled}
|
|
512
|
+
aria-busy=${this.loading}
|
|
329
513
|
aria-label=${ifDefined(this.ariaLabel ?? undefined)}
|
|
330
514
|
>
|
|
331
|
-
|
|
515
|
+
${this.loading
|
|
516
|
+
? html`<span part="indicator" class="spinner" aria-hidden="true"></span>`
|
|
517
|
+
: null}
|
|
518
|
+
<span class="content"><slot></slot></span>
|
|
332
519
|
</button>
|
|
333
520
|
${showBadge
|
|
334
521
|
? html`<rel-badge
|
|
@@ -101,9 +101,9 @@ calendar.addEventListener('rel-change', (e) => {
|
|
|
101
101
|
The component uses design tokens from the Reliability Design System. All colors, spacing, and typography are inherited from CSS variables.
|
|
102
102
|
|
|
103
103
|
Key tokens used:
|
|
104
|
-
- `--rel-
|
|
104
|
+
- `--rel-bg-*` / `--rel-text-*` / `--rel-border-*` semantic colors
|
|
105
105
|
- `--rel-spacing-*` for spacing
|
|
106
|
-
- `--rel-
|
|
107
|
-
- `--rel-
|
|
108
|
-
- Focus outline uses `--rel-
|
|
109
|
-
- Selected state uses `--rel-
|
|
106
|
+
- `--rel-body-*` / `--rel-headings-*` composite typography roles
|
|
107
|
+
- `--rel-border-radius-*` for border radius
|
|
108
|
+
- Focus outline uses `--rel-bg-info`
|
|
109
|
+
- Selected state uses `--rel-bg-brand`
|