@usefillo/react 0.11.0 → 0.12.1

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,40 +626,113 @@
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: 1fr auto auto;
629
+ grid-template-columns: auto minmax(0, 1fr) auto;
582
630
  align-items: center;
583
- gap: 0.3rem 0.8rem;
631
+ gap: 0.45rem 0.625rem;
584
632
  border: 1px solid var(--fillo-border);
585
633
  border-radius: var(--fillo-radius);
586
- padding: 0.5rem 0.75rem;
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
- align-items: start;
593
- 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;
670
+ }
671
+ .fillo-file-name {
672
+ display: block;
673
+ min-width: 0;
674
+ overflow: hidden;
675
+ text-overflow: ellipsis;
676
+ white-space: nowrap;
677
+ font-weight: 500;
678
+ line-height: 1.35;
679
+ }
680
+ .fillo-file-meta {
681
+ color: var(--fillo-muted);
682
+ font-variant-numeric: tabular-nums;
683
+ line-height: 1.35;
684
+ white-space: nowrap;
685
+ }
686
+ .fillo-file-actions {
687
+ display: inline-flex;
688
+ align-items: center;
689
+ justify-content: flex-end;
690
+ gap: 0.25rem;
691
+ white-space: nowrap;
692
+ }
693
+ .fillo-file-error {
694
+ color: var(--fillo-error);
695
+ font-size: 0.82rem;
696
+ line-height: 1.35;
697
+ overflow-wrap: anywhere;
594
698
  }
595
- .fillo-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
596
- .fillo-file-meta { color: var(--fillo-muted); font-variant-numeric: tabular-nums; }
597
- .fillo-file-actions { display: inline-flex; align-items: center; gap: 0.45rem; }
598
- .fillo-file-error { grid-column: 1 / -1; color: var(--fillo-error); font-size: 0.82rem; line-height: 1.35; }
599
- .fillo-file-retry { border: 0; padding: 0; background: none; color: var(--fillo-primary); cursor: pointer; font: inherit; font-weight: 550; }
600
- .fillo-file-retry:hover { text-decoration: underline; }
601
699
  .fillo-file-remove {
602
- background: none; border: none; cursor: pointer;
603
- color: var(--fillo-muted); font-size: 1.1rem; line-height: 1; padding: 0 0.2rem;
700
+ appearance: none;
701
+ display: inline-flex;
702
+ align-items: center;
703
+ justify-content: center;
704
+ background: none;
705
+ border: none;
706
+ cursor: pointer;
707
+ color: var(--fillo-muted);
708
+ line-height: 1;
709
+ padding: 0;
604
710
  }
605
- .fillo-file-remove:hover { color: var(--fillo-error); }
606
- /* Same reset as .fillo-file-remove above (was completely unstyled, audit
607
- P2.14) — primary on hover, not error, since retry is constructive. */
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); }
608
720
  .fillo-file-retry {
609
- background: none; border: none; cursor: pointer;
610
- color: var(--fillo-muted); font: inherit; font-weight: 500; padding: 0 0.2rem;
721
+ appearance: none;
722
+ display: inline-flex;
723
+ align-items: center;
724
+ justify-content: center;
725
+ background: none;
726
+ border: none;
727
+ cursor: pointer;
728
+ color: var(--fillo-primary);
729
+ font: inherit;
730
+ font-weight: 550;
731
+ padding: 0 0.5rem;
611
732
  }
612
- .fillo-file-retry:hover { color: var(--fillo-primary); }
733
+ .fillo-file-retry:hover { text-decoration: underline; }
613
734
  .fillo-progress {
614
- grid-column: 1 / -1;
735
+ grid-column: 2 / -1;
615
736
  height: 4px;
616
737
  border-radius: 999px;
617
738
  background: var(--fillo-border);
@@ -877,18 +998,6 @@
877
998
  line-height: 1.45;
878
999
  }
879
1000
 
