@24vlh/vds 0.1.2 → 0.1.3

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 (35) hide show
  1. package/dist/components/accordion.css +170 -170
  2. package/dist/components/authoring.css +332 -332
  3. package/dist/components/buttons.css +683 -683
  4. package/dist/components/charts.css +502 -502
  5. package/dist/components/command.css +520 -520
  6. package/dist/components/content-blocks.css +944 -944
  7. package/dist/components/doc-block.css +782 -782
  8. package/dist/components/feedback.css +657 -657
  9. package/dist/components/flows.css +1101 -1101
  10. package/dist/components/forms-advanced.css +462 -462
  11. package/dist/components/forms.css +627 -1831
  12. package/dist/components/forms.min.css +1 -1
  13. package/dist/components/header-footer.css +348 -348
  14. package/dist/components/hero.css +498 -498
  15. package/dist/components/icons.css +937 -937
  16. package/dist/components/icons.min.css +1 -1
  17. package/dist/components/navigation.css +900 -900
  18. package/dist/components/overlays.css +498 -498
  19. package/dist/components/sections.css +450 -450
  20. package/dist/components/skeleton.css +385 -385
  21. package/dist/components/tables.css +591 -591
  22. package/dist/components/tabs.css +307 -307
  23. package/dist/components/toasts.css +421 -421
  24. package/dist/components/tooltips-popovers.css +447 -447
  25. package/dist/components/typography.css +250 -250
  26. package/dist/components/utilities.css +3433 -3433
  27. package/dist/core.css +866 -866
  28. package/dist/identity.css +266 -266
  29. package/dist/themes/carbon.css +658 -658
  30. package/dist/themes/graphite.css +658 -658
  31. package/dist/themes/navy.css +657 -657
  32. package/dist/themes/slate.css +659 -659
  33. package/dist/vds.css +19009 -20312
  34. package/dist/vds.min.css +1 -1
  35. package/package.json +3 -2
