@authon/js 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -60,6 +60,7 @@ var DEFAULT_BRANDING = {
60
60
  borderRadius: 12,
61
61
  showEmailPassword: true,
62
62
  showDivider: true,
63
+ showSecuredBy: true,
63
64
  locale: "en"
64
65
  };
65
66
 
@@ -93,6 +94,7 @@ var ModalRenderer = class {
93
94
  hostElement = null;
94
95
  containerElement = null;
95
96
  mode;
97
+ theme;
96
98
  branding;
97
99
  enabledProviders = [];
98
100
  onProviderClick;
@@ -100,6 +102,7 @@ var ModalRenderer = class {
100
102
  onClose;
101
103
  constructor(options) {
102
104
  this.mode = options.mode;
105
+ this.theme = options.theme || "auto";
103
106
  this.branding = { ...DEFAULT_BRANDING, ...options.branding };
104
107
  this.onProviderClick = options.onProviderClick;
105
108
  this.onEmailSubmit = options.onEmailSubmit;
@@ -147,9 +150,13 @@ var ModalRenderer = class {
147
150
  const title = isSignUp ? "Create your account" : "Welcome back";
148
151
  const subtitle = isSignUp ? "Already have an account?" : "Don't have an account?";
149
152
  const subtitleLink = isSignUp ? "Sign in" : "Sign up";
153
+ const dark = this.isDark();
150
154
  const providerButtons = this.enabledProviders.filter((p) => !b.hiddenProviders?.includes(p)).map((p) => {
151
155
  const config = getProviderButtonConfig(p);
152
- return `<button class="provider-btn" data-provider="${p}" style="background:${config.bgColor};color:${config.textColor};border:1px solid ${config.bgColor === "#ffffff" ? "#e5e7eb" : config.bgColor}">
156
+ const isWhiteBg = config.bgColor === "#ffffff";
157
+ const btnBg = dark && isWhiteBg ? "#f8fafc" : config.bgColor;
158
+ const btnBorder = isWhiteBg ? dark ? "#475569" : "#e5e7eb" : config.bgColor;
159
+ return `<button class="provider-btn" data-provider="${p}" style="background:${btnBg};color:${config.textColor};border:1px solid ${btnBorder}">
153
160
  <span class="provider-icon">${config.iconSvg}</span>
154
161
  <span>${config.label}</span>
155
162
  </button>`;
@@ -178,41 +185,60 @@ var ModalRenderer = class {
178
185
  ${emailForm}
179
186
  <p class="switch-view">${subtitle} <a href="#" id="switch-link">${subtitleLink}</a></p>
180
187
  ${footer}
188
+ ${b.showSecuredBy !== false ? `<div class="secured-by">Secured by <span class="secured-brand">Authon</span></div>` : ""}
181
189
  </div>
182
190
  `;
183
191
  }
192
+ isDark() {
193
+ if (this.theme === "dark") return true;
194
+ if (this.theme === "light") return false;
195
+ return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
196
+ }
184
197
  buildCSS() {
185
198
  const b = this.branding;
199
+ const dark = this.isDark();
200
+ const bg = dark ? b.darkBg || "#0f172a" : b.lightBg || "#ffffff";
201
+ const text = dark ? b.darkText || "#f1f5f9" : b.lightText || "#111827";
202
+ const mutedText = dark ? "#94a3b8" : "#6b7280";
203
+ const dimText = dark ? "#64748b" : "#9ca3af";
204
+ const borderColor = dark ? "#334155" : "#d1d5db";
205
+ const dividerColor = dark ? "#334155" : "#e5e7eb";
206
+ const inputBg = dark ? "#1e293b" : "#ffffff";
186
207
  return `
187
208
  :host {
188
209
  --authon-primary-start: ${b.primaryColorStart || "#7c3aed"};
189
210
  --authon-primary-end: ${b.primaryColorEnd || "#4f46e5"};
190
- --authon-light-bg: ${b.lightBg || "#ffffff"};
191
- --authon-light-text: ${b.lightText || "#111827"};
192
- --authon-dark-bg: ${b.darkBg || "#0f172a"};
193
- --authon-dark-text: ${b.darkText || "#f1f5f9"};
211
+ --authon-bg: ${bg};
212
+ --authon-text: ${text};
213
+ --authon-muted: ${mutedText};
214
+ --authon-dim: ${dimText};
215
+ --authon-border: ${borderColor};
216
+ --authon-divider: ${dividerColor};
217
+ --authon-input-bg: ${inputBg};
194
218
  --authon-radius: ${b.borderRadius ?? 12}px;
195
219
  --authon-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
196
220
  font-family: var(--authon-font);
197
- color: var(--authon-light-text);
221
+ color: var(--authon-text);
198
222
  }
199
223
  * { box-sizing: border-box; margin: 0; padding: 0; }
200
224
  .backdrop {
201
225
  position: fixed; inset: 0; z-index: 99998;
202
- background: rgba(0,0,0,0.5); backdrop-filter: blur(4px);
226
+ background: rgba(0,0,0,${dark ? "0.7" : "0.5"}); backdrop-filter: blur(4px);
203
227
  animation: fadeIn 0.2s ease;
204
228
  }
205
229
  .modal-container {
206
230
  ${this.mode === "popup" ? "position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 99999; max-height: 90vh; overflow-y: auto;" : ""}
207
- background: var(--authon-light-bg);
231
+ background: var(--authon-bg);
232
+ color: var(--authon-text);
233
+ border: 1px solid var(--authon-border);
208
234
  border-radius: var(--authon-radius);
209
235
  padding: 32px;
210
236
  width: 400px; max-width: 100%;
211
- ${this.mode === "popup" ? "box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25); animation: slideIn 0.3s ease;" : ""}
237
+ ${this.mode === "popup" ? `box-shadow: 0 25px 50px -12px rgba(0,0,0,${dark ? "0.5" : "0.25"}); animation: slideIn 0.3s ease;` : ""}
212
238
  }
213
239
  .logo { display: block; margin: 0 auto 16px; max-height: 48px; }
214
- .title { text-align: center; font-size: 24px; font-weight: 700; margin-bottom: 8px; }
215
- .brand-name { text-align: center; font-size: 14px; color: #6b7280; margin-bottom: 24px; }
240
+ .title { text-align: center; font-size: 24px; font-weight: 700; margin-bottom: 8px; color: var(--authon-text); }
241
+ .brand-name { text-align: center; font-size: 14px; color: var(--authon-muted); margin-bottom: 24px; }
216
242
  .providers { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
217
243
  .provider-btn {
218
244
  display: flex; align-items: center; gap: 12px;
@@ -226,19 +252,22 @@ var ModalRenderer = class {
226
252
  .provider-icon { display: flex; align-items: center; flex-shrink: 0; }
227
253
  .divider {
228
254
  display: flex; align-items: center; gap: 12px;
229
- margin: 16px 0; color: #9ca3af; font-size: 13px;
255
+ margin: 16px 0; color: var(--authon-dim); font-size: 13px;
230
256
  }
231
257
  .divider::before, .divider::after {
232
- content: ''; flex: 1; height: 1px; background: #e5e7eb;
258
+ content: ''; flex: 1; height: 1px; background: var(--authon-divider);
233
259
  }
234
260
  .email-form { display: flex; flex-direction: column; gap: 10px; }
235
261
  .input {
236
262
  width: 100%; padding: 10px 14px;
237
- border: 1px solid #d1d5db; border-radius: calc(var(--authon-radius) * 0.5);
263
+ background: var(--authon-input-bg);
264
+ color: var(--authon-text);
265
+ border: 1px solid var(--authon-border); border-radius: calc(var(--authon-radius) * 0.5);
238
266
  font-size: 14px; font-family: var(--authon-font);
239
267
  outline: none; transition: border-color 0.15s;
240
268
  }
241
- .input:focus { border-color: var(--authon-primary-start); box-shadow: 0 0 0 3px rgba(124,58,237,0.1); }
269
+ .input::placeholder { color: var(--authon-dim); }
270
+ .input:focus { border-color: var(--authon-primary-start); box-shadow: 0 0 0 3px rgba(124,58,237,0.15); }
242
271
  .submit-btn {
243
272
  width: 100%; padding: 10px;
244
273
  background: linear-gradient(135deg, var(--authon-primary-start), var(--authon-primary-end));
@@ -247,12 +276,18 @@ var ModalRenderer = class {
247
276
  font-family: var(--authon-font); transition: opacity 0.15s;
248
277
  }
249
278
  .submit-btn:hover { opacity: 0.9; }
250
- .switch-view { text-align: center; margin-top: 16px; font-size: 13px; color: #6b7280; }
279
+ .switch-view { text-align: center; margin-top: 16px; font-size: 13px; color: var(--authon-muted); }
251
280
  .switch-view a { color: var(--authon-primary-start); text-decoration: none; font-weight: 500; }
252
281
  .switch-view a:hover { text-decoration: underline; }
253
- .footer { text-align: center; margin-top: 16px; font-size: 12px; color: #9ca3af; }
254
- .footer a { color: #9ca3af; text-decoration: none; }
282
+ .footer { text-align: center; margin-top: 16px; font-size: 12px; color: var(--authon-dim); }
283
+ .footer a { color: var(--authon-dim); text-decoration: none; }
255
284
  .footer a:hover { text-decoration: underline; }
285
+ .secured-by {
286
+ text-align: center; margin-top: 20px; padding-top: 16px;
287
+ border-top: 1px solid var(--authon-divider);
288
+ font-size: 11px; color: var(--authon-dim);
289
+ }
290
+ .secured-brand { font-weight: 600; color: var(--authon-muted); }
256
291
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
257
292
  @keyframes slideIn { from { opacity: 0; transform: translate(-50%, -48%); } to { opacity: 1; transform: translate(-50%, -50%); } }
258
293
  ${b.customCss || ""}
@@ -473,6 +508,7 @@ var Authon = class {
473
508
  if (!this.modal) {
474
509
  this.modal = new ModalRenderer({
475
510
  mode: this.config.mode,
511
+ theme: this.config.theme,
476
512
  containerId: this.config.containerId,
477
513
  branding: this.branding || void 0,
478
514
  onProviderClick: (provider) => this.startOAuthFlow(provider),
package/dist/index.mjs CHANGED
@@ -33,6 +33,7 @@ var DEFAULT_BRANDING = {
33
33
  borderRadius: 12,
34
34
  showEmailPassword: true,
35
35
  showDivider: true,
36
+ showSecuredBy: true,
36
37
  locale: "en"
37
38
  };
38
39
 
@@ -66,6 +67,7 @@ var ModalRenderer = class {
66
67
  hostElement = null;
67
68
  containerElement = null;
68
69
  mode;
70
+ theme;
69
71
  branding;
70
72
  enabledProviders = [];
71
73
  onProviderClick;
@@ -73,6 +75,7 @@ var ModalRenderer = class {
73
75
  onClose;
74
76
  constructor(options) {
75
77
  this.mode = options.mode;
78
+ this.theme = options.theme || "auto";
76
79
  this.branding = { ...DEFAULT_BRANDING, ...options.branding };
77
80
  this.onProviderClick = options.onProviderClick;
78
81
  this.onEmailSubmit = options.onEmailSubmit;
@@ -120,9 +123,13 @@ var ModalRenderer = class {
120
123
  const title = isSignUp ? "Create your account" : "Welcome back";
121
124
  const subtitle = isSignUp ? "Already have an account?" : "Don't have an account?";
122
125
  const subtitleLink = isSignUp ? "Sign in" : "Sign up";
126
+ const dark = this.isDark();
123
127
  const providerButtons = this.enabledProviders.filter((p) => !b.hiddenProviders?.includes(p)).map((p) => {
124
128
  const config = getProviderButtonConfig(p);
125
- return `<button class="provider-btn" data-provider="${p}" style="background:${config.bgColor};color:${config.textColor};border:1px solid ${config.bgColor === "#ffffff" ? "#e5e7eb" : config.bgColor}">
129
+ const isWhiteBg = config.bgColor === "#ffffff";
130
+ const btnBg = dark && isWhiteBg ? "#f8fafc" : config.bgColor;
131
+ const btnBorder = isWhiteBg ? dark ? "#475569" : "#e5e7eb" : config.bgColor;
132
+ return `<button class="provider-btn" data-provider="${p}" style="background:${btnBg};color:${config.textColor};border:1px solid ${btnBorder}">
126
133
  <span class="provider-icon">${config.iconSvg}</span>
127
134
  <span>${config.label}</span>
128
135
  </button>`;
@@ -151,41 +158,60 @@ var ModalRenderer = class {
151
158
  ${emailForm}
152
159
  <p class="switch-view">${subtitle} <a href="#" id="switch-link">${subtitleLink}</a></p>
153
160
  ${footer}
161
+ ${b.showSecuredBy !== false ? `<div class="secured-by">Secured by <span class="secured-brand">Authon</span></div>` : ""}
154
162
  </div>
155
163
  `;
156
164
  }
165
+ isDark() {
166
+ if (this.theme === "dark") return true;
167
+ if (this.theme === "light") return false;
168
+ return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
169
+ }
157
170
  buildCSS() {
158
171
  const b = this.branding;
172
+ const dark = this.isDark();
173
+ const bg = dark ? b.darkBg || "#0f172a" : b.lightBg || "#ffffff";
174
+ const text = dark ? b.darkText || "#f1f5f9" : b.lightText || "#111827";
175
+ const mutedText = dark ? "#94a3b8" : "#6b7280";
176
+ const dimText = dark ? "#64748b" : "#9ca3af";
177
+ const borderColor = dark ? "#334155" : "#d1d5db";
178
+ const dividerColor = dark ? "#334155" : "#e5e7eb";
179
+ const inputBg = dark ? "#1e293b" : "#ffffff";
159
180
  return `
160
181
  :host {
161
182
  --authon-primary-start: ${b.primaryColorStart || "#7c3aed"};
162
183
  --authon-primary-end: ${b.primaryColorEnd || "#4f46e5"};
163
- --authon-light-bg: ${b.lightBg || "#ffffff"};
164
- --authon-light-text: ${b.lightText || "#111827"};
165
- --authon-dark-bg: ${b.darkBg || "#0f172a"};
166
- --authon-dark-text: ${b.darkText || "#f1f5f9"};
184
+ --authon-bg: ${bg};
185
+ --authon-text: ${text};
186
+ --authon-muted: ${mutedText};
187
+ --authon-dim: ${dimText};
188
+ --authon-border: ${borderColor};
189
+ --authon-divider: ${dividerColor};
190
+ --authon-input-bg: ${inputBg};
167
191
  --authon-radius: ${b.borderRadius ?? 12}px;
168
192
  --authon-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
169
193
  font-family: var(--authon-font);
170
- color: var(--authon-light-text);
194
+ color: var(--authon-text);
171
195
  }
172
196
  * { box-sizing: border-box; margin: 0; padding: 0; }
173
197
  .backdrop {
174
198
  position: fixed; inset: 0; z-index: 99998;
175
- background: rgba(0,0,0,0.5); backdrop-filter: blur(4px);
199
+ background: rgba(0,0,0,${dark ? "0.7" : "0.5"}); backdrop-filter: blur(4px);
176
200
  animation: fadeIn 0.2s ease;
177
201
  }
178
202
  .modal-container {
179
203
  ${this.mode === "popup" ? "position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 99999; max-height: 90vh; overflow-y: auto;" : ""}
180
- background: var(--authon-light-bg);
204
+ background: var(--authon-bg);
205
+ color: var(--authon-text);
206
+ border: 1px solid var(--authon-border);
181
207
  border-radius: var(--authon-radius);
182
208
  padding: 32px;
183
209
  width: 400px; max-width: 100%;
184
- ${this.mode === "popup" ? "box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25); animation: slideIn 0.3s ease;" : ""}
210
+ ${this.mode === "popup" ? `box-shadow: 0 25px 50px -12px rgba(0,0,0,${dark ? "0.5" : "0.25"}); animation: slideIn 0.3s ease;` : ""}
185
211
  }
186
212
  .logo { display: block; margin: 0 auto 16px; max-height: 48px; }
187
- .title { text-align: center; font-size: 24px; font-weight: 700; margin-bottom: 8px; }
188
- .brand-name { text-align: center; font-size: 14px; color: #6b7280; margin-bottom: 24px; }
213
+ .title { text-align: center; font-size: 24px; font-weight: 700; margin-bottom: 8px; color: var(--authon-text); }
214
+ .brand-name { text-align: center; font-size: 14px; color: var(--authon-muted); margin-bottom: 24px; }
189
215
  .providers { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
190
216
  .provider-btn {
191
217
  display: flex; align-items: center; gap: 12px;
@@ -199,19 +225,22 @@ var ModalRenderer = class {
199
225
  .provider-icon { display: flex; align-items: center; flex-shrink: 0; }
200
226
  .divider {
201
227
  display: flex; align-items: center; gap: 12px;
202
- margin: 16px 0; color: #9ca3af; font-size: 13px;
228
+ margin: 16px 0; color: var(--authon-dim); font-size: 13px;
203
229
  }
204
230
  .divider::before, .divider::after {
205
- content: ''; flex: 1; height: 1px; background: #e5e7eb;
231
+ content: ''; flex: 1; height: 1px; background: var(--authon-divider);
206
232
  }
207
233
  .email-form { display: flex; flex-direction: column; gap: 10px; }
208
234
  .input {
209
235
  width: 100%; padding: 10px 14px;
210
- border: 1px solid #d1d5db; border-radius: calc(var(--authon-radius) * 0.5);
236
+ background: var(--authon-input-bg);
237
+ color: var(--authon-text);
238
+ border: 1px solid var(--authon-border); border-radius: calc(var(--authon-radius) * 0.5);
211
239
  font-size: 14px; font-family: var(--authon-font);
212
240
  outline: none; transition: border-color 0.15s;
213
241
  }
214
- .input:focus { border-color: var(--authon-primary-start); box-shadow: 0 0 0 3px rgba(124,58,237,0.1); }
242
+ .input::placeholder { color: var(--authon-dim); }
243
+ .input:focus { border-color: var(--authon-primary-start); box-shadow: 0 0 0 3px rgba(124,58,237,0.15); }
215
244
  .submit-btn {
216
245
  width: 100%; padding: 10px;
217
246
  background: linear-gradient(135deg, var(--authon-primary-start), var(--authon-primary-end));
@@ -220,12 +249,18 @@ var ModalRenderer = class {
220
249
  font-family: var(--authon-font); transition: opacity 0.15s;
221
250
  }
222
251
  .submit-btn:hover { opacity: 0.9; }
223
- .switch-view { text-align: center; margin-top: 16px; font-size: 13px; color: #6b7280; }
252
+ .switch-view { text-align: center; margin-top: 16px; font-size: 13px; color: var(--authon-muted); }
224
253
  .switch-view a { color: var(--authon-primary-start); text-decoration: none; font-weight: 500; }
225
254
  .switch-view a:hover { text-decoration: underline; }
226
- .footer { text-align: center; margin-top: 16px; font-size: 12px; color: #9ca3af; }
227
- .footer a { color: #9ca3af; text-decoration: none; }
255
+ .footer { text-align: center; margin-top: 16px; font-size: 12px; color: var(--authon-dim); }
256
+ .footer a { color: var(--authon-dim); text-decoration: none; }
228
257
  .footer a:hover { text-decoration: underline; }
258
+ .secured-by {
259
+ text-align: center; margin-top: 20px; padding-top: 16px;
260
+ border-top: 1px solid var(--authon-divider);
261
+ font-size: 11px; color: var(--authon-dim);
262
+ }
263
+ .secured-brand { font-weight: 600; color: var(--authon-muted); }
229
264
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
230
265
  @keyframes slideIn { from { opacity: 0; transform: translate(-50%, -48%); } to { opacity: 1; transform: translate(-50%, -50%); } }
231
266
  ${b.customCss || ""}
@@ -446,6 +481,7 @@ var Authon = class {
446
481
  if (!this.modal) {
447
482
  this.modal = new ModalRenderer({
448
483
  mode: this.config.mode,
484
+ theme: this.config.theme,
449
485
  containerId: this.config.containerId,
450
486
  branding: this.branding || void 0,
451
487
  onProviderClick: (provider) => this.startOAuthFlow(provider),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authon/js",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Authon core SDK — ShadowDOM login modal for any app",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -21,6 +21,8 @@
21
21
  "tsup": "^8.4.0",
22
22
  "typescript": "^5.7.0"
23
23
  },
24
- "files": ["dist"],
24
+ "files": [
25
+ "dist"
26
+ ],
25
27
  "license": "MIT"
26
28
  }