@dravyn/auth-ui 0.1.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.
Files changed (52) hide show
  1. package/dist/chunk-35G4WYX3.mjs +154 -0
  2. package/dist/chunk-35G4WYX3.mjs.map +1 -0
  3. package/dist/chunk-6BBGRN4E.mjs +1 -0
  4. package/dist/chunk-6BBGRN4E.mjs.map +1 -0
  5. package/dist/chunk-FAI3ERB3.mjs +209 -0
  6. package/dist/chunk-FAI3ERB3.mjs.map +1 -0
  7. package/dist/chunk-FVXZZTKL.mjs +152 -0
  8. package/dist/chunk-FVXZZTKL.mjs.map +1 -0
  9. package/dist/chunk-Z2ICX4UY.mjs +132 -0
  10. package/dist/chunk-Z2ICX4UY.mjs.map +1 -0
  11. package/dist/index.css +235 -0
  12. package/dist/index.css.map +1 -0
  13. package/dist/index.d.mts +5 -0
  14. package/dist/index.d.ts +5 -0
  15. package/dist/index.js +678 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/index.mjs +22 -0
  18. package/dist/index.mjs.map +1 -0
  19. package/dist/screens/ForgotResetScreen.css +235 -0
  20. package/dist/screens/ForgotResetScreen.css.map +1 -0
  21. package/dist/screens/ForgotResetScreen.d.mts +17 -0
  22. package/dist/screens/ForgotResetScreen.d.ts +17 -0
  23. package/dist/screens/ForgotResetScreen.js +245 -0
  24. package/dist/screens/ForgotResetScreen.js.map +1 -0
  25. package/dist/screens/ForgotResetScreen.mjs +11 -0
  26. package/dist/screens/ForgotResetScreen.mjs.map +1 -0
  27. package/dist/screens/LoginScreen.css +235 -0
  28. package/dist/screens/LoginScreen.css.map +1 -0
  29. package/dist/screens/LoginScreen.d.mts +36 -0
  30. package/dist/screens/LoginScreen.d.ts +36 -0
  31. package/dist/screens/LoginScreen.js +167 -0
  32. package/dist/screens/LoginScreen.js.map +1 -0
  33. package/dist/screens/LoginScreen.mjs +9 -0
  34. package/dist/screens/LoginScreen.mjs.map +1 -0
  35. package/dist/screens/OTPScreen.css +235 -0
  36. package/dist/screens/OTPScreen.css.map +1 -0
  37. package/dist/screens/OTPScreen.d.mts +16 -0
  38. package/dist/screens/OTPScreen.d.ts +16 -0
  39. package/dist/screens/OTPScreen.js +187 -0
  40. package/dist/screens/OTPScreen.js.map +1 -0
  41. package/dist/screens/OTPScreen.mjs +9 -0
  42. package/dist/screens/OTPScreen.mjs.map +1 -0
  43. package/dist/screens/RegisterScreen.css +235 -0
  44. package/dist/screens/RegisterScreen.css.map +1 -0
  45. package/dist/screens/RegisterScreen.d.mts +21 -0
  46. package/dist/screens/RegisterScreen.d.ts +21 -0
  47. package/dist/screens/RegisterScreen.js +189 -0
  48. package/dist/screens/RegisterScreen.js.map +1 -0
  49. package/dist/screens/RegisterScreen.mjs +9 -0
  50. package/dist/screens/RegisterScreen.mjs.map +1 -0
  51. package/package.json +39 -0
  52. package/src/screens/auth.css +260 -0
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/screens/RegisterScreen.tsx
32
+ var RegisterScreen_exports = {};
33
+ __export(RegisterScreen_exports, {
34
+ RegisterScreen: () => RegisterScreen
35
+ });
36
+ module.exports = __toCommonJS(RegisterScreen_exports);
37
+ var import_react = __toESM(require("react"));
38
+ var import_ui = require("@dravyn/ui");
39
+ var import_ui2 = require("@dravyn/ui");
40
+ var import_ui3 = require("@dravyn/ui");
41
+ var RegisterScreen = ({
42
+ redirectTo = "/auth/verify",
43
+ onSuccess,
44
+ logo,
45
+ brandName = "Dravyn",
46
+ apiBase = process.env.NEXT_PUBLIC_AUTH_API_URL ?? "http://localhost:3001",
47
+ extraFields,
48
+ requireOTP = true
49
+ }) => {
50
+ const [form, setForm] = import_react.default.useState({
51
+ firstName: "",
52
+ lastName: "",
53
+ email: "",
54
+ password: "",
55
+ confirm: ""
56
+ });
57
+ const [showPass, setShowPass] = import_react.default.useState(false);
58
+ const [loading, setLoading] = import_react.default.useState(false);
59
+ const [error, setError] = import_react.default.useState("");
60
+ const [fieldErrs, setFieldErrs] = import_react.default.useState({});
61
+ const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));
62
+ const validate = () => {
63
+ const errs = {};
64
+ if (!form.firstName.trim()) errs.firstName = "First name is required.";
65
+ if (!form.lastName.trim()) errs.lastName = "Last name is required.";
66
+ if (!form.email.trim()) errs.email = "Email is required.";
67
+ else if (!/\S+@\S+\.\S+/.test(form.email)) errs.email = "Enter a valid email.";
68
+ if (!form.password) errs.password = "Password is required.";
69
+ else if (form.password.length < 8) errs.password = "Password must be at least 8 characters.";
70
+ if (form.confirm !== form.password) errs.confirm = "Passwords do not match.";
71
+ return errs;
72
+ };
73
+ const handleSubmit = async (e) => {
74
+ e.preventDefault();
75
+ const errs = validate();
76
+ if (Object.keys(errs).length) {
77
+ setFieldErrs(errs);
78
+ return;
79
+ }
80
+ setFieldErrs({});
81
+ setLoading(true);
82
+ setError("");
83
+ try {
84
+ const res = await fetch(`${apiBase}/auth/register`, {
85
+ method: "POST",
86
+ headers: { "Content-Type": "application/json" },
87
+ body: JSON.stringify({
88
+ firstName: form.firstName.trim(),
89
+ lastName: form.lastName.trim(),
90
+ email: form.email.trim().toLowerCase(),
91
+ password: form.password
92
+ })
93
+ });
94
+ const data = await res.json();
95
+ if (!res.ok) {
96
+ setError(data.message ?? "Registration failed.");
97
+ return;
98
+ }
99
+ if (requireOTP) {
100
+ sessionStorage.setItem("dravyn_pending_email", form.email.trim().toLowerCase());
101
+ window.location.href = redirectTo;
102
+ } else {
103
+ localStorage.setItem("dravyn_token", data.accessToken);
104
+ localStorage.setItem("dravyn_user", JSON.stringify(data.user));
105
+ if (onSuccess) onSuccess(data.accessToken, data.user);
106
+ else window.location.href = redirectTo;
107
+ }
108
+ } catch {
109
+ setError("Unable to connect. Check your internet connection.");
110
+ } finally {
111
+ setLoading(false);
112
+ }
113
+ };
114
+ const EyeIcon = () => /* @__PURE__ */ import_react.default.createElement("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" }));
115
+ const EyeOffIcon = () => /* @__PURE__ */ import_react.default.createElement("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "2", x2: "22", y1: "2", y2: "22" }));
116
+ return /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-root" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-card" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-brand" }, logo ?? /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-logo-default" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "dauth-logo-dot" }), brandName)), /* @__PURE__ */ import_react.default.createElement("h1", { className: "dauth-title" }, "Create your account"), /* @__PURE__ */ import_react.default.createElement("p", { className: "dauth-sub" }, "Join ", brandName, " \u2014 it's free"), error && /* @__PURE__ */ import_react.default.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ import_react.default.createElement(import_ui3.Alert, { variant: "danger" }, error)), /* @__PURE__ */ import_react.default.createElement("form", { onSubmit: handleSubmit, noValidate: true }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-fields" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-row" }, /* @__PURE__ */ import_react.default.createElement(
117
+ import_ui2.Input,
118
+ {
119
+ label: "First name",
120
+ placeholder: "Jeremiah",
121
+ value: form.firstName,
122
+ onChange: set("firstName"),
123
+ error: fieldErrs.firstName,
124
+ required: true
125
+ }
126
+ ), /* @__PURE__ */ import_react.default.createElement(
127
+ import_ui2.Input,
128
+ {
129
+ label: "Last name",
130
+ placeholder: "Adeniyi",
131
+ value: form.lastName,
132
+ onChange: set("lastName"),
133
+ error: fieldErrs.lastName,
134
+ required: true
135
+ }
136
+ )), /* @__PURE__ */ import_react.default.createElement(
137
+ import_ui2.Input,
138
+ {
139
+ label: "Email address",
140
+ type: "email",
141
+ placeholder: "you@example.com",
142
+ value: form.email,
143
+ onChange: set("email"),
144
+ error: fieldErrs.email,
145
+ autoComplete: "email",
146
+ required: true
147
+ }
148
+ ), /* @__PURE__ */ import_react.default.createElement(
149
+ import_ui2.Input,
150
+ {
151
+ label: "Password",
152
+ type: showPass ? "text" : "password",
153
+ placeholder: "Min. 8 characters",
154
+ value: form.password,
155
+ onChange: set("password"),
156
+ error: fieldErrs.password,
157
+ hint: !fieldErrs.password ? "At least 8 characters." : void 0,
158
+ rightIcon: /* @__PURE__ */ import_react.default.createElement(
159
+ "button",
160
+ {
161
+ type: "button",
162
+ className: "dauth-eye",
163
+ onClick: () => setShowPass((v) => !v),
164
+ "aria-label": showPass ? "Hide" : "Show"
165
+ },
166
+ showPass ? /* @__PURE__ */ import_react.default.createElement(EyeOffIcon, null) : /* @__PURE__ */ import_react.default.createElement(EyeIcon, null)
167
+ ),
168
+ autoComplete: "new-password",
169
+ required: true
170
+ }
171
+ ), /* @__PURE__ */ import_react.default.createElement(
172
+ import_ui2.Input,
173
+ {
174
+ label: "Confirm password",
175
+ type: showPass ? "text" : "password",
176
+ placeholder: "Re-enter your password",
177
+ value: form.confirm,
178
+ onChange: set("confirm"),
179
+ error: fieldErrs.confirm,
180
+ autoComplete: "new-password",
181
+ required: true
182
+ }
183
+ ), extraFields), /* @__PURE__ */ import_react.default.createElement(import_ui.Button, { type: "submit", variant: "primary", fullWidth: true, loading, style: { marginTop: 8 } }, "Create account")), /* @__PURE__ */ import_react.default.createElement("p", { className: "dauth-footer-text", style: { marginTop: 20 } }, "Already have an account?", " ", /* @__PURE__ */ import_react.default.createElement("a", { href: "/auth/login", className: "dauth-link" }, "Sign in")), /* @__PURE__ */ import_react.default.createElement("p", { className: "dauth-terms" }, "By creating an account you agree to our", " ", /* @__PURE__ */ import_react.default.createElement("a", { href: "/terms", className: "dauth-link" }, "Terms of Service"), " ", "and", " ", /* @__PURE__ */ import_react.default.createElement("a", { href: "/privacy", className: "dauth-link" }, "Privacy Policy"), ".")));
184
+ };
185
+ // Annotate the CommonJS export names for ESM import in node:
186
+ 0 && (module.exports = {
187
+ RegisterScreen
188
+ });
189
+ //# sourceMappingURL=RegisterScreen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/screens/RegisterScreen.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\nimport { Button } from '@dravyn/ui';\nimport { Input } from '@dravyn/ui';\nimport { Alert } from '@dravyn/ui';\nimport './auth.css';\n\nexport interface RegisterScreenProps {\n redirectTo?: string;\n onSuccess?: (token: string, user: AuthUser) => void;\n logo?: React.ReactNode;\n brandName?: string;\n apiBase?: string;\n /** Extra fields to collect — rendered after the core fields */\n extraFields?: React.ReactNode;\n /** If true, redirect to OTP screen after register instead of auto-login */\n requireOTP?: boolean;\n}\n\ninterface AuthUser {\n id: string; email: string; name: string;\n}\n\nexport const RegisterScreen: React.FC<RegisterScreenProps> = ({\n redirectTo = '/auth/verify',\n onSuccess,\n logo,\n brandName = 'Dravyn',\n apiBase = process.env.NEXT_PUBLIC_AUTH_API_URL ?? 'http://localhost:3001',\n extraFields,\n requireOTP = true,\n}) => {\n const [form, setForm] = React.useState({\n firstName: '', lastName: '', email: '', password: '', confirm: '',\n });\n const [showPass, setShowPass] = React.useState(false);\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState('');\n const [fieldErrs, setFieldErrs] = React.useState<Record<string, string>>({});\n\n const set = (k: keyof typeof form) => (e: React.ChangeEvent<HTMLInputElement>) =>\n setForm(f => ({ ...f, [k]: e.target.value }));\n\n const validate = () => {\n const errs: Record<string, string> = {};\n if (!form.firstName.trim()) errs.firstName = 'First name is required.';\n if (!form.lastName.trim()) errs.lastName = 'Last name is required.';\n if (!form.email.trim()) errs.email = 'Email is required.';\n else if (!/\\S+@\\S+\\.\\S+/.test(form.email)) errs.email = 'Enter a valid email.';\n if (!form.password) errs.password = 'Password is required.';\n else if (form.password.length < 8) errs.password = 'Password must be at least 8 characters.';\n if (form.confirm !== form.password) errs.confirm = 'Passwords do not match.';\n return errs;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n const errs = validate();\n if (Object.keys(errs).length) { setFieldErrs(errs); return; }\n setFieldErrs({});\n setLoading(true);\n setError('');\n\n try {\n const res = await fetch(`${apiBase}/auth/register`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n firstName: form.firstName.trim(),\n lastName: form.lastName.trim(),\n email: form.email.trim().toLowerCase(),\n password: form.password,\n }),\n });\n const data = await res.json();\n if (!res.ok) { setError(data.message ?? 'Registration failed.'); return; }\n\n if (requireOTP) {\n // Store email for OTP screen to use\n sessionStorage.setItem('dravyn_pending_email', form.email.trim().toLowerCase());\n window.location.href = redirectTo;\n } else {\n localStorage.setItem('dravyn_token', data.accessToken);\n localStorage.setItem('dravyn_user', JSON.stringify(data.user));\n if (onSuccess) onSuccess(data.accessToken, data.user);\n else window.location.href = redirectTo;\n }\n } catch {\n setError('Unable to connect. Check your internet connection.');\n } finally {\n setLoading(false);\n }\n };\n\n const EyeIcon = () => <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>;\n const EyeOffIcon = () => <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M9.88 9.88a3 3 0 1 0 4.24 4.24\"/><path d=\"M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68\"/><path d=\"M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61\"/><line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\"/></svg>;\n\n return (\n <div className=\"dauth-root\">\n <div className=\"dauth-card\">\n <div className=\"dauth-brand\">\n {logo ?? <div className=\"dauth-logo-default\"><span className=\"dauth-logo-dot\" />{brandName}</div>}\n </div>\n\n <h1 className=\"dauth-title\">Create your account</h1>\n <p className=\"dauth-sub\">Join {brandName} — it's free</p>\n\n {error && <div style={{ marginBottom: 16 }}><Alert variant=\"danger\">{error}</Alert></div>}\n\n <form onSubmit={handleSubmit} noValidate>\n <div className=\"dauth-fields\">\n <div className=\"dauth-row\">\n <Input\n label=\"First name\"\n placeholder=\"Jeremiah\"\n value={form.firstName}\n onChange={set('firstName')}\n error={fieldErrs.firstName}\n required\n />\n <Input\n label=\"Last name\"\n placeholder=\"Adeniyi\"\n value={form.lastName}\n onChange={set('lastName')}\n error={fieldErrs.lastName}\n required\n />\n </div>\n\n <Input\n label=\"Email address\"\n type=\"email\"\n placeholder=\"you@example.com\"\n value={form.email}\n onChange={set('email')}\n error={fieldErrs.email}\n autoComplete=\"email\"\n required\n />\n\n <Input\n label=\"Password\"\n type={showPass ? 'text' : 'password'}\n placeholder=\"Min. 8 characters\"\n value={form.password}\n onChange={set('password')}\n error={fieldErrs.password}\n hint={!fieldErrs.password ? 'At least 8 characters.' : undefined}\n rightIcon={\n <button type=\"button\" className=\"dauth-eye\"\n onClick={() => setShowPass(v => !v)}\n aria-label={showPass ? 'Hide' : 'Show'}>\n {showPass ? <EyeOffIcon /> : <EyeIcon />}\n </button>\n }\n autoComplete=\"new-password\"\n required\n />\n\n <Input\n label=\"Confirm password\"\n type={showPass ? 'text' : 'password'}\n placeholder=\"Re-enter your password\"\n value={form.confirm}\n onChange={set('confirm')}\n error={fieldErrs.confirm}\n autoComplete=\"new-password\"\n required\n />\n\n {/* Slot for extra fields (university, department, etc.) */}\n {extraFields}\n </div>\n\n <Button type=\"submit\" variant=\"primary\" fullWidth loading={loading} style={{ marginTop: 8 }}>\n Create account\n </Button>\n </form>\n\n <p className=\"dauth-footer-text\" style={{ marginTop: 20 }}>\n Already have an account?{' '}\n <a href=\"/auth/login\" className=\"dauth-link\">Sign in</a>\n </p>\n\n <p className=\"dauth-terms\">\n By creating an account you agree to our{' '}\n <a href=\"/terms\" className=\"dauth-link\">Terms of Service</a>{' '}\n and{' '}\n <a href=\"/privacy\" className=\"dauth-link\">Privacy Policy</a>.\n </p>\n </div>\n </div>\n );\n};\n"],"mappings":"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","names":["import_ui","React"]}
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ import {
3
+ RegisterScreen
4
+ } from "../chunk-35G4WYX3.mjs";
5
+ import "../chunk-6BBGRN4E.mjs";
6
+ export {
7
+ RegisterScreen
8
+ };
9
+ //# sourceMappingURL=RegisterScreen.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@dravyn/auth-ui",
3
+ "version": "0.1.1",
4
+ "description": "Dravyn Auth UI — Pre-built authentication screens for React/Next.js",
5
+ "author": "Durotoye Jeremiah Adeniyi <team@dravyn.it.com>",
6
+ "license": "MIT",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.mjs",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.js"
15
+ },
16
+ "./auth.css": "./src/screens/auth.css"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "src/screens/auth.css"
21
+ ],
22
+ "sideEffects": [
23
+ "*.css"
24
+ ],
25
+ "peerDependencies": {
26
+ "react": ">=17.0.0",
27
+ "react-dom": ">=17.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "typescript": "^5.0.0",
31
+ "@types/react": "^18.0.0",
32
+ "@types/node": "^20.0.0",
33
+ "tsup": "^8.0.0"
34
+ },
35
+ "scripts": {
36
+ "build": "tsup",
37
+ "dev": "tsup --watch"
38
+ }
39
+ }
@@ -0,0 +1,260 @@
1
+ /*
2
+ Dravyn Auth UI — Styles
3
+ Consumes @dravyn/ui CSS custom properties.
4
+ Import tokens first: import 'dravyn-ui/tokens'
5
+ Then import this file once: import '@dravyn/auth-ui/auth.css'
6
+ */
7
+
8
+ /* ── Root / centred layout ──────────────────────────────────────────────── */
9
+ .dauth-root {
10
+ min-height: 100vh;
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+ background: var(--dui-bg, #0d0d0d);
15
+ padding: 24px 16px;
16
+ font-family: var(--dui-font-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
17
+ }
18
+
19
+ /* ── Card ───────────────────────────────────────────────────────────────── */
20
+ .dauth-card {
21
+ width: 100%;
22
+ max-width: 420px;
23
+ background: var(--dui-bg-raised, #161616);
24
+ border: 0.5px solid var(--dui-border-strong, rgba(255,255,255,0.16));
25
+ border-radius: 16px;
26
+ padding: 36px 32px 32px;
27
+ animation: dauth-in 220ms cubic-bezier(0.16, 1, 0.3, 1);
28
+ }
29
+
30
+ @keyframes dauth-in {
31
+ from { opacity: 0; transform: translateY(10px) scale(0.98); }
32
+ to { opacity: 1; transform: translateY(0) scale(1); }
33
+ }
34
+
35
+ /* ── Brand / logo ───────────────────────────────────────────────────────── */
36
+ .dauth-brand {
37
+ margin-bottom: 24px;
38
+ }
39
+
40
+ .dauth-logo-default {
41
+ display: inline-flex;
42
+ align-items: center;
43
+ gap: 7px;
44
+ font-size: 15px;
45
+ font-weight: 700;
46
+ color: var(--dui-text, #f0f0f0);
47
+ letter-spacing: -0.01em;
48
+ }
49
+
50
+ .dauth-logo-dot {
51
+ width: 8px;
52
+ height: 8px;
53
+ border-radius: 50%;
54
+ background: var(--dui-teal-400, #4ecdc4);
55
+ flex-shrink: 0;
56
+ }
57
+
58
+ /* ── Headings ────────────────────────────────────────────────────────────── */
59
+ .dauth-title {
60
+ font-size: 22px;
61
+ font-weight: 700;
62
+ color: var(--dui-text, #f0f0f0);
63
+ letter-spacing: -0.02em;
64
+ line-height: 1.2;
65
+ margin: 0 0 6px;
66
+ }
67
+
68
+ .dauth-sub {
69
+ font-size: 13.5px;
70
+ color: var(--dui-text-muted, #888);
71
+ line-height: 1.55;
72
+ margin: 0 0 22px;
73
+ }
74
+
75
+ /* ── Form fields ─────────────────────────────────────────────────────────── */
76
+ .dauth-fields {
77
+ display: flex;
78
+ flex-direction: column;
79
+ gap: 14px;
80
+ margin-bottom: 14px;
81
+ }
82
+
83
+ .dauth-row {
84
+ display: grid;
85
+ grid-template-columns: 1fr 1fr;
86
+ gap: 10px;
87
+ }
88
+
89
+ /* ── Password wrap (holds input + forgot link) ───────────────────────────── */
90
+ .dauth-password-wrap {
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: 6px;
94
+ }
95
+
96
+ .dauth-forgot {
97
+ font-size: 12px;
98
+ color: var(--dui-text-muted, #888);
99
+ text-decoration: none;
100
+ align-self: flex-end;
101
+ transition: color 0.15s;
102
+ }
103
+
104
+ .dauth-forgot:hover { color: var(--dui-teal-400, #4ecdc4); }
105
+
106
+ /* ── Show/hide password button ───────────────────────────────────────────── */
107
+ .dauth-eye {
108
+ background: none;
109
+ border: none;
110
+ cursor: pointer;
111
+ color: var(--dui-text-hint, #555);
112
+ display: inline-flex;
113
+ align-items: center;
114
+ padding: 0;
115
+ transition: color 0.15s;
116
+ }
117
+ .dauth-eye:hover { color: var(--dui-text-muted, #888); }
118
+
119
+ /* ── Divider ─────────────────────────────────────────────────────────────── */
120
+ .dauth-divider {
121
+ display: flex;
122
+ align-items: center;
123
+ gap: 10px;
124
+ margin: 18px 0;
125
+ color: var(--dui-text-hint, #555);
126
+ font-size: 12px;
127
+ }
128
+ .dauth-divider::before,
129
+ .dauth-divider::after {
130
+ content: '';
131
+ flex: 1;
132
+ height: 0.5px;
133
+ background: var(--dui-border, rgba(255,255,255,0.07));
134
+ }
135
+
136
+ /* ── Google OAuth button ─────────────────────────────────────────────────── */
137
+ .dauth-google-btn {
138
+ width: 100%;
139
+ display: flex;
140
+ align-items: center;
141
+ justify-content: center;
142
+ gap: 10px;
143
+ background: transparent;
144
+ border: 0.5px solid var(--dui-border-strong, rgba(255,255,255,0.16));
145
+ border-radius: 8px;
146
+ padding: 10px 18px;
147
+ font-family: var(--dui-font-sans);
148
+ font-size: 13px;
149
+ font-weight: 500;
150
+ color: var(--dui-text, #f0f0f0);
151
+ cursor: pointer;
152
+ transition: background 0.15s, border-color 0.15s;
153
+ }
154
+ .dauth-google-btn:hover:not(:disabled) {
155
+ background: var(--dui-bg-overlay, #1e1e1e);
156
+ border-color: rgba(255,255,255,0.24);
157
+ }
158
+ .dauth-google-btn:disabled { opacity: 0.4; cursor: not-allowed; }
159
+
160
+ /* ── OTP boxes ───────────────────────────────────────────────────────────── */
161
+ .dauth-otp-icon {
162
+ font-size: 28px;
163
+ margin-bottom: 12px;
164
+ color: var(--dui-teal-400, #4ecdc4);
165
+ }
166
+
167
+ .dauth-otp-boxes {
168
+ display: flex;
169
+ gap: 8px;
170
+ justify-content: center;
171
+ margin: 20px 0 8px;
172
+ }
173
+
174
+ .dauth-otp-box {
175
+ width: 46px;
176
+ height: 54px;
177
+ background: var(--dui-bg, #0d0d0d);
178
+ border: 0.5px solid var(--dui-border-strong, rgba(255,255,255,0.16));
179
+ border-radius: 8px;
180
+ color: var(--dui-text, #f0f0f0);
181
+ font-size: 22px;
182
+ font-weight: 700;
183
+ font-family: var(--dui-font-sans);
184
+ text-align: center;
185
+ outline: none;
186
+ transition: border-color 0.15s, box-shadow 0.15s;
187
+ caret-color: var(--dui-teal-400, #4ecdc4);
188
+ }
189
+
190
+ .dauth-otp-box:focus {
191
+ border-color: var(--dui-teal-400, #4ecdc4);
192
+ box-shadow: 0 0 0 3px rgba(78, 205, 196, 0.12);
193
+ }
194
+
195
+ .dauth-otp-box--filled {
196
+ border-color: var(--dui-teal-400, #4ecdc4);
197
+ background: rgba(78, 205, 196, 0.04);
198
+ }
199
+
200
+ /* ── Resend row ──────────────────────────────────────────────────────────── */
201
+ .dauth-resend {
202
+ text-align: center;
203
+ font-size: 13px;
204
+ color: var(--dui-text-muted, #888);
205
+ margin: 14px 0 4px;
206
+ }
207
+
208
+ /* ── Links ───────────────────────────────────────────────────────────────── */
209
+ .dauth-link {
210
+ color: var(--dui-teal-400, #4ecdc4);
211
+ text-decoration: none;
212
+ font-weight: 500;
213
+ background: none;
214
+ border: none;
215
+ cursor: pointer;
216
+ font-size: inherit;
217
+ font-family: inherit;
218
+ padding: 0;
219
+ transition: opacity 0.15s;
220
+ }
221
+ .dauth-link:hover { opacity: 0.8; }
222
+
223
+ /* ── Footer text + terms ─────────────────────────────────────────────────── */
224
+ .dauth-footer-text {
225
+ text-align: center;
226
+ font-size: 13px;
227
+ color: var(--dui-text-muted, #888);
228
+ margin-top: 18px;
229
+ }
230
+
231
+ .dauth-terms {
232
+ text-align: center;
233
+ font-size: 11.5px;
234
+ color: var(--dui-text-hint, #555);
235
+ margin-top: 12px;
236
+ line-height: 1.6;
237
+ }
238
+
239
+ /* ── CSS variable aliases (readable names inside auth components) ─────────── */
240
+ :root {
241
+ --dauth-text: var(--dui-text, #f0f0f0);
242
+ --dauth-muted: var(--dui-text-muted, #888888);
243
+ --dauth-teal: var(--dui-teal-400, #4ecdc4);
244
+ --dauth-green: #22c55e;
245
+ --dauth-label: var(--dui-text-muted, #888888);
246
+ }
247
+
248
+ /* ── Label helper (used in ResetPasswordScreen) ───────────────────────────── */
249
+ .dauth-label {
250
+ font-size: 12px;
251
+ font-weight: 500;
252
+ color: var(--dauth-label);
253
+ }
254
+
255
+ /* ── Responsive ──────────────────────────────────────────────────────────── */
256
+ @media (max-width: 480px) {
257
+ .dauth-card { padding: 28px 20px 24px; }
258
+ .dauth-row { grid-template-columns: 1fr; }
259
+ .dauth-otp-box { width: 40px; height: 48px; font-size: 20px; }
260
+ }