@usefillo/react 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.css CHANGED
@@ -84,11 +84,12 @@
84
84
  /* ---------- Field chrome ---------- */
85
85
  .fillo-field { display: flex; flex-direction: column; gap: 0.45rem; }
86
86
  .fillo-label { font-weight: 550; font-size: 0.95rem; }
87
+ :where(.fillo-field--error) .fillo-label { color: var(--fillo-error); }
87
88
  /* Fillo marks the few optional fields rather than starring the many required
88
89
  ones — so a clean label reads as required by default. */
89
90
  .fillo-optional { color: var(--fillo-muted); font-weight: 400; font-size: 0.85em; }
90
91
  .fillo-description { color: var(--fillo-muted); font-size: 0.85rem; margin: 0; line-height: 1.5; }
91
- .fillo-error { color: var(--fillo-error); font-size: 0.85rem; margin: 0; }
92
+ .fillo-error { color: var(--fillo-error); font-size: 0.85rem; font-weight: 500; line-height: 1.4; margin: 0; }
92
93
  .fillo-submit-error { color: var(--fillo-error); font-size: 0.85rem; font-weight: 500; margin: 0; }
93
94
 
94
95
  /* ---------- Inputs ---------- */
@@ -111,6 +112,11 @@
111
112
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
112
113
  }
113
114
  :where(.fillo-field--error) .fillo-input { border-color: var(--fillo-error); }
115
+ :where(.fillo-field--error) .fillo-input:focus-visible {
116
+ border-color: var(--fillo-error);
117
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
118
+ outline: none;
119
+ }
114
120
  .fillo-textarea { resize: vertical; min-height: 6rem; }
115
121
 
116
122
  /* Number field adornments: the wrapper draws the field's box (border/
@@ -134,6 +140,11 @@
134
140
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
135
141
  }
136
142
  :where(.fillo-field--error) .fillo-number { border-color: var(--fillo-error); }
143
+ :where(.fillo-field--error) .fillo-number:focus-within {
144
+ border-color: var(--fillo-error);
145
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
146
+ outline: none;
147
+ }
137
148
  :where(.fillo-number) .fillo-input {
138
149
  flex: 1 1 auto;
139
150
  min-width: 0;
@@ -143,6 +154,7 @@
143
154
  padding: 0;
144
155
  }
145
156
  :where(.fillo-number) .fillo-input:focus-visible { box-shadow: none; }
157
+ :where(.fillo-field--error) :where(.fillo-number) .fillo-input:focus-visible { outline: none; }
146
158
  .fillo-number-prefix, .fillo-number-suffix { flex: 0 0 auto; color: var(--fillo-muted); }
147
159
 
148
160
  /* Phone field: country picker + national input */
@@ -242,6 +254,15 @@
242
254
  border-color: var(--fillo-primary);
243
255
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 14%, transparent);
244
256
  }
257
+ :where(.fillo-field--error) .fillo-option {
258
+ border-color: var(--fillo-error);
259
+ background: color-mix(in srgb, var(--fillo-error) 2%, var(--fillo-control-bg));
260
+ }
261
+ :where(.fillo-field--error) .fillo-option:focus-within {
262
+ border-color: var(--fillo-error);
263
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
264
+ outline: none;
265
+ }
245
266
  .fillo-option--selected {
246
267
  border-color: var(--fillo-primary);
247
268
  background: color-mix(in srgb, var(--fillo-primary) 6%, var(--fillo-control-bg));
@@ -357,6 +378,13 @@
357
378
  inset 0 0 0 1px var(--fillo-primary),
358
379
  0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
359
380
  }
381
+ :where(.fillo-field--error) .fillo-toggle { border-color: var(--fillo-error); }
382
+ :where(.fillo-field--error) :where(.fillo-toggle-input:focus-visible) + .fillo-toggle-track {
383
+ box-shadow:
384
+ inset 0 0 0 1px var(--fillo-error),
385
+ 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
386
+ outline: none;
387
+ }
360
388
  .fillo-toggle:where(:has(.fillo-toggle-input:checked)) {
361
389
  border-color: var(--fillo-primary);
362
390
  background: color-mix(in srgb, var(--fillo-primary) 5%, var(--fillo-control-bg));
@@ -416,6 +444,11 @@
416
444
  border-color: var(--fillo-primary);
417
445
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
418
446
  }
