@attrus-ds/core 0.1.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.
Files changed (54) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +123 -0
  3. package/dist/assets/flags/Brazil-rounded.svg +16 -0
  4. package/dist/assets/flags/Brazil-square.svg +17 -0
  5. package/dist/assets/flags/Chile-rounded.svg +11 -0
  6. package/dist/assets/flags/Chile-square.svg +11 -0
  7. package/dist/assets/flags/Colombia-rounded.svg +10 -0
  8. package/dist/assets/flags/Colombia-square.svg +10 -0
  9. package/dist/assets/flags/EUA-rounded.svg +11 -0
  10. package/dist/assets/flags/EUA-square.svg +11 -0
  11. package/dist/assets/flags/EUR-rounded.svg +9 -0
  12. package/dist/assets/flags/EUR-square.svg +9 -0
  13. package/dist/assets/flags/Mexico-rounded.svg +19 -0
  14. package/dist/assets/flags/Mexico-square.svg +19 -0
  15. package/dist/assets/icon-attrus-dark.svg +3 -0
  16. package/dist/assets/icon-attrus-light.svg +3 -0
  17. package/dist/assets/logo-attrus-dark.svg +8 -0
  18. package/dist/assets/logo-attrus-light.svg +8 -0
  19. package/dist/css/components/buttons.css +301 -0
  20. package/dist/css/components/callout.css +195 -0
  21. package/dist/css/components/cards.css +272 -0
  22. package/dist/css/components/combobox.css +303 -0
  23. package/dist/css/components/empty-states.css +61 -0
  24. package/dist/css/components/inputs.css +1027 -0
  25. package/dist/css/components/number-input.css +138 -0
  26. package/dist/css/components/overlays.css +405 -0
  27. package/dist/css/components/primitives.css +305 -0
  28. package/dist/css/components/sidebar.css +179 -0
  29. package/dist/css/components/status-pills.css +207 -0
  30. package/dist/css/components/stepper.css +309 -0
  31. package/dist/css/components/table.css +600 -0
  32. package/dist/css/components/tabs.css +319 -0
  33. package/dist/css/components/toast.css +239 -0
  34. package/dist/css/components/tooltip.css +226 -0
  35. package/dist/css/components/topbar.css +148 -0
  36. package/dist/css/fonts.css +91 -0
  37. package/dist/css/index.css +23 -0
  38. package/dist/css/tokens.css +691 -0
  39. package/dist/fonts/LICENSE-JetBrainsMono.txt +93 -0
  40. package/dist/fonts/LICENSE-Lato.txt +93 -0
  41. package/dist/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  42. package/dist/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
  43. package/dist/fonts/jetbrains-mono-latin-600-normal.woff2 +0 -0
  44. package/dist/fonts/lato-latin-100-normal.woff2 +0 -0
  45. package/dist/fonts/lato-latin-300-italic.woff2 +0 -0
  46. package/dist/fonts/lato-latin-300-normal.woff2 +0 -0
  47. package/dist/fonts/lato-latin-400-italic.woff2 +0 -0
  48. package/dist/fonts/lato-latin-400-normal.woff2 +0 -0
  49. package/dist/fonts/lato-latin-700-italic.woff2 +0 -0
  50. package/dist/fonts/lato-latin-700-normal.woff2 +0 -0
  51. package/dist/fonts/lato-latin-900-normal.woff2 +0 -0
  52. package/dist/tokens/attrus-tokens.flat.json +1235 -0
  53. package/dist/tokens/attrus-tokens.json +1090 -0
  54. package/package.json +46 -0
