@usefillo/react 0.3.2 → 0.5.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
@@ -24,6 +24,7 @@
24
24
  --fillo-font: inherit;
25
25
  --fillo-control-bg: #ffffff;
26
26
 
27
+ color-scheme: light;
27
28
  font-family: var(--fillo-font);
28
29
  color: var(--fillo-text);
29
30
  background: var(--fillo-bg);
@@ -33,8 +34,38 @@
33
34
  max-width: 100%;
34
35
  }
35
36
 
37
+ .fillo-form[data-fillo-color-scheme="light"] {
38
+ color-scheme: light;
39
+ }
40
+
41
+ .fillo-form[data-fillo-color-scheme="dark"] {
42
+ --fillo-primary: #fafafa;
43
+ --fillo-primary-contrast: #18181b;
44
+ --fillo-text: #f4f4f5;
45
+ --fillo-muted: #a1a1aa;
46
+ --fillo-border: #3f3f46;
47
+ --fillo-error: #f87171;
48
+ --fillo-control-bg: #18181b;
49
+
50
+ color-scheme: dark;
51
+ }
52
+
53
+ @media (prefers-color-scheme: dark) {
54
+ .fillo-form:not([data-fillo-color-scheme="light"]):not([data-fillo-has-color-theme]) {
55
+ --fillo-primary: #fafafa;
56
+ --fillo-primary-contrast: #18181b;
57
+ --fillo-text: #f4f4f5;
58
+ --fillo-muted: #a1a1aa;
59
+ --fillo-border: #3f3f46;
60
+ --fillo-error: #f87171;
61
+ --fillo-control-bg: #18181b;
62
+
63
+ color-scheme: dark;
64
+ }
65
+ }
66
+
36
67
  .fillo-header { display: flex; flex-direction: column; gap: 0.5rem; }
37
- .fillo-title { font-size: 1.6rem; font-weight: 650; letter-spacing: -0.02em; margin: 0; }
68
+ .fillo-title { font-size: 1.6rem; font-weight: 650; letter-spacing: 0; margin: 0; }
38
69
  .fillo-form-description { color: var(--fillo-muted); margin: 0; line-height: 1.55; }
39
70
  .fillo-page-title { font-size: 1.2rem; font-weight: 600; margin: 0; }
40
71
 
@@ -48,6 +79,7 @@
48
79
  .fillo-optional { color: var(--fillo-muted); font-weight: 400; font-size: 0.85em; }
49
80
  .fillo-description { color: var(--fillo-muted); font-size: 0.85rem; margin: 0; line-height: 1.5; }
50
81
  .fillo-error { color: var(--fillo-error); font-size: 0.85rem; margin: 0; }
82
+ .fillo-submit-error { color: var(--fillo-error); font-size: 0.85rem; font-weight: 500; margin: 0; }
51
83
 
52
84
  /* ---------- Inputs ---------- */
53
85
  .fillo-input {
@@ -57,23 +89,93 @@
57
89
  background: var(--fillo-control-bg);
58
90
  border: 1px solid var(--fillo-border);
59
91
  border-radius: var(--fillo-radius);
92
+ min-height: 2.5rem;
60
93
  padding: 0.6rem 0.8rem;
61
94
  transition: border-color 120ms ease, box-shadow 120ms ease;
62
95
  width: 100%;
63
96
  box-sizing: border-box;
64
97
  }
