@authon/js 0.1.4 → 0.1.5

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
@@ -93,6 +93,7 @@ var ModalRenderer = class {
93
93
  hostElement = null;
94
94
  containerElement = null;
95
95
  mode;
96
+ theme;
96
97
  branding;
97
98
  enabledProviders = [];
98
99
  onProviderClick;
@@ -100,6 +101,7 @@ var ModalRenderer = class {
100
101
  onClose;
101
102
  constructor(options) {
102
103
  this.mode = options.mode;
104
+ this.theme = options.theme || "auto";
103
105
  this.branding = { ...DEFAULT_BRANDING, ...options.branding };
104
106
  this.onProviderClick = options.onProviderClick;
105
107
  this.onEmailSubmit = options.onEmailSubmit;
@@ -147,9 +149,13 @@ var ModalRenderer = class {
147
149
  const title = isSignUp ? "Create your account" : "Welcome back";
148
150
  const subtitle = isSignUp ? "Already have an account?" : "Don't have an account?";
149
151
  const subtitleLink = isSignUp ? "Sign in" : "Sign up";
152
+ const dark = this.isDark();
150
153
  const providerButtons = this.enabledProviders.filter((p) => !b.hiddenProviders?.includes(p)).map((p) => {
151
154
  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}">
155
+ const isWhiteBg = config.bgColor === "#ffffff";
156
+ const btnBg = dark && isWhiteBg ? "#f8fafc" : config.bgColor;
157
+ const btnBorder = isWhiteBg ? dark ? "#475569" : "#e5e7eb" : config.bgColor;
158
+ return `<button class="provider-btn" data-provider="${p}" style="background:${btnBg};color:${config.textColor};border:1px solid ${btnBorder}">
153
159
  <span class="provider-icon">${config.iconSvg}</span>
154
160
  <span>${config.label}</span>
155
161
  </button>`;
@@ -181,38 +187,56 @@ var ModalRenderer = class {
181
187
  </div>
182
188
  `;
183
189
  }
190
+ isDark() {
191
+ if (this.theme === "dark") return true;
192
+ if (this.theme === "light") return false;
193
+ return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
194
+ }
184
195
  buildCSS() {
185
196
  const b = this.branding;
197
+ const dark = this.isDark();
198
+ const bg = dark ? b.darkBg || "#0f172a" : b.lightBg || "#ffffff";
199
+ const text = dark ? b.darkText || "#f1f5f9" : b.lightText || "#111827";
200
+ const mutedText = dark ? "#94a3b8" : "#6b7280";
201
+ const dimText = dark ? "#64748b" : "#9ca3af";
202
+ const borderColor = dark ? "#334155" : "#d1d5db";
203
+ const dividerColor = dark ? "#334155" : "#e5e7eb";
204
+ const inputBg = dark ? "#1e293b" : "#ffffff";
186
205
  return `
187
206
  :host {
188
207
  --authon-primary-start: ${b.primaryColorStart || "#7c3aed"};
189
208
  --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"};
209
+ --authon-bg: ${bg};
210
+ --authon-text: ${text};
211
+ --authon-muted: ${mutedText};
212
+ --authon-dim: ${dimText};
213
+ --authon-border: ${borderColor};
214
+ --authon-divider: ${dividerColor};
215
+ --authon-input-bg: ${inputBg};
194
216
  --authon-radius: ${b.borderRadius ?? 12}px;
195
217
  --authon-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
196
218
  font-family: var(--authon-font);
197
- color: var(--authon-light-text);
219
+ color: var(--authon-text);
198
220
  }
199
221
  * { box-sizing: border-box; margin: 0; padding: 0; }
200
222
  .backdrop {
201
223
  position: fixed; inset: 0; z-index: 99998;
202
- background: rgba(0,0,0,0.5); backdrop-filter: blur(4px);
224
+ background: rgba(0,0,0,${dark ? "0.7" : "0.5"}); backdrop-filter: blur(4px);
203
225
  animation: fadeIn 0.2s ease;
204
226
  }
205
227
  .modal-container {
206
228
  ${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);
229
+ background: var(--authon-bg);
230
+ color: var(--authon-text);
231
+ border: 1px solid var(--authon-border);
208
232
  border-radius: var(--authon-radius);
209
233
  padding: 32px;
210
234
  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;" : ""}
235
+ ${this.mode === "popup" ? `box-shadow: 0 25px 50px -12px rgba(0,0,0,${dark ? "0.5" : "0.25"}); animation: slideIn 0.3s ease;` : ""}
212
236
  }
213
237
  .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; }
238
+ .title { text-align: center; font-size: 24px; font-weight: 700; margin-bottom: 8px; color: var(--authon-text); }
239
+ .brand-name { text-align: center; font-size: 14px; color: var(--authon-muted); margin-bottom: 24px; }
216
240
  .providers { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
217
241
  .provider-btn {
218
242
  display: flex; align-items: center; gap: 12px;
@@ -226,19 +250,22 @@ var ModalRenderer = class {
226
250
  .provider-icon { display: flex; align-items: center; flex-shrink: 0; }
227
251
  .divider {
228
252
  display: flex; align-items: center; gap: 12px;
229
- margin: 16px 0; color: #9ca3af; font-size: 13px;
253
+ margin: 16px 0; color: var(--authon-dim); font-size: 13px;
230
254
  }
231
255
  .divider::before, .divider::after {
232
- content: ''; flex: 1; height: 1px; background: #e5e7eb;
256
+ content: ''; flex: 1; height: 1px; background: var(--authon-divider);
233
257
  }
234
258
  .email-form { display: flex; flex-direction: column; gap: 10px; }
235
259
  .input {
236
260
  width: 100%; padding: 10px 14px;
237
- border: 1px solid #d1d5db; border-radius: calc(var(--authon-radius) * 0.5);
261
+ background: var(--authon-input-bg);
262
+ color: var(--authon-text);
263
+ border: 1px solid var(--authon-border); border-radius: calc(var(--authon-radius) * 0.5);
238
264
  font-size: 14px; font-family: var(--authon-font);
239
265
  outline: none; transition: border-color 0.15s;
240
266
  }
241
- .input:focus { border-color: var(--authon-primary-start); box-shadow: 0 0 0 3px rgba(124,58,237,0.1); }
267
+ .input::placeholder { color: var(--authon-dim); }
268
+ .input:focus { border-color: var(--authon-primary-start); box-shadow: 0 0 0 3px rgba(124,58,237,0.15); }
242
269
  .submit-btn {
243
270
  width: 100%; padding: 10px;
244
271
  background: linear-gradient(135deg, var(--authon-primary-start), var(--authon-primary-end));
@@ -247,11 +274,11 @@ var ModalRenderer = class {
247
274
  font-family: var(--authon-font); transition: opacity 0.15s;
248
275
  }
249
276
  .submit-btn:hover { opacity: 0.9; }
250
- .switch-view { text-align: center; margin-top: 16px; font-size: 13px; color: #6b7280; }
277
+ .switch-view { text-align: center; margin-top: 16px; font-size: 13px; color: var(--authon-muted); }
251
278
  .switch-view a { color: var(--authon-primary-start); text-decoration: none; font-weight: 500; }
252
279
  .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; }
280
+ .footer { text-align: center; margin-top: 16px; font-size: 12px; color: var(--authon-dim); }
281
+ .footer a { color: var(--authon-dim); text-decoration: none; }
255
282
  .footer a:hover { text-decoration: underline; }
256
283
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
257
284
  @keyframes slideIn { from { opacity: 0; transform: translate(-50%, -48%); } to { opacity: 1; transform: translate(-50%, -50%); } }
@@ -473,6 +500,7 @@ var Authon = class {
473
500
  if (!this.modal) {
474
501
  this.modal = new ModalRenderer({
475
502
  mode: this.config.mode,
503
+ theme: this.config.theme,
476
504
  containerId: this.config.containerId,
477
505
  branding: this.branding || void 0,
478
506
  onProviderClick: (provider) => this.startOAuthFlow(provider),
package/dist/index.mjs CHANGED
@@ -66,6 +66,7 @@ var ModalRenderer = class {
66
66
  hostElement = null;
67
67
  containerElement = null;
68
68
  mode;
69
+ theme;
69
70
  branding;
70
71
  enabledProviders = [];
71
72
  onProviderClick;
@@ -73,6 +74,7 @@ var ModalRenderer = class {
73
74
  onClose;
74
75
  constructor(options) {
75
76
  this.mode = options.mode;
77
+ this.theme = options.theme || "auto";
76
78
  this.branding = { ...DEFAULT_BRANDING, ...options.branding };
77
79
  this.onProviderClick = options.onProviderClick;
78
80
  this.onEmailSubmit = options.onEmailSubmit;
@@ -120,9 +122,13 @@ var ModalRenderer = class {
120
122
  const title = isSignUp ? "Create your account" : "Welcome back";
121
123
  const subtitle = isSignUp ? "Already have an account?" : "Don't have an account?";
122
124
  const subtitleLink = isSignUp ? "Sign in" : "Sign up";
125
+ const dark = this.isDark();
123
126
  const providerButtons = this.enabledProviders.filter((p) => !b.hiddenProviders?.includes(p)).map((p) => {
124
127
  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}">
128
+ const isWhiteBg = config.bgColor === "#ffffff";
129
+ const btnBg = dark && isWhiteBg ? "#f8fafc" : config.bgColor;
130
+ const btnBorder = isWhiteBg ? dark ? "#475569" : "#e5e7eb" : config.bgColor;
131
+ return `<button class="provider-btn" data-provider="${p}" style="background:${btnBg};color:${config.textColor};border:1px solid ${btnBorder}">
126
132
  <span class="provider-icon">${config.iconSvg}</span>
127
133
  <span>${config.label}</span>
128
134
  </button>`;
@@ -154,38 +160,56 @@ var ModalRenderer = class {
154
160
  </div>
155
161
  `;
156
162
  }
163
+ isDark() {
164
+ if (this.theme === "dark") return true;
165
+ if (this.theme === "light") return false;
166
+ return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
167
+ }
157
168
  buildCSS() {
158
169
  const b = this.branding;
170
+ const dark = this.isDark();
171
+ const bg = dark ? b.darkBg || "#0f172a" : b.lightBg || "#ffffff";
172
+ const text = dark ? b.darkText || "#f1f5f9" : b.lightText || "#111827";
173
+ const mutedText = dark ? "#94a3b8" : "#6b7280";
174
+ const dimText = dark ? "#64748b" : "#9ca3af";
175
+ const borderColor = dark ? "#334155" : "#d1d5db";
176
+ const dividerColor = dark ? "#334155" : "#e5e7eb";
177
+ const inputBg = dark ? "#1e293b" : "#ffffff";
159
178
  return `
160
179
  :host {
161
180
  --authon-primary-start: ${b.primaryColorStart || "#7c3aed"};
162
181
  --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"};
182
+ --authon-bg: ${bg};
183
+ --authon-text: ${text};
184
+ --authon-muted: ${mutedText};
185
+ --authon-dim: ${dimText};
186
+ --authon-border: ${borderColor};
187
+ --authon-divider: ${dividerColor};
188
+ --authon-input-bg: ${inputBg};
167
189
  --authon-radius: ${b.borderRadius ?? 12}px;
168
190
  --authon-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
169
191
  font-family: var(--authon-font);
170
- color: var(--authon-light-text);
192
+ color: var(--authon-text);
171
193
  }
172
194
  * { box-sizing: border-box; margin: 0; padding: 0; }
173
195
  .backdrop {
174
196
  position: fixed; inset: 0; z-index: 99998;
175
- background: rgba(0,0,0,0.5); backdrop-filter: blur(4px);
197
+ background: rgba(0,0,0,${dark ? "0.7" : "0.5"}); backdrop-filter: blur(4px);
176
198
  animation: fadeIn 0.2s ease;
177
199
  }
178
200
  .modal-container {
179
201
  ${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);
202
+ background: var(--authon-bg);
203
+ color: var(--authon-text);
204
+ border: 1px solid var(--authon-border);
181
205
  border-radius: var(--authon-radius);
182
206
  padding: 32px;
183
207
  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;" : ""}
208
+ ${this.mode === "popup" ? `box-shadow: 0 25px 50px -12px rgba(0,0,0,${dark ? "0.5" : "0.25"}); animation: slideIn 0.3s ease;` : ""}
185
209
  }
186
210
  .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; }
211
+ .title { text-align: center; font-size: 24px; font-weight: 700; margin-bottom: 8px; color: var(--authon-text); }
212
+ .brand-name { text-align: center; font-size: 14px; color: var(--authon-muted); margin-bottom: 24px; }
189
213
  .providers { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
190
214
  .provider-btn {
191
215
  display: flex; align-items: center; gap: 12px;
@@ -199,19 +223,22 @@ var ModalRenderer = class {
199
223
  .provider-icon { display: flex; align-items: center; flex-shrink: 0; }
200
224
  .divider {
201
225
  display: flex; align-items: center; gap: 12px;
202
- margin: 16px 0; color: #9ca3af; font-size: 13px;
226
+ margin: 16px 0; color: var(--authon-dim); font-size: 13px;
203
227
  }
204
228
  .divider::before, .divider::after {
205
- content: ''; flex: 1; height: 1px; background: #e5e7eb;
229
+ content: ''; flex: 1; height: 1px; background: var(--authon-divider);
206
230
  }
207
231
  .email-form { display: flex; flex-direction: column; gap: 10px; }
208
232
  .input {
209
233
  width: 100%; padding: 10px 14px;
210
- border: 1px solid #d1d5db; border-radius: calc(var(--authon-radius) * 0.5);
234
+ background: var(--authon-input-bg);
235
+ color: var(--authon-text);
236
+ border: 1px solid var(--authon-border); border-radius: calc(var(--authon-radius) * 0.5);
211
237
  font-size: 14px; font-family: var(--authon-font);
212
238
  outline: none; transition: border-color 0.15s;
213
239
  }
214
- .input:focus { border-color: var(--authon-primary-start); box-shadow: 0 0 0 3px rgba(124,58,237,0.1); }
240
+ .input::placeholder { color: var(--authon-dim); }
241
+ .input:focus { border-color: var(--authon-primary-start); box-shadow: 0 0 0 3px rgba(124,58,237,0.15); }
215
242
  .submit-btn {
216
243
  width: 100%; padding: 10px;
217
244
  background: linear-gradient(135deg, var(--authon-primary-start), var(--authon-primary-end));
@@ -220,11 +247,11 @@ var ModalRenderer = class {
220
247
  font-family: var(--authon-font); transition: opacity 0.15s;
221
248
  }
222
249
  .submit-btn:hover { opacity: 0.9; }
223
- .switch-view { text-align: center; margin-top: 16px; font-size: 13px; color: #6b7280; }
250
+ .switch-view { text-align: center; margin-top: 16px; font-size: 13px; color: var(--authon-muted); }
224
251
  .switch-view a { color: var(--authon-primary-start); text-decoration: none; font-weight: 500; }
225
252
  .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; }
253
+ .footer { text-align: center; margin-top: 16px; font-size: 12px; color: var(--authon-dim); }
254
+ .footer a { color: var(--authon-dim); text-decoration: none; }
228
255
  .footer a:hover { text-decoration: underline; }
229
256
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
230
257
  @keyframes slideIn { from { opacity: 0; transform: translate(-50%, -48%); } to { opacity: 1; transform: translate(-50%, -50%); } }
@@ -446,6 +473,7 @@ var Authon = class {
446
473
  if (!this.modal) {
447
474
  this.modal = new ModalRenderer({
448
475
  mode: this.config.mode,
476
+ theme: this.config.theme,
449
477
  containerId: this.config.containerId,
450
478
  branding: this.branding || void 0,
451
479
  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.5",
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
  }