canny-ui 1.0.0 → 2.0.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/canny-ui.min.css +1 -1
- package/package.json +1 -1
- package/src/_utilities.scss +16 -0
- package/src/canny-ui.scss +3 -0
- package/src/components/_alerts.scss +177 -33
- package/src/components/_command-palette.scss +251 -0
- package/src/components/_datepicker.scss +511 -0
- package/src/components/_forms.scss +107 -21
- package/src/components/_navbar.scss +3 -3
- package/src/components/_sidebar.scss +56 -11
- package/src/components/_toast.scss +271 -0
- package/src/tokens/_semantic.scss +3 -1
- package/dist/canny-ui.css +0 -887
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
@use '../tokens/primitives' as *;
|
|
2
|
+
|
|
3
|
+
// DatePicker — enterprise-compact unified picker.
|
|
4
|
+
// Modes: date · month · year · time · datetime.
|
|
5
|
+
// Panel is portaled to document.body so it never gets clipped by overflow:hidden ancestors.
|
|
6
|
+
|
|
7
|
+
@keyframes cny-dp-pop {
|
|
8
|
+
from { opacity: 0; transform: translateY(-4px) scale(0.985); }
|
|
9
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────
|
|
13
|
+
// Trigger — stays in its original DOM position
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
.cny-datepicker {
|
|
17
|
+
position: relative;
|
|
18
|
+
display: inline-block;
|
|
19
|
+
width: 100%;
|
|
20
|
+
|
|
21
|
+
&.is-disabled { pointer-events: none; }
|
|
22
|
+
|
|
23
|
+
&-trigger {
|
|
24
|
+
display: inline-flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: $space-2;
|
|
27
|
+
width: 100%;
|
|
28
|
+
text-align: left;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
padding: 0 $space-3;
|
|
31
|
+
|
|
32
|
+
&:focus,
|
|
33
|
+
&.is-open { border-color: var(--border-focus); }
|
|
34
|
+
&[disabled] { cursor: not-allowed; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&-icon {
|
|
38
|
+
display: inline-flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
color: var(--text-muted);
|
|
42
|
+
flex: none;
|
|
43
|
+
transition: color 140ms ease;
|
|
44
|
+
}
|
|
45
|
+
&-trigger:hover &-icon,
|
|
46
|
+
&-trigger.is-open &-icon { color: var(--border-focus); }
|
|
47
|
+
&-trigger.danger &-icon { color: var(--color-danger); }
|
|
48
|
+
|
|
49
|
+
&-value {
|
|
50
|
+
flex: 1 1 auto;
|
|
51
|
+
min-width: 0;
|
|
52
|
+
color: var(--text-primary);
|
|
53
|
+
font-size: inherit;
|
|
54
|
+
line-height: 1.4;
|
|
55
|
+
white-space: nowrap;
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
text-overflow: ellipsis;
|
|
58
|
+
font-variant-numeric: tabular-nums;
|
|
59
|
+
|
|
60
|
+
&.is-placeholder { color: var(--text-muted); }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&-clear {
|
|
64
|
+
flex: none;
|
|
65
|
+
display: inline-flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
justify-content: center;
|
|
68
|
+
width: 18px;
|
|
69
|
+
height: 18px;
|
|
70
|
+
border-radius: var(--radius-xs);
|
|
71
|
+
color: var(--text-muted);
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
transition: color 120ms ease, background-color 120ms ease;
|
|
74
|
+
|
|
75
|
+
&:hover { color: var(--text-primary); background: var(--bg-surface-low); }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
&-error {
|
|
79
|
+
display: block;
|
|
80
|
+
margin-top: 6px;
|
|
81
|
+
font-size: 0.75rem;
|
|
82
|
+
color: var(--color-danger);
|
|
83
|
+
line-height: 1.4;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─────────────────────────────────────────────────────────────────
|
|
88
|
+
// Portaled panel — rendered into document.body
|
|
89
|
+
// Escapes overflow:hidden / transform / contain ancestors.
|
|
90
|
+
// ─────────────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
.cny-datepicker-panel {
|
|
93
|
+
position: fixed;
|
|
94
|
+
z-index: 1000;
|
|
95
|
+
padding: 10px;
|
|
96
|
+
background: var(--bg-surface-lowest);
|
|
97
|
+
border: 1px solid var(--border-color);
|
|
98
|
+
border-radius: var(--radius-sm);
|
|
99
|
+
box-shadow:
|
|
100
|
+
0 1px 0 rgba(15, 23, 42, 0.02),
|
|
101
|
+
0 6px 18px -6px rgba(15, 23, 42, 0.14),
|
|
102
|
+
0 14px 32px -16px rgba(15, 23, 42, 0.16);
|
|
103
|
+
font-family: var(--font-body);
|
|
104
|
+
color: var(--text-primary);
|
|
105
|
+
animation: cny-dp-pop 140ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
|
|
106
|
+
|
|
107
|
+
&.is-date,
|
|
108
|
+
&.is-month,
|
|
109
|
+
&.is-year { width: 264px; }
|
|
110
|
+
&.is-time { width: 208px; }
|
|
111
|
+
&.is-datetime { width: 264px; }
|
|
112
|
+
|
|
113
|
+
max-width: calc(100vw - 16px);
|
|
114
|
+
|
|
115
|
+
@media (prefers-reduced-motion: reduce) { animation: none; }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Panel-internal elements (all portaled together)
|
|
119
|
+
|
|
120
|
+
.cny-datepicker-body {
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-direction: column;
|
|
123
|
+
gap: 8px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.cny-datepicker-header {
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
justify-content: space-between;
|
|
130
|
+
gap: $space-1;
|
|
131
|
+
margin-bottom: 2px;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.cny-datepicker-title {
|
|
135
|
+
font-weight: 600;
|
|
136
|
+
font-size: 0.8125rem;
|
|
137
|
+
letter-spacing: -0.003em;
|
|
138
|
+
color: var(--text-primary);
|
|
139
|
+
flex: 1 1 auto;
|
|
140
|
+
text-align: center;
|
|
141
|
+
font-variant-numeric: tabular-nums;
|
|
142
|
+
|
|
143
|
+
// Click-to-drill-up button variant
|
|
144
|
+
&.is-clickable {
|
|
145
|
+
display: inline-flex;
|
|
146
|
+
align-items: center;
|
|
147
|
+
justify-content: center;
|
|
148
|
+
gap: 4px;
|
|
149
|
+
height: 24px;
|
|
150
|
+
padding: 0 8px;
|
|
151
|
+
background: transparent;
|
|
152
|
+
border: 0;
|
|
153
|
+
border-radius: var(--radius-xs);
|
|
154
|
+
color: inherit;
|
|
155
|
+
font: inherit;
|
|
156
|
+
letter-spacing: inherit;
|
|
157
|
+
cursor: pointer;
|
|
158
|
+
transition: background-color 120ms ease, color 120ms ease;
|
|
159
|
+
|
|
160
|
+
&::after {
|
|
161
|
+
content: '';
|
|
162
|
+
width: 5px;
|
|
163
|
+
height: 5px;
|
|
164
|
+
border-right: 1.5px solid currentColor;
|
|
165
|
+
border-bottom: 1.5px solid currentColor;
|
|
166
|
+
transform: rotate(45deg) translateY(-1px);
|
|
167
|
+
opacity: 0.5;
|
|
168
|
+
transition: opacity 120ms ease, transform 120ms ease;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
&:hover {
|
|
172
|
+
background: var(--bg-surface-low);
|
|
173
|
+
color: var(--color-info);
|
|
174
|
+
|
|
175
|
+
&::after { opacity: 1; transform: rotate(45deg) translateY(0); }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
&:focus-visible {
|
|
179
|
+
outline: 2px solid var(--border-focus);
|
|
180
|
+
outline-offset: 1px;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.cny-datepicker-nav {
|
|
186
|
+
flex: none;
|
|
187
|
+
display: inline-flex;
|
|
188
|
+
align-items: center;
|
|
189
|
+
justify-content: center;
|
|
190
|
+
width: 24px;
|
|
191
|
+
height: 24px;
|
|
192
|
+
border: 0;
|
|
193
|
+
background: transparent;
|
|
194
|
+
color: var(--text-secondary);
|
|
195
|
+
border-radius: var(--radius-xs);
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
transition: background-color 120ms ease, color 120ms ease;
|
|
198
|
+
|
|
199
|
+
&:hover:not(:disabled) { background: var(--bg-surface-low); color: var(--text-primary); }
|
|
200
|
+
&:active:not(:disabled) { transform: scale(0.92); }
|
|
201
|
+
&:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 1px; }
|
|
202
|
+
&:disabled { opacity: 0.35; cursor: not-allowed; }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ── Calendar grid ────────────────────────────────────────────────
|
|
206
|
+
.cny-datepicker-grid { display: grid; gap: 1px; }
|
|
207
|
+
|
|
208
|
+
.cny-datepicker-weekdays,
|
|
209
|
+
.cny-datepicker-row {
|
|
210
|
+
display: grid;
|
|
211
|
+
grid-template-columns: repeat(7, 1fr);
|
|
212
|
+
gap: 1px;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.cny-datepicker-weekday {
|
|
216
|
+
text-align: center;
|
|
217
|
+
font-size: 0.625rem;
|
|
218
|
+
font-weight: 600;
|
|
219
|
+
letter-spacing: 0.04em;
|
|
220
|
+
text-transform: uppercase;
|
|
221
|
+
color: var(--text-muted);
|
|
222
|
+
padding: 4px 0 6px;
|
|
223
|
+
line-height: 1;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.cny-datepicker-cell {
|
|
227
|
+
position: relative;
|
|
228
|
+
display: inline-flex;
|
|
229
|
+
align-items: center;
|
|
230
|
+
justify-content: center;
|
|
231
|
+
height: 30px;
|
|
232
|
+
min-width: 0;
|
|
233
|
+
width: 100%;
|
|
234
|
+
border: 0;
|
|
235
|
+
background: transparent;
|
|
236
|
+
color: var(--text-primary);
|
|
237
|
+
font-size: 0.8125rem;
|
|
238
|
+
font-variant-numeric: tabular-nums;
|
|
239
|
+
border-radius: var(--radius-xs);
|
|
240
|
+
cursor: pointer;
|
|
241
|
+
transition: background-color 100ms ease, color 100ms ease;
|
|
242
|
+
|
|
243
|
+
&:hover:not(:disabled):not(.is-selected) { background: var(--bg-surface-low); }
|
|
244
|
+
&:focus-visible { outline: 2px solid var(--border-focus); outline-offset: -1px; z-index: 1; }
|
|
245
|
+
|
|
246
|
+
&.is-outside { color: var(--text-muted); opacity: 0.5; }
|
|
247
|
+
|
|
248
|
+
&.is-today:not(.is-selected) {
|
|
249
|
+
color: var(--color-info);
|
|
250
|
+
font-weight: 600;
|
|
251
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-info) 35%, transparent);
|
|
252
|
+
|
|
253
|
+
&::after {
|
|
254
|
+
content: '';
|
|
255
|
+
position: absolute;
|
|
256
|
+
bottom: 3px;
|
|
257
|
+
left: 50%;
|
|
258
|
+
width: 3px;
|
|
259
|
+
height: 3px;
|
|
260
|
+
border-radius: 50%;
|
|
261
|
+
background: currentColor;
|
|
262
|
+
transform: translateX(-50%);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
&.is-selected {
|
|
267
|
+
background: var(--color-info);
|
|
268
|
+
color: var(--color-info-on);
|
|
269
|
+
font-weight: 600;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
&:disabled { color: var(--text-disabled); cursor: not-allowed; opacity: 0.4; }
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ── Month / year chip grids ─────────────────────────────────────
|
|
276
|
+
.cny-datepicker-chip-grid {
|
|
277
|
+
display: grid;
|
|
278
|
+
grid-template-columns: repeat(3, 1fr);
|
|
279
|
+
gap: 4px;
|
|
280
|
+
padding: 2px 0;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.cny-datepicker-chip {
|
|
284
|
+
display: inline-flex;
|
|
285
|
+
align-items: center;
|
|
286
|
+
justify-content: center;
|
|
287
|
+
height: 34px;
|
|
288
|
+
padding: 0 $space-2;
|
|
289
|
+
border: 1px solid transparent;
|
|
290
|
+
background: transparent;
|
|
291
|
+
color: var(--text-primary);
|
|
292
|
+
font-size: 0.8125rem;
|
|
293
|
+
font-variant-numeric: tabular-nums;
|
|
294
|
+
border-radius: var(--radius-xs);
|
|
295
|
+
cursor: pointer;
|
|
296
|
+
transition: background-color 120ms ease, color 120ms ease;
|
|
297
|
+
|
|
298
|
+
&:hover:not(:disabled):not(.is-selected) { background: var(--bg-surface-low); }
|
|
299
|
+
&:focus-visible { outline: 2px solid var(--border-focus); outline-offset: -1px; }
|
|
300
|
+
|
|
301
|
+
&.is-outside { color: var(--text-muted); opacity: 0.55; }
|
|
302
|
+
|
|
303
|
+
// Marks the currently-viewed month/year when drilling up for navigation
|
|
304
|
+
&.is-current {
|
|
305
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-info) 40%, transparent);
|
|
306
|
+
color: var(--color-info);
|
|
307
|
+
font-weight: 600;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
&.is-selected {
|
|
311
|
+
background: var(--color-info);
|
|
312
|
+
color: var(--color-info-on);
|
|
313
|
+
font-weight: 600;
|
|
314
|
+
box-shadow: none;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
&:disabled { color: var(--text-disabled); cursor: not-allowed; opacity: 0.4; }
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ─────────────────────────────────────────────────────────────────
|
|
321
|
+
// Compact time picker — stepper digits + segmented AM/PM
|
|
322
|
+
// ─────────────────────────────────────────────────────────────────
|
|
323
|
+
|
|
324
|
+
.cny-dp-time {
|
|
325
|
+
display: flex;
|
|
326
|
+
flex-direction: column;
|
|
327
|
+
align-items: center;
|
|
328
|
+
gap: 6px;
|
|
329
|
+
padding: 6px 0 2px;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.cny-datepicker-panel.is-datetime .cny-dp-time {
|
|
333
|
+
padding-top: 8px;
|
|
334
|
+
border-top: 1px solid var(--hairline-color, var(--border-color));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.cny-dp-time-row {
|
|
338
|
+
display: inline-flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
gap: 4px;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.cny-dp-time-sep {
|
|
344
|
+
font-size: 1rem;
|
|
345
|
+
font-weight: 600;
|
|
346
|
+
color: var(--text-muted);
|
|
347
|
+
font-variant-numeric: tabular-nums;
|
|
348
|
+
padding: 0 1px;
|
|
349
|
+
user-select: none;
|
|
350
|
+
line-height: 1;
|
|
351
|
+
transform: translateY(-1px);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Editable time field — input on the left, stacked ▲/▼ on the right.
|
|
355
|
+
.cny-dp-timefield {
|
|
356
|
+
display: inline-flex;
|
|
357
|
+
align-items: stretch;
|
|
358
|
+
height: 30px;
|
|
359
|
+
background: var(--bg-surface-lowest);
|
|
360
|
+
border: 1px solid var(--border-color);
|
|
361
|
+
border-radius: var(--radius-xs);
|
|
362
|
+
overflow: hidden;
|
|
363
|
+
transition: border-color 120ms ease, box-shadow 120ms ease;
|
|
364
|
+
|
|
365
|
+
&:hover { border-color: var(--border-color-strong, var(--border-color)); }
|
|
366
|
+
|
|
367
|
+
&:focus-within {
|
|
368
|
+
border-color: var(--border-focus);
|
|
369
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--border-focus) 18%, transparent);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
&-input {
|
|
373
|
+
width: 32px;
|
|
374
|
+
height: 100%;
|
|
375
|
+
padding: 0 2px;
|
|
376
|
+
text-align: center;
|
|
377
|
+
font-family: var(--font-body);
|
|
378
|
+
font-size: 0.875rem;
|
|
379
|
+
font-weight: 600;
|
|
380
|
+
font-variant-numeric: tabular-nums;
|
|
381
|
+
color: var(--text-primary);
|
|
382
|
+
background: transparent;
|
|
383
|
+
border: 0;
|
|
384
|
+
outline: none;
|
|
385
|
+
line-height: 1;
|
|
386
|
+
|
|
387
|
+
// Hide spinners if a browser renders type=text as numeric
|
|
388
|
+
-moz-appearance: textfield;
|
|
389
|
+
&::-webkit-outer-spin-button,
|
|
390
|
+
&::-webkit-inner-spin-button {
|
|
391
|
+
-webkit-appearance: none;
|
|
392
|
+
margin: 0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
&::selection { background: color-mix(in srgb, var(--color-info) 30%, transparent); }
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
&-controls {
|
|
399
|
+
display: flex;
|
|
400
|
+
flex-direction: column;
|
|
401
|
+
border-left: 1px solid var(--hairline-color, var(--border-color));
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
&-btn {
|
|
405
|
+
flex: 1 1 0;
|
|
406
|
+
display: inline-flex;
|
|
407
|
+
align-items: center;
|
|
408
|
+
justify-content: center;
|
|
409
|
+
width: 18px;
|
|
410
|
+
padding: 0;
|
|
411
|
+
border: 0;
|
|
412
|
+
background: transparent;
|
|
413
|
+
color: var(--text-muted);
|
|
414
|
+
cursor: pointer;
|
|
415
|
+
transition: background-color 100ms ease, color 100ms ease;
|
|
416
|
+
|
|
417
|
+
&:first-child { border-bottom: 1px solid var(--hairline-color, var(--border-color)); }
|
|
418
|
+
|
|
419
|
+
&:hover {
|
|
420
|
+
background: var(--bg-surface-low);
|
|
421
|
+
color: var(--text-primary);
|
|
422
|
+
}
|
|
423
|
+
&:active { background: var(--bg-surface-edge, var(--bg-surface-low)); }
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.cny-dp-ampm {
|
|
428
|
+
display: inline-flex;
|
|
429
|
+
align-items: center;
|
|
430
|
+
margin-left: 6px;
|
|
431
|
+
padding: 2px;
|
|
432
|
+
background: var(--bg-surface-low);
|
|
433
|
+
border: 1px solid var(--hairline-color, var(--border-color));
|
|
434
|
+
border-radius: var(--radius-xs);
|
|
435
|
+
|
|
436
|
+
&-btn {
|
|
437
|
+
display: inline-flex;
|
|
438
|
+
align-items: center;
|
|
439
|
+
justify-content: center;
|
|
440
|
+
height: 20px;
|
|
441
|
+
min-width: 26px;
|
|
442
|
+
padding: 0 6px;
|
|
443
|
+
font-family: var(--font-body);
|
|
444
|
+
font-size: 0.6875rem;
|
|
445
|
+
font-weight: 600;
|
|
446
|
+
letter-spacing: 0.04em;
|
|
447
|
+
color: var(--text-muted);
|
|
448
|
+
background: transparent;
|
|
449
|
+
border: 0;
|
|
450
|
+
border-radius: 2px;
|
|
451
|
+
cursor: pointer;
|
|
452
|
+
transition: background-color 120ms ease, color 120ms ease;
|
|
453
|
+
|
|
454
|
+
&:hover:not(.is-active) { color: var(--text-primary); }
|
|
455
|
+
|
|
456
|
+
&.is-active {
|
|
457
|
+
background: var(--bg-surface-lowest);
|
|
458
|
+
color: var(--color-info);
|
|
459
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08), inset 0 0 0 1px var(--hairline-color, var(--border-color));
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
&:focus-visible {
|
|
463
|
+
outline: 2px solid var(--border-focus);
|
|
464
|
+
outline-offset: 1px;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ─────────────────────────────────────────────────────────────────
|
|
470
|
+
// Footer
|
|
471
|
+
// ─────────────────────────────────────────────────────────────────
|
|
472
|
+
|
|
473
|
+
.cny-datepicker-footer {
|
|
474
|
+
display: flex;
|
|
475
|
+
align-items: center;
|
|
476
|
+
gap: 4px;
|
|
477
|
+
margin-top: 8px;
|
|
478
|
+
padding-top: 6px;
|
|
479
|
+
border-top: 1px solid var(--hairline-color, var(--border-color));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.cny-datepicker-footer-spacer { flex: 1 1 auto; }
|
|
483
|
+
|
|
484
|
+
.cny-datepicker-action {
|
|
485
|
+
border: 0;
|
|
486
|
+
background: transparent;
|
|
487
|
+
color: var(--color-info);
|
|
488
|
+
font-family: var(--font-body);
|
|
489
|
+
font-size: 0.75rem;
|
|
490
|
+
font-weight: 500;
|
|
491
|
+
padding: 4px 8px;
|
|
492
|
+
border-radius: var(--radius-xs);
|
|
493
|
+
cursor: pointer;
|
|
494
|
+
transition: background-color 120ms ease;
|
|
495
|
+
|
|
496
|
+
&:hover:not(:disabled) { background: color-mix(in srgb, var(--color-info) 10%, transparent); }
|
|
497
|
+
&:disabled { color: var(--text-disabled); cursor: not-allowed; }
|
|
498
|
+
&:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 1px; }
|
|
499
|
+
|
|
500
|
+
&.is-primary {
|
|
501
|
+
background: var(--color-info);
|
|
502
|
+
color: var(--color-info-on);
|
|
503
|
+
padding: 4px 10px;
|
|
504
|
+
font-weight: 600;
|
|
505
|
+
|
|
506
|
+
&:hover:not(:disabled) {
|
|
507
|
+
background: var(--color-info-hover, var(--color-info));
|
|
508
|
+
filter: brightness(1.05);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
@@ -4,11 +4,19 @@
|
|
|
4
4
|
// All use the native element with class-based styling (no JS required).
|
|
5
5
|
|
|
6
6
|
// ── Checkbox & Radio ─────────────────────────────────────────────────────
|
|
7
|
+
// Two markup patterns supported per element:
|
|
8
|
+
// 1. Legacy native input — <input type="checkbox|radio" class="checkbox|radio">
|
|
9
|
+
// uses :checked / :indeterminate.
|
|
10
|
+
// 2. Radix React component — <button class="checkbox|radio"
|
|
11
|
+
// data-state="checked|unchecked|indeterminate"> with a Radix Indicator child.
|
|
7
12
|
.checkbox,
|
|
8
13
|
.radio {
|
|
9
14
|
appearance: none;
|
|
10
15
|
-webkit-appearance: none;
|
|
11
|
-
display: inline-
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
flex: none;
|
|
12
20
|
width: 16px;
|
|
13
21
|
height: 16px;
|
|
14
22
|
margin: 0;
|
|
@@ -22,42 +30,77 @@
|
|
|
22
30
|
border-color var(--transition-fast),
|
|
23
31
|
box-shadow var(--transition-fast);
|
|
24
32
|
|
|
25
|
-
&:hover:not(:disabled) { border-color: var(--color-primary); }
|
|
33
|
+
&:hover:not(:disabled):not([data-disabled]) { border-color: var(--color-primary); }
|
|
26
34
|
&:focus-visible {
|
|
27
35
|
outline: none;
|
|
28
36
|
box-shadow: 0 0 0 var(--ring-width) var(--ring-color);
|
|
29
37
|
}
|
|
30
|
-
&:disabled
|
|
38
|
+
&:disabled,
|
|
39
|
+
&[data-disabled] { opacity: 0.5; cursor: not-allowed; }
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
.checkbox {
|
|
34
43
|
border-radius: var(--radius-xs);
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
// Native input pattern — no child possible, so the check is painted via
|
|
46
|
+
// background-image on a colored fill.
|
|
47
|
+
&:not([data-state]) {
|
|
48
|
+
&:checked {
|
|
49
|
+
background-color: var(--color-primary);
|
|
50
|
+
border-color: var(--color-primary);
|
|
51
|
+
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'><path d='M3.5 8.5L6.5 11.5L12.5 5' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
|
|
52
|
+
background-repeat: no-repeat;
|
|
53
|
+
background-position: center;
|
|
54
|
+
}
|
|
55
|
+
&:indeterminate {
|
|
56
|
+
background-color: var(--color-primary);
|
|
57
|
+
border-color: var(--color-primary);
|
|
58
|
+
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' fill='white' rx='1'/></svg>");
|
|
59
|
+
background-repeat: no-repeat;
|
|
60
|
+
background-position: center;
|
|
61
|
+
}
|
|
42
62
|
}
|
|
43
63
|
|
|
44
|
-
|
|
64
|
+
// Radix pattern — Indicator child carries the Lucide check/minus icon.
|
|
65
|
+
&[data-state='checked'],
|
|
66
|
+
&[data-state='indeterminate'] {
|
|
45
67
|
background-color: var(--color-primary);
|
|
46
68
|
border-color: var(--color-primary);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Indicator wrapper (Radix renders this only when checked/indeterminate).
|
|
72
|
+
> * {
|
|
73
|
+
display: inline-flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
justify-content: center;
|
|
76
|
+
color: #fff;
|
|
77
|
+
line-height: 0;
|
|
50
78
|
}
|
|
51
79
|
}
|
|
52
80
|
|
|
53
81
|
.radio {
|
|
54
82
|
border-radius: 50%;
|
|
83
|
+
position: relative;
|
|
55
84
|
|
|
56
|
-
&:checked
|
|
85
|
+
&:checked,
|
|
86
|
+
&[data-state='checked'] {
|
|
57
87
|
border-color: var(--color-primary);
|
|
58
88
|
background-color: var(--bg-surface-lowest);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Native input uses a radial-gradient dot (no child element available).
|
|
92
|
+
&:not([data-state]):checked {
|
|
59
93
|
background-image: radial-gradient(circle, var(--color-primary) 0 4px, transparent 5px);
|
|
60
94
|
}
|
|
95
|
+
|
|
96
|
+
// Radix Indicator paints the dot — 8 px circle inside the 16 px ring.
|
|
97
|
+
> * {
|
|
98
|
+
display: block;
|
|
99
|
+
width: 8px;
|
|
100
|
+
height: 8px;
|
|
101
|
+
border-radius: 50%;
|
|
102
|
+
background: var(--color-primary);
|
|
103
|
+
}
|
|
61
104
|
}
|
|
62
105
|
|
|
63
106
|
// Label wrapper: <label class="control"><input class="checkbox"/> Label</label>
|
|
@@ -75,36 +118,55 @@
|
|
|
75
118
|
}
|
|
76
119
|
|
|
77
120
|
// ── Switch ──────────────────────────────────────────────────────────────
|
|
121
|
+
// Two markup patterns supported:
|
|
122
|
+
// 1. Legacy native input — <input type="checkbox" class="switch"> uses :checked
|
|
123
|
+
// and a ::before pseudo for the thumb.
|
|
124
|
+
// 2. Radix React component — <button class="switch" data-state="checked|unchecked">
|
|
125
|
+
// with a child <span class="switch-thumb">. Borders and animation are driven
|
|
126
|
+
// by [data-state] and the real .switch-thumb element.
|
|
78
127
|
.switch {
|
|
128
|
+
// Track sizing / colors shared by both patterns.
|
|
129
|
+
--switch-w: 36px;
|
|
130
|
+
--switch-h: 20px;
|
|
131
|
+
--switch-pad: 2px;
|
|
132
|
+
--switch-thumb: calc(var(--switch-h) - var(--switch-pad) * 2);
|
|
133
|
+
--switch-travel: calc(var(--switch-w) - var(--switch-thumb) - var(--switch-pad) * 2);
|
|
134
|
+
|
|
79
135
|
appearance: none;
|
|
80
136
|
-webkit-appearance: none;
|
|
81
137
|
position: relative;
|
|
82
|
-
|
|
83
|
-
|
|
138
|
+
display: inline-block;
|
|
139
|
+
flex: none;
|
|
140
|
+
width: var(--switch-w);
|
|
141
|
+
height: var(--switch-h);
|
|
84
142
|
margin: 0;
|
|
85
143
|
padding: 0;
|
|
86
144
|
background: var(--border-color-strong);
|
|
145
|
+
border: 0;
|
|
87
146
|
border-radius: var(--radius-full);
|
|
88
147
|
cursor: pointer;
|
|
89
148
|
vertical-align: middle;
|
|
90
149
|
transition: background-color var(--transition-fast);
|
|
91
150
|
|
|
151
|
+
// Legacy input pattern — pseudo-thumb.
|
|
92
152
|
&::before {
|
|
93
153
|
content: '';
|
|
94
154
|
position: absolute;
|
|
95
|
-
top:
|
|
96
|
-
left:
|
|
97
|
-
width:
|
|
98
|
-
height:
|
|
155
|
+
top: var(--switch-pad);
|
|
156
|
+
left: var(--switch-pad);
|
|
157
|
+
width: var(--switch-thumb);
|
|
158
|
+
height: var(--switch-thumb);
|
|
99
159
|
background: #fff;
|
|
100
160
|
border-radius: 50%;
|
|
101
161
|
box-shadow: var(--shadow-xs);
|
|
102
162
|
transition: transform var(--transition-fast);
|
|
103
163
|
}
|
|
164
|
+
// Hide the pseudo-thumb when a real .switch-thumb child exists (Radix).
|
|
165
|
+
&:has(.switch-thumb)::before { content: none; }
|
|
104
166
|
|
|
105
167
|
&:checked {
|
|
106
168
|
background: var(--color-primary);
|
|
107
|
-
&::before { transform: translateX(
|
|
169
|
+
&::before { transform: translateX(var(--switch-travel)); }
|
|
108
170
|
}
|
|
109
171
|
|
|
110
172
|
&:focus-visible {
|
|
@@ -113,6 +175,30 @@
|
|
|
113
175
|
}
|
|
114
176
|
|
|
115
177
|
&:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
178
|
+
|
|
179
|
+
// Radix pattern — drive track color from [data-state]; thumb slides via .switch-thumb.
|
|
180
|
+
&[data-state='checked'] { background: var(--color-primary); }
|
|
181
|
+
&[data-disabled] { opacity: 0.5; cursor: not-allowed; }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.switch-thumb {
|
|
185
|
+
display: block;
|
|
186
|
+
width: var(--switch-thumb, 16px);
|
|
187
|
+
height: var(--switch-thumb, 16px);
|
|
188
|
+
margin: var(--switch-pad, 2px);
|
|
189
|
+
background: #fff;
|
|
190
|
+
border-radius: 50%;
|
|
191
|
+
box-shadow: var(--shadow-xs);
|
|
192
|
+
transform: translateX(0);
|
|
193
|
+
transition: transform var(--transition-fast);
|
|
194
|
+
will-change: transform;
|
|
195
|
+
pointer-events: none;
|
|
196
|
+
|
|
197
|
+
&[data-state='checked'] { transform: translateX(var(--switch-travel, 14px)); }
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@media (prefers-reduced-motion: reduce) {
|
|
201
|
+
.switch, .switch::before, .switch-thumb { transition: none; }
|
|
116
202
|
}
|
|
117
203
|
|
|
118
204
|
// ── Select ──────────────────────────────────────────────────────────────
|