65
- .fillo-input:focus {
98
+ .fillo-input:focus-visible {
66
99
  outline: none;
67
100
  border-color: var(--fillo-primary);
68
101
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
69
102
  }
70
103
  .fillo-field--error .fillo-input { border-color: var(--fillo-error); }
71
104
  .fillo-textarea { resize: vertical; min-height: 6rem; }
72
- .fillo-select { cursor: pointer; }
105
+
106
+ /* Phone field: country picker + national input */
107
+ .fillo-phone { position: relative; display: flex; align-items: stretch; gap: 0.4rem; }
108
+ .fillo-phone-country { position: relative; flex: 0 0 auto; }
109
+ .fillo-phone-flag {
110
+ display: inline-flex; align-items: center; gap: 0.35rem;
111
+ height: 100%; min-height: 2.5rem; padding: 0 0.6rem;
112
+ font: inherit; color: var(--fillo-text);
113
+ background: var(--fillo-control-bg);
114
+ border: 1px solid var(--fillo-border); border-radius: var(--fillo-radius);
115
+ cursor: pointer; white-space: nowrap;
116
+ transition: border-color 120ms ease, box-shadow 120ms ease;
117
+ }
118
+ .fillo-phone-flag:focus-visible {
119
+ outline: none; border-color: var(--fillo-primary);
120
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
121
+ }
122
+ .fillo-phone-flag-emoji { font-size: 1.15em; line-height: 1; }
123
+ .fillo-phone-dial { font-variant-numeric: tabular-nums; color: var(--fillo-muted); }
124
+ .fillo-phone-caret { font-size: 0.65em; color: var(--fillo-muted); }
125
+ .fillo-phone-input { flex: 1 1 auto; min-width: 0; }
126
+ .fillo-field--error .fillo-phone-flag { border-color: var(--fillo-error); }
127
+ .fillo-phone-popover {
128
+ position: absolute; z-index: 30; top: calc(100% + 0.3rem); left: 0;
129
+ width: 18rem; max-width: 84vw; padding: 0.4rem;
130
+ display: flex; flex-direction: column; gap: 0.35rem;
131
+ background: var(--fillo-control-bg);
132
+ border: 1px solid var(--fillo-border); border-radius: var(--fillo-radius);
133
+ box-shadow: 0 12px 32px -14px rgba(0, 0, 0, 0.35);
134
+ }
135
+ .fillo-phone-search {
136
+ appearance: none; font: inherit; width: 100%; box-sizing: border-box;
137
+ color: var(--fillo-text); background: var(--fillo-bg);
138
+ border: 1px solid var(--fillo-border);
139
+ border-radius: calc(var(--fillo-radius) - 3px); padding: 0.45rem 0.6rem;
140
+ }
141
+ .fillo-phone-search:focus-visible { outline: none; border-color: var(--fillo-primary); }
142
+ .fillo-phone-list { list-style: none; margin: 0; padding: 0; max-height: 14rem; overflow-y: auto; }
143
+ .fillo-phone-option {
144
+ display: flex; align-items: center; gap: 0.5rem;
145
+ padding: 0.45rem 0.5rem; border-radius: calc(var(--fillo-radius) - 3px);
146
+ cursor: pointer; color: var(--fillo-text);
147
+ }
148
+ .fillo-phone-option--active { background: color-mix(in srgb, var(--fillo-primary) 10%, transparent); }
149
+ .fillo-phone-option-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
150
+ .fillo-phone-option-dial { color: var(--fillo-muted); font-variant-numeric: tabular-nums; }
151
+ .fillo-phone-empty { padding: 0.6rem 0.5rem; color: var(--fillo-muted); }
152
+ .fillo-select-wrap {
153
+ position: relative;
154
+ display: block;
155
+ }
156
+ .fillo-select {
157
+ cursor: pointer;
158
+ padding-right: 2.45rem;
159
+ }
160
+ .fillo-select-icon {
161
+ pointer-events: none;
162
+ position: absolute;
163
+ top: 50%;
164
+ right: 0.9rem;
165
+ width: 0.62rem;
166
+ height: 0.62rem;
167
+ border-right: 1.5px solid currentColor;
168
+ border-bottom: 1.5px solid currentColor;
169
+ color: var(--fillo-muted);
170
+ transform: translateY(-65%) rotate(45deg);
171
+ transition: color 120ms ease;
172
+ }
173
+ .fillo-select-wrap:focus-within .fillo-select-icon { color: var(--fillo-primary); }
73
174
 
74
175
  /* ---------- Choices ---------- */
75
176
  .fillo-options { display: flex; flex-direction: column; gap: 0.5rem; }
76
177
  .fillo-option {
178
+ position: relative;
77
179
  display: flex;
78
180
  align-items: center;
79
181
  gap: 0.6rem;
@@ -85,25 +187,129 @@
85
187
  transition: border-color 120ms ease, background 120ms ease;
86
188
  }
87
189
  .fillo-option:hover { border-color: color-mix(in srgb, var(--fillo-primary) 45%, var(--fillo-border)); }
190
+ .fillo-option:focus-within {
191
+ border-color: var(--fillo-primary);
192
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 14%, transparent);
193
+ }
88
194
  .fillo-option--selected {
89
195
  border-color: var(--fillo-primary);
90
196
  background: color-mix(in srgb, var(--fillo-primary) 6%, var(--fillo-control-bg));
91
197
  }
