-
Notifications
You must be signed in to change notification settings - Fork 308
Expand file tree
/
Copy pathauth.svelte
More file actions
187 lines (167 loc) · 7.54 KB
/
auth.svelte
File metadata and controls
187 lines (167 loc) · 7.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<script lang="ts">
import { animate, hover, inView, motionValue, type AnimationSequence } from 'motion';
import Google from '../../../(assets)/logos/google.svg';
import { isMobile } from '$lib/utils/is-mobile';
import { cn } from '$lib/utils/cn';
import GridPaper from '../../grid-paper.svelte';
import { unwrite, write } from '$lib/animations';
import { trackEvent } from '$lib/actions/analytics';
let container: HTMLElement;
let password = $state('');
let button: HTMLButtonElement;
let controller: AbortController | null = null;
$effect(() => {
inView(
container,
() => {
if (!isMobile()) return;
controller?.abort();
controller = new AbortController();
write('•••••••••••••', (v) => (password = v), 1000, {
signal: controller.signal,
startIndex: password.length
})
.then(() => {
animate(button, { scale: [1, 0.95, 1] }, { duration: 0.25 });
})
.catch((err: unknown) => {
if (err instanceof Error && err.message !== 'Aborted') console.error(err);
});
return () => {
controller?.abort();
controller = new AbortController();
unwrite('•••••••••••••', (v) => (password = v), 500, {
signal: controller.signal,
startIndex: password.length
}).catch((err: unknown) => {
if (err instanceof Error && err.message !== 'Aborted') console.error(err);
});
};
},
{ amount: 'all' }
);
hover(container, () => {
if (isMobile()) return;
controller?.abort();
controller = new AbortController();
write('•••••••••••••', (v) => (password = v), 1000, {
signal: controller.signal,
startIndex: password.length
})
.then(() => {
animate(button, { scale: [1, 0.95, 1] }, { duration: 0.25 });
})
.catch((err: unknown) => {
if (err instanceof Error && err.message !== 'Aborted') console.error(err);
});
return () => {
controller?.abort();
controller = new AbortController();
unwrite('•••••••••••••', (v) => (password = v), 500, {
signal: controller.signal,
startIndex: password.length
}).catch((err: unknown) => {
if (err instanceof Error && err.message !== 'Aborted') console.error(err);
});
};
});
return () => {
controller?.abort();
};
});
</script>
<a
href="/products/auth"
class="border-smooth col-span-12 flex flex-col rounded-2xl border bg-white/2 p-2 transition-shadow duration-300 hover:shadow-[0px_0px_0px_4px_var(--color-offset)] focus:shadow-[0px_0px_0px_4px_var(--color-offset)] md:col-span-6"
onclick={() => {
trackEvent(`bento-auth-click`);
}}
bind:this={container}
>
<div class="space-y-3 px-3 pt-2 pb-4">
<div class="flex items-center gap-2">
<img
loading="lazy"
src="/images/icons/illustrated/dark/auth.png"
alt="Auth icon"
class="size-7"
/>
<h3 class="font-aeonik-pro text-label text-primary">Auth</h3>
</div>
<p class="text-sub-body text-primary max-w-lg font-medium">
<span class="text-secondary"
>Authenticate users securely with multiple login methods like</span
> Email/Password, SMS, OAuth, Anonymous, and Magic URLs.
</p>
</div>
<div
class="relative mt-auto mb-0 flex h-85 items-center justify-between overflow-clip rounded-xl bg-black/24 px-8"
>
<div class="flex h-full w-full items-center justify-center">
<div
class="border-smooth flex w-[264px] flex-col rounded-[40px] border bg-[#232325]/90"
>
<div class="pointer-events-none relative m-2 flex-1 rounded-4xl bg-[#19191C] p-4">
<div class="flex flex-col gap-3">
<div class="flex flex-col gap-1">
<label
for="email"
class="leading-micro text-secondary text-[0.625rem] tracking-tighter"
>Email</label
>
<input
type="text"
name="email"
class="border-smooth text-eyebrow w-full rounded-lg border bg-[#19191C] px-3 py-2 tracking-tighter text-white"
value="walter@acme.dev"
disabled
/>
</div>
<div class="flex flex-col gap-1">
<label
for="password"
class="leading-micro text-secondary text-[0.625rem] tracking-tighter"
>Create Password</label
>
<input
type="text"
name="password"
class={cn(
'text-eyebrow! w-full rounded-lg border border-white/24 bg-[#19191C] px-3 py-2 tracking-tighter text-white'
)}
placeholder="Your Password"
value={password}
disabled
/>
</div>
<button
class="text-eyebrow w-full rounded-lg bg-white py-2 font-medium tracking-tighter text-[#19191C]"
disabled
bind:this={button}>Sign up</button
>
</div>
<span
class={cn(
'leading-micro text-secondary relative my-3 flex items-center justify-center gap-3 text-center text-[0.625rem] tracking-tighter'
)}
>
<span class="bg-smooth h-px flex-1"></span>
or sign up with
<span class="bg-smooth h-px flex-1"></span>
</span>
<button
class={cn(
'text-eyebrow border-smooth flex w-full items-center justify-center gap-3 rounded-lg border py-2 font-medium text-white transition'
)}
disabled
>
<img loading="lazy" src={Google} alt="Google Icon" class="size-4" />
Google</button
>
<div class="absolute inset-x-3 flex flex-col gap-3"></div>
</div>
</div>
</div>
<GridPaper class="absolute inset-0 -z-10 bg-size-[calc(100%/11)]" />
</div>
</a>