@@ -1,1831 +1,627 @@
1
- /************************************************************
2
- * VLAH DESIGN SYSTEM (VDS) - Forms
3
- *
4
- * Responsibilities:
5
- * - Provide two parallel form architectures:
6
- * A) Legacy VDS form controls: input, textarea, select,
7
- * segmented controls, toggles, switches, radio/checkbox, input groups
8
- * B) UFAL (Unified Form Abstraction Layer): form-control,
9
- * wrapper elements, adaptive surfaces, loading/error states,
10
- * prefix/suffix architecture, file/drop zones, and layout primitives
11
- *
12
- * - Supply semantic/interactive states:
13
- * focus, hover, disabled, readonly, error, success, warning, info,
14
- * dirty, loading, validated, invalidated
15
- *
16
- * - Define full visual contract for:
17
- * control sizing tokens, label/caption hierarchy,
18
- * density modes, inline/stack layouts, and responsive adaptations
19
- *
20
- * System Notes:
21
- * - 100% token-driven: spacing, radii, borders, typography, motion
22
- * - No JS assumptions; all behaviours (loading shimmer, validation highlights)
23
- * are CSS-based
24
- * - Legacy and UFAL architectures MUST co-exist in v1; no cross-overwrites
25
- ************************************************************/
26
-
27
- /* ---------------------------------------------------------
28
- 1. FORM TOKEN DEFINITIONS
29
- --------------------------------------------------------- */
30
-
31
- [data-vds-form],
32
- .vds-form {
33
- --form-flow-gap-xs: var(--space-1);
34
- --form-flow-textarea-min-height: 5rem;
35
-
36
- --size-9: 2.75rem;
37
- --size-10: 3.00rem;
38
- --size-11: 3.25rem;
39
-
40
- --control-min-height: var(--size-10);
41
-
42
- --control-height-md: 40px;
43
- }
44
-
45
- [data-vds-form] .form--a .form-control,
46
- .vds-form .form--a .form-control {
47
- --control-min-height: var(--size-11);
48
- }
49
-
50
- [data-vds-form] .form--c .form-control,
51
- .vds-form .form--c .form-control {
52
- --control-min-height: var(--size-9);
53
- }
54
-
55
- /* ---------------------------------------------------------
56
- 2. BASE FORM RESET
57
- --------------------------------------------------------- */
58
-
59
- input,
60
- select,
61
- textarea,
62
- button {
63
- font-family: inherit;
64
- font-size: 100%;
65
- line-height: inherit;
66
- color: inherit;
67
- }
68
-
69
- input,
70
- select,
71
- textarea {
72
- background-clip: padding-box;
73
- }
74
-
75
- input:focus,
76
- select:focus,
77
- textarea:focus {
78
- outline: none;
79
- }
80
-
81
- /* ---------------------------------------------------------
82
- 3. DENSITY LAYERS (A/B/C)
83
- --------------------------------------------------------- */
84
-
85
- .form--a .input,
86
- .form--a .select,
87
- .form--a .textarea {
88
- padding: var(--space-4);
89
- font-size: var(--text-md);
90
- }
91
-
92
- .form--a .form-check {
93
- font-size: var(--text-md);
94
- }
95
-
96
- .form--a .segmented__option {
97
- padding: var(--space-3) var(--space-5);
98
- font-size: var(--text-md);
99
- }
100
-
101
- .form--a .file-upload__control {
102
- padding: var(--space-5);
103
- }
104
-
105
- .form--c .input,
106
- .form--c .select,
107
- .form--c .textarea {
108
- padding: var(--space-2);
109
- font-size: var(--text-xs);
110
- }
111
-
112
- .form--c .form-check {
113
- font-size: var(--text-xs);
114
- }
115
-
116
- .form--c .segmented__option {
117
- padding: var(--space-1) var(--space-3);
118
- font-size: var(--text-xs);
119
- }
120
-
121
- .form--c .file-upload__control {
122
- padding: var(--space-3);
123
- }
124
-
125
- /* ---------------------------------------------------------
126
- 4. FIELD WRAPPER
127
- --------------------------------------------------------- */
128
-
129
- .form-field {
130
- display: flex;
131
- flex-direction: column;
132
- gap: var(--form-flow-gap-xs);
133
- width: 100%;
134
- }
135
-
136
- .form-field--disabled {
137
- opacity: 0.7;
138
- pointer-events: none;
139
- }
140
-
141
- .form-label {
142
- font-weight: 500;
143
- color: var(--color-text);
144
- }
145
-
146
- .form-label--required::after {
147
- content: " *";
148
- color: var(--color-danger);
149
- font-weight: 600;
150
- }
151
-
152
- input[type="search"]::-webkit-search-decoration,
153
- input[type="search"]::-webkit-search-results-button,
154
- input[type="search"]::-webkit-search-cancel-button {
155
- -webkit-appearance: none;
156
- appearance: none;
157
- }
158
-
159
- .form-label--sr-only {
160
- position: absolute;
161
- width: 1px;
162
- height: 1px;
163
- padding: 0;
164
- margin: -1px;
165
- overflow: hidden;
166
- clip: rect(0, 0, 0, 0);
167
- border: 0;
168
- }
169
-
170
- .form-help {
171
- font-size: var(--text-xs);
172
- color: var(--color-text-muted);
173
- }
174
-
175
- .form-error {
176
- font-size: var(--text-xs);
177
- color: var(--color-danger);
178
- }
179
-
180
- .form-field--error .form-label {
181
- color: var(--color-danger);
182
- }
183
-
184
- .form-field--error .input,
185
- .form-field--error .select,
186
- .form-field--error .textarea {
187
- border-color: var(--color-danger);
188
- }
189
-
190
- .form-field--error .input-group {
191
- border-color: var(--color-danger);
192
- }
193
-
194
- .form-field--error .segmented {
195
- border-color: var(--color-danger);
196
- }
197
-
198
- .form-field--error .slider {
199
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-danger-soft);
200
- }
201
-
202
- .form-field--error .file-upload__control {
203
- border-color: var(--color-danger);
204
- background-color: var(--color-danger-soft);
205
- }
206
-
207
- /* ---------------------------------------------------------
208
- 5. BASE INPUT
209
- --------------------------------------------------------- */
210
-
211
- .input {
212
- width: 100%;
213
- padding: var(--space-3) var(--space-3);
214
-
215
- border: var(--border-width) solid var(--color-border-subtle);
216
- border-radius: var(--radius-md);
217
- background-color: var(--color-surface);
218
- color: var(--color-text);
219
-
220
- transition: border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
221
- }
222
-
223
- .input:focus {
224
- border-color: var(--color-accent);
225
- }
226
-
227
- .input:focus-visible {
228
- border-color: var(--focus-ring-color);
229
- box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
230
- }
231
-
232
- .input:hover {
233
- border-color: var(--color-border-strong);
234
- }
235
-
236
- .input:disabled,
237
- .input--disabled {
238
- background-color: var(--color-muted-bg);
239
- color: var(--color-text-muted);
240
- cursor: not-allowed;
241
- }
242
-
243
- .input[readonly],
244
- .input--readonly {
245
- background-color: var(--color-surface-subtle);
246
- color: var(--color-text-muted);
247
- border-style: dashed;
248
- }
249
-
250
- .input::-moz-placeholder {
251
- color: var(--color-placeholder);
252
- opacity: 1;
253
- }
254
-
255
- .input::placeholder {
256
- color: var(--color-placeholder);
257
- opacity: 1;
258
- }
259
-
260
- /* ---------------------------------------------------------
261
- 5A. INPUT WITH ICONS
262
- Pattern:
263
- <div class="input-icon input-icon--left">
264
- <span class="input-icon__icon">...</span>
265
- <input class="input">
266
- </div>
267
- --------------------------------------------------------- */
268
-
269
- .input-icon {
270
- position: relative;
271
- display: flex;
272
- align-items: center;
273
- width: 100%;
274
- }
275
-
276
- .input-icon .input {
277
- width: 100%;
278
- }
279
-
280
- .input-icon--left .input {
281
- padding-left: var(--space-8);
282
- }
283
-
284
- .input-icon--right .input {
285
- padding-right: var(--space-8);
286
- }
287
-
288
- .input-icon--both .input {
289
- padding-left: var(--space-8);
290
- padding-right: var(--space-8);
291
- }
292
-
293
- .input-icon__icon {
294
- position: absolute;
295
- top: 50%;
296
- transform: translateY(-50%);
297
- display: inline-flex;
298
- align-items: center;
299
- justify-content: center;
300
- width: var(--icon-sm);
301
- height: var(--icon-sm);
302
- color: var(--color-text-muted);
303
- }
304
-
305
- .input-icon--left .input-icon__icon {
306
- left: var(--space-3);
307
- }
308
-
309
- .input-icon--right .input-icon__icon {
310
- right: var(--space-3);
311
- }
312
-
313
- /* ---------------------------------------------------------
314
- 6. TEXTAREA
315
- --------------------------------------------------------- */
316
-
317
- .textarea {
318
- width: 100%;
319
- resize: vertical;
320
- min-height: var(--form-flow-textarea-min-height);
321
- padding: var(--space-3);
322
-
323
- border: var(--border-width) solid var(--color-border-subtle);
324
- border-radius: var(--radius-md);
325
- background-color: var(--color-surface);
326
- color: var(--color-text);
327
-
328
- transition: border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
329
- }
330
-
331
- .textarea:focus {
332
- border-color: var(--color-accent);
333
- }
334
-
335
- .textarea:focus-visible {
336
- border-color: var(--focus-ring-color);
337
- box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
338
- }
339
-
340
- .textarea:hover {
341
- border-color: var(--color-border-strong);
342
- }
343
-
344
- .textarea:disabled,
345
- .textarea--disabled {
346
- background-color: var(--color-muted-bg);
347
- color: var(--color-text-muted);
348
- cursor: not-allowed;
349
- }
350
-
351
- .textarea[readonly],
352
- .textarea--readonly {
353
- background-color: var(--color-surface-subtle);
354
- color: var(--color-text-muted);
355
- border-style: dashed;
356
- }
357
-
358
- .textarea::-moz-placeholder {
359
- color: var(--color-placeholder);
360
- opacity: 1;
361
- }
362
-
363
- .textarea::placeholder {
364
- color: var(--color-placeholder);
365
- opacity: 1;
366
- }
367
-
368
- /* ---------------------------------------------------------
369
- 7. SELECT (NATIVE + CUSTOM DROPDOWN ICON)
370
- --------------------------------------------------------- */
371
-
372
- .select {
373
- width: 100%;
374
- padding: var(--space-3) var(--space-4) var(--space-3) var(--space-3);
375
-
376
- -webkit-appearance: none;
377
-
378
- -moz-appearance: none;
379
-
380
- appearance: none;
381
- background-color: var(--color-surface);
382
- color: var(--color-text);
383
-
384
- border: var(--border-width) solid var(--color-border-subtle);
385
- border-radius: var(--radius-md);
386
-
387
- position: relative;
388
- transition: border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
389
-
390
- background-image: linear-gradient(45deg, transparent 50%, var(--color-text-muted) 50%),
391
- linear-gradient(135deg, var(--color-text-muted) 50%, transparent 50%);
392
- background-position: calc(100% - var(--space-3)) center,
393
- calc(100% - var(--space-2)) center;
394
- background-size: 0.4rem 0.4rem;
395
- background-repeat: no-repeat;
396
- padding-right: var(--space-6);
397
- }
398
-
399
- .select:focus {
400
- border-color: var(--color-accent);
401
- }
402
-
403
- .select:focus-visible {
404
- border-color: var(--focus-ring-color);
405
- box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
406
- }
407
-
408
- .select:hover {
409
- border-color: var(--color-border-strong);
410
- }
411
-
412
- .select:disabled,
413
- .select--disabled {
414
- background-color: var(--color-muted-bg);
415
- color: var(--color-text-muted);
416
- cursor: not-allowed;
417
- background-image: linear-gradient(45deg, transparent 50%, var(--color-text-muted) 50%),
418
- linear-gradient(135deg, var(--color-text-muted) 50%, transparent 50%);
419
- }
420
-
421
- /* ---------------------------------------------------------
422
- 8. CHECKBOXES & RADIOS
423
- --------------------------------------------------------- */
424
-
425
- .form-check {
426
- display: flex;
427
- align-items: center;
428
- gap: var(--space-2);
429
- margin-bottom: var(--space-3);
430
- cursor: pointer;
431
- font-size: var(--text-sm);
432
- }
433
-
434
- .form-check-group .form-check:last-child {
435
- margin-bottom: 0;
436
- }
437
-
438
- .form-check--disabled {
439
- opacity: 0.7;
440
- cursor: not-allowed;
441
- }
442
-
443
- .form-check input {
444
- width: 1.15rem;
445
- height: 1.15rem;
446
- cursor: pointer;
447
- }
448
-
449
- .checkbox {
450
- -webkit-appearance: none;
451
- -moz-appearance: none;
452
- appearance: none;
453
- border: var(--border-width) solid var(--color-border-subtle);
454
- border-radius: var(--radius-sm);
455
- background-color: var(--color-surface);
456
- cursor: pointer;
457
- position: relative;
458
- transition: border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
459
- }
460
-
461
- .checkbox:hover {
462
- border-color: var(--color-border-strong);
463
- }
464
-
465
- .checkbox:focus-visible {
466
- box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
467
- outline: none;
468
- }
469
-
470
- .checkbox:checked {
471
- border-color: var(--color-accent);
472
- background-color: var(--color-accent-soft);
473
- }
474
-
475
- .checkbox:checked::after {
476
- content: "";
477
- position: absolute;
478
- top: 50%;
479
- left: 50%;
480
- width: 0.55rem;
481
- height: 0.55rem;
482
- transform: translate(-50%, -50%);
483
- background-color: var(--color-accent);
484
- -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='16' height='16' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolyline points='3 8 7 12 13 4'/%3E%3C/svg%3E");
485
- mask-image: url("data:image/svg+xml,%3Csvg width='16' height='16' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolyline points='3 8 7 12 13 4'/%3E%3C/svg%3E");
486
- -webkit-mask-size: cover;
487
- mask-size: cover;
488
- }
489
-
490
- .checkbox:disabled {
491
- background-color: var(--color-muted-bg);
492
- border-color: var(--color-muted-border);
493
- cursor: not-allowed;
494
- }
495
-
496
- .checkbox--error {
497
- border-color: var(--color-danger);
498
- }
499
-
500
- .checkbox--warning {
501
- border-color: var(--color-warning);
502
- }
503
-
504
- .checkbox--success {
505
- border-color: var(--color-success);
506
- }
507
-
508
- .checkbox--info {
509
- border-color: var(--color-info);
510
- }
511
-
512
- .radio {
513
- -webkit-appearance: none;
514
- -moz-appearance: none;
515
- appearance: none;
516
- border: var(--border-width) solid var(--color-border-subtle);
517
- border-radius: 50%;
518
- background-color: var(--color-surface);
519
- cursor: pointer;
520
- position: relative;
521
- transition: border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
522
- }
523
-
524
- .radio:hover {
525
- border-color: var(--color-border-strong);
526
- }
527
-
528
- .radio:focus-visible {
529
- box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
530
- outline: none;
531
- }
532
-
533
- .radio:checked {
534
- border-color: var(--color-accent);
535
- background-color: var(--color-accent-soft);
536
- }
537
-
538
- .radio:checked::after {
539
- content: "";
540
- position: absolute;
541
- top: 50%;
542
- left: 50%;
543
- width: 0.55rem;
544
- height: 0.55rem;
545
- background-color: var(--color-accent);
546
- border-radius: 50%;
547
- transform: translate(-50%, -50%);
548
- }
549
-
550
- .radio:disabled {
551
- background-color: var(--color-muted-bg);
552
- border-color: var(--color-muted-border);
553
- cursor: not-allowed;
554
- }
555
-
556
- .radio--error {
557
- border-color: var(--color-danger);
558
- }
559
-
560
- .radio--warning {
561
- border-color: var(--color-warning);
562
- }
563
-
564
- .radio--success {
565
- border-color: var(--color-success);
566
- }
567
-
568
- .radio--info {
569
- border-color: var(--color-info);
570
- }
571
-
572
- /* ---------------------------------------------------------
573
- 9. SWITCHES
574
- Pattern: <input type="checkbox" class="switch">
575
- --------------------------------------------------------- */
576
-
577
- .switch {
578
- -webkit-appearance: none;
579
- -moz-appearance: none;
580
- appearance: none;
581
- border: var(--border-width) solid var(--color-border-subtle);
582
- border-radius: 50%;
583
- background-color: var(--color-surface);
584
- cursor: pointer;
585
- position: relative;
586
- transition: border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
587
- width: 1.15rem;
588
- height: 1.15rem;
589
- }
590
-
591
- .switch::after {
592
- content: "";
593
- position: absolute;
594
- top: 22%;
595
- left: 22%;
596
- width: 0.55rem;
597
- height: 0.55rem;
598
- background-color: var(--color-accent);
599
- border-radius: 50%;
600
- transform: translate(-50%, -50%);
601
- opacity: 0;
602
- }
603
-
604
- .switch:checked {
605
- background-color: var(--color-accent-strong);
606
- border: var(--border-width) solid var(--color-accent);
607
- }
608
-
609
- .switch:checked::after {
610
- opacity: 1;
611
- transform: scale(1);
612
- background-color: var(--color-accent);
613
- }
614
-
615
- .switch:disabled {
616
- opacity: 0.5;
617
- cursor: not-allowed;
618
- }
619
-
620
- .switch:focus-visible {
621
- outline: none;
622
- box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
623
- }
624
-
625
- .switch--error {
626
- background-color: var(--color-danger-soft);
627
- }
628
-
629
- .switch--warning {
630
- background-color: var(--color-warning-soft);
631
- }
632
-
633
- .switch--success {
634
- background-color: var(--color-success-soft);
635
- }
636
-
637
- .switch--info {
638
- background-color: var(--color-info-soft);
639
- }
640
-
641
- .switch--error:checked {
642
- background-color: var(--color-danger);
643
- }
644
-
645
- .switch--warning:checked {
646
- background-color: var(--color-warning);
647
- }
648
-
649
- .switch--success:checked {
650
- background-color: var(--color-success);
651
- }
652
-
653
- .switch--info:checked {
654
- background-color: var(--color-info);
655
- }
656
-
657
- /* ---------------------------------------------------------
658
- 10. INPUT GROUPS (ICONS + ADDONS)
659
- --------------------------------------------------------- */
660
-
661
- .input-group {
662
- display: flex;
663
- align-items: center;
664
- border: var(--border-width) solid var(--color-border-subtle);
665
- border-radius: var(--radius-md);
666
- background-color: var(--color-surface);
667
- overflow: hidden;
668
- }
669
-
670
- .input-group .input {
671
- border: none;
672
- flex: 1;
673
- box-shadow: none;
674
- }
675
-
676
- .input-group-addon {
677
- padding: 0 var(--space-3);
678
- display: flex;
679
- align-items: center;
680
- color: var(--color-text-muted);
681
- background-color: var(--color-surface);
682
- border-right: var(--border-width) solid var(--color-border-subtle);
683
- }
684
-
685
- .input-group-addon + .input {
686
- padding-left: var(--space-2);
687
- }
688
-
689
- .input-group-icon {
690
- display: flex;
691
- align-items: center;
692
- padding: 0 var(--space-3);
693
- color: var(--color-text-muted);
694
- }
695
-
696
- .input-group-icon .icon {
697
- width: var(--icon-sm);
698
- height: var(--icon-sm);
699
- }
700
-
701
- .input-group--error {
702
- border-color: var(--color-danger);
703
- }
704
-
705
- .input-group--warning {
706
- border-color: var(--color-warning);
707
- }
708
-
709
- .input-group--success {
710
- border-color: var(--color-success);
711
- }
712
-
713
- .input-group--info {
714
- border-color: var(--color-info);
715
- }
716
-
717
- .input-group--disabled {
718
- opacity: 0.7;
719
- pointer-events: none;
720
- }
721
-
722
- .input-group:focus-within {
723
- border-color: var(--focus-ring-color);
724
- box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
725
- }
726
-
727
- .input-group:hover {
728
- border-color: var(--color-border-strong);
729
- }
730
-
731
- /* ---------------------------------------------------------
732
- 11. SEGMENTED CONTROL
733
- --------------------------------------------------------- */
734
-
735
- .segmented {
736
- display: flex;
737
- width: 100%;
738
- border: var(--border-width) solid var(--color-border-subtle);
739
- border-radius: var(--radius-md);
740
- overflow: hidden;
741
- height: 2.5rem;
742
- height: var(--control-height-md, 2.5rem);
743
- background-color: var(--color-surface);
744
- }
745
-
746
- .segmented__option {
747
- flex: 1 1 0;
748
- text-align: center;
749
- -webkit-appearance: none;
750
- -moz-appearance: none;
751
- appearance: none;
752
- border: none;
753
- font: inherit;
754
- line-height: inherit;
755
- padding: var(--space-2) var(--space-4);
756
- cursor: pointer;
757
- -webkit-user-select: none;
758
- -moz-user-select: none;
759
- user-select: none;
760
- display: inline-flex;
761
- align-items: center;
762
- justify-content: center;
763
- color: var(--color-text-muted);
764
- background: var(--color-surface);
765
- height: 100%;
766
- transition: background-color 0.2s, color 0.2s;
767
- }
768
-
769
- .segmented__option:not(:last-child) {
770
- border-right: var(--border-width) solid var(--color-border-subtle);
771
- }
772
-
773
- .segmented__option:hover {
774
- background-color: var(--color-surface-subtle);
775
- color: var(--color-text);
776
- }
777
-
778
- .segmented__option:focus-visible {
779
- outline: none;
780
- box-shadow: inset 0 0 0 var(--border-width-strong) var(--focus-ring-color);
781
- }
782
-
783
- .segmented__option--active {
784
- background-color: var(--color-accent);
785
- color: var(--color-on-accent);
786
- font-weight: 600;
787
- position: relative;
788
- z-index: 1;
789
- }
790
-
791
- .segmented__option--active + .segmented__option {
792
- border-left-color: transparent;
793
- }
794
-
795
- .segmented--disabled {
796
- opacity: 0.6;
797
- pointer-events: none;
798
- }
799
-
800
- .segmented__option--disabled {
801
- cursor: not-allowed;
802
- opacity: 0.7;
803
- }
804
-
805
- .segmented__option-icon {
806
- width: var(--icon-sm);
807
- height: var(--icon-sm);
808
- margin-right: var(--space-2);
809
- }
810
-
811
- .segmented--error {
812
- border-color: var(--color-danger);
813
- }
814
-
815
- .segmented--warning {
816
- border-color: var(--color-warning);
817
- }
818
-
819
- .segmented--success {
820
- border-color: var(--color-success);
821
- }
822
-
823
- .segmented--info {
824
- border-color: var(--color-info);
825
- }
826
-
827
- .segmented--loading .segmented__option {
828
- position: relative;
829
- overflow: hidden;
830
- }
831
-
832
- .segmented--loading .segmented__option::after {
833
- content: "";
834
- position: absolute;
835
- top: 0;
836
- right: 0;
837
- bottom: 0;
838
- left: 0;
839
- background-image: linear-gradient(
840
- 90deg,
841
- var(--color-muted-bg),
842
- var(--color-surface),
843
- var(--color-muted-bg)
844
- );
845
- background-size: 200% 100%;
846
- animation: input-loading-shimmer 1.5s infinite;
847
- }
848
-
849
- /* ---------------------------------------------------------
850
- 12. SLIDER
851
- --------------------------------------------------------- */
852
-
853
- .slider {
854
- width: 100%;
855
- -webkit-appearance: none;
856
- -moz-appearance: none;
857
- appearance: none;
858
- height: var(--border-width-strong);
859
- background-color: var(--slider-track-bg);
860
- border-radius: var(--radius-sm);
861
- cursor: pointer;
862
- }
863
-
864
- .slider:focus-visible {
865
- outline: none;
866
- box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
867
- }
868
-
869
- .slider:disabled {
870
- cursor: not-allowed;
871
- opacity: 0.7;
872
- }
873
-
874
- .slider::-webkit-slider-thumb {
875
- -webkit-appearance: none;
876
- appearance: none;
877
- width: 1rem;
878
- height: 1rem;
879
- background: var(--slider-thumb-bg);
880
- border-radius: 50%;
881
- cursor: pointer;
882
- }
883
-
884
- .slider::-moz-range-thumb {
885
- width: 1rem;
886
- height: 1rem;
887
- background: var(--slider-thumb-bg);
888
- border-radius: 50%;
889
- cursor: pointer;
890
- }
891
-
892
- .slider--error {
893
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-danger-soft);
894
- }
895
-
896
- .slider--error:focus-visible {
897
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-danger);
898
- outline: none;
899
- }
900
-
901
- .slider--warning {
902
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-warning-soft);
903
- }
904
-
905
- .slider--warning:focus-visible {
906
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-warning);
907
- outline: none;
908
- }
909
-
910
- .slider--success {
911
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-success-soft);
912
- }
913
-
914
- .slider--success:focus-visible {
915
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-success);
916
- outline: none;
917
- }
918
-
919
- .slider--info {
920
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-info-soft);
921
- }
922
-
923
- .slider--info:focus-visible {
924
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-info);
925
- outline: none;
926
- }
927
-
928
- /* ---------------------------------------------------------
929
- 13. FILE UPLOAD (CUSTOM)
930
- --------------------------------------------------------- */
931
-
932
- .file-upload {
933
- display: flex;
934
- flex-direction: column;
935
- gap: var(--space-2);
936
- }
937
-
938
- .file-upload input {
939
- position: absolute;
940
- width: 1px;
941
- height: 1px;
942
- padding: 0;
943
- margin: -1px;
944
- overflow: hidden;
945
- clip: rect(0, 0, 0, 0);
946
- white-space: nowrap;
947
- border: 0;
948
- }
949
-
950
- .file-upload__control {
951
- border: var(--border-width) dashed var(--color-border-subtle);
952
- border-radius: var(--radius-md);
953
- padding: var(--space-4);
954
- text-align: center;
955
- color: var(--color-text-soft);
956
- cursor: pointer;
957
- background-color: var(--color-surface);
958
- transition: border-color 0.2s, background-color 0.2s, box-shadow 0.2s;
959
- }
960
-
961
- .file-upload__control:hover {
962
- border-color: var(--color-border-strong);
963
- background-color: var(--color-surface-subtle);
964
- }
965
-
966
- .file-upload__control:focus-visible {
967
- outline: none;
968
- box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
969
- }
970
-
971
- .file-upload__control--dragover {
972
- border-color: var(--color-accent);
973
- background-color: var(--color-accent-soft);
974
- }
975
-
976
- .file-upload__control--disabled {
977
- opacity: 0.7;
978
- cursor: not-allowed;
979
- }
980
-
981
- .file-upload__control--error {
982
- border-color: var(--color-danger);
983
- background-color: var(--color-danger-soft);
984
- color: var(--color-danger-strong);
985
- }
986
-
987
- .file-upload__control--warning {
988
- border-color: var(--color-warning);
989
- background-color: var(--color-warning-soft);
990
- color: var(--color-warning-strong);
991
- }
992
-
993
- .file-upload__control--success {
994
- border-color: var(--color-success);
995
- background-color: var(--color-success-soft);
996
- color: var(--color-success-strong);
997
- }
998
-
999
- .file-upload__control--info {
1000
- border-color: var(--color-info);
1001
- background-color: var(--color-info-soft);
1002
- color: var(--color-info-strong);
1003
- }
1004
-
1005
- .file-upload__control--loading {
1006
- background-image: linear-gradient(
1007
- 90deg,
1008
- var(--color-muted-bg),
1009
- var(--color-surface),
1010
- var(--color-muted-bg)
1011
- );
1012
- background-size: 200% 100%;
1013
- animation: input-loading-shimmer 1.5s infinite;
1014
- }
1015
-
1016
- /* ---------------------------------------------------------
1017
- 14. FORM GRID / FORM ROWS
1018
- --------------------------------------------------------- */
1019
-
1020
- .form-row {
1021
- display: flex;
1022
- flex-wrap: wrap;
1023
- gap: var(--space-4);
1024
- }
1025
-
1026
- .form-row--two {
1027
- display: grid;
1028
- grid-template-columns: repeat(2, minmax(0, 1fr));
1029
- grid-gap: var(--space-4);
1030
- gap: var(--space-4);
1031
- }
1032
-
1033
- .form-grid {
1034
- display: grid;
1035
- grid-gap: var(--space-4);
1036
- gap: var(--space-4);
1037
- }
1038
-
1039
- .form-grid--two {
1040
- grid-template-columns: repeat(2, minmax(0, 1fr));
1041
- }
1042
-
1043
- .form-grid--three {
1044
- grid-template-columns: repeat(3, minmax(0, 1fr));
1045
- }
1046
-
1047
- .form-grid--four {
1048
- grid-template-columns: repeat(4, minmax(0, 1fr));
1049
- }
1050
-
1051
- @media (max-width: 768px) {
1052
- .form-grid--two,
1053
- .form-grid--three,
1054
- .form-grid--four {
1055
- grid-template-columns: 1fr;
1056
- }
1057
- }
1058
-
1059
- /* ---------------------------------------------------------
1060
- 15. VALIDATION STATES (INPUT / SELECT / TEXTAREA)
1061
- --------------------------------------------------------- */
1062
-
1063
- .input--error,
1064
- .select--error,
1065
- .textarea--error {
1066
- border-color: var(--color-danger);
1067
- }
1068
-
1069
- .input--error:focus-visible,
1070
- .select--error:focus-visible,
1071
- .textarea--error:focus-visible {
1072
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-danger-soft);
1073
- }
1074
-
1075
- .input--success,
1076
- .select--success,
1077
- .textarea--success {
1078
- border-color: var(--color-success);
1079
- }
1080
-
1081
- .input--success:focus-visible,
1082
- .select--success:focus-visible,
1083
- .textarea--success:focus-visible {
1084
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-success-soft);
1085
- }
1086
-
1087
- .input--warning,
1088
- .select--warning,
1089
- .textarea--warning {
1090
- border-color: var(--color-warning);
1091
- }
1092
-
1093
- .input--warning:focus-visible,
1094
- .select--warning:focus-visible,
1095
- .textarea--warning:focus-visible {
1096
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-warning-soft);
1097
- }
1098
-
1099
- .input--info,
1100
- .select--info,
1101
- .textarea--info {
1102
- border-color: var(--color-info);
1103
- }
1104
-
1105
- .input--info:focus-visible,
1106
- .select--info:focus-visible,
1107
- .textarea--info:focus-visible {
1108
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-info-soft);
1109
- }
1110
-
1111
- /* ---------------------------------------------------------
1112
- 16. FIELD META (HELP, ERROR, COUNTER, ICONS, LAYOUT)
1113
- --------------------------------------------------------- */
1114
-
1115
- .form-meta {
1116
- display: flex;
1117
- align-items: flex-start;
1118
- justify-content: space-between;
1119
- gap: var(--form-flow-gap-xs);
1120
- margin-top: var(--space-1);
1121
- }
1122
-
1123
- .form-meta--stacked {
1124
- flex-direction: column;
1125
- align-items: flex-start;
1126
- }
1127
-
1128
- .form-meta--inline {
1129
- flex-direction: row;
1130
- align-items: center;
1131
- }
1132
-
1133
- .form-meta__messages {
1134
- display: flex;
1135
- flex-direction: column;
1136
- gap: var(--space-1);
1137
- min-width: 0;
1138
- }
1139
-
1140
- .form-counter {
1141
- margin-left: auto;
1142
- font-size: var(--text-xs);
1143
- font-family: var(--font-family-mono), monospace;
1144
- color: var(--color-text-muted);
1145
- white-space: nowrap;
1146
- }
1147
-
1148
- .form-counter--warning {
1149
- color: var(--color-warning);
1150
- }
1151
-
1152
- .form-counter--danger {
1153
- color: var(--color-danger);
1154
- }
1155
-
1156
- .form-field--error .form-counter {
1157
- color: var(--color-danger);
1158
- }
1159
-
1160
- .form-control-with-icon {
1161
- position: relative;
1162
- display: inline-flex;
1163
- width: 100%;
1164
- }
1165
-
1166
- .form-control-with-icon .input,
1167
- .form-control-with-icon .select,
1168
- .form-control-with-icon .textarea {
1169
- width: 100%;
1170
- padding-right: calc(var(--space-8) + var(--space-1));
1171
- }
1172
-
1173
- .form-status-icon {
1174
- position: absolute;
1175
- right: var(--space-3);
1176
- top: 50%;
1177
- transform: translateY(-50%);
1178
- display: inline-flex;
1179
- align-items: center;
1180
- justify-content: center;
1181
- width: var(--icon-sm);
1182
- height: var(--icon-sm);
1183
- color: var(--color-text-muted);
1184
- pointer-events: none;
1185
- }
1186
-
1187
- .form-field--error .form-status-icon {
1188
- color: var(--color-danger);
1189
- }
1190
-
1191
- .form-field--success .form-status-icon {
1192
- color: var(--color-success);
1193
- }
1194
-
1195
- .form-field--warning .form-status-icon {
1196
- color: var(--color-warning);
1197
- }
1198
-
1199
- .form-field--info .form-status-icon {
1200
- color: var(--color-info);
1201
- }
1202
-
1203
- .form-field--success .form-label {
1204
- color: var(--color-success);
1205
- }
1206
-
1207
- .form-field--success .input,
1208
- .form-field--success .select,
1209
- .form-field--success .textarea,
1210
- .form-field--success .input-group {
1211
- border-color: var(--color-success);
1212
- }
1213
-
1214
- .form-field--warning .form-label {
1215
- color: var(--color-warning);
1216
- }
1217
-
1218
- .form-field--warning .input,
1219
- .form-field--warning .select,
1220
- .form-field--warning .textarea,
1221
- .form-field--warning .input-group {
1222
- border-color: var(--color-warning);
1223
- }
1224
-
1225
- .form-field--info .form-label {
1226
- color: var(--color-info);
1227
- }
1228
-
1229
- .form-field--info .input,
1230
- .form-field--info .select,
1231
- .form-field--info .textarea,
1232
- .form-field--info .input-group {
1233
- border-color: var(--color-info);
1234
- }
1235
-
1236
- .form-field--horizontal {
1237
- display: grid;
1238
- grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
1239
- grid-column-gap: var(--space-4);
1240
- -moz-column-gap: var(--space-4);
1241
- column-gap: var(--space-4);
1242
- align-items: flex-start;
1243
- }
1244
-
1245
- .form-field__label {
1246
- padding-top: var(--space-1);
1247
- }
1248
-
1249
- .form-field__control {
1250
- display: flex;
1251
- flex-direction: column;
1252
- gap: var(--form-flow-gap-xs);
1253
- }
1254
-
1255
- @media (max-width: 768px) {
1256
- .form-field--horizontal {
1257
- grid-template-columns: 1fr;
1258
- }
1259
-
1260
- .form-field__label {
1261
- padding-top: 0;
1262
- }
1263
- }
1264
-
1265
- .input--loading,
1266
- .select--loading,
1267
- .textarea--loading {
1268
- background-image: linear-gradient(
1269
- 90deg,
1270
- var(--color-muted-bg),
1271
- var(--color-surface),
1272
- var(--color-muted-bg)
1273
- );
1274
- background-size: 200% 100%;
1275
- animation: input-loading-shimmer 1.5s infinite;
1276
- }
1277
-
1278
- @keyframes input-loading-shimmer {
1279
- 0% {
1280
- background-position: -150% 0;
1281
- }
1282
- 100% {
1283
- background-position: 150% 0;
1284
- }
1285
- }
1286
-
1287
- /* ---------------------------------------------------------
1288
- 17. FORMS INSIDE MODALS / PANELS
1289
- --------------------------------------------------------- */
1290
-
1291
- .modal .form-field {
1292
- margin-bottom: var(--space-3);
1293
- }
1294
-
1295
- /* ---------------------------------------------------------
1296
- 18. FORM-LEVEL MESSAGES (top of form)
1297
- --------------------------------------------------------- */
1298
-
1299
- .form-message {
1300
- width: 100%;
1301
- padding: var(--space-4) var(--space-6);
1302
- border-radius: var(--radius-md);
1303
- border: var(--border-width) solid var(--color-border-subtle);
1304
- background-color: var(--color-surface-subtle);
1305
- font-size: var(--text-sm);
1306
- display: flex;
1307
- align-items: flex-start;
1308
- gap: var(--space-3);
1309
- }
1310
-
1311
- .form-message__icon {
1312
- width: 1.25rem;
1313
- height: 1.25rem;
1314
- flex-shrink: 0;
1315
- stroke: currentColor;
1316
- fill: none;
1317
- }
1318
-
1319
- .form-message__content {
1320
- display: flex;
1321
- flex-direction: column;
1322
- gap: var(--space-1);
1323
- }
1324
-
1325
- .form-message--neutral {
1326
- border-color: var(--color-muted-border);
1327
- background-color: var(--color-muted-bg);
1328
- color: var(--color-text-muted);
1329
- }
1330
-
1331
- .form-message--error {
1332
- background-color: var(--semantic-error-bg-strong, var(--color-danger-soft));
1333
- border-color: var(--semantic-error-border-strong, var(--color-danger));
1334
- color: var(--semantic-error-text-strong, var(--color-danger-strong));
1335
- }
1336
-
1337
- .form-message--info {
1338
- background-color: var(--semantic-info-bg-strong, var(--color-info-soft));
1339
- border-color: var(--semantic-info-border-strong, var(--color-info));
1340
- color: var(--semantic-info-text-strong, var(--color-info-strong));
1341
- }
1342
-
1343
- .form-message--success {
1344
- background-color: var(--semantic-success-bg-strong, var(--color-success-soft));
1345
- border-color: var(--semantic-success-border-strong, var(--color-success));
1346
- color: var(--semantic-success-text-strong, var(--color-success-strong));
1347
- }
1348
-
1349
- .form-message--warning {
1350
- background-color: var(--semantic-warning-bg-strong, var(--color-warning-soft));
1351
- border-color: var(--semantic-warning-border-strong, var(--color-warning));
1352
- color: var(--semantic-warning-text-strong, var(--color-warning-strong));
1353
- }
1354
-
1355
- /* ---------------------------------------------------------
1356
- 19. FIELD-LEVEL STATE MESSAGES (below inputs)
1357
- --------------------------------------------------------- */
1358
-
1359
- .form-field-message {
1360
- font-size: var(--text-xs);
1361
- margin-top: var(--space-1);
1362
- display: flex;
1363
- align-items: center;
1364
- gap: var(--space-1);
1365
- }
1366
-
1367
- .form-field-message--neutral {
1368
- color: var(--color-text-muted);
1369
- }
1370
-
1371
- .form-field-message--error {
1372
- color: var(--semantic-error-text-strong, var(--color-danger-strong));
1373
- }
1374
-
1375
- .form-field-message--warning {
1376
- color: var(--semantic-warning-text-strong, var(--color-warning-strong));
1377
- }
1378
-
1379
- .form-field-message--info {
1380
- color: var(--semantic-info-text-strong, var(--color-info-strong));
1381
- }
1382
-
1383
- .form-field-message--success {
1384
- color: var(--semantic-success-text-strong, var(--color-success-strong));
1385
- }
1386
-
1387
- .form-field-message__icon {
1388
- width: 1rem;
1389
- height: 1rem;
1390
- stroke: currentColor;
1391
- fill: none;
1392
- flex-shrink: 0;
1393
- }
1394
-
1395
- /* ---------------------------------------------------------
1396
- 20. HORIZONTAL CHECKBOXES / RADIOS
1397
- --------------------------------------------------------- */
1398
-
1399
- .form-check-inline {
1400
- display: inline-flex;
1401
- align-items: center;
1402
- gap: var(--space-2);
1403
- margin-right: var(--space-6);
1404
- cursor: pointer;
1405
- }
1406
-
1407
- .form-check-group {
1408
- display: flex;
1409
- flex-direction: column;
1410
- gap: var(--space-1);
1411
- align-items: flex-start;
1412
- }
1413
-
1414
- .form-check-group--horizontal {
1415
- flex-direction: row;
1416
- flex-wrap: wrap;
1417
- gap: var(--space-6);
1418
- align-items: center;
1419
- }
1420
-
1421
- .form-check-group--horizontal .form-check {
1422
- margin-right: var(--space-6);
1423
- margin-bottom: var(--space-2);
1424
- }
1425
-
1426
- /* -----------------------------------------------
1427
- 21. FORM SECTION STATES
1428
- Add semantic state styling for entire sections.
1429
- Intended for wrapping form-row or form-grid.
1430
- ------------------------------------------------ */
1431
-
1432
- .form-section--error {
1433
- padding: var(--space-3);
1434
- border-left: var(--border-width-strong) solid var(--semantic-error-border-strong, var(--color-danger));
1435
- background-color: transparent;
1436
- background-color: var(--semantic-error-bg-strong, transparent);
1437
- }
1438
-
1439
- .form-section--warning {
1440
- padding: var(--space-3);
1441
- border-left: var(--border-width-strong) solid var(--semantic-warning-border-strong, var(--color-warning));
1442
- background-color: transparent;
1443
- background-color: var(--semantic-warning-bg-strong, transparent);
1444
- }
1445
-
1446
- .form-section--success {
1447
- padding: var(--space-3);
1448
- border-left: var(--border-width-strong) solid var(--semantic-success-border-strong, var(--color-success));
1449
- background-color: transparent;
1450
- background-color: var(--semantic-success-bg-strong, transparent);
1451
- }
1452
-
1453
- .form-section--info {
1454
- padding: var(--space-3);
1455
- border-left: var(--border-width-strong) solid var(--semantic-info-border-strong, var(--color-info));
1456
- background-color: transparent;
1457
- background-color: var(--semantic-info-bg-strong, transparent);
1458
- }
1459
-
1460
- .form-section--error:not(:last-child),
1461
- .form-section--warning:not(:last-child),
1462
- .form-section--success:not(:last-child),
1463
- .form-section--info:not(:last-child) {
1464
- margin-bottom: var(--space-4);
1465
- }
1466
-
1467
- /* -----------------------------------------------
1468
- 22. ARIA hooks for validation
1469
- ------------------------------------------------ */
1470
-
1471
- .input[aria-invalid="true"],
1472
- .select[aria-invalid="true"],
1473
- .textarea[aria-invalid="true"] {
1474
- border-color: var(--color-danger);
1475
- }
1476
-
1477
- .input[aria-invalid="true"]:focus-visible,
1478
- .select[aria-invalid="true"]:focus-visible,
1479
- .textarea[aria-invalid="true"]:focus-visible {
1480
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-danger-soft);
1481
- }
1482
-
1483
- /* ----------------------------------------------------------
1484
- 23. Base form control primitive (UFAL)
1485
- ---------------------------------------------------------- */
1486
-
1487
- .form-control {
1488
- display: block;
1489
- width: 100%;
1490
- min-height: var(--control-min-height);
1491
-
1492
- margin: 0;
1493
- box-sizing: border-box;
1494
- }
1495
-
1496
- .form-control-wrapper {
1497
- position: relative;
1498
- width: 100%;
1499
- display: flex;
1500
- align-items: center;
1501
- }
1502
-
1503
- .form-control-wrapper > .form-control {
1504
- flex: 1 1 auto;
1505
- }
1506
-
1507
- /* ----------------------------------------------------------
1508
- 23A. State modifiers
1509
- ---------------------------------------------------------- */
1510
-
1511
- .form-control--loading {
1512
- cursor: progress;
1513
- opacity: 0.55;
1514
- pointer-events: none;
1515
- }
1516
-
1517
- .form-control--disabled {
1518
- opacity: 0.45;
1519
- pointer-events: none;
1520
- }
1521
-
1522
- .form-control--readonly {
1523
- opacity: 0.75;
1524
- cursor: default;
1525
- }
1526
-
1527
- .form-control--error {
1528
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-danger);
1529
- }
1530
-
1531
- .form-control--warning {
1532
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-warning);
1533
- }
1534
-
1535
- .form-control--success {
1536
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-success);
1537
- }
1538
-
1539
- .form-control--info {
1540
- box-shadow: 0 0 0 var(--border-width-strong) var(--color-info);
1541
- }
1542
-
1543
- .form-control--password {
1544
- padding-right: calc(var(--space-8) + var(--space-2));
1545
- }
1546
-
1547
- .form-control-wrapper[data-icon="right"] .form-control--password {
1548
- padding-right: calc(var(--space-8) + var(--space-2));
1549
- }
1550
-
1551
- .form-control--password:focus {
1552
- border-color: var(--color-accent);
1553
- }
1554
-
1555
- .form-control--password::-moz-placeholder {
1556
- color: var(--color-placeholder);
1557
- }
1558
-
1559
- .form-control--password::placeholder {
1560
- color: var(--color-placeholder);
1561
- }
1562
-
1563
- /* ----------------------------------------------------------
1564
- 23B. Icon-inside-input patterns
1565
- ---------------------------------------------------------- */
1566
-
1567
- .form-control-wrapper[data-icon="left"] .form-control {
1568
- padding-left: calc(var(--space-7) + var(--space-2));
1569
- }
1570
-
1571
- .form-control-wrapper[data-icon="left"] .form-control-icon {
1572
- position: absolute;
1573
- left: var(--space-2);
1574
- display: flex;
1575
- align-items: center;
1576
- }
1577
-
1578
- .form-control-wrapper[data-icon="right"] .form-control {
1579
- padding-right: calc(var(--space-7) + var(--space-2));
1580
- }
1581
-
1582
- .form-control-wrapper[data-icon="right"] .form-control-icon {
1583
- position: absolute;
1584
- right: var(--space-2);
1585
- display: flex;
1586
- align-items: center;
1587
- }
1588
-
1589
- .form-control-icon {
1590
- font-size: var(--text-md);
1591
- color: var(--color-text-muted);
1592
- pointer-events: none;
1593
- }
1594
-
1595
- /* ----------------------------------------------------------
1596
- 23C. Loading overlay animation
1597
- ---------------------------------------------------------- */
1598
-
1599
- .form-control--loading::after {
1600
- content: "";
1601
- position: absolute;
1602
- top: 0;
1603
- right: 0;
1604
- bottom: 0;
1605
- left: 0;
1606
- border-radius: inherit;
1607
- background: var(--color-surface-subtle);
1608
- animation: formControlLoading 1.5s ease-in-out infinite;
1609
- }
1610
-
1611
- @keyframes formControlLoading {
1612
- 0% {
1613
- opacity: .35;
1614
- }
1615
- 50% {
1616
- opacity: .6;
1617
- }
1618
- 100% {
1619
- opacity: .35;
1620
- }
1621
- }
1622
-
1623
- /* ----------------------------------------------------------
1624
- 23D. File upload primitives (UFAL)
1625
- ---------------------------------------------------------- */
1626
-
1627
- /* Base dashed file control (rarely exposed directly; internal primitive) */
1628
-
1629
- .form-control--file {
1630
- position: relative;
1631
- padding: var(--space-3);
1632
- border: var(--border-width) dashed var(--color-border-subtle);
1633
- border-radius: var(--radius-md);
1634
- background-color: var(--color-surface);
1635
- color: var(--color-text-muted);
1636
- cursor: pointer;
1637
- transition: border-color .2s, background-color .2s;
1638
- }
1639
-
1640
- .form-control--file[type="file"] {
1641
- padding: 0;
1642
- border: none;
1643
- background: none;
1644
- position: static;
1645
- opacity: 1;
1646
- }
1647
-
1648
- /* ----------------------------------------------------------
1649
- 23E. UFAL Drop Surface (corrected)
1650
- ---------------------------------------------------------- */
1651
-
1652
- .form-control-file-surface {
1653
- position: relative; /* ★ FIX: contain absolute input */
1654
- display: flex;
1655
- flex-direction: column;
1656
- gap: var(--space-2);
1657
- align-items: center;
1658
- justify-content: center;
1659
- padding: var(--space-6);
1660
-
1661
- border: var(--border-width) dashed var(--color-border-subtle);
1662
- border-radius: var(--radius-md);
1663
- background-color: var(--color-surface);
1664
- cursor: pointer;
1665
-
1666
- transition: border-color .2s, background-color .2s;
1667
- }
1668
-
1669
- .form-control-file-surface input[type="file"] {
1670
- position: absolute;
1671
- top: 0;
1672
- right: 0;
1673
- bottom: 0;
1674
- left: 0;
1675
- opacity: 0;
1676
- cursor: pointer;
1677
- }
1678
-
1679
- .form-control-file-surface:hover {
1680
- border-color: var(--color-border-strong);
1681
- background-color: var(--color-surface-subtle);
1682
- }
1683
-
1684
- .form-control-file-surface--dragover {
1685
- border-color: var(--color-accent);
1686
- background-color: var(--color-accent-soft);
1687
- }
1688
-
1689
- .form-control-file-surface--disabled {
1690
- opacity: .65;
1691
- cursor: not-allowed;
1692
- }
1693
-
1694
- .form-control-file-surface--error {
1695
- border-color: var(--color-danger);
1696
- background-color: var(--color-danger-soft);
1697
- }
1698
-
1699
- .form-control-file-surface--warning {
1700
- border-color: var(--color-warning);
1701
- background-color: var(--color-warning-soft);
1702
- }
1703
-
1704
- .form-control-file-surface--success {
1705
- border-color: var(--color-success);
1706
- background-color: var(--color-success-soft);
1707
- }
1708
-
1709
- .form-control-file-surface--info {
1710
- border-color: var(--color-info);
1711
- background-color: var(--color-info-soft);
1712
- }
1713
-
1714
- /* ----------------------------------------------------------
1715
- 23F. Inline “button-as-trigger” upload
1716
- ---------------------------------------------------------- */
1717
-
1718
- .file-upload-inline {
1719
- display: inline-flex;
1720
- align-items: center;
1721
- gap: var(--space-2);
1722
- }
1723
-
1724
- .file-upload-inline__input {
1725
- position: absolute;
1726
- width: 1px;
1727
- height: 1px;
1728
- padding: 0;
1729
- margin: -1px;
1730
- overflow: hidden;
1731
- clip: rect(0, 0, 0, 0);
1732
- white-space: nowrap;
1733
- border: 0;
1734
- }
1735
-
1736
- .file-upload-inline__label {
1737
- cursor: pointer;
1738
- display: inline-flex;
1739
- align-items: center;
1740
- justify-content: center;
1741
- }
1742
-
1743
- /* ----------------------------------------------------------
1744
- 23G. Shared inner patterns
1745
- ---------------------------------------------------------- */
1746
-
1747
- .file-upload,
1748
- .file-upload * {
1749
- box-sizing: border-box;
1750
- }
1751
-
1752
- .file-upload {
1753
- display: flex;
1754
- flex-direction: column;
1755
- gap: var(--space-2);
1756
- width: 100%;
1757
- }
1758
-
1759
- /* Loading shimmer on drop surface */
1760
-
1761
- .form-control-file-surface.form-control--loading::after {
1762
- content: "";
1763
- position: absolute;
1764
- top: 0;
1765
- right: 0;
1766
- bottom: 0;
1767
- left: 0;
1768
- border-radius: inherit;
1769
- background-image: linear-gradient(
1770
- 90deg,
1771
- var(--color-muted-bg),
1772
- var(--color-surface),
1773
- var(--color-muted-bg)
1774
- );
1775
- background-size: 200% 100%;
1776
- animation: input-loading-shimmer 1.5s infinite;
1777
- }
1778
-
1779
- .form-control-file-icon {
1780
- font-size: var(--text-lg);
1781
- color: var(--color-text-muted);
1782
- }
1783
-
1784
- .form-control-file-label {
1785
- font-size: var(--text-sm);
1786
- color: var(--color-text);
1787
- }
1788
-
1789
- .form-control-file-hint {
1790
- font-size: var(--text-xs);
1791
- color: var(--color-text-muted);
1792
- }
1793
-
1794
- .slider--error:focus-visible,
1795
- .input[aria-invalid="true"]:focus-visible,
1796
- .select[aria-invalid="true"]:focus-visible,
1797
- .textarea[aria-invalid="true"]:focus-visible,
1798
- .form-control--error:focus-visible {
1799
- box-shadow: 0 0 0 var(--border-width-strong) var(--semantic-error-border-strong, var(--color-danger));
1800
- }
1801
-
1802
- .checkbox:checked,
1803
- .radio:checked,
1804
- .switch:checked {
1805
- background-color: var(--accent-soft-surface, var(--color-accent-soft));
1806
- border-color: var(--accent-soft-border, var(--color-accent));
1807
- box-shadow: 0 0 0 1px transparent;
1808
- box-shadow: 0 0 0 1px var(--accent-soft-shadow, transparent);
1809
- color: var(--accent-soft-on, var(--color-on-accent));
1810
- }
1811
-
1812
- .checkbox:checked::after,
1813
- .radio:checked::after,
1814
- .switch:checked::after {
1815
- background-color: var(--accent-soft-border, var(--color-accent));
1816
- }
1817
-
1818
- .checkbox:checked:hover,
1819
- .radio:checked:hover,
1820
- .switch:checked:hover {
1821
- background-color: var(--accent-soft-surface-strong, var(--accent-soft-surface));
1822
- border-color: var(--accent-soft-border, var(--color-accent));
1823
- }
1824
-
1825
- .checkbox:checked:disabled,
1826
- .radio:checked:disabled,
1827
- .switch:checked:disabled {
1828
- background-color: var(--color-surface-subtle);
1829
- border-color: var(--color-border-subtle);
1830
- opacity: 0.6;
1831
- }
1
+ /************************************************************
2
+ * VLAH DESIGN SYSTEM (VDS) - Forms (UFAL)
3
+ *
4
+ * Responsibilities:
5
+ * - Provide the Unified Form Abstraction Layer (UFAL)
6
+ * - Define a single, token-driven form system:
7
+ * controls, wrappers, icons, prefixes/suffixes,
8
+ * semantic states, file surfaces, layout primitives,
9
+ * and form-level messaging
10
+ *
11
+ * System Notes:
12
+ * - Token-driven (spacing, radii, borders, typography, motion)
13
+ * - No JS assumptions; JS may toggle state classes only
14
+ * - UFAL is the sole form system in the VDS library
15
+ ************************************************************/
16
+
17
+ /* ---------------------------------------------------------
18
+ 1. TOKENS & SHARED ANIMATIONS
19
+ --------------------------------------------------------- */
20
+
21
+ [data-vds-form],
22
+ .vds-form {
23
+ --form-flow-textarea-min-height: 5rem;
24
+ --size-10: 3.00rem;
25
+ --control-min-height: var(--size-10);
26
+ }
27
+
28
+ @keyframes input-loading-shimmer {
29
+ 0% {
30
+ background-position: -150% 0;
31
+ }
32
+ 100% {
33
+ background-position: 150% 0;
34
+ }
35
+ }
36
+
37
+ /* ---------------------------------------------------------
38
+ 2. BASE CONTROL PRIMITIVE
39
+ --------------------------------------------------------- */
40
+
41
+ .form-control {
42
+ position: relative;
43
+ z-index: 0;
44
+ display: block;
45
+ width: 100%;
46
+ min-height: var(--control-min-height);
47
+
48
+ margin: 0;
49
+ box-sizing: border-box;
50
+
51
+ padding: var(--space-3);
52
+ border: var(--border-width) solid var(--color-border-subtle);
53
+ border-radius: var(--radius-md);
54
+ background-color: var(--color-surface);
55
+ color: var(--color-text);
56
+
57
+ transition: border-color .2s, background-color .2s, box-shadow .2s;
58
+ }
59
+
60
+ /* Native control normalization (only when used as UFAL control) */
61
+
62
+ input.form-control,
63
+ select.form-control,
64
+ textarea.form-control {
65
+ font: inherit;
66
+ line-height: inherit;
67
+ background-clip: padding-box;
68
+ }
69
+
70
+ textarea.form-control {
71
+ resize: vertical;
72
+ min-height: var(--form-flow-textarea-min-height);
73
+ }
74
+
75
+ select.form-control {
76
+ -webkit-appearance: none;
77
+ -moz-appearance: none;
78
+ appearance: none;
79
+ }
80
+
81
+ /* Select dropdown affordance */
82
+
83
+ select.form-control:not([multiple]):not([size]) {
84
+ padding-right: calc(var(--space-7) + var(--space-2));
85
+ background-image: linear-gradient(45deg, transparent 50%, var(--color-text-muted) 50%),
86
+ linear-gradient(135deg, var(--color-text-muted) 50%, transparent 50%);
87
+ background-position: calc(100% - var(--space-3)) 50%,
88
+ calc(100% - var(--space-2)) 50%;
89
+ background-size: 0.4rem 0.4rem;
90
+ background-repeat: no-repeat;
91
+ }
92
+
93
+ .form-control::-moz-placeholder {
94
+ color: var(--color-placeholder);
95
+ opacity: 1;
96
+ }
97
+
98
+ .form-control::placeholder {
99
+ color: var(--color-placeholder);
100
+ opacity: 1;
101
+ }
102
+
103
+ .form-control:hover {
104
+ border-color: var(--color-border-strong);
105
+ }
106
+
107
+ .form-control:focus {
108
+ outline: none;
109
+ border-color: var(--color-accent);
110
+ }
111
+
112
+ .form-control:focus-visible {
113
+ outline: none;
114
+ border-color: var(--focus-ring-color);
115
+ box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
116
+ }
117
+
118
+ /* ARIA validation */
119
+
120
+ .form-control[aria-invalid="true"] {
121
+ border-color: var(--color-danger);
122
+ }
123
+
124
+ .form-control[aria-invalid="true"]:focus-visible {
125
+ box-shadow: 0 0 0 var(--border-width-strong) var(--color-danger-soft);
126
+ }
127
+
128
+ /* ---------------------------------------------------------
129
+ 3. WRAPPERS & GROUPING
130
+ --------------------------------------------------------- */
131
+
132
+ .form-control-wrapper {
133
+ position: relative;
134
+ width: 100%;
135
+ display: flex;
136
+ align-items: center;
137
+ }
138
+
139
+ .form-control-wrapper > .form-control {
140
+ flex: 1 1 auto;
141
+ width: auto;
142
+ }
143
+
144
+ /* ---------------------------------------------------------
145
+ 4. STATE MODIFIERS
146
+ --------------------------------------------------------- */
147
+
148
+ .form-control--loading {
149
+ cursor: progress;
150
+ opacity: .55;
151
+ pointer-events: none;
152
+ }
153
+
154
+ .form-control--disabled {
155
+ opacity: .45;
156
+ pointer-events: none;
157
+ }
158
+
159
+ .form-control--readonly {
160
+ opacity: .75;
161
+ cursor: default;
162
+ }
163
+
164
+ .form-control--error {
165
+ box-shadow: 0 0 0 var(--border-width-strong) var(--color-danger);
166
+ }
167
+
168
+ .form-control--warning {
169
+ box-shadow: 0 0 0 var(--border-width-strong) var(--color-warning);
170
+ }
171
+
172
+ .form-control--success {
173
+ box-shadow: 0 0 0 var(--border-width-strong) var(--color-success);
174
+ }
175
+
176
+ .form-control--info {
177
+ box-shadow: 0 0 0 var(--border-width-strong) var(--color-info);
178
+ }
179
+
180
+ /* Native disabled / readonly */
181
+
182
+ .form-control:disabled,
183
+ .form-control[disabled] {
184
+ background-color: var(--color-muted-bg);
185
+ color: var(--color-text-muted);
186
+ cursor: not-allowed;
187
+ }
188
+
189
+ .form-control[readonly] {
190
+ background-color: var(--color-surface-subtle);
191
+ color: var(--color-text-muted);
192
+ border-style: dashed;
193
+ }
194
+
195
+ .form-control--error:focus-visible {
196
+ box-shadow: 0 0 0 var(--border-width-strong) var(--semantic-error-border-strong, var(--color-danger));
197
+ }
198
+
199
+ /* ---------------------------------------------------------
200
+ 5. ICONS / PREFIX / SUFFIX
201
+ --------------------------------------------------------- */
202
+
203
+ .form-control-wrapper[data-icon="left"] .form-control {
204
+ padding-left: calc(var(--space-7) + var(--space-2));
205
+ }
206
+
207
+ .form-control-wrapper[data-icon="right"] .form-control {
208
+ padding-right: calc(var(--space-7) + var(--space-2));
209
+ }
210
+
211
+ .form-control-wrapper[data-icon="left"] .form-control-icon,
212
+ .form-control-wrapper[data-icon="right"] .form-control-icon {
213
+ position: absolute;
214
+ top: 50%;
215
+ transform: translateY(-50%);
216
+ display: flex;
217
+ align-items: center;
218
+ pointer-events: none;
219
+ }
220
+
221
+ .form-control-wrapper[data-icon="left"] .form-control-icon {
222
+ left: var(--space-2);
223
+ }
224
+
225
+ .form-control-wrapper[data-icon="right"] .form-control-icon {
226
+ right: var(--space-2);
227
+ }
228
+
229
+ .form-control-icon {
230
+ font-size: var(--text-md);
231
+ color: var(--color-text-muted);
232
+ }
233
+
234
+ .form-control-icon,
235
+ .form-control-prefix,
236
+ .form-control-suffix {
237
+ position: absolute;
238
+ z-index: 1;
239
+ }
240
+
241
+ /* Prefix / suffix */
242
+
243
+ .form-control-prefix,
244
+ .form-control-suffix {
245
+ position: static;
246
+ display: inline-flex;
247
+ align-items: center;
248
+ white-space: nowrap;
249
+ color: var(--color-text-muted);
250
+ font-size: var(--text-sm);
251
+ pointer-events: none;
252
+ }
253
+
254
+ .form-control-prefix {
255
+ margin-left: var(--space-3);
256
+ margin-right: var(--space-2);
257
+ }
258
+
259
+ .form-control-suffix {
260
+ margin-left: var(--space-2);
261
+ margin-right: var(--space-3);
262
+ }
263
+
264
+ /* ---------------------------------------------------------
265
+ 5A. GROUPED CONTROLS (FIXED, ORDER-INDEPENDENT)
266
+ - Group owns the outer border + radius
267
+ - Children have no outer borders (prevents double border)
268
+ - Group draws internal dividers (always present, any child type)
269
+ - Works for: .form-control, .form-control-wrapper (icons), .button
270
+ --------------------------------------------------------- */
271
+
272
+ .form-control-group {
273
+ position: relative;
274
+ display: flex;
275
+ align-items: stretch;
276
+ width: 100%;
277
+
278
+ border: var(--border-width) solid var(--color-border-subtle);
279
+ border-radius: var(--radius-md);
280
+ background-color: var(--color-surface);
281
+ }
282
+
283
+ /* Outer rounding is applied to slots, to preserve radius even when children are wrappers */
284
+
285
+ .form-control-group > * {
286
+ position: relative;
287
+ border-radius: 0;
288
+ background: transparent;
289
+ }
290
+
291
+ /* Default flex behavior: controls/wrappers flex, buttons stay auto-sized below */
292
+
293
+ .form-control-group > .form-control,
294
+ .form-control-group > .form-control-wrapper {
295
+ flex: 1 1 auto;
296
+ }
297
+
298
+ .form-control-group > .button {
299
+ flex: 0 0 auto;
300
+ }
301
+
302
+ /* Edge radii */
303
+
304
+ .form-control-group > :first-child {
305
+ border-top-left-radius: var(--radius-md);
306
+ border-bottom-left-radius: var(--radius-md);
307
+ }
308
+
309
+ .form-control-group > :last-child {
310
+ border-top-right-radius: var(--radius-md);
311
+ border-bottom-right-radius: var(--radius-md);
312
+ }
313
+
314
+ /* Internal divider (single source of truth) */
315
+
316
+ .form-control-group > * + * {
317
+ box-shadow: inset var(--border-width) 0 0 var(--color-border-subtle);
318
+ }
319
+
320
+ /* Suppress per-control focus ring inside groups (prevents double focus visuals) */
321
+
322
+ .form-control-group .form-control:focus-visible {
323
+ border-color: var(--focus-ring-color);
324
+ box-shadow: inset var(--border-width) 0 0 var(--color-border-subtle),
325
+ 0 0 0 var(--border-width-strong) var(--focus-ring-color);
326
+ }
327
+
328
+ /* Children inside group should not draw their own outer borders */
329
+
330
+ .form-control-group > .form-control,
331
+ .form-control-group > .form-control-wrapper > .form-control {
332
+ border: none;
333
+ background: transparent;
334
+ }
335
+
336
+ /* Action button integration inside groups */
337
+
338
+ .form-control-group > .button {
339
+ border: none;
340
+ background: transparent;
341
+ color: var(--color-text);
342
+
343
+ height: var(--control-min-height);
344
+ line-height: 1;
345
+ display: inline-flex;
346
+ align-items: center;
347
+ justify-content: center;
348
+
349
+ padding: 0 var(--space-3);
350
+ box-sizing: border-box;
351
+ }
352
+
353
+ /* ---------------------------------------------------------
354
+ 6. LOADING OVERLAYS
355
+ --------------------------------------------------------- */
356
+
357
+ .form-control--loading::after {
358
+ content: "";
359
+ position: absolute;
360
+ top: 0;
361
+ right: 0;
362
+ bottom: 0;
363
+ left: 0;
364
+ border-radius: inherit;
365
+ background: var(--color-surface-subtle);
366
+ animation: formControlLoading 1.5s ease-in-out infinite;
367
+ pointer-events: none;
368
+ }
369
+
370
+ @keyframes formControlLoading {
371
+ 0% {
372
+ opacity: .35;
373
+ }
374
+ 50% {
375
+ opacity: .6;
376
+ }
377
+ 100% {
378
+ opacity: .35;
379
+ }
380
+ }
381
+
382
+ /* ---------------------------------------------------------
383
+ 7. FILE UPLOAD PRIMITIVES
384
+ --------------------------------------------------------- */
385
+
386
+ .form-control--file {
387
+ position: relative;
388
+ padding: var(--space-3);
389
+ border: var(--border-width) dashed var(--color-border-subtle);
390
+ border-radius: var(--radius-md);
391
+ background-color: var(--color-surface);
392
+ color: var(--color-text-muted);
393
+ cursor: pointer;
394
+ }
395
+
396
+ .form-control--file[type="file"] {
397
+ padding: 0;
398
+ border: none;
399
+ background: none;
400
+ position: static;
401
+ }
402
+
403
+ /* Drop surface */
404
+
405
+ .form-control-file-surface {
406
+ position: relative;
407
+ display: flex;
408
+ flex-direction: column;
409
+ gap: var(--space-2);
410
+ align-items: center;
411
+ justify-content: center;
412
+ padding: var(--space-6);
413
+
414
+ border: var(--border-width) dashed var(--color-border-subtle);
415
+ border-radius: var(--radius-md);
416
+ background-color: var(--color-surface);
417
+ cursor: pointer;
418
+ }
419
+
420
+ .form-control-file-surface input[type="file"] {
421
+ position: absolute;
422
+ top: 0;
423
+ right: 0;
424
+ bottom: 0;
425
+ left: 0;
426
+ opacity: 0;
427
+ cursor: pointer;
428
+ }
429
+
430
+ .form-control-file-surface:hover {
431
+ border-color: var(--color-border-strong);
432
+ background-color: var(--color-surface-subtle);
433
+ }
434
+
435
+ .form-control-file-surface:focus-within {
436
+ box-shadow: 0 0 0 var(--border-width-strong) var(--focus-ring-color);
437
+ border-color: var(--focus-ring-color);
438
+ }
439
+
440
+ .form-control-file-surface--disabled {
441
+ opacity: .65;
442
+ cursor: not-allowed;
443
+ pointer-events: none;
444
+ }
445
+
446
+ .form-control-file-surface--error {
447
+ border-color: var(--color-danger);
448
+ background-color: var(--color-danger-soft);
449
+ }
450
+
451
+ .form-control-file-surface--warning {
452
+ border-color: var(--color-warning);
453
+ background-color: var(--color-warning-soft);
454
+ }
455
+
456
+ .form-control-file-surface--success {
457
+ border-color: var(--color-success);
458
+ background-color: var(--color-success-soft);
459
+ }
460
+
461
+ .form-control-file-surface--info {
462
+ border-color: var(--color-info);
463
+ background-color: var(--color-info-soft);
464
+ }
465
+
466
+ /* Inline upload */
467
+
468
+ .file-upload-inline {
469
+ display: inline-flex;
470
+ align-items: center;
471
+ gap: var(--space-2);
472
+ }
473
+
474
+ .file-upload-inline__input {
475
+ position: absolute;
476
+ width: 1px;
477
+ height: 1px;
478
+ overflow: hidden;
479
+ clip: rect(0, 0, 0, 0);
480
+ }
481
+
482
+ .file-upload-inline__label {
483
+ cursor: pointer;
484
+ }
485
+
486
+ /* Loading shimmer on surface */
487
+
488
+ .form-control-file-surface.form-control--loading::after {
489
+ content: "";
490
+ position: absolute;
491
+ top: 0;
492
+ right: 0;
493
+ bottom: 0;
494
+ left: 0;
495
+ border-radius: inherit;
496
+ background-image: linear-gradient(
497
+ 90deg,
498
+ var(--color-muted-bg),
499
+ var(--color-surface),
500
+ var(--color-muted-bg)
501
+ );
502
+ background-size: 200% 100%;
503
+ animation: input-loading-shimmer 1.5s infinite;
504
+ }
505
+
506
+ /* ---------------------------------------------------------
507
+ 8. LAYOUT PRIMITIVES
508
+ --------------------------------------------------------- */
509
+
510
+ .form-row {
511
+ display: flex;
512
+ flex-wrap: wrap;
513
+ gap: var(--space-4);
514
+ }
515
+
516
+ .form-grid {
517
+ display: grid;
518
+ grid-gap: var(--space-4);
519
+ gap: var(--space-4);
520
+ }
521
+
522
+ .form-grid--two {
523
+ grid-template-columns: repeat(2, minmax(0, 1fr));
524
+ }
525
+
526
+ .form-grid--three {
527
+ grid-template-columns: repeat(3, minmax(0, 1fr));
528
+ }
529
+
530
+ .form-grid--four {
531
+ grid-template-columns: repeat(4, minmax(0, 1fr));
532
+ }
533
+
534
+ @media (max-width: 768px) {
535
+ .form-grid--two,
536
+ .form-grid--three,
537
+ .form-grid--four {
538
+ grid-template-columns: 1fr;
539
+ }
540
+ }
541
+
542
+ /* ---------------------------------------------------------
543
+ 9. FORM-LEVEL MESSAGES
544
+ --------------------------------------------------------- */
545
+
546
+ .form-message {
547
+ width: 100%;
548
+ padding: var(--space-4) var(--space-6);
549
+ border-radius: var(--radius-md);
550
+ border: var(--border-width) solid var(--color-border-subtle);
551
+ background-color: var(--color-surface-subtle);
552
+ font-size: var(--text-sm);
553
+ display: flex;
554
+ align-items: flex-start;
555
+ gap: var(--space-3);
556
+ }
557
+
558
+ .form-message__icon {
559
+ width: 1.25rem;
560
+ height: 1.25rem;
561
+ flex-shrink: 0;
562
+ }
563
+
564
+ .form-message__content {
565
+ display: flex;
566
+ flex-direction: column;
567
+ gap: var(--space-1);
568
+ }
569
+
570
+ .form-message--neutral {
571
+ color: var(--color-text-muted);
572
+ }
573
+
574
+ .form-message--error {
575
+ background-color: var(--color-danger-soft);
576
+ border-color: var(--color-danger);
577
+ }
578
+
579
+ .form-message--warning {
580
+ background-color: var(--color-warning-soft);
581
+ border-color: var(--color-warning);
582
+ }
583
+
584
+ .form-message--success {
585
+ background-color: var(--color-success-soft);
586
+ border-color: var(--color-success);
587
+ }
588
+
589
+ .form-message--info {
590
+ background-color: var(--color-info-soft);
591
+ border-color: var(--color-info);
592
+ }
593
+
594
+ /* ---------------------------------------------------------
595
+ 10. FORM SECTION STATES
596
+ --------------------------------------------------------- */
597
+
598
+ .form-section--error,
599
+ .form-section--warning,
600
+ .form-section--success,
601
+ .form-section--info {
602
+ padding: var(--space-3);
603
+ border-left: var(--border-width-strong) solid;
604
+ }
605
+
606
+ .form-section--error {
607
+ border-color: var(--color-danger);
608
+ }
609
+
610
+ .form-section--warning {
611
+ border-color: var(--color-warning);
612
+ }
613
+
614
+ .form-section--success {
615
+ border-color: var(--color-success);
616
+ }
617
+
618
+ .form-section--info {
619
+ border-color: var(--color-info);
620
+ }
621
+
622
+ .form-section--error:not(:last-child),
623
+ .form-section--warning:not(:last-child),
624
+ .form-section--success:not(:last-child),
625
+ .form-section--info:not(:last-child) {
626
+ margin-bottom: var(--space-4);
627
+ }