92
- .fillo-option-input { accent-color: var(--fillo-primary); margin: 0; }
198
+ .fillo-option-input {
199
+ accent-color: var(--fillo-primary);
200
+ flex: 0 0 auto;
201
+ width: 1rem;
202
+ height: 1rem;
203
+ margin: 0;
204
+ }
205
+ .fillo-option-input:focus-visible { outline: none; }
206
+ .fillo-option--has-icon .fillo-option-input {
207
+ position: absolute;
208
+ width: 1px;
209
+ height: 1px;
210
+ overflow: hidden;
211
+ opacity: 0;
212
+ pointer-events: none;
213
+ }
214
+ .fillo-option-icon {
215
+ width: 1.05rem;
216
+ height: 1.05rem;
217
+ flex: 0 0 auto;
218
+ color: var(--fillo-muted);
219
+ transition: color 120ms ease;
220
+ }
221
+ .fillo-option--selected .fillo-option-icon { color: var(--fillo-primary); }
93
222
  .fillo-option-label { font-size: 0.95rem; }
94
223
 
95
- /* "Other" free-text input — inline inside the option row, block under dropdowns. */
96
- .fillo-other-input {
97
- flex: 1;
224
+ .fillo-option-main {
225
+ display: flex;
226
+ align-items: center;
227
+ gap: 0.6rem;
98
228
  min-width: 0;
99
- margin-left: 0.2rem;
100
- padding: 0.3rem 0.6rem;
101
- font-size: 0.9rem;
102
229
  }
103
- .fillo-other-input--block {
104
- flex: none;
230
+ .fillo-option--with-other {
231
+ align-items: stretch;
232
+ flex-direction: column;
233
+ gap: 0.55rem;
234
+ }
235
+ .fillo-option--with-other .fillo-option-label {
236
+ font-weight: 550;
237
+ }
238
+
239
+ /* ---------- Toggle checkbox ---------- */
240
+ .fillo-toggle {
241
+ position: relative;
242
+ display: flex;
243
+ align-items: center;
244
+ justify-content: space-between;
245
+ gap: 1rem;
246
+ border: 1px solid var(--fillo-border);
247
+ border-radius: var(--fillo-radius);
248
+ background: var(--fillo-control-bg);
249
+ padding: 0.65rem 0.8rem;
250
+ cursor: pointer;
251
+ transition: border-color 120ms ease, background 120ms ease, box-shadow 120ms ease;
252
+ }
253
+ .fillo-toggle:hover { border-color: color-mix(in srgb, var(--fillo-primary) 45%, var(--fillo-border)); }
254
+ .fillo-toggle-copy { min-width: 0; }
255
+ .fillo-toggle-input {
256
+ position: absolute;
257
+ width: 1px;
258
+ height: 1px;
259
+ overflow: hidden;
260
+ clip: rect(0 0 0 0);
261
+ clip-path: inset(50%);
262
+ white-space: nowrap;
263
+ }
264
+ .fillo-toggle-track {
265
+ position: relative;
266
+ display: inline-flex;
267
+ width: 2.35rem;
268
+ height: 1.35rem;
269
+ flex: 0 0 auto;
270
+ align-items: center;
271
+ border-radius: 999px;
272
+ background: color-mix(in srgb, var(--fillo-muted) 18%, var(--fillo-control-bg));
273
+ box-shadow: inset 0 0 0 1px var(--fillo-border);
274
+ transition: background 160ms ease, box-shadow 160ms ease;
275
+ }
276
+ .fillo-toggle-thumb {
277
+ width: 1rem;
278
+ height: 1rem;
279
+ margin-left: 0.18rem;
280
+ border-radius: 999px;
281
+ background: var(--fillo-control-bg);
282
+ box-shadow: 0 1px 2px color-mix(in srgb, var(--fillo-text) 18%, transparent);
283
+ transition: transform 160ms ease, background 160ms ease;
284
+ }
285
+ .fillo-toggle-input:checked + .fillo-toggle-track {
286
+ background: var(--fillo-primary);
287
+ box-shadow: inset 0 0 0 1px var(--fillo-primary);
288
+ }
289
+ .fillo-toggle-input:checked + .fillo-toggle-track .fillo-toggle-thumb {
290
+ transform: translateX(1rem);
291
+ background: var(--fillo-primary-contrast);
292
+ }
293
+ .fillo-toggle-input:focus-visible + .fillo-toggle-track {
294
+ box-shadow:
295
+ inset 0 0 0 1px var(--fillo-primary),
296
+ 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
297
+ }
298
+ .fillo-toggle:has(.fillo-toggle-input:checked) {
299
+ border-color: var(--fillo-primary);
300
+ background: color-mix(in srgb, var(--fillo-primary) 5%, var(--fillo-control-bg));
301
+ }
302
+
303
+ /* "Other" free-text input — nested inside choice rows, block under dropdowns. */
304
+ .fillo-other-input {
105
305
  width: 100%;
306
+ min-width: 0;
106
307
  margin-left: 0;
308
+ padding: 0.5rem 0.7rem;
309
+ font-size: 0.92rem;
310
+ background: color-mix(in srgb, var(--fillo-primary) 3%, var(--fillo-control-bg));
311
+ }
312
+ .fillo-other-input--block {
107
313
  margin-top: 0.45rem;
108
314
  padding: 0.55rem 0.8rem;
109
315
  }
@@ -122,6 +328,11 @@
122
328
  }
123
329
  .fillo-star:hover { transform: scale(1.15); }
124
330
  .fillo-star--active { color: var(--fillo-primary); }
331
+ .fillo-star:focus-visible {
332
+ outline: none;
333
+ border-radius: calc(var(--fillo-radius) * 0.5);
334
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 30%, transparent);
335
+ }
125
336
 