880
- /* Error summary (GOV.UK pattern): ONE alert on failed submit; links focus
881
- their fields. Per-field messages are plain describedby text. */
882
- .fillo-error-summary {
883
- border: 2px solid var(--fillo-error);
884
- border-radius: var(--fillo-radius);
885
- padding: 0.8rem 1rem;
886
- }
887
- .fillo-error-summary-title { margin: 0 0 0.4rem; font-size: 1rem; font-weight: 600; }
888
- .fillo-error-summary ul { margin: 0; padding-inline-start: 1.1rem; }
889
- .fillo-error-summary li { margin: 0.2rem 0; }
890
- .fillo-error-summary a { color: var(--fillo-error); }
891
-
892
1001
  /* ---------- Accessibility: focus rings, pointer targets, forced-colors
893
1002
  (input-quality audit P1.1-P1.3) ---------- */
894
1003
  /* Focus visibility: controls without a compensating border swap get a
@@ -905,11 +1014,10 @@
905
1014
  outline-offset: 2px;
906
1015
  box-shadow: none;
907
1016
  }
1017
+ :where(.fillo-field--error) .fillo-star:focus-visible { outline-color: var(--fillo-error); }
908
1018
 
909
1019
  /* Minimum pointer targets (WCAG 2.2 AA 24px; 1.5rem = 24px @16px root). */
910
1020
  .fillo-ranking-move,
911
- .fillo-file-remove,
912
- .fillo-file-retry,
913
1021
  .fillo-signature-clear {
914
1022
  min-width: 1.5rem;
915
1023
  min-height: 1.5rem;
@@ -917,6 +1025,12 @@
917
1025
  align-items: center;
918
1026
  justify-content: center;
919
1027
  }
1028
+ /* Upload row actions use a calmer, easier 32px target. */
1029
+ .fillo-file-remove,
1030
+ .fillo-file-retry {
1031
+ min-width: 2rem;
1032
+ min-height: 2rem;
1033
+ }
920
1034
  .fillo-star { min-width: 1.75rem; min-height: 1.75rem; }
