sveltekit-auth-example 5.1.1 → 5.1.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  - Add password complexity checking on /register and /profile pages (only checks for length currently despite what the pages say)
4
4
 
5
+ # 5.1.2
6
+
7
+ - Dark mode
8
+
5
9
  # 5.1.0
6
10
 
7
11
  - Convert to Tailwind CSS
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sveltekit-auth-example",
3
3
  "description": "SvelteKit Authentication Example",
4
- "version": "5.1.1",
4
+ "version": "5.1.2",
5
5
  "author": "Nate Stuyvesant",
6
6
  "license": "https://github.com/nstuyvesant/sveltekit-auth-example/blob/master/LICENSE",
7
7
  "repository": {
package/src/app.html CHANGED
@@ -4,6 +4,7 @@
4
4
  <meta charset="utf-8" />
5
5
  <link rel="icon" href="%sveltekit.assets%/favicon.png" sizes="any" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta name="color-scheme" content="light dark" />
7
8
  <script
8
9
  nonce="%sveltekit.nonce%"
9
10
  src="https://accounts.google.com/gsi/client"
package/src/lib/google.ts CHANGED
@@ -5,11 +5,12 @@ import { redirectAfterLogin } from '$lib/auth-redirect'
5
5
  export function renderGoogleButton() {
6
6
  const btn = document.getElementById('googleButton')
7
7
  if (btn) {
8
+ const width = btn.offsetWidth || btn.parentElement?.offsetWidth || 400
8
9
  google.accounts.id.renderButton(btn, {
9
10
  type: 'standard',
10
- theme: 'filled_blue',
11
+ theme: 'outline',
11
12
  size: 'large',
12
- width: btn.offsetWidth || 400
13
+ width: Math.floor(width)
13
14
  })
14
15
  }
15
16
  }
@@ -55,13 +55,13 @@
55
55
 
56
56
  <svelte:window onclick={handleWindowClick} />
57
57
 
58
- <nav class="tw:bg-gray-100 tw:border-b tw:border-gray-200">
58
+ <nav class="tw:bg-gray-100 tw:border-b tw:border-gray-200 tw:dark:bg-gray-900 tw:dark:border-gray-700">
59
59
  <div class="tw:mx-auto tw:max-w-5xl tw:px-4 tw:flex tw:items-center tw:justify-between tw:h-14">
60
- <a class="tw:font-semibold tw:text-gray-800 tw:no-underline" href="/">SvelteKit-Auth-Example</a>
60
+ <a class="tw:font-semibold tw:text-gray-800 tw:no-underline tw:dark:text-gray-100" href="/">SvelteKit-Auth-Example</a>
61
61
 
62
62
  <!-- Mobile toggle -->
63
63
  <button
64
- class="tw:sm:hidden tw:p-2 tw:rounded tw:text-gray-600 hover:tw:bg-gray-200"
64
+ class="tw:sm:hidden tw:p-2 tw:rounded tw:text-gray-600 hover:tw:bg-gray-200 tw:dark:text-gray-300 tw:dark:hover:bg-gray-700"
65
65
  aria-label="Toggle navigation"
66
66
  onclick={() => (navOpen = !navOpen)}
67
67
  >
@@ -71,22 +71,22 @@
71
71
  </button>
72
72
 
73
73
  <!-- Nav links -->
74
- <div class="tw:hidden tw:sm:flex tw:items-center tw:gap-6 {navOpen ? '!tw:flex tw:flex-col tw:absolute tw:top-14 tw:left-0 tw:right-0 tw:bg-gray-100 tw:p-4 tw:border-b tw:border-gray-200' : ''}">
75
- <a class="tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:text-gray-900" href="/">Home</a>
76
- <a class="tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:text-gray-900" href="/info">Info</a>
74
+ <div class="tw:hidden tw:sm:flex tw:items-center tw:gap-6 {navOpen ? '!tw:flex tw:flex-col tw:absolute tw:top-14 tw:left-0 tw:right-0 tw:bg-gray-100 tw:dark:bg-gray-900 tw:p-4 tw:border-b tw:border-gray-200 tw:dark:border-gray-700' : ''}">
75
+ <a class="tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:text-gray-900 tw:dark:text-gray-300 tw:dark:hover:text-white" href="/">Home</a>
76
+ <a class="tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:text-gray-900 tw:dark:text-gray-300 tw:dark:hover:text-white" href="/info">Info</a>
77
77
 
78
78
  {#if appState.user?.role === 'admin'}
79
- <a class="tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:text-gray-900" href="/admin">Admin</a>
79
+ <a class="tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:text-gray-900 tw:dark:text-gray-300 tw:dark:hover:text-white" href="/admin">Admin</a>
80
80
  {/if}
81
81
  {#if appState.user && appState.user.role !== 'student'}
82
- <a class="tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:text-gray-900" href="/teachers">Teachers</a>
82
+ <a class="tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:text-gray-900 tw:dark:text-gray-300 tw:dark:hover:text-white" href="/teachers">Teachers</a>
83
83
  {/if}
84
84
 
85
85
  {#if appState.user}
86
86
  <!-- User dropdown -->
87
87
  <div class="tw:relative" bind:this={dropdownEl}>
88
88
  <button
89
- class="tw:flex tw:items-center tw:gap-1 tw:text-sm tw:text-gray-700 hover:tw:text-gray-900 tw:bg-transparent tw:border-0 tw:cursor-pointer"
89
+ class="tw:flex tw:items-center tw:gap-1 tw:text-sm tw:text-gray-700 hover:tw:text-gray-900 tw:bg-transparent tw:border-0 tw:cursor-pointer tw:dark:text-gray-300 tw:dark:hover:text-white"
90
90
  onclick={() => (dropdownOpen = !dropdownOpen)}
91
91
  aria-expanded={dropdownOpen}
92
92
  aria-haspopup="true"
@@ -101,13 +101,13 @@
101
101
  </svg>
102
102
  </button>
103
103
  {#if dropdownOpen}
104
- <ul class="tw:absolute tw:right-0 tw:mt-1 tw:w-36 tw:rounded tw:border tw:border-gray-200 tw:bg-white tw:shadow-md tw:py-1 tw:z-50 tw:list-none">
105
- <li><a class="tw:block tw:px-4 tw:py-2 tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:bg-gray-100" href="/profile">Profile</a></li>
104
+ <ul class="tw:absolute tw:right-0 tw:mt-1 tw:w-36 tw:rounded tw:border tw:border-gray-200 tw:bg-white tw:shadow-md tw:py-1 tw:z-50 tw:list-none tw:dark:bg-gray-800 tw:dark:border-gray-700">
105
+ <li><a class="tw:block tw:px-4 tw:py-2 tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:bg-gray-100 tw:dark:text-gray-300 tw:dark:hover:bg-gray-700" href="/profile">Profile</a></li>
106
106
  {#if appState.user?.id !== 0}
107
107
  <li>
108
108
  <button
109
109
  onclick={logout}
110
- class="tw:block tw:w-full tw:text-left tw:px-4 tw:py-2 tw:text-sm tw:text-gray-700 tw:bg-transparent tw:border-0 tw:cursor-pointer hover:tw:bg-gray-100"
110
+ class="tw:block tw:w-full tw:text-left tw:px-4 tw:py-2 tw:text-sm tw:text-gray-700 tw:bg-transparent tw:border-0 tw:cursor-pointer hover:tw:bg-gray-100 tw:dark:text-gray-300 tw:dark:hover:bg-gray-700"
111
111
  >Logout</button>
112
112
  </li>
113
113
  {/if}
@@ -115,7 +115,7 @@
115
115
  {/if}
116
116
  </div>
117
117
  {:else}
118
- <a class="tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:text-gray-900" href="/login">Login</a>
118
+ <a class="tw:text-sm tw:text-gray-700 tw:no-underline hover:tw:text-gray-900 tw:dark:text-gray-300 tw:dark:hover:text-white" href="/login">Login</a>
119
119
  {/if}
120
120
  </div>
121
121
  </div>
@@ -131,7 +131,7 @@
131
131
  role="alert"
132
132
  aria-live="assertive"
133
133
  aria-atomic="true"
134
- class="tw:fixed tw:top-4 tw:right-4 tw:z-50 tw:min-w-64 tw:rounded tw:shadow-lg tw:border tw:border-gray-200 tw:bg-white tw:overflow-hidden"
134
+ class="tw:fixed tw:top-4 tw:right-4 tw:z-50 tw:min-w-64 tw:rounded tw:shadow-lg tw:border tw:border-gray-200 tw:bg-white tw:overflow-hidden tw:dark:bg-gray-800 tw:dark:border-gray-700"
135
135
  >
136
136
  <div class="tw:flex tw:items-center tw:justify-between tw:bg-blue-600 tw:px-4 tw:py-2">
137
137
  <strong class="tw:text-white tw:text-sm">{appState.toast.title}</strong>
@@ -141,6 +141,6 @@
141
141
  class="tw:text-white tw:bg-transparent tw:border-0 tw:cursor-pointer tw:text-lg tw:leading-none"
142
142
  onclick={() => (appState.toast = { ...appState.toast, isOpen: false })}>&times;</button>
143
143
  </div>
144
- <div class="tw:px-4 tw:py-3 tw:text-sm">{appState.toast.body}</div>
144
+ <div class="tw:px-4 tw:py-3 tw:text-sm tw:dark:text-gray-100">{appState.toast.body}</div>
145
145
  </div>
146
146
  {/if}
@@ -12,7 +12,10 @@
12
12
 
13
13
  html,
14
14
  :host {
15
- @apply tw:font-sans tw:text-gray-800;
15
+ @apply tw:font-sans tw:text-gray-800 tw:bg-white;
16
+ @variant dark {
17
+ @apply tw:text-gray-100 tw:bg-gray-900;
18
+ }
16
19
  }
17
20
  }
18
21
 
@@ -22,8 +25,9 @@
22
25
  display: block;
23
26
  width: 100%;
24
27
  margin-top: 0.25rem;
25
- border-radius: var(--radius, 0.25rem);
26
- border: 1px solid var(--color-gray-300, #d1d5db);
28
+ appearance: none;
29
+ -webkit-appearance: none;
30
+ @apply tw:rounded tw:border tw:border-gray-300 tw:bg-white tw:text-gray-900 tw:dark:bg-gray-800 tw:dark:border-gray-600 tw:dark:text-gray-200;
27
31
  padding: 0.375rem 0.75rem;
28
32
  font-size: var(--text-sm, 0.875rem);
29
33
  line-height: var(--tw-leading-sm, 1.25rem);
@@ -38,7 +38,6 @@
38
38
  onMount(() => {
39
39
  initializeGoogleAccounts()
40
40
  renderGoogleButton()
41
-
42
41
  focusedField?.focus()
43
42
  })
44
43
 
@@ -86,7 +85,20 @@
86
85
  <h4>Sign In</h4>
87
86
  <p>Welcome back.</p>
88
87
 
89
- <div id="googleButton" class="tw:w-full"></div>
88
+ <div class="tw:group tw:relative tw:w-full">
89
+ <!-- Real Google button: invisible but receives clicks -->
90
+ <div id="googleButton" class="tw:opacity-0 tw:w-full"></div>
91
+ <!-- Visual overlay: looks good, no pointer events -->
92
+ <div class="tw:pointer-events-none tw:absolute tw:inset-0 tw:flex tw:items-center tw:justify-center tw:gap-3 tw:rounded tw:border tw:border-gray-300 tw:bg-white tw:group-hover:bg-gray-50 tw:text-sm tw:font-medium tw:text-gray-700 tw:dark:bg-gray-800 tw:dark:group-hover:bg-gray-700 tw:dark:border-gray-600 tw:dark:text-gray-200">
93
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
94
+ <path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
95
+ <path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
96
+ <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z" fill="#FBBC05"/>
97
+ <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
98
+ </svg>
99
+ Sign in with Google
100
+ </div>
101
+ </div>
90
102
 
91
103
  <div class="tw:flex tw:items-center tw:gap-2 tw:text-gray-400 tw:text-sm">
92
104
  <span class="tw:flex-1 tw:border-t tw:border-gray-300"></span>
@@ -208,7 +208,7 @@
208
208
  <p class="tw:text-sm tw:text-gray-500 tw:mb-2">Danger zone</p>
209
209
  <button
210
210
  type="button"
211
- class="tw:w-full tw:rounded tw:border tw:border-red-400 tw:bg-white tw:px-4 tw:py-2 tw:text-sm tw:font-medium tw:text-red-600 tw:cursor-pointer hover:tw:bg-red-50 disabled:tw:opacity-50 disabled:tw:cursor-not-allowed"
211
+ class="tw:w-full tw:rounded tw:border tw:border-red-600 tw:bg-red-600 tw:px-4 tw:py-2 tw:text-sm tw:font-medium tw:text-white tw:cursor-pointer hover:tw:bg-red-700 hover:tw:border-red-700 disabled:tw:opacity-50 disabled:tw:cursor-not-allowed"
212
212
  disabled={deleting}
213
213
  onclick={deleteAccount}
214
214
  >
@@ -54,7 +54,6 @@
54
54
  onMount(() => {
55
55
  initializeGoogleAccounts()
56
56
  renderGoogleButton()
57
-
58
57
  focusedField?.focus()
59
58
  })
60
59
 
@@ -117,7 +116,20 @@
117
116
  <h4>Register</h4>
118
117
  <p>Welcome to our community.</p>
119
118
 
120
- <div id="googleButton" class="tw:w-full"></div>
119
+ <div class="tw:relative tw:w-full">
120
+ <!-- Real Google button: invisible but receives clicks -->
121
+ <div id="googleButton" class="tw:opacity-0 tw:w-full"></div>
122
+ <!-- Visual overlay: looks good, no pointer events -->
123
+ <div class="tw:pointer-events-none tw:absolute tw:inset-0 tw:flex tw:items-center tw:justify-center tw:gap-3 tw:rounded tw:border tw:border-gray-300 tw:bg-white tw:text-sm tw:font-medium tw:text-gray-700 tw:dark:bg-gray-800 tw:dark:border-gray-600 tw:dark:text-gray-200">
124
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
125
+ <path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
126
+ <path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
127
+ <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z" fill="#FBBC05"/>
128
+ <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
129
+ </svg>
130
+ Sign in with Google
131
+ </div>
132
+ </div>
121
133
 
122
134
  <label class="tw:block tw:text-sm tw:font-medium" for="email">
123
135
  Email