126
337
  /* ---------- Linear scale ---------- */
127
338
  /* Steps share one row and shrink to fit — an 11-step NPS scale must never
@@ -142,6 +353,11 @@
142
353
  transition: border-color 120ms ease, background 120ms ease, color 120ms ease;
143
354
  }
144
355
  .fillo-scale-step:hover { border-color: var(--fillo-primary); }
356
+ .fillo-scale-step:focus-visible {
357
+ outline: none;
358
+ border-color: var(--fillo-primary);
359
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
360
+ }
145
361
  .fillo-scale-step--active {
146
362
  background: var(--fillo-primary);
147
363
  border-color: var(--fillo-primary);
@@ -244,6 +460,21 @@
244
460
  }
245
461
  .fillo-signature-clear:hover { color: var(--fillo-error); border-color: var(--fillo-error); }
246
462
 
463
+ /* Type-to-sign: the keyboard/screen-reader path under the canvas. */
464
+ .fillo-signature-type {
465
+ display: flex;
466
+ flex-direction: column;
467
+ gap: 0.35rem;
468
+ margin-top: 0.55rem;
469
+ }
470
+ .fillo-signature-type-label {
471
+ color: var(--fillo-muted);
472
+ font-size: 0.8rem;
473
+ }
474
+ .fillo-signature-type-input {
475
+ margin-top: 0;
476
+ }
477
+
247
478
  /* ---------- File upload ---------- */
248
479
  .fillo-dropzone {
249
480
  display: flex;
@@ -263,6 +494,11 @@
263
494
  border-color: var(--fillo-primary);
264
495
  background: color-mix(in srgb, var(--fillo-primary) 4%, var(--fillo-control-bg));
265
496
  }