@@ -0,0 +1,1027 @@
1
+ /* ============================================================
2
+ INPUTS & FORM CONTROLS — ATTRUS Design System
3
+ Component CSS — pure tokens, no hardcoded values.
4
+
5
+ Anatomy (vertical stack):
6
+ [label] .field > label
7
+ [field row] .field > .input | .group | .affix
8
+ [helper / error] .field > .help
9
+
10
+ Field variants:
11
+ .input base text input (size: md = 40px)
12
+ .input.is-error danger border + danger focus ring
13
+ .input.is-success success border + success focus ring
14
+ .input[disabled] opacity .5 + bg-subtle
15
+ .input.is-readonly no border, only value text
16
+
17
+ Sizes (apply to .input or wrapper):
18
+ .input-sm (32h, fs-sm)
19
+ default (40h, fs-md)
20
+ .input-lg (48h, fs-lg)
21
+
22
+ Wrappers (compose with .input):
23
+ .affix .lead | .trail leading / trailing icon
24
+ .group .fix.l | .fix.r prefix / suffix attached
25
+ .pill-search ⌘K-style search bar
26
+ .underline ghost / quiet input
27
+ .chips multi-select with embedded text input
28
+ .otp one-time-password digit cells
29
+
30
+ Other controls (siblings, not .input):
31
+ .select wrapper around <select>
32
+ .switch toggle
33
+ .check custom checkbox
34
+ .radio custom radio
35
+ .number-input number input with +/- buttons
36
+ .file-drop dropzone
37
+ .slider range with track + thumb
38
+ .date-trigger trigger button for date/time picker
39
+
40
+ Inverse:
41
+ .input.on-inverse dark-aware text input
42
+ surfaces with .dark inherit white-on-dark text
43
+ ============================================================ */
44
+
45
+
46
+ /* ----------------------------------------------------------------
47
+ 1. FIELD WRAPPER
48
+ ---------------------------------------------------------------- */
49
+ .field {
50
+ display: flex;
51
+ flex-direction: column;
52
+ gap: var(--space-2);
53
+ min-width: 0;
54
+ }
55
+ .field > label {
56
+ font-family: var(--font-sans);
57
+ font-size: var(--fs-sm);
58
+ font-weight: var(--w-semibold);
59
+ color: var(--color-foreground-default);
60
+ letter-spacing: var(--ls-body);
61
+ }
62
+ .field > label .req {
63
+ color: var(--color-feedback-danger-foreground-muted);
64
+ margin-left: 2px;
65
+ }
66
+ .field > label .opt {
67
+ color: var(--color-foreground-muted);
68
+ font-weight: var(--w-regular);
69
+ margin-left: var(--space-2);
70
+ font-size: var(--fs-xs);
71
+ }
72
+ .field > .help {
73
+ font-size: var(--fs-xs);
74
+ color: var(--color-foreground-muted);
75
+ display: flex;
76
+ gap: var(--space-2);
77
+ align-items: flex-start;
78
+ line-height: var(--lh-snug, 1.4);
79
+ }
80
+ .field > .help i, .field > .help svg,
81
+ .field > .help svg {
82
+ width: var(--icon-xs);
83
+ height: var(--icon-xs);
84
+ stroke-width: var(--icon-stroke);
85
+ flex-shrink: 0;
86
+ margin-top: 1px;
87
+ }
88
+ .field > .help.is-error { color: var(--color-feedback-danger-foreground); }
89
+ .field > .help.is-error i, .field > .help.is-error svg { color: var(--color-feedback-danger-foreground-muted); }
90
+ .field > .help.is-success { color: var(--color-feedback-success-foreground); }
91
+ .field > .help.is-success i, .field > .help.is-success svg{ color: var(--color-feedback-success-foreground-muted); }
92
+ .field > .help.is-warning { color: var(--color-feedback-warning-foreground); }
93
+ .field > .help.is-warning i, .field > .help.is-warning svg{ color: var(--color-feedback-warning-foreground-muted); }
94
+
95
+
96
+ /* ----------------------------------------------------------------
97
+ 2. BASE INPUT
98
+ ---------------------------------------------------------------- */
99
+ .input {
100
+ /* Box — default size = md (40px tall) */
101
+ width: 100%;
102
+ box-sizing: border-box;
103
+ height: 40px;
104
+ padding: 0 var(--space-4);
105
+ border: 1px solid var(--color-border-default);
106
+ border-radius: var(--radius-sm);
107
+ background: var(--color-background-surface);
108
+ color: var(--color-foreground-default);
109
+
110
+ /* Type */
111
+ font-family: var(--font-sans);
112
+ font-size: var(--fs-md);
113
+ font-weight: var(--w-regular);
114
+ line-height: 1;
115
+ letter-spacing: var(--ls-body);
116
+
117
+ /* Behavior */
118
+ outline: none;
119
+ transition:
120
+ background var(--transition-fast),
121
+ border-color var(--transition-fast),
122
+ color var(--transition-fast),
123
+ box-shadow var(--transition-fast);
124
+ }
125
+ .input::placeholder { color: var(--color-foreground-placeholder); }
126
+
127
+ .input:hover:not(:focus):not([disabled]):not(.is-error):not(.is-success) {
128
+ border-color: var(--color-border-strong);
129
+ }
130
+ .input:focus,
131
+ .input:focus-visible {
132
+ border-color: var(--color-border-focus);
133
+ box-shadow: var(--shadow-focus);
134
+ }
135
+ .input[disabled],
136
+ .input.is-disabled {
137
+ background: var(--color-background-subtle);
138
+ color: var(--color-foreground-placeholder);
139
+ cursor: not-allowed;
140
+ opacity: 0.7;
141
+ }
142
+ .input[readonly],
143
+ .input.is-readonly {
144
+ background: transparent;
145
+ border-color: transparent;
146
+ padding-left: 0;
147
+ padding-right: 0;
148
+ cursor: default;
149
+ }
150
+
151
+ /* Validation states */
152
+ .input.is-error { border-color: var(--color-border-danger); }
153
+ .input.is-error:focus { box-shadow: var(--shadow-focus-danger); }
154
+ .input.is-success { border-color: var(--color-border-success); }
155
+ .input.is-success:focus { box-shadow: var(--shadow-focus-success); }
156
+ .input.is-warning { border-color: var(--color-border-warning); }
157
+ .input.is-warning:focus { box-shadow: var(--shadow-focus-warning); }
158
+
159
+ /* Textarea */
160
+ textarea.input {
161
+ height: auto;
162
+ min-height: 88px;
163
+ padding: var(--space-3) var(--space-4);
164
+ resize: vertical;
165
+ line-height: var(--lh-normal, 1.5);
166
+ }
167
+
168
+
169
+ /* ----------------------------------------------------------------
170
+ 3. SIZES
171
+ ---------------------------------------------------------------- */
172
+ .input-sm {
173
+ height: 32px;
174
+ padding: 0 var(--space-3);
175
+ font-size: var(--fs-sm);
176
+ border-radius: var(--radius-xs);
177
+ }
178
+ .input-lg {
179
+ height: 48px;
180
+ padding: 0 var(--space-5);
181
+ font-size: var(--fs-lg);
182
+ border-radius: var(--radius-md);
183
+ }
184
+ textarea.input.input-sm { height: auto; min-height: 64px; padding: var(--space-2) var(--space-3); }
185
+ textarea.input.input-lg { height: auto; min-height: 120px; padding: var(--space-4) var(--space-5); }
186
+
187
+
188
+ /* ----------------------------------------------------------------
189
+ 4. AFFIX — leading / trailing icon
190
+ ---------------------------------------------------------------- */
191
+ .affix { position: relative; display: block; }
192
+ .affix .lead,
193
+ .affix .trail {
194
+ position: absolute;
195
+ top: 50%;
196
+ transform: translateY(-50%);
197
+ width: var(--icon-sm);
198
+ height: var(--icon-sm);
199
+ stroke-width: var(--icon-stroke);
200
+ color: var(--color-foreground-muted);
201
+ pointer-events: none;
202
+ flex-shrink: 0;
203
+ }
204
+ .affix .lead { left: var(--space-4); }
205
+ .affix .trail { right: var(--space-4); }
206
+
207
+ /* Pad the input to make room for the icon */
208
+ .affix.lead-pad > .input { padding-left: calc(var(--space-4) + var(--icon-sm) + var(--space-3)); }
209
+ .affix.trail-pad > .input { padding-right: calc(var(--space-4) + var(--icon-sm) + var(--space-3)); }
210
+
211
+ /* Trailing button (e.g. password reveal, clear) — clickable */
212
+ .affix .trail-btn {
213
+ position: absolute;
214
+ top: 50%;
215
+ right: var(--space-2);
216
+ transform: translateY(-50%);
217
+ width: 28px;
218
+ height: 28px;
219
+ display: inline-flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ border: 0;
223
+ border-radius: var(--radius-xs);
224
+ background: transparent;
225
+ color: var(--color-foreground-muted);
226
+ cursor: pointer;
227
+ transition: background var(--transition-fast), color var(--transition-fast);
228
+ }
229
+ .affix .trail-btn:hover { background: var(--color-background-hover); color: var(--color-foreground-default); }
230
+ .affix .trail-btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
231
+ .affix .trail-btn > i, .affix .trail-btn > svg,
232
+ .affix .trail-btn > svg { width: var(--icon-xs); height: var(--icon-xs); stroke-width: var(--icon-stroke); }
233
+
234
+
235
+ /* ----------------------------------------------------------------
236
+ 5. PREFIX / SUFFIX GROUP
237
+ ---------------------------------------------------------------- */
238
+ .group {
239
+ display: flex;
240
+ align-items: stretch;
241
+ width: 100%;
242
+ height: 40px;
243
+ border: 1px solid var(--color-border-default);
244
+ border-radius: var(--radius-sm);
245
+ background: var(--color-background-surface);
246
+ overflow: hidden;
247
+ transition:
248
+ border-color var(--transition-fast),
249
+ box-shadow var(--transition-fast);
250
+ }
251
+ .group:focus-within {
252
+ border-color: var(--color-border-focus);
253
+ box-shadow: var(--shadow-focus);
254
+ }
255
+ .group .fix {
256
+ display: flex;
257
+ align-items: center;
258
+ gap: var(--space-2);
259
+ padding: 0 var(--space-4);
260
+ background: var(--color-background-subtle);
261
+ color: var(--color-foreground-muted);
262
+ font-family: var(--font-mono);
263
+ font-size: var(--fs-sm);
264
+ flex-shrink: 0;
265
+ }
266
+ .group .fix.l { border-right: 1px solid var(--color-border-default); }
267
+ .group .fix.r { border-left: 1px solid var(--color-border-default); }
268
+ .group > input {
269
+ flex: 1;
270
+ min-width: 0;
271
+ border: 0;
272
+ outline: none;
273
+ padding: 0 var(--space-4);
274
+ background: transparent;
275
+ color: var(--color-foreground-default);
276
+ font-family: var(--font-sans);
277
+ font-size: var(--fs-md);
278
+ }
279
+ .group > input::placeholder { color: var(--color-foreground-placeholder); }
280
+ .group.is-error { border-color: var(--color-border-danger); }
281
+ .group.is-error:focus-within { box-shadow: var(--shadow-focus-danger); }
282
+
283
+
284
+ /* ----------------------------------------------------------------
285
+ 6. PILL SEARCH (⌘K-style)
286
+ ---------------------------------------------------------------- */
287
+ .pill-search {
288
+ display: flex;
289
+ align-items: center;
290
+ gap: var(--space-3);
291
+ height: 40px;
292
+ padding: 0 var(--space-4);
293
+ border: 1px solid var(--color-border-default);
294
+ border-radius: var(--radius-pill);
295
+ background: var(--color-background-surface);
296
+ transition:
297
+ border-color var(--transition-fast),
298
+ box-shadow var(--transition-fast),
299
+ background var(--transition-fast);
300
+ }
301
+ .pill-search:hover { border-color: var(--color-border-strong); }
302
+ .pill-search:focus-within {
303
+ border-color: var(--color-border-focus);
304
+ box-shadow: var(--shadow-focus);
305
+ }
306
+ .pill-search > i, .pill-search > svg,
307
+ .pill-search > svg {
308
+ width: var(--icon-sm);
309
+ height: var(--icon-sm);
310
+ stroke-width: var(--icon-stroke);
311
+ color: var(--color-foreground-muted);
312
+ flex-shrink: 0;
313
+ }
314
+ .pill-search > input {
315
+ flex: 1;
316
+ min-width: 0;
317
+ border: 0;
318
+ outline: none;
319
+ background: transparent;
320
+ font-family: var(--font-sans);
321
+ font-size: var(--fs-md);
322
+ color: var(--color-foreground-default);
323
+ }
324
+ .pill-search > input::placeholder { color: var(--color-foreground-placeholder); }
325
+ .kbd {
326
+ display: inline-flex;
327
+ align-items: center;
328
+ font-family: var(--font-mono);
329
+ font-size: var(--fs-xxs);
330
+ font-weight: var(--w-semibold);
331
+ color: var(--color-foreground-muted);
332
+ background: var(--color-background-subtle);
333
+ border: 1px solid var(--color-border-default);
334
+ border-radius: var(--radius-2xs);
335
+ padding: 1px var(--space-2);
336
+ letter-spacing: var(--ls-caps);
337
+ }
338
+
339
+
340
+ /* ----------------------------------------------------------------
341
+ 7. UNDERLINE — quiet / ghost input
342
+ ---------------------------------------------------------------- */
343
+ .input.underline {
344
+ border: 0;
345
+ border-bottom: 1px solid var(--color-border-default);
346
+ border-radius: 0;
347
+ background: transparent;
348
+ padding: var(--space-2) 0;
349
+ }
350
+ .input.underline:focus,
351
+ .input.underline:focus-visible {
352
+ border-bottom-color: var(--color-border-focus);
353
+ box-shadow: none;
354
+ }
355
+ .input.underline.is-error { border-bottom-color: var(--color-border-danger); }
356
+
357
+
358
+ /* ----------------------------------------------------------------
359
+ 8. CHIPS — multi-select / token input
360
+ ---------------------------------------------------------------- */
361
+ .chips {
362
+ display: flex;
363
+ flex-wrap: wrap;
364
+ gap: 6px;
365
+ align-items: center;
366
+ min-height: 40px;
367
+ padding: 5px 8px;
368
+ border: 1px solid var(--color-border-default);
369
+ border-radius: var(--radius-sm);
370
+ background: var(--color-background-surface);
371
+ transition:
372
+ border-color var(--transition-fast),
373
+ box-shadow var(--transition-fast);
374
+ }
375
+ .chips:hover { border-color: var(--color-border-strong); }
376
+ .chips:focus-within {
377
+ border-color: var(--color-border-focus);
378
+ box-shadow: var(--shadow-focus);
379
+ }
380
+ .chip {
381
+ display: inline-flex;
382
+ align-items: center;
383
+ gap: 4px;
384
+ height: 26px;
385
+ padding: 0 4px 0 10px;
386
+ background: var(--color-background-subtle);
387
+ color: var(--color-foreground-default);
388
+ border: 1px solid var(--color-border-default);
389
+ border-radius: var(--radius-xs);
390
+ font-family: var(--font-sans);
391
+ font-size: var(--fs-xs);
392
+ font-weight: var(--w-medium);
393
+ line-height: 1;
394
+ white-space: nowrap;
395
+ flex-shrink: 0;
396
+ transition: background var(--transition-fast), border-color var(--transition-fast);
397
+ }
398
+ .chip:hover { background: var(--color-background-canvas); border-color: var(--color-border-strong); }
399
+ .chip-x {
400
+ display: inline-flex;
401
+ align-items: center;
402
+ justify-content: center;
403
+ width: 18px;
404
+ height: 18px;
405
+ margin-left: 2px;
406
+ border: 0;
407
+ padding: 0;
408
+ background: transparent;
409
+ cursor: pointer;
410
+ border-radius: var(--radius-xs);
411
+ color: var(--color-foreground-muted);
412
+ transition: background var(--transition-fast), color var(--transition-fast);
413
+ }
414
+ .chip-x svg, .chip-x i { width: 11px; height: 11px; stroke-width: 2.25; }
415
+ .chip-x:hover { background: var(--color-border-default); color: var(--color-foreground-default); }
416
+ .chips > input {
417
+ flex: 1;
418
+ min-width: 80px;
419
+ height: 26px;
420
+ border: 0;
421
+ outline: none;
422
+ background: transparent;
423
+ font-family: var(--font-sans);
424
+ font-size: var(--fs-sm);
425
+ padding: 0 6px;
426
+ color: var(--color-foreground-default);
427
+ }
428
+ .chips > input::placeholder { color: var(--color-foreground-placeholder); }
429
+
430
+
431
+ /* ----------------------------------------------------------------
432
+ 9. SELECT (native)
433
+ ---------------------------------------------------------------- */
434
+ .select {
435
+ position: relative;
436
+ display: block;
437
+ }
438
+ .select > select.input {
439
+ appearance: none;
440
+ -webkit-appearance: none;
441
+ cursor: pointer;
442
+ padding-right: calc(var(--space-4) + var(--icon-sm) + var(--space-3));
443
+ background-image: none;
444
+ }
445
+ .select > .chev {
446
+ position: absolute;
447
+ top: 50%;
448
+ right: var(--space-4);
449
+ transform: translateY(-50%);
450
+ width: var(--icon-sm);
451
+ height: var(--icon-sm);
452
+ stroke-width: var(--icon-stroke);
453
+ color: var(--color-foreground-muted);
454
+ pointer-events: none;
455
+ }
456
+
457
+
458
+ /* ----------------------------------------------------------------
459
+ 10. NUMBER INPUT (with +/- buttons)
460
+ ---------------------------------------------------------------- */
461
+ .number-input {
462
+ display: inline-flex;
463
+ align-items: stretch;
464
+ height: 40px;
465
+ border: 1px solid var(--color-border-default);
466
+ border-radius: var(--radius-sm);
467
+ background: var(--color-background-surface);
468
+ overflow: hidden;
469
+ transition:
470
+ border-color var(--transition-fast),
471
+ box-shadow var(--transition-fast);
472
+ }
473
+ .number-input:focus-within {
474
+ border-color: var(--color-border-focus);
475
+ box-shadow: var(--shadow-focus);
476
+ }
477
+ .number-input button {
478
+ width: 36px;
479
+ border: 0;
480
+ background: var(--color-background-subtle);
481
+ color: var(--color-foreground-default);
482
+ cursor: pointer;
483
+ display: inline-flex;
484
+ align-items: center;
485
+ justify-content: center;
486
+ font-family: var(--font-sans);
487
+ font-size: var(--fs-md);
488
+ font-weight: var(--w-semibold);
489
+ transition: background var(--transition-fast);
490
+ }
491
+ .number-input button:hover { background: var(--color-background-hover); }
492
+ .number-input button:active { background: var(--color-background-press); }
493
+ .number-input button:disabled { opacity: 0.4; cursor: not-allowed; }
494
+ .number-input button.minus { border-right: 1px solid var(--color-border-default); }
495
+ .number-input button.plus { border-left: 1px solid var(--color-border-default); }
496
+ .number-input > input {
497
+ width: 64px;
498
+ border: 0;
499
+ outline: none;
500
+ text-align: center;
501
+ font-family: var(--font-mono);
502
+ font-size: var(--fs-md);
503
+ font-weight: var(--w-semibold);
504
+ background: transparent;
505
+ color: var(--color-foreground-default);
506
+ -moz-appearance: textfield;
507
+ }
508
+ .number-input > input::-webkit-outer-spin-button,
509
+ .number-input > input::-webkit-inner-spin-button { appearance: none; margin: 0; }
510
+
511
+
512
+ /* ----------------------------------------------------------------
513
+ 11. OTP — one-time password digit cells
514
+ ---------------------------------------------------------------- */
515
+ .otp { display: inline-flex; gap: var(--space-2); }
516
+ .otp > input {
517
+ width: 40px;
518
+ height: 48px;
519
+ text-align: center;
520
+ font-family: var(--font-mono);
521
+ font-size: var(--fs-xl);
522
+ font-weight: var(--w-semibold);
523
+ border: 1px solid var(--color-border-default);
524
+ border-radius: var(--radius-sm);
525
+ outline: none;
526
+ background: var(--color-background-surface);
527
+ color: var(--color-foreground-default);
528
+ transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
529
+ }
530
+ .otp > input:focus { border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }
531
+ .otp > input.is-error { border-color: var(--color-border-danger); }
532
+
533
+
534
+ /* ----------------------------------------------------------------
535
+ 12. SWITCH (toggle)
536
+ ---------------------------------------------------------------- */
537
+ .switch {
538
+ display: inline-flex;
539
+ align-items: center;
540
+ gap: var(--space-3);
541
+ cursor: pointer;
542
+ user-select: none;
543
+ }
544
+ .switch-track {
545
+ position: relative;
546
+ width: 36px;
547
+ height: 20px;
548
+ border-radius: var(--radius-pill);
549
+ background: var(--color-neutral-300);
550
+ transition: background var(--transition-base);
551
+ flex-shrink: 0;
552
+ }
553
+ .switch-thumb {
554
+ position: absolute;
555
+ top: 2px;
556
+ left: 2px;
557
+ width: 16px;
558
+ height: 16px;
559
+ border-radius: var(--radius-pill);
560
+ background: var(--color-pure-white);
561
+ box-shadow: 0 1px 2px color-mix(in srgb, var(--color-pure-black) 18%, transparent);
562
+ transition: transform var(--transition-base);
563
+ }
564
+ .switch input { position: absolute; opacity: 0; pointer-events: none; }
565
+ .switch input:checked ~ .switch-track { background: var(--color-brand-secondary-500); }
566
+ .switch input:checked ~ .switch-track .switch-thumb { transform: translateX(16px); }
567
+ .switch input:focus-visible ~ .switch-track { box-shadow: var(--shadow-focus); }
568
+ .switch input:disabled ~ .switch-track { opacity: 0.5; cursor: not-allowed; }
569
+ .switch-label {
570
+ font-family: var(--font-sans);
571
+ font-size: var(--fs-sm);
572
+ color: var(--color-foreground-default);
573
+ }
574
+
575
+
576
+ /* ----------------------------------------------------------------
577
+ 13. CHECKBOX
578
+ ---------------------------------------------------------------- */
579
+ .check {
580
+ display: inline-flex;
581
+ align-items: center;
582
+ gap: var(--space-3);
583
+ cursor: pointer;
584
+ user-select: none;
585
+ font-family: var(--font-sans);
586
+ font-size: var(--fs-sm);
587
+ color: var(--color-foreground-default);
588
+ }
589
+ .check input { position: absolute; opacity: 0; pointer-events: none; }
590
+ .check-box {
591
+ width: 18px;
592
+ height: 18px;
593
+ border: 1px solid var(--color-border-strong);
594
+ border-radius: var(--radius-xs);
595
+ background: var(--color-background-surface);
596
+ display: inline-flex;
597
+ align-items: center;
598
+ justify-content: center;
599
+ flex-shrink: 0;
600
+ color: var(--color-pure-white);
601
+ transition:
602
+ background var(--transition-fast),
603
+ border-color var(--transition-fast),
604
+ box-shadow var(--transition-fast);
605
+ }
606
+ .check-box svg { width: 14px; height: 14px; stroke-width: 3; opacity: 0; transform: scale(0.8); transition: opacity var(--transition-fast), transform var(--transition-fast); }
607
+ .check input:checked ~ .check-box {
608
+ background: var(--color-brand-secondary-500);
609
+ border-color: var(--color-brand-secondary-500);
610
+ }
611
+ .check input:checked ~ .check-box svg { opacity: 1; transform: scale(1); }
612
+ .check input:indeterminate ~ .check-box {
613
+ background: var(--color-brand-secondary-500);
614
+ border-color: var(--color-brand-secondary-500);
615
+ }
616
+ .check input:indeterminate ~ .check-box::after {
617
+ content: '';
618
+ width: 10px;
619
+ height: 2px;
620
+ background: var(--color-pure-white);
621
+ border-radius: 1px;
622
+ }
623
+ .check input:indeterminate ~ .check-box svg { display: none; }
624
+ .check input:focus-visible ~ .check-box { box-shadow: var(--shadow-focus); }
625
+ .check input:disabled ~ .check-box { opacity: 0.5; cursor: not-allowed; }
626
+ .check.is-error .check-box { border-color: var(--color-border-danger); }
627
+
628
+
629
+ /* ----------------------------------------------------------------
630
+ 14. RADIO
631
+ ---------------------------------------------------------------- */
632
+ .radio {
633
+ display: inline-flex;
634
+ align-items: center;
635
+ gap: var(--space-3);
636
+ cursor: pointer;
637
+ user-select: none;
638
+ font-family: var(--font-sans);
639
+ font-size: var(--fs-sm);
640
+ color: var(--color-foreground-default);
641
+ }
642
+ .radio input { position: absolute; opacity: 0; pointer-events: none; }
643
+ .radio-dot {
644
+ width: 18px;
645
+ height: 18px;
646
+ border: 1px solid var(--color-border-strong);
647
+ border-radius: var(--radius-pill);
648
+ background: var(--color-background-surface);
649
+ display: inline-flex;
650
+ align-items: center;
651
+ justify-content: center;
652
+ flex-shrink: 0;
653
+ transition:
654
+ border-color var(--transition-fast),
655
+ box-shadow var(--transition-fast);
656
+ }
657
+ .radio-dot::after {
658
+ content: '';
659
+ width: 8px;
660
+ height: 8px;
661
+ border-radius: var(--radius-pill);
662
+ background: var(--color-brand-secondary-500);
663
+ transform: scale(0);
664
+ transition: transform var(--transition-fast);
665
+ }
666
+ .radio input:checked ~ .radio-dot { border-color: var(--color-brand-secondary-500); }
667
+ .radio input:checked ~ .radio-dot::after { transform: scale(1); }
668
+ .radio input:focus-visible ~ .radio-dot { box-shadow: var(--shadow-focus); }
669
+ .radio input:disabled ~ .radio-dot { opacity: 0.5; cursor: not-allowed; }
670
+
671
+
672
+ /* ----------------------------------------------------------------
673
+ 15. DATE / TIME TRIGGER
674
+ ---------------------------------------------------------------- */
675
+ .date-trigger {
676
+ display: inline-flex;
677
+ align-items: center;
678
+ justify-content: space-between;
679
+ gap: var(--space-3);
680
+ width: 100%;
681
+ height: 40px;
682
+ padding: 0 var(--space-4);
683
+ border: 1px solid var(--color-border-default);
684
+ border-radius: var(--radius-sm);
685
+ background: var(--color-background-surface);
686
+ color: var(--color-foreground-default);
687
+ font-family: var(--font-sans);
688
+ font-size: var(--fs-md);
689
+ cursor: pointer;
690
+ transition:
691
+ border-color var(--transition-fast),
692
+ box-shadow var(--transition-fast),
693
+ background var(--transition-fast);
694
+ }
695
+ .date-trigger:hover { border-color: var(--color-border-strong); }
696
+ .date-trigger:focus,
697
+ .date-trigger:focus-visible {
698
+ outline: none;
699
+ border-color: var(--color-border-focus);
700
+ box-shadow: var(--shadow-focus);
701
+ }
702
+ .date-trigger.is-error { border-color: var(--color-border-danger); }
703
+ .date-trigger.is-error:focus,
704
+ .date-trigger.is-error:focus-visible { box-shadow: var(--shadow-focus-danger); }
705
+ .date-trigger .placeholder { color: var(--color-foreground-placeholder); }
706
+ .date-trigger > i, .date-trigger > svg,
707
+ .date-trigger > svg {
708
+ width: var(--icon-sm);
709
+ height: var(--icon-sm);
710
+ stroke-width: var(--icon-stroke);
711
+ color: var(--color-foreground-muted);
712
+ flex-shrink: 0;
713
+ }
714
+
715
+
716
+ /* ----------------------------------------------------------------
717
+ 16. FILE DROPZONE
718
+ ---------------------------------------------------------------- */
719
+ .file-drop {
720
+ display: flex;
721
+ flex-direction: column;
722
+ align-items: center;
723
+ justify-content: center;
724
+ gap: var(--space-3);
725
+ padding: var(--space-7) var(--space-5);
726
+ border: 1.5px dashed var(--color-border-default);
727
+ border-radius: var(--radius-md);
728
+ background: var(--color-background-canvas);
729
+ color: var(--color-foreground-secondary);
730
+ text-align: center;
731
+ cursor: pointer;
732
+ transition:
733
+ border-color var(--transition-fast),
734
+ background var(--transition-fast);
735
+ }
736
+ .file-drop:hover {
737
+ border-color: var(--color-border-focus);
738
+ background: var(--color-background-selected);
739
+ }
740
+ .file-drop.is-dragging {
741
+ border-color: var(--color-border-focus);
742
+ background: var(--color-background-selected);
743
+ border-style: solid;
744
+ }
745
+ .file-drop > i, .file-drop > svg,
746
+ .file-drop > svg {
747
+ width: 28px;
748
+ height: 28px;
749
+ stroke-width: var(--icon-stroke);
750
+ color: var(--color-foreground-muted);
751
+ }
752
+ .file-drop .ttl { font-size: var(--fs-md); font-weight: var(--w-semibold); color: var(--color-foreground-default); }
753
+ .file-drop .sub { font-size: var(--fs-xs); color: var(--color-foreground-muted); }
754
+ .file-drop .anchor { color: var(--color-foreground-highlight); font-weight: var(--w-semibold); }
755
+
756
+ /* ----------------------------------------------------------------
757
+ 16b. FILE LIST (post-upload state)
758
+ ---------------------------------------------------------------- */
759
+ .file-list {
760
+ width: 100%;
761
+ background: var(--color-background-surface);
762
+ border: 1px solid var(--color-border-default);
763
+ border-radius: var(--radius-md);
764
+ overflow: hidden;
765
+ }
766
+ .file-list-head {
767
+ display: flex;
768
+ align-items: center;
769
+ justify-content: space-between;
770
+ padding: var(--space-3) var(--space-5);
771
+ background: var(--color-background-subtle);
772
+ border-bottom: 1px solid var(--color-border-subtle);
773
+ font-family: var(--font-mono);
774
+ font-size: var(--fs-xxs);
775
+ color: var(--color-foreground-muted);
776
+ text-transform: uppercase;
777
+ letter-spacing: var(--ls-caps);
778
+ }
779
+ .file-list-action {
780
+ display: inline-flex;
781
+ align-items: center;
782
+ gap: var(--space-2);
783
+ background: transparent;
784
+ border: 0;
785
+ padding: 0;
786
+ font-family: var(--font-mono);
787
+ font-size: var(--fs-xxs);
788
+ color: var(--color-foreground-highlight);
789
+ text-transform: uppercase;
790
+ letter-spacing: var(--ls-caps);
791
+ cursor: pointer;
792
+ transition: color var(--transition-fast);
793
+ }
794
+ .file-list-action:hover { color: var(--color-brand-secondary-700); }
795
+
796
+ .file-rows {
797
+ list-style: none;
798
+ margin: 0;
799
+ padding: 0;
800
+ }
801
+ .file-row {
802
+ display: grid;
803
+ grid-template-columns: auto 1fr auto auto;
804
+ align-items: center;
805
+ gap: var(--space-4);
806
+ padding: var(--space-4) var(--space-5);
807
+ border-bottom: 1px solid var(--color-border-subtle);
808
+ transition: background var(--transition-fast);
809
+ }
810
+ .file-row:last-child { border-bottom: 0; }
811
+ .file-row:hover { background: var(--color-background-canvas); }
812
+
813
+ .file-icon {
814
+ position: relative;
815
+ width: 40px;
816
+ height: 48px;
817
+ display: flex;
818
+ align-items: center;
819
+ justify-content: center;
820
+ flex-shrink: 0;
821
+ border-radius: var(--radius-xs);
822
+ background: var(--color-background-subtle);
823
+ color: var(--color-foreground-muted);
824
+ }
825
+ .file-icon[data-kind="pdf"] { background: color-mix(in srgb, var(--color-danger-500) 8%, var(--color-background-surface)); color: var(--color-feedback-danger-foreground); }
826
+ .file-icon[data-kind="img"] { background: color-mix(in srgb, var(--color-brand-secondary-500) 8%, var(--color-background-surface)); color: var(--color-brand-secondary-700); }
827
+ .file-icon[data-kind="csv"] { background: color-mix(in srgb, var(--color-success-500) 8%, var(--color-background-surface)); color: var(--color-feedback-success-foreground); }
828
+ .file-icon[data-kind="doc"] { background: color-mix(in srgb, var(--color-brand-secondary-500) 10%, var(--color-background-surface)); color: var(--color-brand-secondary-700); }
829
+
830
+ .file-icon-tag {
831
+ position: absolute;
832
+ bottom: 4px;
833
+ left: 50%;
834
+ transform: translateX(-50%);
835
+ font-family: var(--font-mono);
836
+ font-size: 8px;
837
+ font-weight: var(--w-bold);
838
+ letter-spacing: 0.04em;
839
+ line-height: 1;
840
+ color: currentColor;
841
+ background: var(--color-background-surface);
842
+ padding: 1px 3px;
843
+ border-radius: 2px;
844
+ }
845
+
846
+ .file-meta {
847
+ min-width: 0;
848
+ display: flex;
849
+ flex-direction: column;
850
+ gap: 2px;
851
+ }
852
+ .file-name {
853
+ font-family: var(--font-sans);
854
+ font-size: var(--fs-sm);
855
+ font-weight: var(--w-semibold);
856
+ color: var(--color-foreground-default);
857
+ white-space: nowrap;
858
+ overflow: hidden;
859
+ text-overflow: ellipsis;
860
+ }
861
+ .file-sub {
862
+ font-family: var(--font-mono);
863
+ font-size: var(--fs-xxs);
864
+ color: var(--color-foreground-muted);
865
+ }
866
+ .file-sub.err { color: var(--color-feedback-danger-foreground); }
867
+
868
+ .file-progress {
869
+ margin-top: 6px;
870
+ height: 3px;
871
+ background: var(--color-background-subtle);
872
+ border-radius: var(--radius-pill);
873
+ overflow: hidden;
874
+ }
875
+ .file-progress > span {
876
+ display: block;
877
+ height: 100%;
878
+ background: var(--color-brand-secondary-500);
879
+ border-radius: inherit;
880
+ transition: width var(--transition-fast);
881
+ }
882
+
883
+ .file-status {
884
+ display: inline-flex;
885
+ align-items: center;
886
+ gap: 4px;
887
+ padding: 3px var(--space-2);
888
+ border-radius: var(--radius-pill);
889
+ font-family: var(--font-mono);
890
+ font-size: var(--fs-xxs);
891
+ font-weight: var(--w-semibold);
892
+ text-transform: uppercase;
893
+ letter-spacing: var(--ls-caps);
894
+ white-space: nowrap;
895
+ }
896
+ .file-status.ok { color: var(--color-feedback-success-foreground); background: var(--color-feedback-success-background); }
897
+ .file-status.pending { color: var(--color-brand-secondary-700); background: color-mix(in srgb, var(--color-brand-secondary-500) 10%, transparent); }
898
+ .file-status.err { color: var(--color-feedback-danger-foreground); background: var(--color-feedback-danger-background); cursor: pointer; }
899
+
900
+ .file-remove {
901
+ width: 28px;
902
+ height: 28px;
903
+ display: inline-flex;
904
+ align-items: center;
905
+ justify-content: center;
906
+ background: transparent;
907
+ border: 0;
908
+ border-radius: var(--radius-xs);
909
+ color: var(--color-foreground-muted);
910
+ cursor: pointer;
911
+ transition: background var(--transition-fast), color var(--transition-fast);
912
+ }
913
+ .file-remove:hover { background: var(--color-background-subtle); color: var(--color-foreground-default); }
914
+ .file-remove > svg, .file-remove > i { width: 14px; height: 14px; }
915
+
916
+
917
+ /* ----------------------------------------------------------------
918
+ 17. SLIDER (range)
919
+ ---------------------------------------------------------------- */
920
+ .slider {
921
+ -webkit-appearance: none;
922
+ appearance: none;
923
+ width: 100%;
924
+ height: 24px;
925
+ background: transparent;
926
+ cursor: pointer;
927
+ }
928
+ .slider::-webkit-slider-runnable-track {
929
+ height: 4px;
930
+ background: var(--color-neutral-150);
931
+ border-radius: var(--radius-pill);
932
+ }
933
+ .slider::-moz-range-track {
934
+ height: 4px;
935
+ background: var(--color-neutral-150);
936
+ border-radius: var(--radius-pill);
937
+ }
938
+ .slider::-webkit-slider-thumb {
939
+ -webkit-appearance: none;
940
+ appearance: none;
941
+ width: 18px;
942
+ height: 18px;
943
+ border-radius: var(--radius-pill);
944
+ background: var(--color-brand-secondary-500);
945
+ border: 2px solid var(--color-pure-white);
946
+ box-shadow: 0 1px 4px color-mix(in srgb, var(--color-shadow-tint) 22%, transparent);
947
+ margin-top: -7px;
948
+ transition: transform var(--transition-fast);
949
+ }
950
+ .slider::-moz-range-thumb {
951
+ width: 18px;
952
+ height: 18px;
953
+ border-radius: var(--radius-pill);
954
+ background: var(--color-brand-secondary-500);
955
+ border: 2px solid var(--color-pure-white);
956
+ box-shadow: 0 1px 4px color-mix(in srgb, var(--color-shadow-tint) 22%, transparent);
957
+ }
958
+ .slider:focus-visible::-webkit-slider-thumb {
959
+ box-shadow: var(--shadow-focus);
960
+ }
961
+ .slider::-webkit-slider-thumb:hover { transform: scale(1.1); }
962
+
963
+
964
+ /* ----------------------------------------------------------------
965
+ 18. ON INVERSE — dark surfaces
966
+ ---------------------------------------------------------------- */
967
+ .input.on-inverse,
968
+ .dark .input,
969
+ .dark .group,
970
+ .dark .pill-search,
971
+ .dark .chips,
972
+ .dark .number-input,
973
+ .dark .date-trigger {
974
+ background: color-mix(in srgb, var(--color-pure-white) 6%, transparent);
975
+ border-color: var(--color-border-inverse);
976
+ color: var(--color-foreground-inverse-default);
977
+ }
978
+ .input.on-inverse::placeholder,
979
+ .dark .input::placeholder,
980
+ .dark .group > input::placeholder,
981
+ .dark .pill-search > input::placeholder,
982
+ .dark .chips > input::placeholder {
983
+ color: var(--color-foreground-inverse-placeholder);
984
+ }
985
+ .dark .group > input,
986
+ .dark .pill-search > input,
987
+ .dark .chips > input,
988
+ .dark .number-input > input,
989
+ .dark .date-trigger { color: var(--color-foreground-inverse-default); }
990
+ .dark .group .fix {
991
+ background: color-mix(in srgb, var(--color-pure-white) 4%, transparent);
992
+ color: var(--color-foreground-inverse-secondary);
993
+ border-color: var(--color-border-inverse);
994
+ }
995
+ .dark .number-input button {
996
+ background: color-mix(in srgb, var(--color-pure-white) 4%, transparent);
997
+ color: var(--color-foreground-inverse-default);
998
+ border-color: var(--color-border-inverse);
999
+ }
1000
+ .dark .number-input button:hover { background: color-mix(in srgb, var(--color-pure-white) 10%, transparent); }
1001
+ .dark .input:focus,
1002
+ .dark .group:focus-within,
1003
+ .dark .pill-search:focus-within,
1004
+ .dark .chips:focus-within,
1005
+ .dark .number-input:focus-within,
1006
+ .dark .date-trigger:focus-visible {
1007
+ border-color: var(--color-brand-secondary-300);
1008
+ box-shadow: var(--shadow-focus-sky);
1009
+ }
1010
+ .dark .field > label { color: var(--color-foreground-inverse-default); }
1011
+ .dark .field > .help { color: var(--color-foreground-inverse-muted); }
1012
+ .dark .kbd {
1013
+ background: color-mix(in srgb, var(--color-pure-white) 8%, transparent);
1014
+ color: var(--color-foreground-inverse-secondary);
1015
+ border-color: var(--color-border-inverse);
1016
+ }
1017
+ .dark .file-drop {
1018
+ background: color-mix(in srgb, var(--color-pure-white) 4%, transparent);
1019
+ border-color: var(--color-border-inverse);
1020
+ color: var(--color-foreground-inverse-muted);
1021
+ }
1022
+ .dark .file-drop .ttl { color: var(--color-foreground-inverse-default); }
1023
+ .dark .check-box,
1024
+ .dark .radio-dot {
1025
+ background: color-mix(in srgb, var(--color-pure-white) 6%, transparent);
1026
+ border-color: var(--color-border-inverse);
1027
+ }