@usefillo/react 0.2.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.
@@ -0,0 +1,382 @@
1
+ /**
2
+ * @usefillo/react default theme.
3
+ * Entirely optional — everything is driven by fillo-* classes and CSS variables,
4
+ * so you can theme with variables, override classes, or skip this file and
5
+ * style from scratch.
6
+ */
7
+
8
+ /* Cascade layers: these defaults live in the `components` layer so that, in
9
+ * apps using Tailwind (or any utility CSS in the `utilities` layer), YOUR
10
+ * classes always win over ours — className="bg-red-50" on an option just
11
+ * works. Without Tailwind the layer has no competition and nothing changes. */
12
+ @layer theme, base, components, utilities;
13
+
14
+ @layer components {
15
+ .fillo-form {
16
+ --fillo-primary: #18181b;
17
+ --fillo-primary-contrast: #ffffff;
18
+ --fillo-bg: transparent;
19
+ --fillo-text: #18181b;
20
+ --fillo-muted: #71717a;
21
+ --fillo-border: #e4e4e7;
22
+ --fillo-error: #dc2626;
23
+ --fillo-radius: 10px;
24
+ --fillo-font: inherit;
25
+ --fillo-control-bg: #ffffff;
26
+
27
+ font-family: var(--fillo-font);
28
+ color: var(--fillo-text);
29
+ background: var(--fillo-bg);
30
+ display: flex;
31
+ flex-direction: column;
32
+ gap: 1.75rem;
33
+ max-width: 100%;
34
+ }
35
+
36
+ .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; }
38
+ .fillo-form-description { color: var(--fillo-muted); margin: 0; line-height: 1.55; }
39
+ .fillo-page-title { font-size: 1.2rem; font-weight: 600; margin: 0; }
40
+
41
+ .fillo-blocks { display: flex; flex-direction: column; gap: 1.5rem; }
42
+
43
+ /* ---------- Field chrome ---------- */
44
+ .fillo-field { display: flex; flex-direction: column; gap: 0.45rem; }
45
+ .fillo-label { font-weight: 550; font-size: 0.95rem; }
46
+ .fillo-required { color: var(--fillo-error); }
47
+ .fillo-description { color: var(--fillo-muted); font-size: 0.85rem; margin: 0; line-height: 1.5; }
48
+ .fillo-error { color: var(--fillo-error); font-size: 0.85rem; margin: 0; }
49
+
50
+ /* ---------- Inputs ---------- */
51
+ .fillo-input {
52
+ appearance: none;
53
+ font: inherit;
54
+ color: var(--fillo-text);
55
+ background: var(--fillo-control-bg);
56
+ border: 1px solid var(--fillo-border);
57
+ border-radius: var(--fillo-radius);
58
+ padding: 0.6rem 0.8rem;
59
+ transition: border-color 120ms ease, box-shadow 120ms ease;
60
+ width: 100%;
61
+ box-sizing: border-box;
62
+ }
63
+ .fillo-input:focus {
64
+ outline: none;
65
+ border-color: var(--fillo-primary);
66
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fillo-primary) 15%, transparent);
67
+ }
68
+ .fillo-field--error .fillo-input { border-color: var(--fillo-error); }
69
+ .fillo-textarea { resize: vertical; min-height: 6rem; }
70
+ .fillo-select { cursor: pointer; }
71
+
72
+ /* ---------- Choices ---------- */
73
+ .fillo-options { display: flex; flex-direction: column; gap: 0.5rem; }
74
+ .fillo-option {
75
+ display: flex;
76
+ align-items: center;
77
+ gap: 0.6rem;
78
+ border: 1px solid var(--fillo-border);
79
+ border-radius: var(--fillo-radius);
80
+ background: var(--fillo-control-bg);
81
+ padding: 0.55rem 0.8rem;
82
+ cursor: pointer;
83
+ transition: border-color 120ms ease, background 120ms ease;
84
+ }
85
+ .fillo-option:hover { border-color: color-mix(in srgb, var(--fillo-primary) 45%, var(--fillo-border)); }
86
+ .fillo-option--selected {
87
+ border-color: var(--fillo-primary);
88
+ background: color-mix(in srgb, var(--fillo-primary) 6%, var(--fillo-control-bg));
89
+ }
90
+ .fillo-option-input { accent-color: var(--fillo-primary); margin: 0; }
91
+ .fillo-option-label { font-size: 0.95rem; }
92
+
93
+ /* "Other" free-text input — inline inside the option row, block under dropdowns. */
94
+ .fillo-other-input {
95
+ flex: 1;
96
+ min-width: 0;
97
+ margin-left: 0.2rem;
98
+ padding: 0.3rem 0.6rem;
99
+ font-size: 0.9rem;
100
+ }
101
+ .fillo-other-input--block {
102
+ flex: none;
103
+ width: 100%;
104
+ margin-left: 0;
105
+ margin-top: 0.45rem;
106
+ padding: 0.55rem 0.8rem;
107
+ }
108
+
109
+ /* ---------- Rating ---------- */
110
+ .fillo-rating { display: flex; gap: 0.25rem; }
111
+ .fillo-star {
112
+ font-size: 1.6rem;
113
+ line-height: 1;
114
+ background: none;
115
+ border: none;
116
+ cursor: pointer;
117
+ color: var(--fillo-border);
118
+ padding: 0.1rem;
119
+ transition: color 100ms ease, transform 100ms ease;
120
+ }
121
+ .fillo-star:hover { transform: scale(1.15); }
122
+ .fillo-star--active { color: var(--fillo-primary); }
123
+
124
+ /* ---------- Linear scale ---------- */
125
+ /* Steps share one row and shrink to fit — an 11-step NPS scale must never
126
+ wrap onto a second line, even in narrow embeds. */
127
+ .fillo-scale { display: flex; gap: 0.35rem; }
128
+ .fillo-scale-step {
129
+ flex: 1 1 0;
130
+ min-width: 0;
131
+ padding: 0.5rem 0.15rem;
132
+ font: inherit;
133
+ font-size: 0.92em;
134
+ font-variant-numeric: tabular-nums;
135
+ text-align: center;
136
+ border: 1px solid var(--fillo-border);
137
+ border-radius: var(--fillo-radius);
138
+ background: var(--fillo-control-bg);
139
+ cursor: pointer;
140
+ transition: border-color 120ms ease, background 120ms ease, color 120ms ease;
141
+ }
142
+ .fillo-scale-step:hover { border-color: var(--fillo-primary); }
143
+ .fillo-scale-step--active {
144
+ background: var(--fillo-primary);
145
+ border-color: var(--fillo-primary);
146
+ color: var(--fillo-primary-contrast);
147
+ }
148
+ .fillo-scale-labels {
149
+ display: flex;
150
+ justify-content: space-between;
151
+ color: var(--fillo-muted);
152
+ font-size: 0.78rem;
153
+ margin-top: 0.35rem;
154
+ }
155
+
156
+ /* ---------- Ranking ---------- */
157
+ .fillo-ranking { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.45rem; }
158
+ .fillo-ranking-item {
159
+ display: flex;
160
+ align-items: center;
161
+ gap: 0.7rem;
162
+ border: 1px solid var(--fillo-border);
163
+ border-radius: var(--fillo-radius);
164
+ background: var(--fillo-control-bg);
165
+ padding: 0.5rem 0.7rem;
166
+ }
167
+ .fillo-ranking-index {
168
+ display: grid;
169
+ place-items: center;
170
+ width: 1.5rem;
171
+ height: 1.5rem;
172
+ border-radius: 999px;
173
+ background: color-mix(in srgb, var(--fillo-primary) 10%, transparent);
174
+ color: var(--fillo-primary);
175
+ font-size: 0.78rem;
176
+ font-weight: 650;
177
+ font-variant-numeric: tabular-nums;
178
+ }
179
+ .fillo-ranking-label { flex: 1; font-size: 0.95rem; }
180
+ .fillo-ranking-controls { display: flex; gap: 0.2rem; }
181
+ .fillo-ranking-move {
182
+ font: inherit;
183
+ border: 1px solid var(--fillo-border);
184
+ background: transparent;
185
+ border-radius: calc(var(--fillo-radius) * 0.6);
186
+ cursor: pointer;
187
+ padding: 0.15rem 0.45rem;
188
+ color: var(--fillo-muted);
189
+ transition: color 120ms ease, border-color 120ms ease;
190
+ }
191
+ .fillo-ranking-move:hover:not(:disabled) { color: var(--fillo-primary); border-color: var(--fillo-primary); }
192
+ .fillo-ranking-move:disabled { opacity: 0.35; cursor: default; }
193
+
194
+ /* ---------- Matrix ---------- */
195
+ .fillo-matrix-wrap { overflow-x: auto; }
196
+ .fillo-matrix { border-collapse: collapse; width: 100%; font-size: 0.9rem; }
197
+ .fillo-matrix th, .fillo-matrix td {
198
+ padding: 0.55rem 0.7rem;
199
+ text-align: center;
200
+ border-bottom: 1px solid var(--fillo-border);
201
+ }
202
+ .fillo-matrix thead th { color: var(--fillo-muted); font-weight: 500; font-size: 0.8rem; }
203
+ .fillo-matrix tbody th { text-align: left; font-weight: 500; }
204
+ .fillo-matrix tbody tr:last-child th, .fillo-matrix tbody tr:last-child td { border-bottom: none; }
205
+
206
+ /* ---------- Signature ---------- */
207
+ .fillo-signature {
208
+ position: relative;
209
+ border: 1.5px dashed var(--fillo-border);
210
+ border-radius: var(--fillo-radius);
211
+ background: var(--fillo-control-bg);
212
+ }
213
+ .fillo-signature-canvas {
214
+ display: block;
215
+ width: 100%;
216
+ height: 9rem;
217
+ color: var(--fillo-text);
218
+ touch-action: none;
219
+ cursor: crosshair;
220
+ }
221
+ .fillo-signature-hint {
222
+ position: absolute;
223
+ inset: 0;
224
+ display: grid;
225
+ place-items: center;
226
+ color: var(--fillo-muted);
227
+ font-size: 0.85rem;
228
+ pointer-events: none;
229
+ }
230
+ .fillo-signature-clear {
231
+ position: absolute;
232
+ top: 0.4rem;
233
+ right: 0.4rem;
234
+ font: inherit;
235
+ font-size: 0.75rem;
236
+ border: 1px solid var(--fillo-border);
237
+ border-radius: calc(var(--fillo-radius) * 0.6);
238
+ background: var(--fillo-control-bg);
239
+ color: var(--fillo-muted);
240
+ padding: 0.15rem 0.5rem;
241
+ cursor: pointer;
242
+ }
243
+ .fillo-signature-clear:hover { color: var(--fillo-error); border-color: var(--fillo-error); }
244
+
245
+ /* ---------- File upload ---------- */
246
+ .fillo-dropzone {
247
+ display: flex;
248
+ flex-direction: column;
249
+ align-items: center;
250
+ gap: 0.25rem;
251
+ border: 1.5px dashed var(--fillo-border);
252
+ border-radius: var(--fillo-radius);
253
+ padding: 1.6rem 1rem;
254
+ text-align: center;
255
+ cursor: pointer;
256
+ background: color-mix(in srgb, var(--fillo-control-bg) 60%, transparent);
257
+ transition: border-color 120ms ease, background 120ms ease;
258
+ }
259
+ .fillo-dropzone:hover,
260
+ .fillo-dropzone--over {
261
+ border-color: var(--fillo-primary);
262
+ background: color-mix(in srgb, var(--fillo-primary) 4%, var(--fillo-control-bg));
263
+ }
264
+ .fillo-dropzone--disabled { cursor: not-allowed; opacity: 0.6; }
265
+ .fillo-dropzone-title { font-weight: 550; font-size: 0.95rem; }
266
+ .fillo-dropzone-hint { color: var(--fillo-muted); font-size: 0.82rem; }
267
+
268
+ .fillo-files { list-style: none; display: flex; flex-direction: column; gap: 0.45rem; margin: 0; padding: 0; }
269
+ .fillo-file {
270
+ display: grid;
271
+ grid-template-columns: 1fr auto auto;
272
+ align-items: center;
273
+ gap: 0.3rem 0.8rem;
274
+ border: 1px solid var(--fillo-border);
275
+ border-radius: var(--fillo-radius);
276
+ padding: 0.5rem 0.75rem;
277
+ font-size: 0.88rem;
278
+ background: var(--fillo-control-bg);
279
+ }
280
+ .fillo-file--failed { border-color: var(--fillo-error); }
281
+ .fillo-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
282
+ .fillo-file-meta { color: var(--fillo-muted); font-variant-numeric: tabular-nums; }
283
+ .fillo-file-remove {
284
+ background: none; border: none; cursor: pointer;
285
+ color: var(--fillo-muted); font-size: 1.1rem; line-height: 1; padding: 0 0.2rem;
286
+ }
287
+ .fillo-file-remove:hover { color: var(--fillo-error); }
288
+ .fillo-progress {
289
+ grid-column: 1 / -1;
290
+ height: 4px;
291
+ border-radius: 999px;
292
+ background: var(--fillo-border);
293
+ overflow: hidden;
294
+ }
295
+ .fillo-progress-bar {
296
+ display: block;
297
+ height: 100%;
298
+ background: var(--fillo-primary);
299
+ border-radius: inherit;
300
+ transition: width 200ms ease;
301
+ }
302
+
303
+ /* ---------- Content blocks ---------- */
304
+ .fillo-heading { font-size: 1.15rem; font-weight: 600; margin: 0.5rem 0 0; }
305
+ .fillo-paragraph { color: var(--fillo-muted); line-height: 1.6; margin: 0; }
306
+ .fillo-divider { border: none; border-top: 1px solid var(--fillo-border); margin: 0.25rem 0; }
307
+
308
+ /* ---------- Footer / buttons ---------- */
309
+ .fillo-footer { display: flex; justify-content: flex-end; gap: 0.6rem; }
310
+ .fillo-button {
311
+ font: inherit;
312
+ font-weight: 550;
313
+ border-radius: var(--fillo-radius);
314
+ padding: 0.6rem 1.4rem;
315
+ cursor: pointer;
316
+ border: 1px solid transparent;
317
+ transition: opacity 120ms ease, transform 60ms ease;
318
+ }
319
+ .fillo-button:active { transform: translateY(1px); }
320
+ .fillo-button:disabled { opacity: 0.55; cursor: not-allowed; }
321
+ .fillo-button--primary { background: var(--fillo-primary); color: var(--fillo-primary-contrast); }
322
+ .fillo-button--ghost { background: transparent; color: var(--fillo-text); border-color: var(--fillo-border); }
323
+
324
+ /* ---------- Progress (multi-page) ---------- */
325
+ .fillo-progress-track {
326
+ height: 4px;
327
+ border-radius: 999px;
328
+ background: var(--fillo-border);
329
+ overflow: hidden;
330
+ }
331
+ .fillo-progress-fill {
332
+ height: 100%;
333
+ background: var(--fillo-primary);
334
+ border-radius: inherit;
335
+ transition: width 300ms ease;
336
+ }
337
+
338
+ /* ---------- Closed ---------- */
339
+ .fillo-closed {
340
+ text-align: center;
341
+ color: var(--fillo-muted);
342
+ padding: 2rem 1rem;
343
+ margin: 0;
344
+ }
345
+
346
+ /* ---------- Success ---------- */
347
+ .fillo-success { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; text-align: center; padding: 2.5rem 1rem; }
348
+ .fillo-success-mark {
349
+ width: 3rem; height: 3rem;
350
+ display: grid; place-items: center;
351
+ border-radius: 999px;
352
+ background: color-mix(in srgb, var(--fillo-primary) 10%, transparent);
353
+ color: var(--fillo-primary);
354
+ font-size: 1.4rem; font-weight: 700;
355
+ }
356
+ .fillo-success-title { margin: 0; font-size: 1.35rem; letter-spacing: -0.01em; }
357
+ .fillo-success-message { margin: 0; color: var(--fillo-muted); }
358
+
359
+ /* Honeypot — off-screen, never display:none (bots check that) */
360
+ .fillo-hp {
361
+ position: absolute;
362
+ left: -9999px;
363
+ width: 1px;
364
+ height: 1px;
365
+ opacity: 0;
366
+ pointer-events: none;
367
+ }
368
+
369
+ /* ---------- Loading skeleton ---------- */
370
+ .fillo-form--loading { gap: 0.9rem; }
371
+ .fillo-skeleton {
372
+ height: 2.4rem;
373
+ border-radius: var(--fillo-radius);
374
+ background: linear-gradient(90deg, var(--fillo-border) 25%, color-mix(in srgb, var(--fillo-border) 40%, transparent) 50%, var(--fillo-border) 75%);
375
+ background-size: 200% 100%;
376
+ animation: fillo-shimmer 1.4s infinite;
377
+ }
378
+ .fillo-skeleton--short { width: 40%; }
379
+ @keyframes fillo-shimmer {
380
+ to { background-position: -200% 0; }
381
+ }
382
+ }
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@usefillo/react",
3
+ "version": "0.2.0",
4
+ "description": "Headless React components for embedding Fillo forms natively in your product.",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "forms",
8
+ "form-builder",
9
+ "react",
10
+ "headless",
11
+ "embed",
12
+ "typescript"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/jacobfunch/fillo.git",
17
+ "directory": "packages/react"
18
+ },
19
+ "type": "module",
20
+ "sideEffects": [
21
+ "**/*.css"
22
+ ],
23
+ "main": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js"
29
+ },
30
+ "./styles.css": "./dist/styles.css"
31
+ },
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "dependencies": {
39
+ "@usefillo/core": "^0.2.0"
40
+ },
41
+ "peerDependencies": {
42
+ "react": "^18.0.0 || ^19.0.0",
43
+ "react-dom": "^18.0.0 || ^19.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/react": "^19.1.0",
47
+ "react": "^19.1.0",
48
+ "tsup": "^8.4.0",
49
+ "typescript": "^5.8.3"
50
+ },
51
+ "scripts": {
52
+ "build": "tsup && cp src/styles.css dist/styles.css",
53
+ "dev": "tsup --watch --onSuccess 'cp src/styles.css dist/styles.css'",
54
+ "typecheck": "tsc --noEmit"
55
+ }
56
+ }