497
+ .fillo-dropzone:focus-visible {
498
+ outline: none;
499
+ border-color: var(--fillo-primary);
500
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
501
+ }
266
502
  .fillo-dropzone--disabled { cursor: not-allowed; opacity: 0.6; }
267
503
  .fillo-dropzone-title { font-weight: 550; font-size: 0.95rem; }
268
504
  .fillo-dropzone-hint { color: var(--fillo-muted); font-size: 0.82rem; }
@@ -320,6 +556,10 @@
320
556
  }
321
557
  .fillo-button:active { transform: translateY(1px); }
322
558
  .fillo-button:disabled { opacity: 0.55; cursor: not-allowed; }
559
+ .fillo-button:focus-visible {
560
+ outline: none;
561
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 30%, transparent);
562
+ }
323
563
  .fillo-button--primary { background: var(--fillo-primary); color: var(--fillo-primary-contrast); }
324
564
  .fillo-button--ghost { background: transparent; color: var(--fillo-text); border-color: var(--fillo-border); }
325
565
 
@@ -355,7 +595,7 @@
355
595
  color: var(--fillo-primary);
356
596
  font-size: 1.4rem; font-weight: 700;
357
597
  }
358
- .fillo-success-title { margin: 0; font-size: 1.35rem; letter-spacing: -0.01em; }
598
+ .fillo-success-title { margin: 0; font-size: 1.35rem; letter-spacing: 0; }
359
599
  .fillo-success-message { margin: 0; color: var(--fillo-muted); }
360
600
 
361
601
  /* Honeypot — off-screen, never display:none (bots check that) */
@@ -368,6 +608,20 @@
368
608
  pointer-events: none;
369
609
  }
370
610
 
611
+ /* Visually hidden but exposed to assistive tech — for aria-live narration. */
612
+ .fillo-sr-only {
613
+ position: absolute;
614
+ width: 1px;
615
+ height: 1px;
616
+ margin: -1px;
617
+ padding: 0;
618
+ overflow: hidden;
619
+ clip: rect(0 0 0 0);
620
+ clip-path: inset(50%);
621
+ white-space: nowrap;
622
+ border: 0;
623
+ }
624
+
371
625
  /* ---------- Loading skeleton ---------- */
372
626
  .fillo-form--loading { gap: 0.9rem; }
373
627
  .fillo-skeleton {
@@ -382,6 +636,22 @@
382
636
  to { background-position: -200% 0; }
383
637
  }
384
638
 
639
+ /* Respect a reduced-motion preference: drop transitions, the press nudge,
640
+ the rating hover scale, and the skeleton shimmer. */
641
+ @media (prefers-reduced-motion: reduce) {
642
+ .fillo-form *,
643
+ .fillo-form *::before,
644
+ .fillo-form *::after {
645
+ animation-duration: 0.001ms !important;
646
+ animation-iteration-count: 1 !important;
647
+ transition-duration: 0.001ms !important;
648
+ scroll-behavior: auto !important;
649
+ }
650
+ .fillo-button:active { transform: none; }
651
+ .fillo-star:hover { transform: none; }
652
+ .fillo-skeleton { animation: none; }
653
+ }
654
+
385
655
  /* ---------- "Powered by Fillo" badge ---------- */
386
656
  .fillo-powered {
387
657
  display: block;
@@ -389,7 +659,7 @@
389
659
  text-align: center;
390
660
  font-size: 0.75rem;
391
661
  font-weight: 500;
392
- letter-spacing: 0.01em;
662
+ letter-spacing: 0;
393
663
  color: var(--fillo-muted);
394
664
  text-decoration: none;
395
665
  opacity: 0.75;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefillo/react",
3
- "version": "0.3.2",
3
+ "version": "0.5.0",
4
4
  "description": "Headless React components for embedding Fillo forms natively in your product.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -32,7 +32,7 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@usefillo/core": "^0.3.2"
35
+ "@usefillo/core": "^0.5.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "react": "^18.0.0 || ^19.0.0",