921
1035
  .fillo-matrix-cell {
922
1036
  display: flex;
@@ -938,6 +1052,7 @@
938
1052
  /* Matrix on narrow viewports: stacked rows, never a horizontal scroll
939
1053
  trap. Column names come from data-label (set by both renderers). */
940
1054
  @media (max-width: 30rem) {
1055
+ .fillo-file { gap: 0.4rem 0.5rem; padding: 0.55rem 0.625rem; }
941
1056
  .fillo-matrix thead { position: absolute; width: 1px; height: 1px;
942
1057
  overflow: hidden; clip-path: inset(50%); }
943
1058
  .fillo-matrix, .fillo-matrix tbody, .fillo-matrix tr,
@@ -955,6 +1070,12 @@
955
1070
 
956
1071
  /* Forced-colors: restore what author colors can't carry. */
957
1072
  @media (forced-colors: active) {
1073
+ .fillo-file--failed {
1074
+ border-color: CanvasText;
1075
+ border-inline-start-color: Highlight;
1076
+ background: Canvas;
1077
+ }
1078
+ .fillo-file-state--failed { color: Highlight; }
958
1079
  .fillo-star--active,
959
1080
  .fillo-scale-step--active,
960
1081
  :where(.fillo-option--selected) {
@@ -965,7 +1086,11 @@
965
1086
  .fillo-number:focus-within,
966
1087
  .fillo-phone-flag:focus-visible,
967
1088
  .fillo-phone-search:focus-visible,
1089
+ .fillo-option:focus-within,
1090
+ :where(.fillo-toggle-input:focus-visible) + .fillo-toggle-track,
1091
+ .fillo-star:focus-visible,
968
1092
  .fillo-scale-step:focus-visible,
1093
+ .fillo-matrix-cell:focus-within,
969
1094
  .fillo-dropzone:focus-visible,
970
1095
  .fillo-select:focus-visible {
971
1096
  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,40 +626,113 @@
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: 1fr auto auto;
629
+ grid-template-columns: auto minmax(0, 1fr) auto;
582
630
  align-items: center;
583
- gap: 0.3rem 0.8rem;
631
+ gap: 0.45rem 0.625rem;
584
632
  border: 1px solid var(--fillo-border);
585
633
  border-radius: var(--fillo-radius);
586
- padding: 0.5rem 0.75rem;
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
- align-items: start;
593
- 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;
670
+ }
671
+ .fillo-file-name {
672
+ display: block;
673
+ min-width: 0;
674
+ overflow: hidden;
675
+ text-overflow: ellipsis;
676
+ white-space: nowrap;
677
+ font-weight: 500;
678
+ line-height: 1.35;
679
+ }
680
+ .fillo-file-meta {
681
+ color: var(--fillo-muted);
682
+ font-variant-numeric: tabular-nums;
683
+ line-height: 1.35;
684
+ white-space: nowrap;
685
+ }
686
+ .fillo-file-actions {
687
+ display: inline-flex;
688
+ align-items: center;
689
+ justify-content: flex-end;
690
+ gap: 0.25rem;
691
+ white-space: nowrap;
692
+ }
693
+ .fillo-file-error {
694
+ color: var(--fillo-error);
695
+ font-size: 0.82rem;
696
+ line-height: 1.35;
697
+ overflow-wrap: anywhere;
594
698
  }
595
- .fillo-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
596
- .fillo-file-meta { color: var(--fillo-muted); font-variant-numeric: tabular-nums; }
597
- .fillo-file-actions { display: inline-flex; align-items: center; gap: 0.45rem; }
598
- .fillo-file-error { grid-column: 1 / -1; color: var(--fillo-error); font-size: 0.82rem; line-height: 1.35; }
599
- .fillo-file-retry { border: 0; padding: 0; background: none; color: var(--fillo-primary); cursor: pointer; font: inherit; font-weight: 550; }
600
- .fillo-file-retry:hover { text-decoration: underline; }
601
699
  .fillo-file-remove {
602
- background: none; border: none; cursor: pointer;
603
- color: var(--fillo-muted); font-size: 1.1rem; line-height: 1; padding: 0 0.2rem;
700
+ appearance: none;
701
+ display: inline-flex;
702
+ align-items: center;
703
+ justify-content: center;
704
+ background: none;
705
+ border: none;
706
+ cursor: pointer;
707
+ color: var(--fillo-muted);
708
+ line-height: 1;
709
+ padding: 0;
604
710
  }
605
- .fillo-file-remove:hover { color: var(--fillo-error); }
606
- /* Same reset as .fillo-file-remove above (was completely unstyled, audit
607
- P2.14) — primary on hover, not error, since retry is constructive. */
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); }
608
720
  .fillo-file-retry {
609
- background: none; border: none; cursor: pointer;
610
- color: var(--fillo-muted); font: inherit; font-weight: 500; padding: 0 0.2rem;
721
+ appearance: none;
722
+ display: inline-flex;
723
+ align-items: center;
724
+ justify-content: center;
725
+ background: none;
726
+ border: none;
727
+ cursor: pointer;
728
+ color: var(--fillo-primary);
729
+ font: inherit;
730
+ font-weight: 550;
731
+ padding: 0 0.5rem;
611
732
  }
612
- .fillo-file-retry:hover { color: var(--fillo-primary); }
733
+ .fillo-file-retry:hover { text-decoration: underline; }
613
734
  .fillo-progress {
614
- grid-column: 1 / -1;
735
+ grid-column: 2 / -1;
615
736
  height: 4px;
616
737
  border-radius: 999px;
617
738
  background: var(--fillo-border);
@@ -877,18 +998,6 @@
877
998
  line-height: 1.45;
878
999
  }
879
1000
 
880
- /* Error summary (GOV.UK pattern): ONE alert on failed submit; links focus
881
- their fields. Per-field messages are plain describedby text. */
882
- .fillo-error-summary {
883
- border: 2px solid var(--fillo-error);
884
- border-radius: var(--fillo-radius);
885
- padding: 0.8rem 1rem;
886
- }
887
- .fillo-error-summary-title { margin: 0 0 0.4rem; font-size: 1rem; font-weight: 600; }
888
- .fillo-error-summary ul { margin: 0; padding-inline-start: 1.1rem; }
889
- .fillo-error-summary li { margin: 0.2rem 0; }
890
- .fillo-error-summary a { color: var(--fillo-error); }
891
-
892
1001
  /* ---------- Accessibility: focus rings, pointer targets, forced-colors
893
1002
  (input-quality audit P1.1-P1.3) ---------- */
894
1003
  /* Focus visibility: controls without a compensating border swap get a
@@ -905,11 +1014,10 @@
905
1014
  outline-offset: 2px;
906
1015
  box-shadow: none;
907
1016
  }
1017
+ :where(.fillo-field--error) .fillo-star:focus-visible { outline-color: var(--fillo-error); }
908
1018
 
909
1019
  /* Minimum pointer targets (WCAG 2.2 AA 24px; 1.5rem = 24px @16px root). */
910
1020
  .fillo-ranking-move,
911
- .fillo-file-remove,
912
- .fillo-file-retry,
913
1021
  .fillo-signature-clear {
914
1022
  min-width: 1.5rem;
915
1023
  min-height: 1.5rem;
@@ -917,6 +1025,12 @@
917
1025
  align-items: center;
918
1026
  justify-content: center;
919
1027
  }
1028
+ /* Upload row actions use a calmer, easier 32px target. */
1029
+ .fillo-file-remove,
1030
+ .fillo-file-retry {
1031
+ min-width: 2rem;
1032
+ min-height: 2rem;
1033
+ }
920
1034
  .fillo-star { min-width: 1.75rem; min-height: 1.75rem; }
921
1035
  .fillo-matrix-cell {
922
1036
  display: flex;
@@ -938,6 +1052,7 @@
938
1052
  /* Matrix on narrow viewports: stacked rows, never a horizontal scroll
939
1053
  trap. Column names come from data-label (set by both renderers). */
940
1054
  @media (max-width: 30rem) {
1055
+ .fillo-file { gap: 0.4rem 0.5rem; padding: 0.55rem 0.625rem; }
941
1056
  .fillo-matrix thead { position: absolute; width: 1px; height: 1px;
942
1057
  overflow: hidden; clip-path: inset(50%); }
943
1058
  .fillo-matrix, .fillo-matrix tbody, .fillo-matrix tr,
@@ -955,6 +1070,12 @@
955
1070
 
956
1071
  /* Forced-colors: restore what author colors can't carry. */
957
1072
  @media (forced-colors: active) {
1073
+ .fillo-file--failed {
1074
+ border-color: CanvasText;
1075
+ border-inline-start-color: Highlight;
1076
+ background: Canvas;
1077
+ }
1078
+ .fillo-file-state--failed { color: Highlight; }
958
1079
  .fillo-star--active,
959
1080
  .fillo-scale-step--active,
960
1081
  :where(.fillo-option--selected) {
@@ -965,7 +1086,11 @@
965
1086
  .fillo-number:focus-within,
966
1087
  .fillo-phone-flag:focus-visible,
967
1088
  .fillo-phone-search:focus-visible,
1089
+ .fillo-option:focus-within,
1090
+ :where(.fillo-toggle-input:focus-visible) + .fillo-toggle-track,
1091
+ .fillo-star:focus-visible,
968
1092
  .fillo-scale-step:focus-visible,
1093
+ .fillo-matrix-cell:focus-within,
969
1094
  .fillo-dropzone:focus-visible,
970
1095
  .fillo-select:focus-visible {
971
1096
  outline: 2px solid Highlight;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefillo/react",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "Headless React components for embedding Fillo forms natively in your product.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -34,7 +34,7 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@usefillo/core": "^0.11.0"
37
+ "@usefillo/core": "^0.12.1"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "react": "^18.0.0 || ^19.0.0",
@@ -52,7 +52,7 @@
52
52
  "scripts": {
53
53
  "build": "tsup && node ../react/scripts/build-css.mjs --src src/styles.css --out dist",
54
54
  "dev": "tsup --watch --onSuccess 'cp src/styles.css dist/styles.css'",
55
- "typecheck": "tsc --noEmit",
55
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.type-tests.json --noEmit",
56
56
  "test": "node --test test/*.test.mjs"
57
57
  }
58
58
  }