447
+ :where(.fillo-field--error) .fillo-scale-step:focus-visible {
448
+ border-color: var(--fillo-error);
449
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
450
+ outline: none;
451
+ }
419
452
  .fillo-scale-step--active {
420
453
  background: var(--fillo-primary);
421
454
  border-color: var(--fillo-primary);
@@ -480,6 +513,12 @@
480
513
  .fillo-matrix thead th { color: var(--fillo-muted); font-weight: 500; font-size: 0.8rem; }
481
514
  .fillo-matrix tbody th { text-align: start; font-weight: 500; }
482
515
  .fillo-matrix tbody tr:last-child th, .fillo-matrix tbody tr:last-child td { border-bottom: none; }
516
+ .fillo-matrix-cell:focus-within {
517
+ border-radius: var(--fillo-radius);
518
+ outline: 2px solid var(--fillo-primary);
519
+ outline-offset: 2px;
520
+ }
521
+ :where(.fillo-field--error) .fillo-matrix-cell:focus-within { outline-color: var(--fillo-error); }
483
522
 
484
523
  /* ---------- Calculated ---------- */
485
524
  /* A quiet computed-value line, not a control: no border or input chrome —
@@ -571,6 +610,15 @@
571
610
  border-color: var(--fillo-primary);
572
611
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
573
612
  }
613
+ :where(.fillo-field--error) .fillo-dropzone {
614
+ border-color: var(--fillo-error);
615
+ background: color-mix(in srgb, var(--fillo-error) 2%, var(--fillo-control-bg));
616
+ }
617
+ :where(.fillo-field--error) .fillo-dropzone:focus-visible {
618
+ border-color: var(--fillo-error);
619
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
620
+ outline: none;
621
+ }
574
622
  .fillo-dropzone--disabled { cursor: not-allowed; opacity: 0.6; }
575
623
  .fillo-dropzone-title { font-weight: 550; font-size: 0.95rem; }
576
624
  .fillo-dropzone-hint { color: var(--fillo-muted); font-size: 0.82rem; }
@@ -578,20 +626,50 @@
578
626
  .fillo-files { list-style: none; display: flex; flex-direction: column; gap: 0.45rem; margin: 0; padding: 0; }
579
627
  .fillo-file {
580
628
  display: grid;
581
- grid-template-columns: minmax(0, 1fr) auto auto;
629
+ grid-template-columns: auto minmax(0, 1fr) auto;
582
630
  align-items: center;
583
- gap: 0.45rem 0.65rem;
631
+ gap: 0.45rem 0.625rem;
584
632
  border: 1px solid var(--fillo-border);
585
633
  border-radius: var(--fillo-radius);
586
634
  padding: 0.625rem 0.75rem;
587
635
  font-size: 0.88rem;
588
636
  background: var(--fillo-control-bg);
637
+ box-sizing: border-box;
638
+ min-width: 0;
589
639
  }
590
640
  .fillo-file--failed {
591
- grid-template-columns: minmax(0, 1fr) auto;
592
- border-color: var(--fillo-error);
641
+ border-color: color-mix(in srgb, var(--fillo-error) 32%, var(--fillo-border));
642
+ border-inline-start: 3px solid var(--fillo-error);
643
+ background: color-mix(in srgb, var(--fillo-error) 4%, var(--fillo-control-bg));
644
+ }
645
+ .fillo-file-state {
646
+ display: inline-flex;
647
+ align-items: center;
648
+ justify-content: center;
649
+ width: 1.5rem;
650
+ height: 1.5rem;
651
+ color: var(--fillo-muted);
652
+ }
653
+ .fillo-file-state--done { color: var(--fillo-primary); }
654
+ .fillo-file-state--failed { color: var(--fillo-error); }
655
+ .fillo-file-state-icon {
656
+ display: block;
657
+ width: 1.35rem;
658
+ height: 1.35rem;
659
+ stroke: currentColor;
660
+ stroke-width: 1.75;
661
+ stroke-linecap: round;
662
+ stroke-linejoin: round;
663
+ }
664
+ .fillo-file-content {
665
+ display: flex;
666
+ flex-direction: column;
667
+ gap: 0.1rem;
668
+ min-width: 0;
669
+ text-align: start;
593
670
  }
594
671
  .fillo-file-name {
672
+ display: block;
595
673
  min-width: 0;
596
674
  overflow: hidden;
597
675
  text-overflow: ellipsis;
@@ -609,14 +687,14 @@
609
687
  display: inline-flex;
610
688
  align-items: center;
611
689
  justify-content: flex-end;
612
- gap: 0.15rem;
690
+ gap: 0.25rem;
613
691
  white-space: nowrap;
614
692
  }
615
693
  .fillo-file-error {
616
- grid-column: 1 / -1;
617
694
  color: var(--fillo-error);
618
695
  font-size: 0.82rem;
619
696
  line-height: 1.35;
697
+ overflow-wrap: anywhere;
620
698
  }
621
699
  .fillo-file-remove {
622
700
  appearance: none;
@@ -627,11 +705,18 @@
627
705
  border: none;
628
706
  cursor: pointer;
629
707
  color: var(--fillo-muted);
630
- font-size: 1.1rem;
631
708
  line-height: 1;
632
709
  padding: 0;
633
710
  }
634
- .fillo-file-remove:hover { color: var(--fillo-error); }
711
+ .fillo-file-action-icon {
712
+ display: block;
713
+ width: 1rem;
714
+ height: 1rem;
715
+ stroke: currentColor;
716
+ stroke-width: 1.75;
717
+ stroke-linecap: round;
718
+ }
719
+ .fillo-file-remove:hover { color: var(--fillo-text); }
635
720
  .fillo-file-retry {
636
721
  appearance: none;
637
722
  display: inline-flex;
@@ -643,11 +728,11 @@
643
728
  color: var(--fillo-primary);
644
729
  font: inherit;
645
730
  font-weight: 550;
646
- padding: 0 0.3rem;
731
+ padding: 0 0.5rem;
647
732
  }
648
733
  .fillo-file-retry:hover { text-decoration: underline; }
649
734
  .fillo-progress {
650
- grid-column: 1 / -1;
735
+ grid-column: 2 / -1;
651
736
  height: 4px;
652
737
  border-radius: 999px;
653
738
  background: var(--fillo-border);
@@ -913,18 +998,45 @@
913
998
  line-height: 1.45;
914
999
  }
915
1000
 
916
- /* Error summary (GOV.UK pattern): ONE alert on failed submit; links focus
917
- their fields. Per-field messages are plain describedby text. */
918
- .fillo-error-summary {
919
- border: 2px solid var(--fillo-error);
1001
+ /* Repeating group: instance cards + add/remove controls. */
1002
+ .fillo-group { display: flex; flex-direction: column; gap: 0.8rem; }
1003
+ .fillo-group-instance {
1004
+ border: 1px solid var(--fillo-border);
920
1005
  border-radius: var(--fillo-radius);
921
- padding: 0.8rem 1rem;
1006
+ padding: 0.8rem;
1007
+ display: flex;
1008
+ flex-direction: column;
1009
+ gap: 0.9rem;
922
1010
  }
923
- .fillo-error-summary-title { margin: 0 0 0.4rem; font-size: 1rem; font-weight: 600; }
924
- .fillo-error-summary ul { margin: 0; padding-inline-start: 1.1rem; }
925
- .fillo-error-summary li { margin: 0.2rem 0; }
926
- .fillo-error-summary a { color: var(--fillo-error); }
927
-
1011
+ .fillo-group-instance:focus-visible { outline: 2px solid var(--fillo-primary); outline-offset: 2px; }
1012
+ .fillo-group-instance-header {
1013
+ display: flex;
1014
+ align-items: center;
1015
+ justify-content: space-between;
1016
+ gap: 0.5rem;
1017
+ }
1018
+ .fillo-group-instance-title { margin: 0; font-size: 0.95rem; font-weight: 600; }
1019
+ .fillo-group-add {
1020
+ align-self: flex-start;
1021
+ font: inherit;
1022
+ padding: 0.45rem 0.9rem;
1023
+ border: 1px solid var(--fillo-border);
1024
+ border-radius: var(--fillo-radius);
1025
+ background: var(--fillo-control-bg);
1026
+ color: var(--fillo-text);
1027
+ cursor: pointer;
1028
+ }
1029
+ .fillo-group-add:disabled { opacity: 0.5; cursor: default; }
1030
+ .fillo-group-remove {
1031
+ font-size: 1.1rem;
1032
+ line-height: 1;
1033
+ padding: 0 0.2rem;
1034
+ border: none;
1035
+ background: none;
1036
+ color: var(--fillo-muted);
1037
+ cursor: pointer;
1038
+ }
1039
+ .fillo-group-remove:disabled { opacity: 0.5; cursor: default; }
928
1040
  /* ---------- Accessibility: focus rings, pointer targets, forced-colors
929
1041
  (input-quality audit P1.1-P1.3) ---------- */
930
1042
  /* Focus visibility: controls without a compensating border swap get a
@@ -936,23 +1048,32 @@
936
1048
  .fillo-ranking-move:focus-visible,
937
1049
  .fillo-file-remove:focus-visible,
938
1050
  .fillo-file-retry:focus-visible,
939
- .fillo-signature-clear:focus-visible {
1051
+ .fillo-signature-clear:focus-visible,
1052
+ .fillo-group-add:focus-visible,
1053
+ .fillo-group-remove:focus-visible {
940
1054
  outline: 2px solid var(--fillo-primary);
941
1055
  outline-offset: 2px;
942
1056
  box-shadow: none;
943
1057
  }
1058
+ :where(.fillo-field--error) .fillo-star:focus-visible { outline-color: var(--fillo-error); }
944
1059
 
945
1060
  /* Minimum pointer targets (WCAG 2.2 AA 24px; 1.5rem = 24px @16px root). */
946
1061
  .fillo-ranking-move,
947
- .fillo-file-remove,
948
- .fillo-file-retry,
949
- .fillo-signature-clear {
1062
+ .fillo-signature-clear,
1063
+ .fillo-group-add,
1064
+ .fillo-group-remove {
950
1065
  min-width: 1.5rem;
951
1066
  min-height: 1.5rem;
952
1067
  display: inline-flex;
953
1068
  align-items: center;
954
1069
  justify-content: center;
955
1070
  }
1071
+ /* Upload row actions use a calmer, easier 32px target. */
1072
+ .fillo-file-remove,
1073
+ .fillo-file-retry {
1074
+ min-width: 2rem;
1075
+ min-height: 2rem;
1076
+ }
956
1077
  .fillo-star { min-width: 1.75rem; min-height: 1.75rem; }
957
1078
  .fillo-matrix-cell {
958
1079
  display: flex;
@@ -974,6 +1095,7 @@
974
1095
  /* Matrix on narrow viewports: stacked rows, never a horizontal scroll
975
1096
  trap. Column names come from data-label (set by both renderers). */
976
1097
  @media (max-width: 30rem) {
1098
+ .fillo-file { gap: 0.4rem 0.5rem; padding: 0.55rem 0.625rem; }
977
1099
  .fillo-matrix thead { position: absolute; width: 1px; height: 1px;
978
1100
  overflow: hidden; clip-path: inset(50%); }
979
1101
  .fillo-matrix, .fillo-matrix tbody, .fillo-matrix tr,
@@ -991,6 +1113,12 @@
991
1113
 
992
1114
  /* Forced-colors: restore what author colors can't carry. */
993
1115
  @media (forced-colors: active) {
1116
+ .fillo-file--failed {
1117
+ border-color: CanvasText;
1118
+ border-inline-start-color: Highlight;
1119
+ background: Canvas;
1120
+ }
1121
+ .fillo-file-state--failed { color: Highlight; }
994
1122
  .fillo-star--active,
995
1123
  .fillo-scale-step--active,
996
1124
  :where(.fillo-option--selected) {
@@ -1001,7 +1129,11 @@
1001
1129
  .fillo-number:focus-within,
1002
1130
  .fillo-phone-flag:focus-visible,
1003
1131
  .fillo-phone-search:focus-visible,
1132
+ .fillo-option:focus-within,
1133
+ :where(.fillo-toggle-input:focus-visible) + .fillo-toggle-track,
1134
+ .fillo-star:focus-visible,
1004
1135
  .fillo-scale-step:focus-visible,
1136
+ .fillo-matrix-cell:focus-within,
1005
1137
  .fillo-dropzone:focus-visible,
1006
1138
  .fillo-select:focus-visible {
1007
1139
  outline: 2px solid Highlight;
@@ -84,11 +84,12 @@
84
84
  /* ---------- Field chrome ---------- */
85
85
  .fillo-field { display: flex; flex-direction: column; gap: 0.45rem; }
86
86
  .fillo-label { font-weight: 550; font-size: 0.95rem; }
87
+ :where(.fillo-field--error) .fillo-label { color: var(--fillo-error); }
87
88
  /* Fillo marks the few optional fields rather than starring the many required
88
89
  ones — so a clean label reads as required by default. */
89
90
  .fillo-optional { color: var(--fillo-muted); font-weight: 400; font-size: 0.85em; }
90
91
  .fillo-description { color: var(--fillo-muted); font-size: 0.85rem; margin: 0; line-height: 1.5; }
91
- .fillo-error { color: var(--fillo-error); font-size: 0.85rem; margin: 0; }
92
+ .fillo-error { color: var(--fillo-error); font-size: 0.85rem; font-weight: 500; line-height: 1.4; margin: 0; }
92
93
  .fillo-submit-error { color: var(--fillo-error); font-size: 0.85rem; font-weight: 500; margin: 0; }
93
94
 
94
95
  /* ---------- Inputs ---------- */
@@ -111,6 +112,11 @@
111
112
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
112
113
  }
113
114
  :where(.fillo-field--error) .fillo-input { border-color: var(--fillo-error); }
115
+ :where(.fillo-field--error) .fillo-input:focus-visible {
116
+ border-color: var(--fillo-error);
117
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
118
+ outline: none;
119
+ }
114
120
  .fillo-textarea { resize: vertical; min-height: 6rem; }
115
121
 
116
122
  /* Number field adornments: the wrapper draws the field's box (border/
@@ -134,6 +140,11 @@
134
140
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
135
141
  }
136
142
  :where(.fillo-field--error) .fillo-number { border-color: var(--fillo-error); }
143
+ :where(.fillo-field--error) .fillo-number:focus-within {
144
+ border-color: var(--fillo-error);
145
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
146
+ outline: none;
147
+ }
137
148
  :where(.fillo-number) .fillo-input {
138
149
  flex: 1 1 auto;
139
150
  min-width: 0;
@@ -143,6 +154,7 @@
143
154
  padding: 0;
144
155
  }
145
156
  :where(.fillo-number) .fillo-input:focus-visible { box-shadow: none; }
157
+ :where(.fillo-field--error) :where(.fillo-number) .fillo-input:focus-visible { outline: none; }
146
158
  .fillo-number-prefix, .fillo-number-suffix { flex: 0 0 auto; color: var(--fillo-muted); }
147
159
 
148
160
  /* Phone field: country picker + national input */
@@ -242,6 +254,15 @@
242
254
  border-color: var(--fillo-primary);
243
255
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 14%, transparent);
244
256
  }
257
+ :where(.fillo-field--error) .fillo-option {
258
+ border-color: var(--fillo-error);
259
+ background: color-mix(in srgb, var(--fillo-error) 2%, var(--fillo-control-bg));
260
+ }
261
+ :where(.fillo-field--error) .fillo-option:focus-within {
262
+ border-color: var(--fillo-error);
263
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
264
+ outline: none;
265
+ }
245
266
  .fillo-option--selected {
246
267
  border-color: var(--fillo-primary);
247
268
  background: color-mix(in srgb, var(--fillo-primary) 6%, var(--fillo-control-bg));
@@ -357,6 +378,13 @@
357
378
  inset 0 0 0 1px var(--fillo-primary),
358
379
  0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
359
380
  }
381
+ :where(.fillo-field--error) .fillo-toggle { border-color: var(--fillo-error); }
382
+ :where(.fillo-field--error) :where(.fillo-toggle-input:focus-visible) + .fillo-toggle-track {
383
+ box-shadow:
384
+ inset 0 0 0 1px var(--fillo-error),
385
+ 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
386
+ outline: none;
387
+ }
360
388
  .fillo-toggle:where(:has(.fillo-toggle-input:checked)) {
361
389
  border-color: var(--fillo-primary);
362
390
  background: color-mix(in srgb, var(--fillo-primary) 5%, var(--fillo-control-bg));
@@ -416,6 +444,11 @@
416
444
  border-color: var(--fillo-primary);
417
445
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
418
446
  }
447
+ :where(.fillo-field--error) .fillo-scale-step:focus-visible {
448
+ border-color: var(--fillo-error);
449
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
450
+ outline: none;
451
+ }
419
452
  .fillo-scale-step--active {
420
453
  background: var(--fillo-primary);
421
454
  border-color: var(--fillo-primary);
@@ -480,6 +513,12 @@
480
513
  .fillo-matrix thead th { color: var(--fillo-muted); font-weight: 500; font-size: 0.8rem; }
481
514
  .fillo-matrix tbody th { text-align: start; font-weight: 500; }
482
515
  .fillo-matrix tbody tr:last-child th, .fillo-matrix tbody tr:last-child td { border-bottom: none; }
516
+ .fillo-matrix-cell:focus-within {
517
+ border-radius: var(--fillo-radius);
518
+ outline: 2px solid var(--fillo-primary);
519
+ outline-offset: 2px;
520
+ }
521
+ :where(.fillo-field--error) .fillo-matrix-cell:focus-within { outline-color: var(--fillo-error); }
483
522
 
484
523
  /* ---------- Calculated ---------- */
485
524
  /* A quiet computed-value line, not a control: no border or input chrome —
@@ -571,6 +610,15 @@
571
610
  border-color: var(--fillo-primary);
572
611
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
573
612
  }
613
+ :where(.fillo-field--error) .fillo-dropzone {
614
+ border-color: var(--fillo-error);
615
+ background: color-mix(in srgb, var(--fillo-error) 2%, var(--fillo-control-bg));
616
+ }
617
+ :where(.fillo-field--error) .fillo-dropzone:focus-visible {
618
+ border-color: var(--fillo-error);
619
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-error) 20%, transparent);
620
+ outline: none;
621
+ }
574
622
  .fillo-dropzone--disabled { cursor: not-allowed; opacity: 0.6; }
575
623
  .fillo-dropzone-title { font-weight: 550; font-size: 0.95rem; }
576
624
  .fillo-dropzone-hint { color: var(--fillo-muted); font-size: 0.82rem; }
@@ -578,20 +626,50 @@
578
626
  .fillo-files { list-style: none; display: flex; flex-direction: column; gap: 0.45rem; margin: 0; padding: 0; }
579
627
  .fillo-file {
580
628
  display: grid;
581
- grid-template-columns: minmax(0, 1fr) auto auto;
629
+ grid-template-columns: auto minmax(0, 1fr) auto;
582
630
  align-items: center;
583
- gap: 0.45rem 0.65rem;
631
+ gap: 0.45rem 0.625rem;
584
632
  border: 1px solid var(--fillo-border);
585
633
  border-radius: var(--fillo-radius);
586
634
  padding: 0.625rem 0.75rem;
587
635
  font-size: 0.88rem;
588
636
  background: var(--fillo-control-bg);
637
+ box-sizing: border-box;
638
+ min-width: 0;
589
639
  }
590
640
  .fillo-file--failed {
591
- grid-template-columns: minmax(0, 1fr) auto;
592
- border-color: var(--fillo-error);
641
+ border-color: color-mix(in srgb, var(--fillo-error) 32%, var(--fillo-border));
642
+ border-inline-start: 3px solid var(--fillo-error);
643
+ background: color-mix(in srgb, var(--fillo-error) 4%, var(--fillo-control-bg));
644
+ }
645
+ .fillo-file-state {
646
+ display: inline-flex;
647
+ align-items: center;
648
+ justify-content: center;
649
+ width: 1.5rem;
650
+ height: 1.5rem;
651
+ color: var(--fillo-muted);
652
+ }
653
+ .fillo-file-state--done { color: var(--fillo-primary); }
654
+ .fillo-file-state--failed { color: var(--fillo-error); }
655
+ .fillo-file-state-icon {
656
+ display: block;
657
+ width: 1.35rem;
658
+ height: 1.35rem;
659
+ stroke: currentColor;
660
+ stroke-width: 1.75;
661
+ stroke-linecap: round;
662
+ stroke-linejoin: round;
663
+ }
664
+ .fillo-file-content {
665
+ display: flex;
666
+ flex-direction: column;
667
+ gap: 0.1rem;
668
+ min-width: 0;
669
+ text-align: start;
593
670
  }
594
671
  .fillo-file-name {
672
+ display: block;
595
673
  min-width: 0;
596
674
  overflow: hidden;
597
675
  text-overflow: ellipsis;
@@ -609,14 +687,14 @@
609
687
  display: inline-flex;
610
688
  align-items: center;
611
689
  justify-content: flex-end;
612
- gap: 0.15rem;
690
+ gap: 0.25rem;
613
691
  white-space: nowrap;
614
692
  }
615
693
  .fillo-file-error {
616
- grid-column: 1 / -1;
617
694
  color: var(--fillo-error);
618
695
  font-size: 0.82rem;
619
696
  line-height: 1.35;
697
+ overflow-wrap: anywhere;
620
698
  }
621
699
  .fillo-file-remove {
622
700
  appearance: none;
@@ -627,11 +705,18 @@
627
705
  border: none;
628
706
  cursor: pointer;
629
707
  color: var(--fillo-muted);
630
- font-size: 1.1rem;
631
708
  line-height: 1;
632
709
  padding: 0;
633
710
  }
634
- .fillo-file-remove:hover { color: var(--fillo-error); }
711
+ .fillo-file-action-icon {
712
+ display: block;
713
+ width: 1rem;
714
+ height: 1rem;
715
+ stroke: currentColor;
716
+ stroke-width: 1.75;
717
+ stroke-linecap: round;
718
+ }
719
+ .fillo-file-remove:hover { color: var(--fillo-text); }
635
720
  .fillo-file-retry {
636
721
  appearance: none;
637
722
  display: inline-flex;
@@ -643,11 +728,11 @@
643
728
  color: var(--fillo-primary);
644
729
  font: inherit;
645
730
  font-weight: 550;
646
- padding: 0 0.3rem;
731
+ padding: 0 0.5rem;
647
732
  }
648
733
  .fillo-file-retry:hover { text-decoration: underline; }
649
734
  .fillo-progress {
650
- grid-column: 1 / -1;
735
+ grid-column: 2 / -1;
651
736
  height: 4px;
652
737
  border-radius: 999px;
653
738
  background: var(--fillo-border);
@@ -913,18 +998,45 @@
913
998
  line-height: 1.45;
914
999
  }
915
1000
 
916
- /* Error summary (GOV.UK pattern): ONE alert on failed submit; links focus
917
- their fields. Per-field messages are plain describedby text. */
918
- .fillo-error-summary {
919
- border: 2px solid var(--fillo-error);
1001
+ /* Repeating group: instance cards + add/remove controls. */
1002
+ .fillo-group { display: flex; flex-direction: column; gap: 0.8rem; }
1003
+ .fillo-group-instance {
1004
+ border: 1px solid var(--fillo-border);
920
1005
  border-radius: var(--fillo-radius);
921
- padding: 0.8rem 1rem;
1006
+ padding: 0.8rem;
1007
+ display: flex;
1008
+ flex-direction: column;
1009
+ gap: 0.9rem;
922
1010
  }
923
- .fillo-error-summary-title { margin: 0 0 0.4rem; font-size: 1rem; font-weight: 600; }
924
- .fillo-error-summary ul { margin: 0; padding-inline-start: 1.1rem; }
925
- .fillo-error-summary li { margin: 0.2rem 0; }
926
- .fillo-error-summary a { color: var(--fillo-error); }
927
-
1011
+ .fillo-group-instance:focus-visible { outline: 2px solid var(--fillo-primary); outline-offset: 2px; }
1012
+ .fillo-group-instance-header {
1013
+ display: flex;
1014
+ align-items: center;
1015
+ justify-content: space-between;
1016
+ gap: 0.5rem;
1017
+ }
1018
+ .fillo-group-instance-title { margin: 0; font-size: 0.95rem; font-weight: 600; }
1019
+ .fillo-group-add {
1020
+ align-self: flex-start;
1021
+ font: inherit;
1022
+ padding: 0.45rem 0.9rem;
1023
+ border: 1px solid var(--fillo-border);
1024
+ border-radius: var(--fillo-radius);
1025
+ background: var(--fillo-control-bg);
1026
+ color: var(--fillo-text);
1027
+ cursor: pointer;
1028
+ }
1029
+ .fillo-group-add:disabled { opacity: 0.5; cursor: default; }
1030
+ .fillo-group-remove {
1031
+ font-size: 1.1rem;
1032
+ line-height: 1;
1033
+ padding: 0 0.2rem;
1034
+ border: none;
1035
+ background: none;
1036
+ color: var(--fillo-muted);
1037
+ cursor: pointer;
1038
+ }
1039
+ .fillo-group-remove:disabled { opacity: 0.5; cursor: default; }
928
1040
  /* ---------- Accessibility: focus rings, pointer targets, forced-colors
929
1041
  (input-quality audit P1.1-P1.3) ---------- */
930
1042
  /* Focus visibility: controls without a compensating border swap get a
@@ -936,23 +1048,32 @@
936
1048
  .fillo-ranking-move:focus-visible,
937
1049
  .fillo-file-remove:focus-visible,
938
1050
  .fillo-file-retry:focus-visible,
939
- .fillo-signature-clear:focus-visible {
1051
+ .fillo-signature-clear:focus-visible,
1052
+ .fillo-group-add:focus-visible,
1053
+ .fillo-group-remove:focus-visible {
940
1054
  outline: 2px solid var(--fillo-primary);
941
1055
  outline-offset: 2px;
942
1056
  box-shadow: none;
943
1057
  }
1058
+ :where(.fillo-field--error) .fillo-star:focus-visible { outline-color: var(--fillo-error); }
944
1059
 
945
1060
  /* Minimum pointer targets (WCAG 2.2 AA 24px; 1.5rem = 24px @16px root). */
946
1061
  .fillo-ranking-move,
947
- .fillo-file-remove,
948
- .fillo-file-retry,
949
- .fillo-signature-clear {
1062
+ .fillo-signature-clear,
1063
+ .fillo-group-add,
1064
+ .fillo-group-remove {
950
1065
  min-width: 1.5rem;
951
1066
  min-height: 1.5rem;
952
1067
  display: inline-flex;
953
1068
  align-items: center;
954
1069
  justify-content: center;
955
1070
  }
1071
+ /* Upload row actions use a calmer, easier 32px target. */
1072
+ .fillo-file-remove,
1073
+ .fillo-file-retry {
1074
+ min-width: 2rem;
1075
+ min-height: 2rem;
1076
+ }
956
1077
  .fillo-star { min-width: 1.75rem; min-height: 1.75rem; }
957
1078
  .fillo-matrix-cell {
958
1079
  display: flex;
@@ -974,6 +1095,7 @@
974
1095
  /* Matrix on narrow viewports: stacked rows, never a horizontal scroll
975
1096
  trap. Column names come from data-label (set by both renderers). */
976
1097
  @media (max-width: 30rem) {
1098
+ .fillo-file { gap: 0.4rem 0.5rem; padding: 0.55rem 0.625rem; }
977
1099
  .fillo-matrix thead { position: absolute; width: 1px; height: 1px;
978
1100
  overflow: hidden; clip-path: inset(50%); }
979
1101
  .fillo-matrix, .fillo-matrix tbody, .fillo-matrix tr,
@@ -991,6 +1113,12 @@
991
1113
 
992
1114
  /* Forced-colors: restore what author colors can't carry. */
993
1115
  @media (forced-colors: active) {
1116
+ .fillo-file--failed {
1117
+ border-color: CanvasText;
1118
+ border-inline-start-color: Highlight;
1119
+ background: Canvas;
1120
+ }
1121
+ .fillo-file-state--failed { color: Highlight; }
994
1122
  .fillo-star--active,
995
1123
  .fillo-scale-step--active,
996
1124
  :where(.fillo-option--selected) {
@@ -1001,7 +1129,11 @@
1001
1129
  .fillo-number:focus-within,
1002
1130
  .fillo-phone-flag:focus-visible,
1003
1131
  .fillo-phone-search:focus-visible,
1132
+ .fillo-option:focus-within,
1133
+ :where(.fillo-toggle-input:focus-visible) + .fillo-toggle-track,
1134
+ .fillo-star:focus-visible,
1004
1135
  .fillo-scale-step:focus-visible,
1136
+ .fillo-matrix-cell:focus-within,
1005
1137
  .fillo-dropzone:focus-visible,
1006
1138
  .fillo-select:focus-visible {
1007
1139
  outline: 2px solid Highlight;