@authowl/react 0.14.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/LICENSE +21 -0
- package/README.md +79 -0
- package/THIRD_PARTY_NOTICES.md +36 -0
- package/dist/index.cjs +8243 -0
- package/dist/index.d.cts +825 -0
- package/dist/index.d.ts +825 -0
- package/dist/index.js +8151 -0
- package/dist/styles.css +350 -0
- package/package.json +74 -0
package/dist/styles.css
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/* Minimal default styles for SignIn / SignUp / UserButton.
|
|
2
|
+
Override with your own classes - the components are unstyled by default
|
|
3
|
+
beyond these light defaults.
|
|
4
|
+
|
|
5
|
+
Theming: <AuthOwlProvider> sets `--ba-primary` (from the `appearance` prop or
|
|
6
|
+
the project's branding) inline, and a `data-authowl-theme` attribute
|
|
7
|
+
(light | dark | system) on a wrapper. The color palette below is expressed as
|
|
8
|
+
CSS variables so light/dark is a single palette swap; `system` follows the OS
|
|
9
|
+
via a media query (no JS, so no hydration mismatch or theme flash). */
|
|
10
|
+
.authowl-root {
|
|
11
|
+
--ba-surface: #fff;
|
|
12
|
+
--ba-fg: #18181b;
|
|
13
|
+
--ba-muted: #66666d;
|
|
14
|
+
--ba-border: #d4d4d8;
|
|
15
|
+
--ba-divider: #e4e4e7;
|
|
16
|
+
--ba-hover: #f4f4f5;
|
|
17
|
+
--ba-skeleton-a: #f4f4f5;
|
|
18
|
+
--ba-skeleton-b: #e4e4e7;
|
|
19
|
+
--ba-success: #15803d;
|
|
20
|
+
/* Brand accent ramp (P1-5). The operator picks ONE brand color; the provider
|
|
21
|
+
derives the ramp from it in JS (brand.ts) - hue+chroma-preserving OKLCH
|
|
22
|
+
lightness steps for hover/active and a WCAG-solved foreground - and emits it
|
|
23
|
+
as inline --ba-rt-* bridge vars. The color-mix fallbacks here cover the
|
|
24
|
+
pre-hydration / no-provider / host-`--ba-primary`-override path and stay
|
|
25
|
+
hue-true (OKLab, replacing the old chroma-collapsing `in srgb` mix that
|
|
26
|
+
turned gold #F5B84C into mud). The default brand is the AuthOwl gold, kept in
|
|
27
|
+
sync with brand.ts DEFAULT_BRAND_COLOR. */
|
|
28
|
+
--ba-accent-solid: var(--ba-primary, #F5B84C);
|
|
29
|
+
--ba-accent-solid-hover: var(--ba-rt-solid-hover-l, color-mix(in oklab, var(--ba-primary, #F5B84C) 90%, black));
|
|
30
|
+
--ba-accent-solid-active: var(--ba-rt-solid-active-l, color-mix(in oklab, var(--ba-primary, #F5B84C) 84%, black));
|
|
31
|
+
--ba-accent-fg: var(--ba-rt-accent-fg, #18181b);
|
|
32
|
+
--ba-accent: var(--ba-rt-accent-l, color-mix(in oklab, var(--ba-primary, #F5B84C) 65%, black));
|
|
33
|
+
--ba-danger-text: #b91c1c;
|
|
34
|
+
--ba-overlay: rgba(9, 9, 11, 0.58);
|
|
35
|
+
}
|
|
36
|
+
[data-authowl-theme='dark'] {
|
|
37
|
+
--ba-surface: #18181b;
|
|
38
|
+
--ba-fg: #fafafa;
|
|
39
|
+
--ba-muted: #a1a1aa;
|
|
40
|
+
--ba-border: #3f3f46;
|
|
41
|
+
--ba-divider: #3f3f46;
|
|
42
|
+
--ba-hover: #27272a;
|
|
43
|
+
--ba-skeleton-a: #27272a;
|
|
44
|
+
--ba-skeleton-b: #3f3f46;
|
|
45
|
+
--ba-success: #4ade80;
|
|
46
|
+
--ba-accent-solid: var(--ba-primary, #F5B84C);
|
|
47
|
+
--ba-accent-solid-hover: var(--ba-rt-solid-hover-d, color-mix(in oklab, var(--ba-primary, #F5B84C) 80%, white));
|
|
48
|
+
--ba-accent-solid-active: var(--ba-rt-solid-active-d, color-mix(in oklab, var(--ba-primary, #F5B84C) 65%, white));
|
|
49
|
+
--ba-accent-fg: var(--ba-rt-accent-fg, #18181b);
|
|
50
|
+
--ba-accent: var(--ba-rt-accent-d, color-mix(in oklab, var(--ba-primary, #F5B84C) 80%, white));
|
|
51
|
+
--ba-danger-text: #f87171;
|
|
52
|
+
--ba-overlay: rgba(0, 0, 0, 0.72);
|
|
53
|
+
}
|
|
54
|
+
@media (prefers-color-scheme: dark) {
|
|
55
|
+
[data-authowl-theme='system'] {
|
|
56
|
+
--ba-surface: #18181b;
|
|
57
|
+
--ba-fg: #fafafa;
|
|
58
|
+
--ba-muted: #a1a1aa;
|
|
59
|
+
--ba-border: #3f3f46;
|
|
60
|
+
--ba-divider: #3f3f46;
|
|
61
|
+
--ba-hover: #27272a;
|
|
62
|
+
--ba-skeleton-a: #27272a;
|
|
63
|
+
--ba-skeleton-b: #3f3f46;
|
|
64
|
+
--ba-success: #4ade80;
|
|
65
|
+
--ba-accent-solid: var(--ba-primary, #F5B84C);
|
|
66
|
+
--ba-accent-solid-hover: var(--ba-rt-solid-hover-d, color-mix(in oklab, var(--ba-primary, #F5B84C) 80%, white));
|
|
67
|
+
--ba-accent-solid-active: var(--ba-rt-solid-active-d, color-mix(in oklab, var(--ba-primary, #F5B84C) 65%, white));
|
|
68
|
+
--ba-accent-fg: var(--ba-rt-accent-fg, #18181b);
|
|
69
|
+
--ba-accent: var(--ba-rt-accent-d, color-mix(in oklab, var(--ba-primary, #F5B84C) 80%, white));
|
|
70
|
+
--ba-danger-text: #f87171;
|
|
71
|
+
--ba-overlay: rgba(0, 0, 0, 0.72);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.ba-form { display: flex; flex-direction: column; gap: 12px; max-width: 360px; color: var(--ba-fg); }
|
|
76
|
+
.ba-fields { display: flex; flex-direction: column; gap: 12px; }
|
|
77
|
+
.ba-title { font-size: 18px; font-weight: 600; margin: 0 0 4px; color: var(--ba-fg); }
|
|
78
|
+
.ba-label { display: flex; flex-direction: column; font-size: 12px; gap: 4px; color: var(--ba-fg); }
|
|
79
|
+
.ba-input { padding: 8px 10px; border: 1px solid var(--ba-border); border-radius: 6px; font-size: 14px; background: var(--ba-surface); color: var(--ba-fg); }
|
|
80
|
+
.ba-input:focus { outline: 2px solid var(--ba-accent); outline-offset: 1px; }
|
|
81
|
+
/* Primary filled button IS the brand color (--ba-accent-solid), with a
|
|
82
|
+
contrast-computed label (--ba-accent-fg). The hairline guard keeps a near-white
|
|
83
|
+
(light) or near-black (dark) brand button visible against the surface - the
|
|
84
|
+
promise the old muddy color-mix was a crude stand-in for. box-sizing keeps the
|
|
85
|
+
40px height stable now that the fill carries a border, matching the outlined
|
|
86
|
+
secondary/social buttons. */
|
|
87
|
+
.ba-button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; line-height: 1.2; min-height: 40px; box-sizing: border-box; background: var(--ba-accent-solid); color: var(--ba-accent-fg); padding: 9px 14px; border: 1px solid color-mix(in oklab, var(--ba-accent-solid) 85%, var(--ba-border)); border-radius: 6px; cursor: pointer; font-weight: 500; font-size: 14px; }
|
|
88
|
+
/* Hover/active darken (light) or lighten (dark) perceptually, staying on-brand.
|
|
89
|
+
Scoped away from the outlined secondary/danger variants so their own fills win
|
|
90
|
+
in every state (they don't define :active, which would otherwise leak here). */
|
|
91
|
+
.ba-button:not(.ba-button-secondary):not(.ba-danger-button):hover:not(:disabled) { background: var(--ba-accent-solid-hover); }
|
|
92
|
+
.ba-button:not(.ba-button-secondary):not(.ba-danger-button):active:not(:disabled) { background: var(--ba-accent-solid-active); }
|
|
93
|
+
.ba-button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
94
|
+
/* Secondary action (e.g. the passwordless alternates under a password form):
|
|
95
|
+
outlined so a single filled button stays the clear primary. */
|
|
96
|
+
.ba-button-secondary { background: transparent; color: var(--ba-fg); border: 1px solid var(--ba-border); font-weight: 500; }
|
|
97
|
+
.ba-button-secondary:hover:not(:disabled) { border-color: var(--ba-accent); background: var(--ba-hover); }
|
|
98
|
+
.ba-error { color: #ef4444; font-size: 13px; margin: 0; }
|
|
99
|
+
.ba-success { color: var(--ba-success); font-size: 13px; margin: 0; }
|
|
100
|
+
.ba-muted { color: var(--ba-muted); font-size: 13px; margin: 0; }
|
|
101
|
+
.ba-turnstile { min-height: 65px; max-width: 100%; overflow: hidden; }
|
|
102
|
+
.ba-auth-challenge .ba-turnstile { min-height: 0; }
|
|
103
|
+
.ba-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
104
|
+
|
|
105
|
+
/* Legal-consent checkbox row (shown on <SignUp/> when the project requires it) */
|
|
106
|
+
.ba-consent { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; color: var(--ba-fg); }
|
|
107
|
+
.ba-consent a { color: var(--ba-accent); }
|
|
108
|
+
|
|
109
|
+
/* "Secured by AuthOwl" attribution (free plan; hidden on paid, or forced on
|
|
110
|
+
via the showBadge prop). A small owl mark + wordmark, like other embeddable
|
|
111
|
+
auth widgets. */
|
|
112
|
+
.ba-badge { align-self: center; display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 12px; color: var(--ba-muted); text-decoration: none; }
|
|
113
|
+
.ba-badge:hover { color: var(--ba-fg); }
|
|
114
|
+
.ba-badge-mark { width: 14px; height: 14px; flex-shrink: 0; display: block; }
|
|
115
|
+
.ba-badge-brand { font-weight: 600; color: var(--ba-fg); }
|
|
116
|
+
|
|
117
|
+
/* Inline text button (e.g. "Use a different email", passkey list actions) */
|
|
118
|
+
.ba-link-button { background: none; border: 0; padding: 0; cursor: pointer; color: var(--ba-accent); font-size: 13px; font-weight: 500; align-self: flex-start; }
|
|
119
|
+
.ba-link-button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
120
|
+
.ba-link-button.ba-danger { color: var(--ba-danger-text); }
|
|
121
|
+
|
|
122
|
+
/* MFA enrolment: the manual TOTP secret (rendered inline in a sentence) and the
|
|
123
|
+
one-time backup codes. Monospace and boxed so they read cleanly, with
|
|
124
|
+
`user-select: all` so one click/tap selects the whole value to copy - shown at
|
|
125
|
+
the one moment the user must read and save these EXACTLY. `direction: ltr`
|
|
126
|
+
keeps the alphanumeric strings readable inside an RTL (Arabic) layout. Colors
|
|
127
|
+
are token-based, so dark mode is automatic. */
|
|
128
|
+
.ba-code { display: inline-block; direction: ltr; unicode-bidi: isolate; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace; font-size: 13px; line-height: 1.5; padding: 2px 6px; border-radius: 5px; background: var(--ba-hover); color: var(--ba-fg); overflow-wrap: anywhere; user-select: all; -webkit-user-select: all; }
|
|
129
|
+
.ba-backup-codes { list-style: none; margin: 6px 0 0; padding: 12px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; border: 1px solid var(--ba-border); border-radius: 8px; background: var(--ba-surface); }
|
|
130
|
+
.ba-backup-codes .ba-code { display: block; text-align: center; padding: 8px 10px; font-size: 14px; letter-spacing: 0.06em; }
|
|
131
|
+
@media (max-width: 400px) { .ba-backup-codes { grid-template-columns: minmax(0, 1fr); } }
|
|
132
|
+
|
|
133
|
+
/* Passkey manager */
|
|
134
|
+
.ba-passkey-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
|
|
135
|
+
.ba-passkey-item { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 10px; border: 1px solid var(--ba-border); border-radius: 6px; font-size: 14px; }
|
|
136
|
+
.ba-passkey-name { color: var(--ba-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
137
|
+
.ba-passkey-actions { display: flex; gap: 12px; flex-shrink: 0; }
|
|
138
|
+
|
|
139
|
+
/* Social sign-in */
|
|
140
|
+
.ba-social { display: flex; flex-direction: column; gap: 8px; }
|
|
141
|
+
.ba-social-button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 40px; box-sizing: border-box; background: var(--ba-surface); color: var(--ba-fg); padding: 9px 14px; border: 1px solid var(--ba-border); border-radius: 6px; cursor: pointer; font-weight: 500; font-size: 14px; }
|
|
142
|
+
.ba-social-button:hover:not(:disabled) { border-color: var(--ba-accent); background: var(--ba-hover); }
|
|
143
|
+
.ba-social-button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
144
|
+
.ba-social-icon { width: 18px; height: 18px; flex-shrink: 0; display: block; }
|
|
145
|
+
.ba-divider { display: flex; align-items: center; text-align: center; color: var(--ba-muted); font-size: 12px; }
|
|
146
|
+
.ba-divider::before, .ba-divider::after { content: ''; flex: 1; border-bottom: 1px solid var(--ba-divider); }
|
|
147
|
+
.ba-divider::before { margin-inline-end: 8px; }
|
|
148
|
+
.ba-divider::after { margin-inline-start: 8px; }
|
|
149
|
+
|
|
150
|
+
/* Loading skeleton */
|
|
151
|
+
.ba-skeleton { height: 38px; border-radius: 6px; background: linear-gradient(90deg, var(--ba-skeleton-a) 25%, var(--ba-skeleton-b) 37%, var(--ba-skeleton-a) 63%); background-size: 400% 100%; animation: ba-shimmer 1.4s ease infinite; }
|
|
152
|
+
@keyframes ba-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }
|
|
153
|
+
|
|
154
|
+
/* In-flight spinner (action buttons via <Busy>, session bootstrap via
|
|
155
|
+
<AuthLoading>). `currentColor` picks up the right hue on filled/secondary/
|
|
156
|
+
social buttons; `border-inline-end-color` + rotation is RTL-safe with no
|
|
157
|
+
[dir] override. It sits inline in the button's existing flex gap. */
|
|
158
|
+
.ba-spinner { width: 14px; height: 14px; flex: none; border: 2px solid currentColor; border-inline-end-color: transparent; border-radius: 50%; animation: ba-spin 0.6s linear infinite; }
|
|
159
|
+
@keyframes ba-spin { to { transform: rotate(360deg); } }
|
|
160
|
+
.ba-auth-loading { display: flex; align-items: center; justify-content: center; padding: 24px; color: var(--ba-muted, currentColor); }
|
|
161
|
+
@media (prefers-reduced-motion: reduce) { .ba-spinner { animation-duration: 1.6s; } .ba-skeleton { animation: none; } }
|
|
162
|
+
|
|
163
|
+
.ba-user-button { position: relative; display: inline-block; }
|
|
164
|
+
.ba-user-button-trigger { background: none; border: 0; padding: 0; cursor: pointer; }
|
|
165
|
+
.ba-avatar { width: 32px; height: 32px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--ba-hover); font-weight: 600; color: var(--ba-fg); }
|
|
166
|
+
.ba-menu { position: absolute; inset-inline-end: 0; margin-top: 4px; background: var(--ba-surface); border: 1px solid var(--ba-border); border-radius: 6px; padding: 4px; min-width: 200px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); z-index: 50; }
|
|
167
|
+
.ba-menu-label { font-size: 12px; padding: 6px 8px; color: var(--ba-muted); margin: 0; border-bottom: 1px solid var(--ba-divider); }
|
|
168
|
+
.ba-menu-item { display: block; width: 100%; text-align: start; padding: 6px 8px; background: none; border: 0; cursor: pointer; font-size: 13px; border-radius: 4px; color: var(--ba-fg); }
|
|
169
|
+
.ba-menu-item:hover { background: var(--ba-hover); }
|
|
170
|
+
|
|
171
|
+
/* UserProfile account settings */
|
|
172
|
+
.ba-profile { color: var(--ba-fg); background: var(--ba-surface); border: 1px solid var(--ba-border); border-radius: 14px; font-size: 14px; }
|
|
173
|
+
.ba-profile-page { width: min(100%, 920px); margin-inline: auto; overflow: hidden; }
|
|
174
|
+
.ba-profile-heading, .ba-profile-modal-header { padding: 24px 28px; border-bottom: 1px solid var(--ba-divider); }
|
|
175
|
+
.ba-profile-heading h1, .ba-profile-modal-header h1 { margin: 0; font-size: 22px; line-height: 1.25; }
|
|
176
|
+
.ba-profile-heading p, .ba-profile-modal-header p { margin: 6px 0 0; color: var(--ba-muted); }
|
|
177
|
+
.ba-profile-layout { display: grid; grid-template-columns: 220px minmax(0, 1fr); min-height: 460px; }
|
|
178
|
+
.ba-profile-nav { display: flex; flex-direction: column; gap: 4px; padding: 18px 12px; border-inline-end: 1px solid var(--ba-divider); background: color-mix(in srgb, var(--ba-hover) 55%, var(--ba-surface)); }
|
|
179
|
+
.ba-profile-nav-item { width: 100%; min-height: 40px; padding: 9px 12px; border: 0; border-radius: 7px; background: transparent; color: var(--ba-muted); cursor: pointer; font: inherit; font-weight: 500; text-align: start; }
|
|
180
|
+
.ba-profile-nav-item:hover { color: var(--ba-fg); background: var(--ba-hover); }
|
|
181
|
+
.ba-profile-nav-item[aria-current='page'] { color: var(--ba-fg); background: var(--ba-surface); box-shadow: 0 0 0 1px var(--ba-border); }
|
|
182
|
+
.ba-profile-content { min-width: 0; padding: 28px; }
|
|
183
|
+
.ba-profile-section { max-width: 560px; }
|
|
184
|
+
.ba-profile-section-header { margin-bottom: 22px; }
|
|
185
|
+
.ba-profile-section-header .ba-title { font-size: 20px; }
|
|
186
|
+
.ba-profile-section-header .ba-muted { margin-top: 6px; line-height: 1.55; }
|
|
187
|
+
.ba-profile-section .ba-fields { max-width: 440px; gap: 16px; }
|
|
188
|
+
.ba-profile-section .ba-label { gap: 7px; font-size: 13px; font-weight: 500; }
|
|
189
|
+
.ba-profile-section .ba-input { min-height: 42px; box-sizing: border-box; width: 100%; }
|
|
190
|
+
.ba-profile-section input[type='email'],
|
|
191
|
+
.ba-profile-section input[inputmode='url'] { direction: ltr; text-align: left; }
|
|
192
|
+
.ba-profile-submit { align-self: flex-start; min-width: 132px; }
|
|
193
|
+
.ba-profile-list + .ba-profile-submit { margin-top: 20px; }
|
|
194
|
+
.ba-profile-current { display: flex; flex-direction: column; gap: 5px; margin: 0 0 20px; padding: 14px 16px; border: 1px solid var(--ba-border); border-radius: 8px; }
|
|
195
|
+
.ba-profile-current span { color: var(--ba-muted); font-size: 12px; }
|
|
196
|
+
.ba-profile-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; border: 1px solid var(--ba-border); border-radius: 9px; overflow: hidden; }
|
|
197
|
+
.ba-profile-list-item { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 64px; padding: 12px 14px; border-bottom: 1px solid var(--ba-divider); }
|
|
198
|
+
.ba-profile-list-item:last-child { border-bottom: 0; }
|
|
199
|
+
.ba-profile-list-item > span { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
|
|
200
|
+
.ba-profile-list-item strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
|
|
201
|
+
.ba-profile-list-item small { display: flex; flex-wrap: wrap; color: var(--ba-muted); font-size: 11px; }
|
|
202
|
+
.ba-profile-list-item small em { color: var(--ba-success); font-style: normal; font-weight: 600; }
|
|
203
|
+
.ba-profile-list-skeleton { display: flex; flex-direction: column; gap: 8px; }
|
|
204
|
+
.ba-profile-provider-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
|
|
205
|
+
.ba-profile-provider-actions .ba-profile-subtitle { width: 100%; margin: 0 0 2px; font-weight: 600; }
|
|
206
|
+
.ba-inline-error,
|
|
207
|
+
.ba-profile-inline-error { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
|
|
208
|
+
.ba-profile-status { display: flex; flex-direction: column; gap: 5px; margin-bottom: 20px; padding: 14px 16px; border: 1px solid var(--ba-border); border-radius: 8px; background: var(--ba-hover); }
|
|
209
|
+
.ba-profile-status span { color: var(--ba-muted); font-size: 12px; }
|
|
210
|
+
.ba-profile-security-form { margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--ba-divider); }
|
|
211
|
+
.ba-profile-action-row { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
212
|
+
.ba-profile-recovery-card { display: flex; flex-direction: column; gap: 7px; margin-bottom: 24px; padding: 14px 16px; border: 1px solid var(--ba-border); border-radius: 8px; }
|
|
213
|
+
.ba-profile-recovery-card p { margin: 0; color: var(--ba-muted); line-height: 1.5; }
|
|
214
|
+
.ba-profile-recovery-card bdi { direction: ltr; text-align: left; font-weight: 600; }
|
|
215
|
+
.ba-profile-danger-zone { padding: 18px; border: 1px solid color-mix(in srgb, #dc2626 45%, var(--ba-border)); border-radius: 10px; }
|
|
216
|
+
.ba-profile-delete-warning { margin: 0; color: var(--ba-fg); line-height: 1.55; }
|
|
217
|
+
/* Fixed-palette destructive action - override the brand-derived hairline the
|
|
218
|
+
base .ba-button now carries with a matching red-toned one. */
|
|
219
|
+
.ba-danger-button { background: #dc2626; color: #fff; border-color: color-mix(in oklab, #dc2626 85%, var(--ba-border)); }
|
|
220
|
+
.ba-danger-button:hover:not(:disabled) { background: #b91c1c; }
|
|
221
|
+
.ba-profile-loading { display: flex; flex-direction: column; gap: 10px; padding: 28px; }
|
|
222
|
+
.ba-profile-overlay { position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 24px; background: var(--ba-overlay); }
|
|
223
|
+
.ba-profile-modal { width: min(920px, 100%); max-height: min(760px, calc(100dvh - 48px)); overflow: auto; box-shadow: 0 24px 70px rgba(0,0,0,0.3); }
|
|
224
|
+
.ba-profile-modal-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; position: sticky; top: 0; z-index: 2; background: var(--ba-surface); }
|
|
225
|
+
.ba-profile-close { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; padding: 0; border: 1px solid var(--ba-border); border-radius: 50%; background: var(--ba-surface); color: var(--ba-fg); cursor: pointer; }
|
|
226
|
+
.ba-profile-close svg { width: 16px; height: 16px; display: block; }
|
|
227
|
+
.ba-profile-close:hover { background: var(--ba-hover); }
|
|
228
|
+
.ba-profile-modal-footer { display: flex; justify-content: center; padding: 14px 28px; border-top: 1px solid var(--ba-divider); }
|
|
229
|
+
.ba-menu-badge { display: flex; justify-content: center; margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--ba-divider); }
|
|
230
|
+
.ba-menu-badge .ba-badge { margin-top: 4px; }
|
|
231
|
+
/* <AuthOwlBadge/> renders null on a paid/comped project (config.badge false), and these
|
|
232
|
+
wrappers are rendered unconditionally by UserButton/UserProfile/UserProfileModal - so
|
|
233
|
+
without this they became a bare divider with dead space under it. Keyed on :empty rather
|
|
234
|
+
than duplicating the plan check at three call sites: React emits a genuinely empty
|
|
235
|
+
element when the child returns null, so this tracks the badge's own visibility
|
|
236
|
+
automatically and still honours its `force` prop. */
|
|
237
|
+
.ba-profile-modal-footer:empty,
|
|
238
|
+
.ba-menu-badge:empty { display: none; }
|
|
239
|
+
|
|
240
|
+
/* Organization lifecycle */
|
|
241
|
+
.ba-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
242
|
+
.ba-organization-avatar { width: 32px; height: 32px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; background: var(--ba-hover); color: var(--ba-fg); font-weight: 700; }
|
|
243
|
+
.ba-organization-avatar-large { width: 42px; height: 42px; font-size: 17px; }
|
|
244
|
+
.ba-organization-switcher { position: relative; display: inline-block; min-width: 220px; }
|
|
245
|
+
.ba-organization-switcher-skeleton { width: 220px; }
|
|
246
|
+
.ba-organization-switcher-trigger { width: 100%; min-height: 48px; display: flex; align-items: center; gap: 10px; padding: 7px 10px; border: 1px solid var(--ba-border); border-radius: 10px; background: var(--ba-surface); color: var(--ba-fg); cursor: pointer; font: inherit; text-align: start; }
|
|
247
|
+
.ba-organization-switcher-trigger > span:nth-child(2) { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
|
|
248
|
+
.ba-organization-menu { position: absolute; z-index: 80; inset-block-start: calc(100% + 6px); inset-inline-end: 0; width: min(320px, calc(100vw - 32px)); padding: 6px; border: 1px solid var(--ba-border); border-radius: 12px; background: var(--ba-surface); box-shadow: 0 16px 40px rgba(0,0,0,0.18); }
|
|
249
|
+
.ba-organization-menu-item { width: 100%; display: flex; align-items: center; gap: 10px; padding: 8px; border: 0; border-radius: 8px; background: transparent; color: var(--ba-fg); cursor: pointer; font: inherit; text-align: start; }
|
|
250
|
+
.ba-organization-menu-item:hover, .ba-organization-menu-item[aria-current='true'] { background: var(--ba-hover); }
|
|
251
|
+
.ba-organization-menu-item > span:nth-child(2) { min-width: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
252
|
+
.ba-organization-menu-item small { overflow: hidden; color: var(--ba-muted); text-overflow: ellipsis; white-space: nowrap; }
|
|
253
|
+
.ba-organization-menu-actions { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--ba-divider); }
|
|
254
|
+
.ba-organization-menu-actions .ba-menu-item { padding: 9px; }
|
|
255
|
+
.ba-organization-overlay { position: fixed; inset: 0; z-index: 1100; display: grid; place-items: center; padding: 24px; background: var(--ba-overlay); }
|
|
256
|
+
.ba-organization-modal { width: min(920px, 100%); max-height: min(820px, calc(100dvh - 48px)); overflow: auto; border: 1px solid var(--ba-border); border-radius: 14px; background: var(--ba-surface); color: var(--ba-fg); box-shadow: 0 24px 70px rgba(0,0,0,0.3); }
|
|
257
|
+
.ba-organization-modal-header { position: sticky; top: 0; z-index: 3; display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 18px 22px; border-bottom: 1px solid var(--ba-divider); background: var(--ba-surface); }
|
|
258
|
+
.ba-organization-modal-header .ba-title { margin: 0; }
|
|
259
|
+
.ba-organization-modal-body { min-width: 0; }
|
|
260
|
+
.ba-organization-create { max-width: 560px; padding: 28px; }
|
|
261
|
+
.ba-organization-section-header { margin-bottom: 20px; }
|
|
262
|
+
.ba-organization-section-header .ba-title { margin: 0; font-size: 19px; }
|
|
263
|
+
.ba-organization-section-header .ba-muted { margin: 6px 0 0; line-height: 1.5; }
|
|
264
|
+
.ba-organization-directory { max-width: 960px; margin-inline: auto; padding: 28px; border: 1px solid var(--ba-border); border-radius: 14px; background: var(--ba-surface); color: var(--ba-fg); }
|
|
265
|
+
.ba-organization-directory-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; margin-bottom: 24px; }
|
|
266
|
+
.ba-organization-directory-header h2, .ba-organization-directory-header p { margin: 0; }
|
|
267
|
+
.ba-organization-directory-header p { margin-top: 6px; }
|
|
268
|
+
.ba-organization-card-grid { list-style: none; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 0; padding: 0; }
|
|
269
|
+
.ba-organization-card-grid > .ba-skeleton { min-height: 120px; }
|
|
270
|
+
.ba-organization-card { position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 12px; min-height: 110px; padding: 16px; border: 1px solid var(--ba-border); border-radius: 10px; }
|
|
271
|
+
.ba-organization-card-copy { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
|
|
272
|
+
.ba-organization-card-copy strong, .ba-organization-card-copy small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
273
|
+
.ba-organization-card-copy small { color: var(--ba-muted); }
|
|
274
|
+
.ba-organization-active { align-self: start; padding: 4px 7px; border-radius: 999px; background: var(--ba-hover); color: var(--ba-success); font-size: 11px; font-weight: 700; }
|
|
275
|
+
.ba-organization-card-actions { grid-column: 1 / -1; display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
|
|
276
|
+
.ba-organization-user-invitations { margin-top: 30px; padding-top: 26px; border-top: 1px solid var(--ba-divider); }
|
|
277
|
+
.ba-organization-list { list-style: none; display: flex; flex-direction: column; margin: 0; padding: 0; border: 1px solid var(--ba-border); border-radius: 10px; overflow: hidden; }
|
|
278
|
+
.ba-organization-member, .ba-organization-invitation { display: flex; align-items: center; gap: 12px; min-height: 68px; padding: 12px 14px; border-bottom: 1px solid var(--ba-divider); }
|
|
279
|
+
.ba-organization-member:last-child, .ba-organization-invitation:last-child { border-bottom: 0; }
|
|
280
|
+
.ba-organization-member-copy, .ba-organization-invitation > span:first-child { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 4px; }
|
|
281
|
+
.ba-organization-member-copy small, .ba-organization-invitation small { color: var(--ba-muted); }
|
|
282
|
+
.ba-organization-member-actions { display: flex; align-items: center; gap: 10px; }
|
|
283
|
+
.ba-organization-role { width: auto; min-width: 110px; min-height: 36px; }
|
|
284
|
+
.ba-organization-role-label { flex: 0 0 auto; color: var(--ba-muted); font-size: 12px; }
|
|
285
|
+
.ba-organization-profile { color: var(--ba-fg); }
|
|
286
|
+
.ba-organization-profile-heading { display: flex; align-items: center; gap: 13px; padding: 22px 26px; border-bottom: 1px solid var(--ba-divider); }
|
|
287
|
+
.ba-organization-profile-heading h2, .ba-organization-profile-heading p { margin: 0; }
|
|
288
|
+
.ba-organization-profile-heading p { margin-top: 4px; }
|
|
289
|
+
.ba-organization-profile-layout { display: grid; grid-template-columns: 190px minmax(0, 1fr); min-height: 480px; }
|
|
290
|
+
.ba-organization-profile-nav { display: flex; flex-direction: column; gap: 4px; padding: 16px 10px; border-inline-end: 1px solid var(--ba-divider); background: color-mix(in srgb, var(--ba-hover) 55%, var(--ba-surface)); }
|
|
291
|
+
.ba-organization-profile-content { min-width: 0; padding: 26px; }
|
|
292
|
+
.ba-organization-section { max-width: 610px; }
|
|
293
|
+
.ba-organization-facts { display: flex; flex-direction: column; gap: 12px; }
|
|
294
|
+
.ba-organization-facts div { display: flex; justify-content: space-between; gap: 20px; padding-bottom: 10px; border-bottom: 1px solid var(--ba-divider); }
|
|
295
|
+
.ba-organization-facts dt { color: var(--ba-muted); }
|
|
296
|
+
.ba-organization-facts dd { margin: 0; font-weight: 600; }
|
|
297
|
+
.ba-organization-invite-form { display: grid; grid-template-columns: minmax(0, 1fr) 150px auto; align-items: end; gap: 10px; margin-bottom: 20px; }
|
|
298
|
+
.ba-organization-invite-form .ba-label { margin: 0; }
|
|
299
|
+
.ba-organization-danger { display: flex; flex-direction: column; gap: 14px; }
|
|
300
|
+
.ba-organization-danger-action { padding: 18px; border: 1px solid var(--ba-border); border-radius: 10px; }
|
|
301
|
+
.ba-organization-danger-action > p { line-height: 1.5; }
|
|
302
|
+
.ba-organization-delete { border-color: color-mix(in srgb, #dc2626 45%, var(--ba-border)); }
|
|
303
|
+
|
|
304
|
+
@media (max-width: 680px) {
|
|
305
|
+
.ba-profile-overlay { align-items: end; padding: 0; }
|
|
306
|
+
.ba-profile-modal { width: 100%; max-height: 94dvh; border-radius: 16px 16px 0 0; border-bottom: 0; }
|
|
307
|
+
.ba-profile-heading, .ba-profile-modal-header { padding: 20px; }
|
|
308
|
+
.ba-profile-layout { display: block; min-height: 0; }
|
|
309
|
+
.ba-profile-nav { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); padding: 10px 14px; border-inline-end: 0; border-bottom: 1px solid var(--ba-divider); }
|
|
310
|
+
.ba-profile-nav-item { width: 100%; min-width: 0; }
|
|
311
|
+
.ba-profile-content { padding: 22px 20px 28px; }
|
|
312
|
+
.ba-profile-list-item { align-items: flex-start; }
|
|
313
|
+
.ba-profile-list-item .ba-link-button { padding-block: 5px; }
|
|
314
|
+
.ba-organization-overlay { align-items: end; padding: 0; }
|
|
315
|
+
.ba-organization-modal { width: 100%; max-height: 94dvh; border-radius: 16px 16px 0 0; border-bottom: 0; }
|
|
316
|
+
.ba-organization-modal-header { padding: 16px 18px; }
|
|
317
|
+
.ba-organization-create, .ba-organization-directory { padding: 20px; }
|
|
318
|
+
.ba-organization-directory { border-radius: 0; }
|
|
319
|
+
.ba-organization-directory-header { flex-direction: column; }
|
|
320
|
+
.ba-organization-card-grid { grid-template-columns: 1fr; }
|
|
321
|
+
.ba-organization-profile-layout { display: block; min-height: 0; }
|
|
322
|
+
.ba-organization-profile-nav { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); padding: 10px 14px; border-inline-end: 0; border-bottom: 1px solid var(--ba-divider); }
|
|
323
|
+
.ba-organization-profile-content { padding: 20px; }
|
|
324
|
+
.ba-organization-member { align-items: flex-start; flex-wrap: wrap; }
|
|
325
|
+
.ba-organization-member-actions { width: 100%; padding-inline-start: 44px; }
|
|
326
|
+
.ba-organization-invite-form { grid-template-columns: 1fr; }
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/* --- RTL (B.3) ------------------------------------------------------------
|
|
330
|
+
Arabic-capable font stack when the components render RTL (plan 04). Font
|
|
331
|
+
files are not bundled: if the host app loads IBM Plex Sans Arabic it is
|
|
332
|
+
used, otherwise the platform's Arabic-capable system fonts render.
|
|
333
|
+
Targeted at the component surfaces directly (not inheritance from the
|
|
334
|
+
root): a host font-family anywhere between the root and our markup would
|
|
335
|
+
silently win by inheritance, and form controls don't inherit fonts at all. */
|
|
336
|
+
.authowl-root[dir='rtl'] .ba-form,
|
|
337
|
+
.authowl-root[dir='rtl'] .ba-menu,
|
|
338
|
+
.authowl-root[dir='rtl'] .ba-input,
|
|
339
|
+
.authowl-root[dir='rtl'] .ba-button,
|
|
340
|
+
.authowl-root[dir='rtl'] .ba-social-button,
|
|
341
|
+
.authowl-root[dir='rtl'] .ba-link-button,
|
|
342
|
+
.authowl-root[dir='rtl'] .ba-organization-switcher-trigger,
|
|
343
|
+
.authowl-root[dir='rtl'] .ba-organization-menu-item {
|
|
344
|
+
font-family: 'IBM Plex Sans Arabic', 'Segoe UI', Tahoma, system-ui, sans-serif;
|
|
345
|
+
}
|
|
346
|
+
.authowl-root[dir='rtl'] .ba-profile,
|
|
347
|
+
.authowl-root[dir='rtl'] .ba-profile button,
|
|
348
|
+
.authowl-root[dir='rtl'] .ba-profile input {
|
|
349
|
+
font-family: 'IBM Plex Sans Arabic', 'Segoe UI', Tahoma, system-ui, sans-serif;
|
|
350
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@authowl/react",
|
|
3
|
+
"version": "0.14.0",
|
|
4
|
+
"description": "React provider, hooks, and drop-in components for AuthOwl, the multi-tenant auth SaaS.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/mstfash/authowl-sdk.git",
|
|
12
|
+
"directory": "packages/auth-react"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/mstfash/authowl-sdk#readme",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"authentication",
|
|
17
|
+
"auth",
|
|
18
|
+
"authowl",
|
|
19
|
+
"react",
|
|
20
|
+
"oauth",
|
|
21
|
+
"passkey",
|
|
22
|
+
"components",
|
|
23
|
+
"hooks"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"import": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"require": {
|
|
34
|
+
"types": "./dist/index.d.cts",
|
|
35
|
+
"default": "./dist/index.cjs"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"./styles.css": "./dist/styles.css"
|
|
39
|
+
},
|
|
40
|
+
"main": "./dist/index.cjs",
|
|
41
|
+
"module": "./dist/index.js",
|
|
42
|
+
"types": "./dist/index.d.ts",
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"dist/styles.css",
|
|
46
|
+
"THIRD_PARTY_NOTICES.md",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
],
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@authowl/core": "0.10.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"react": ">=18",
|
|
54
|
+
"react-dom": ">=18"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@testing-library/react": "16.3.0",
|
|
58
|
+
"@types/react": "^19.0.2",
|
|
59
|
+
"@types/react-dom": "^19.0.2",
|
|
60
|
+
"jsdom": "26.1.0",
|
|
61
|
+
"qrcode-generator": "^2.0.4",
|
|
62
|
+
"react": "^19.0.0",
|
|
63
|
+
"react-dom": "^19.0.0",
|
|
64
|
+
"tsup": "^8.3.5",
|
|
65
|
+
"typescript": "^5.7.2",
|
|
66
|
+
"vitest": "^4.1.10"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean --external react,react-dom && cp src/styles.css dist/styles.css && node ../../scripts/gen-notices.mjs src/index.ts",
|
|
70
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --external react,react-dom",
|
|
71
|
+
"typecheck": "tsc --noEmit",
|
|
72
|
+
"test": "vitest run --passWithNoTests"
|
|
73
|
+
}
|
|
74
|
+
}
|