@cauthyhq/react 0.0.0-stage → 1.0.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,511 @@
1
+ /*
2
+ * @cauthyhq/react/styles.css — a look for the ready-made forms (<SignIn />,
3
+ * <SignUp />, <Account />…), with no dependencies.
4
+ *
5
+ * import '@cauthyhq/react/styles.css'
6
+ *
7
+ * Every rule is inside :where(), so it weighs nothing: any rule of the
8
+ * application's own wins. The colours, corners and typeface are variables
9
+ * (--cauthy-*). The forms set them to what a person chose for the workspace
10
+ * in Cauthy's dashboard; set them yourself with `workspaceLook={false}`. A part
11
+ * given a class of its own (`classNames`) is left to it, and a form with
12
+ * `unstyled` is not touched at all.
13
+ */
14
+
15
+ :where([data-cauthy]) {
16
+ --cauthy-surface: #ffffff;
17
+ --cauthy-text: #12100e;
18
+ --cauthy-text-muted: #514a42;
19
+ --cauthy-border: #e2dbd0;
20
+ --cauthy-input-background: var(--cauthy-surface);
21
+ --cauthy-input-border: #cfc7ba;
22
+ --cauthy-primary: #12100e;
23
+ --cauthy-primary-text: #f5f1ea;
24
+ --cauthy-accent: #8a6e3b;
25
+ --cauthy-error: #b42318;
26
+ --cauthy-error-background: #fef3f2;
27
+ --cauthy-success: #067647;
28
+ --cauthy-success-background: #ecfdf3;
29
+ --cauthy-radius: 6px;
30
+ --cauthy-button-radius: 6px;
31
+ --cauthy-font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
32
+
33
+ font-family: var(--cauthy-font);
34
+ font-size: 14px;
35
+ line-height: 1.45;
36
+ color: var(--cauthy-text);
37
+ box-sizing: border-box;
38
+ }
39
+
40
+ :where([data-cauthy]) *,
41
+ :where([data-cauthy]) *::before,
42
+ :where([data-cauthy]) *::after {
43
+ box-sizing: inherit;
44
+ }
45
+
46
+ /* ── The form, and the account screen's parts ─────────────────────────────── */
47
+
48
+ :where(.cauthy-form[data-cauthy]),
49
+ :where([data-cauthy] .cauthy-section) {
50
+ display: flex;
51
+ flex-direction: column;
52
+ gap: 12px;
53
+ width: 100%;
54
+ max-width: 380px;
55
+ margin: 0;
56
+ }
57
+
58
+ :where(.cauthy-account[data-cauthy]) {
59
+ display: flex;
60
+ flex-direction: column;
61
+ gap: 16px;
62
+ width: 100%;
63
+ max-width: 520px;
64
+ }
65
+
66
+ :where([data-cauthy] .cauthy-section) {
67
+ max-width: none;
68
+ padding: 18px;
69
+ background: var(--cauthy-surface);
70
+ border: 1px solid var(--cauthy-border);
71
+ border-radius: var(--cauthy-radius);
72
+ }
73
+
74
+ :where([data-cauthy] .cauthy-heading) {
75
+ margin: 0;
76
+ font-size: 16px;
77
+ font-weight: 600;
78
+ }
79
+
80
+ /* ── Fields ──────────────────────────────────────────────────────────────── */
81
+
82
+ :where([data-cauthy] .cauthy-field) {
83
+ display: flex;
84
+ flex-direction: column;
85
+ gap: 6px;
86
+ }
87
+
88
+ :where([data-cauthy] .cauthy-label) {
89
+ display: flex;
90
+ justify-content: space-between;
91
+ align-items: baseline;
92
+ gap: 8px;
93
+ font-size: 13px;
94
+ font-weight: 500;
95
+ color: var(--cauthy-text-muted);
96
+ }
97
+
98
+ :where([data-cauthy] .cauthy-input) {
99
+ width: 100%;
100
+ height: 40px;
101
+ padding: 0 12px;
102
+ font: inherit;
103
+ color: var(--cauthy-text);
104
+ background: var(--cauthy-input-background);
105
+ border: 1px solid var(--cauthy-input-border);
106
+ border-radius: var(--cauthy-radius);
107
+ outline: none;
108
+ transition: border-color 0.15s, box-shadow 0.15s;
109
+ }
110
+
111
+ :where([data-cauthy] .cauthy-input:focus-visible) {
112
+ border-color: var(--cauthy-accent);
113
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--cauthy-accent) 25%, transparent);
114
+ }
115
+
116
+ :where([data-cauthy] .cauthy-input[aria-invalid="true"]) {
117
+ border-color: var(--cauthy-error);
118
+ }
119
+
120
+ :where([data-cauthy] .cauthy-fieldError) {
121
+ font-size: 12px;
122
+ color: var(--cauthy-error);
123
+ }
124
+
125
+ /* What a new password must be, under its field. */
126
+ :where([data-cauthy] .cauthy-hint) {
127
+ font-size: 12px;
128
+ color: var(--cauthy-text-muted);
129
+ }
130
+
131
+ /* ── Buttons ─────────────────────────────────────────────────────────────── */
132
+
133
+ :where([data-cauthy] .cauthy-button),
134
+ :where([data-cauthy] .cauthy-providerButton) {
135
+ display: inline-flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ gap: 8px;
139
+ width: 100%;
140
+ min-height: 40px;
141
+ padding: 0 16px;
142
+ font: inherit;
143
+ font-weight: 500;
144
+ border-radius: var(--cauthy-button-radius);
145
+ cursor: pointer;
146
+ transition: filter 0.15s, background-color 0.15s;
147
+ }
148
+
149
+ :where([data-cauthy] .cauthy-button) {
150
+ color: var(--cauthy-primary-text);
151
+ background: var(--cauthy-primary);
152
+ border: 1px solid var(--cauthy-primary);
153
+ }
154
+
155
+ :where([data-cauthy] .cauthy-providerButton) {
156
+ color: var(--cauthy-text);
157
+ background: var(--cauthy-input-background);
158
+ border: 1px solid var(--cauthy-input-border);
159
+ }
160
+
161
+ /* A provider's logo, before "Continue with …". */
162
+ :where([data-cauthy] .cauthy-providerIcon) {
163
+ flex: none;
164
+ width: 18px;
165
+ height: 18px;
166
+ object-fit: contain;
167
+ }
168
+
169
+ :where([data-cauthy] .cauthy-button:hover:not(:disabled)),
170
+ :where([data-cauthy] .cauthy-providerButton:hover:not(:disabled)) {
171
+ filter: brightness(0.94);
172
+ }
173
+
174
+ :where([data-cauthy] .cauthy-button:focus-visible),
175
+ :where([data-cauthy] .cauthy-providerButton:focus-visible) {
176
+ outline: 2px solid var(--cauthy-accent);
177
+ outline-offset: 2px;
178
+ }
179
+
180
+ :where([data-cauthy] .cauthy-button:disabled),
181
+ :where([data-cauthy] .cauthy-providerButton:disabled) {
182
+ opacity: 0.6;
183
+ cursor: default;
184
+ }
185
+
186
+ /*
187
+ * Under way: the pressed button keeps its label with a spinner before it, in
188
+ * the button's own colour; a form still opening shows the spinner alone. An
189
+ * icon and never words: the application may be in any language. Without
190
+ * motion, the ring stays still and pulses nothing: it only shows.
191
+ */
192
+ :where([data-cauthy] .cauthy-spinner) {
193
+ display: inline-block;
194
+ flex: none;
195
+ width: 1em;
196
+ height: 1em;
197
+ vertical-align: -0.125em;
198
+ border: 2px solid currentColor;
199
+ border-right-color: transparent;
200
+ border-radius: 50%;
201
+ animation: cauthy-spin 0.7s linear infinite;
202
+ }
203
+
204
+ :where([data-cauthy] .cauthy-button[aria-busy="true"]),
205
+ :where([data-cauthy] .cauthy-providerButton[aria-busy="true"]) {
206
+ cursor: progress;
207
+ }
208
+
209
+ @keyframes cauthy-spin {
210
+ to { transform: rotate(360deg); }
211
+ }
212
+
213
+ @media (prefers-reduced-motion: reduce) {
214
+ :where([data-cauthy] .cauthy-spinner) {
215
+ animation: none;
216
+ border-right-color: currentColor;
217
+ border-style: dotted;
218
+ }
219
+ }
220
+
221
+ :where(.cauthy-section) > :where(.cauthy-button, .cauthy-providerButton) {
222
+ width: auto;
223
+ align-self: flex-start;
224
+ }
225
+
226
+ /* ── Words around the form ───────────────────────────────────────────────── */
227
+
228
+ /* Always underlined: an action the size of a sentence ("Forgot password?",
229
+ "Create an account") has to look like the link it is, not plain text. */
230
+ :where([data-cauthy] .cauthy-link) {
231
+ color: var(--cauthy-accent);
232
+ font-size: 13px;
233
+ font-weight: 500;
234
+ text-decoration: underline;
235
+ text-underline-offset: 3px;
236
+ }
237
+
238
+ :where([data-cauthy] .cauthy-link:hover) {
239
+ text-decoration-thickness: 2px;
240
+ }
241
+
242
+ :where([data-cauthy] .cauthy-separator) {
243
+ display: flex;
244
+ align-items: center;
245
+ gap: 10px;
246
+ margin: 4px 0;
247
+ font-size: 12px;
248
+ color: var(--cauthy-text-muted);
249
+ }
250
+
251
+ :where([data-cauthy] .cauthy-separator)::before,
252
+ :where([data-cauthy] .cauthy-separator)::after {
253
+ content: "";
254
+ flex: 1;
255
+ height: 1px;
256
+ background: var(--cauthy-border);
257
+ }
258
+
259
+ :where([data-cauthy] .cauthy-notice) {
260
+ margin: 0;
261
+ color: var(--cauthy-text-muted);
262
+ }
263
+
264
+ /* A form still opening: its spinner alone, in the middle. */
265
+ :where([data-cauthy] .cauthy-pending) {
266
+ display: flex;
267
+ align-items: center;
268
+ justify-content: center;
269
+ margin: 0;
270
+ padding: 16px 0;
271
+ color: var(--cauthy-text-muted);
272
+ }
273
+
274
+ :where([data-cauthy] [role="status"].cauthy-notice) {
275
+ padding: 8px 12px;
276
+ color: var(--cauthy-success);
277
+ background: var(--cauthy-success-background);
278
+ border-radius: var(--cauthy-radius);
279
+ }
280
+
281
+ :where([data-cauthy] .cauthy-error) {
282
+ margin: 0;
283
+ padding: 10px 12px;
284
+ font-size: 13px;
285
+ color: var(--cauthy-error);
286
+ background: var(--cauthy-error-background);
287
+ border-radius: var(--cauthy-radius);
288
+ }
289
+
290
+ /* A checkbox with its sentence: the terms accepted, a consent given. */
291
+ :where([data-cauthy] .cauthy-check) {
292
+ display: flex;
293
+ gap: 8px;
294
+ align-items: flex-start;
295
+ margin: 0 0 12px;
296
+ font-size: 13px;
297
+ line-height: 1.45;
298
+ color: var(--cauthy-text-muted);
299
+ }
300
+ :where([data-cauthy] .cauthy-check input) {
301
+ margin-top: 3px;
302
+ }
303
+
304
+ :where([data-cauthy] .cauthy-footer) {
305
+ margin: 4px 0 0;
306
+ font-size: 13px;
307
+ text-align: center;
308
+ color: var(--cauthy-text-muted);
309
+ }
310
+
311
+ /* ── The account screen's lists ──────────────────────────────────────────── */
312
+
313
+ :where([data-cauthy] .cauthy-qr) {
314
+ display: block;
315
+ width: 220px;
316
+ max-width: 100%;
317
+ height: auto;
318
+ image-rendering: pixelated;
319
+ background: #fff;
320
+ border-radius: var(--cauthy-radius);
321
+ }
322
+
323
+ :where([data-cauthy] .cauthy-secret) {
324
+ padding: 8px 10px;
325
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
326
+ font-size: 13px;
327
+ word-break: break-all;
328
+ background: var(--cauthy-input-background);
329
+ border: 1px dashed var(--cauthy-input-border);
330
+ border-radius: var(--cauthy-radius);
331
+ }
332
+
333
+ :where([data-cauthy] .cauthy-codes) {
334
+ display: grid;
335
+ grid-template-columns: repeat(2, minmax(0, 1fr));
336
+ gap: 6px;
337
+ margin: 0;
338
+ padding: 0;
339
+ list-style: none;
340
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
341
+ }
342
+
343
+ :where([data-cauthy] .cauthy-sessions) {
344
+ display: flex;
345
+ flex-direction: column;
346
+ gap: 6px;
347
+ margin: 0;
348
+ padding: 0;
349
+ list-style: none;
350
+ }
351
+
352
+ :where([data-cauthy] .cauthy-session) {
353
+ padding: 8px 10px;
354
+ font-size: 13px;
355
+ color: var(--cauthy-text-muted);
356
+ border: 1px solid var(--cauthy-border);
357
+ border-radius: var(--cauthy-radius);
358
+ }
359
+
360
+ /* The workspace's Turnstile challenge, when it asks for one. */
361
+ :where([data-cauthy] .cauthy-challenge) {
362
+ min-height: 65px;
363
+ }
364
+
365
+ /* ── The user button ─────────────────────────────────────────────────────── */
366
+
367
+ :where(.cauthy-user-root[data-cauthy]) {
368
+ position: relative;
369
+ display: inline-block;
370
+ }
371
+
372
+ :where([data-cauthy] .cauthy-user-trigger) {
373
+ display: inline-flex;
374
+ align-items: center;
375
+ justify-content: center;
376
+ min-width: 36px;
377
+ height: 36px;
378
+ padding: 0 12px;
379
+ font: inherit;
380
+ font-weight: 600;
381
+ color: var(--cauthy-primary-text);
382
+ background: var(--cauthy-primary);
383
+ border: 0;
384
+ border-radius: var(--cauthy-button-radius);
385
+ text-decoration: none;
386
+ cursor: pointer;
387
+ }
388
+
389
+ :where([data-cauthy] .cauthy-user-trigger[aria-haspopup]) {
390
+ width: 36px;
391
+ padding: 0;
392
+ border-radius: 9999px;
393
+ }
394
+
395
+ :where([data-cauthy] .cauthy-user-trigger:disabled) {
396
+ opacity: 0.6;
397
+ cursor: default;
398
+ }
399
+
400
+ :where([data-cauthy] .cauthy-user-trigger:focus-visible),
401
+ :where([data-cauthy] .cauthy-user-item:focus-visible),
402
+ :where([data-cauthy] .cauthy-organization:focus-visible) {
403
+ outline: 2px solid var(--cauthy-accent);
404
+ outline-offset: 2px;
405
+ }
406
+
407
+ :where([data-cauthy] .cauthy-user-menu) {
408
+ position: absolute;
409
+ top: calc(100% + 6px);
410
+ right: 0;
411
+ z-index: 50;
412
+ display: flex;
413
+ flex-direction: column;
414
+ min-width: 220px;
415
+ padding: 6px;
416
+ background: var(--cauthy-surface);
417
+ border: 1px solid var(--cauthy-border);
418
+ border-radius: var(--cauthy-radius);
419
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
420
+ }
421
+
422
+ :where([data-cauthy] .cauthy-user-identity) {
423
+ display: flex;
424
+ flex-direction: column;
425
+ margin-bottom: 4px;
426
+ padding: 8px 10px;
427
+ border-bottom: 1px solid var(--cauthy-border);
428
+ }
429
+
430
+ :where([data-cauthy] .cauthy-user-identity span) {
431
+ font-size: 13px;
432
+ color: var(--cauthy-text-muted);
433
+ }
434
+
435
+ :where([data-cauthy] .cauthy-user-item) {
436
+ display: block;
437
+ width: 100%;
438
+ padding: 8px 10px;
439
+ font: inherit;
440
+ text-align: left;
441
+ color: var(--cauthy-text);
442
+ background: none;
443
+ border: 0;
444
+ border-radius: var(--cauthy-radius);
445
+ text-decoration: none;
446
+ cursor: pointer;
447
+ }
448
+
449
+ :where([data-cauthy] .cauthy-user-item:hover:not(:disabled)) {
450
+ background: color-mix(in srgb, var(--cauthy-text) 6%, transparent);
451
+ }
452
+
453
+ /* ── Organizations ───────────────────────────────────────────────────────── */
454
+
455
+ :where(.cauthy-organizations[data-cauthy]) {
456
+ display: flex;
457
+ flex-direction: column;
458
+ gap: 16px;
459
+ width: 100%;
460
+ max-width: 520px;
461
+ }
462
+
463
+ :where([data-cauthy] .cauthy-organizationList) {
464
+ display: flex;
465
+ flex-direction: column;
466
+ gap: 6px;
467
+ margin: 0;
468
+ padding: 0;
469
+ list-style: none;
470
+ }
471
+
472
+ :where([data-cauthy] .cauthy-organization) {
473
+ display: flex;
474
+ justify-content: space-between;
475
+ gap: 12px;
476
+ width: 100%;
477
+ padding: 10px 12px;
478
+ font: inherit;
479
+ text-align: left;
480
+ color: var(--cauthy-text);
481
+ background: var(--cauthy-surface);
482
+ border: 1px solid var(--cauthy-border);
483
+ border-radius: var(--cauthy-radius);
484
+ cursor: pointer;
485
+ }
486
+
487
+ :where([data-cauthy] .cauthy-organization[aria-current="true"]) {
488
+ border-color: var(--cauthy-primary);
489
+ box-shadow: inset 0 0 0 1px var(--cauthy-primary);
490
+ }
491
+
492
+ :where([data-cauthy] .cauthy-organizationRole) {
493
+ font-size: 13px;
494
+ color: var(--cauthy-text-muted);
495
+ }
496
+
497
+ :where([data-cauthy] .cauthy-retry) {
498
+ align-self: flex-start;
499
+ padding: 8px 14px;
500
+ font: inherit;
501
+ color: var(--cauthy-text);
502
+ background: var(--cauthy-surface);
503
+ border: 1px solid var(--cauthy-border);
504
+ border-radius: var(--cauthy-button-radius);
505
+ cursor: pointer;
506
+ }
507
+
508
+ :where([data-cauthy] .cauthy-retry:focus-visible) {
509
+ outline: 2px solid var(--cauthy-accent);
510
+ outline-offset: 2px;
511
+ }
package/package.json CHANGED
@@ -1,6 +1,108 @@
1
1
  {
2
2
  "name": "@cauthyhq/react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Cauthy for React: hooks and headless sign-in, account and organization components with a default theme.",
5
+ "license": "Apache-2.0",
6
+ "author": "Vennet Developers",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/vennet-developers/cauthy-sdk-react.git"
10
+ },
11
+ "homepage": "https://github.com/vennet-developers/cauthy-sdk-react#readme",
12
+ "bugs": "https://github.com/vennet-developers/cauthy-sdk-react/issues",
13
+ "cauthy": {
14
+ "protocol": 1
15
+ },
16
+ "type": "commonjs",
17
+ "engines": {
18
+ "node": ">=20"
19
+ },
20
+ "main": "./dist/index.js",
21
+ "module": "./dist/index.mjs",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "import": {
26
+ "types": "./dist/index.d.mts",
27
+ "default": "./dist/index.mjs"
28
+ },
29
+ "require": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ }
33
+ },
34
+ "./styles.css": "./dist/styles.css",
35
+ "./package.json": "./package.json"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "README.md",
40
+ "LICENSE",
41
+ "NOTICE"
42
+ ],
43
+ "sideEffects": [
44
+ "*.css"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsup && cp src/styles.css dist/styles.css",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "vitest run",
50
+ "test:watch": "vitest",
51
+ "check:fixtures": "sh scripts/check-fixtures.sh",
52
+ "check:package": "sh scripts/check-package.sh",
53
+ "check:size": "node scripts/check-size.mjs",
54
+ "ssr:smoke": "sh scripts/ssr-smoke.sh",
55
+ "e2e:build": "vite build --config e2e/app/vite.config.ts",
56
+ "e2e:node": "npm run e2e:build && E2E_BACKEND=node playwright test",
57
+ "e2e:record": "npm run e2e:build && E2E_BACKEND=record playwright test --project chromium",
58
+ "e2e:fixture": "npm run e2e:build && E2E_BACKEND=fixture playwright test",
59
+ "e2e:go": "npm run e2e:build && E2E_BACKEND=go playwright test",
60
+ "prepublishOnly": "node scripts/no-file-deps.mjs && npm run build"
61
+ },
62
+ "peerDependencies": {
63
+ "react": ">=18"
64
+ },
65
+ "dependencies": {
66
+ "@cauthyhq/js": "^1.0.0"
67
+ },
68
+ "publishConfig": {
69
+ "access": "public",
70
+ "provenance": true
71
+ },
72
+ "keywords": [
73
+ "cauthy",
74
+ "auth",
75
+ "authentication",
76
+ "react",
77
+ "sign-in",
78
+ "passkey",
79
+ "nextjs",
80
+ "bff"
81
+ ],
82
+ "devDependencies": {
83
+ "@arethetypeswrong/cli": "0.18.5",
84
+ "@cauthyhq/node": "^1.0.0",
85
+ "@playwright/test": "1.56.1",
86
+ "@types/express": "5.0.6",
87
+ "@types/node": "20.19.41",
88
+ "@types/react": "19.2.17",
89
+ "@types/react-dom": "19.2.7",
90
+ "@vitejs/plugin-react": "4.7.0",
91
+ "axe-core": "4.10.3",
92
+ "cauthy-auth": "0.42.0",
93
+ "express": "5.3.0",
94
+ "jsdom": "26.1.0",
95
+ "publint": "0.3.25",
96
+ "react": "19.2.7",
97
+ "react-dom": "19.2.7",
98
+ "tsup": "8.5.1",
99
+ "tsx": "4.22.4",
100
+ "typescript": "5.9.3",
101
+ "vite": "6.4.5",
102
+ "vitest": "3.2.7"
103
+ },
104
+ "allowScripts": {
105
+ "esbuild": false,
106
+ "fsevents": false
107
+ }
108
+ }