├── env.example
├── .npmrc
├── public
├── favicon.ico
└── img
│ └── guilloche.svg
├── .gitignore
├── tsconfig.json
├── app
├── assets
│ ├── fonts
│ │ ├── SuisseIntl-Medium.woff2
│ │ ├── SuisseIntl-Regular.woff2
│ │ └── SuisseIntl-SemiBold.woff2
│ └── css
│ │ └── tailwind.postcss
├── layouts
│ ├── default.vue
│ └── auth.vue
├── components
│ ├── AppBackToLogin.vue
│ ├── ErrorAlert.vue
│ └── SuccessAlert.vue
├── app.vue
└── pages
│ ├── forgot-password.vue
│ ├── index.vue
│ ├── login.vue
│ ├── new-password.vue
│ └── register.vue
├── .github
└── workflows
│ └── release.yml
├── tailwind.config.js
├── package.json
├── SECURITY.md
├── nuxt.config.ts
├── LICENSE
└── README.md
/env.example:
--------------------------------------------------------------------------------
1 | SUPABASE_URL=
2 | SUPABASE_KEY=
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | strict-peer-dependencies=false
3 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zackha/supaAuth/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log*
3 | .nuxt
4 | .nitro
5 | .cache
6 | .output
7 | dist
8 | .env
9 | .data
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // https://nuxt.com/docs/guide/concepts/typescript
3 | "extends": "./.nuxt/tsconfig.json"
4 | }
5 |
--------------------------------------------------------------------------------
/app/assets/fonts/SuisseIntl-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zackha/supaAuth/HEAD/app/assets/fonts/SuisseIntl-Medium.woff2
--------------------------------------------------------------------------------
/app/assets/fonts/SuisseIntl-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zackha/supaAuth/HEAD/app/assets/fonts/SuisseIntl-Regular.woff2
--------------------------------------------------------------------------------
/app/assets/fonts/SuisseIntl-SemiBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zackha/supaAuth/HEAD/app/assets/fonts/SuisseIntl-SemiBold.woff2
--------------------------------------------------------------------------------
/app/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | permissions:
4 | contents: write
5 |
6 | on:
7 | push:
8 | tags:
9 | - 'v*'
10 |
11 | jobs:
12 | release:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | with:
17 | fetch-depth: 0
18 |
19 | - name: Set node
20 | uses: actions/setup-node@v4
21 | with:
22 | registry-url: https://registry.npmjs.org/
23 | node-version: lts/*
24 |
25 | - run: npx changelogithub
26 | env:
27 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
28 |
--------------------------------------------------------------------------------
/app/components/AppBackToLogin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
14 | Login
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | import defaultTheme from 'tailwindcss/defaultTheme'
2 |
3 | export default {
4 | theme: {
5 | extend: {
6 | colors: {
7 | 'fcfcfc': '#fcfcfc',
8 | '12161e': '#12161e',
9 | '656976': '#656976',
10 | 'c2c3c7': '#c2c3c7',
11 | '91949b': '#91949b',
12 | '232730': '#232730',
13 | '575a64': '#575a64',
14 | },
15 | padding: {
16 | '2.25': '0.563rem',
17 | },
18 | maxWidth: {
19 | '75': '18.75rem',
20 | },
21 | fontFamily: {
22 | sans: ['SuisseIntl', ...defaultTheme.fontFamily.sans]
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "supaauth",
3 | "description": "Supabase Auth with Nuxt",
4 | "version": "2.0.4",
5 | "private": true,
6 | "scripts": {
7 | "build": "nuxt build",
8 | "dev": "nuxt dev",
9 | "generate": "nuxt generate",
10 | "preview": "nuxt preview"
11 | },
12 | "devDependencies": {
13 | "@nuxthub/core": "0.9.0",
14 | "@nuxtjs/supabase": "^1.6.1",
15 | "@nuxtjs/tailwindcss": "^6.14.0",
16 | "nuxt": "^4.1.1",
17 | "wrangler": "^4.34.0"
18 | },
19 | "packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0"
20 | }
21 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are
6 | currently being supported with security updates.
7 |
8 | | Version | Supported |
9 | | ------- | ------------------ |
10 | | 5.1.x | :white_check_mark: |
11 | | 5.0.x | :x: |
12 | | 4.0.x | :white_check_mark: |
13 | | < 4.0 | :x: |
14 |
15 | ## Reporting a Vulnerability
16 |
17 | Use this section to tell people how to report a vulnerability.
18 |
19 | Tell them where to go, how often they can expect to get an update on a
20 | reported vulnerability, what to expect if the vulnerability is accepted or
21 | declined, etc.
22 |
--------------------------------------------------------------------------------
/app/layouts/auth.vue:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
--------------------------------------------------------------------------------
/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | export default defineNuxtConfig({
2 | modules: ['@nuxtjs/supabase', '@nuxtjs/tailwindcss', '@nuxthub/core'],
3 |
4 | tailwindcss: {
5 | cssPath: '~/assets/css/tailwind.postcss',
6 | },
7 |
8 | supabase: {
9 | redirectOptions: {
10 | login: '*',
11 | callback: '*',
12 | },
13 | },
14 |
15 | future: {
16 | compatibilityVersion: 4,
17 | },
18 |
19 | app: {
20 | layoutTransition: { name: 'layout', mode: 'out-in' },
21 | pageTransition: { name: 'page', mode: 'out-in' },
22 | head: {
23 | charset: 'utf-16',
24 | viewport: 'width=500, initial-scale=1',
25 | link: [
26 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
27 | { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
28 | { rel: 'preconnect', href: 'https://fonts.gstatic.com' },
29 | { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Roboto+Mono&display=swap' },
30 | ],
31 | },
32 | },
33 |
34 | compatibilityDate: '2025-02-19',
35 | });
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Zack Hatlen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
63 |
--------------------------------------------------------------------------------
/app/pages/forgot-password.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Forgot password
4 |
24 |
25 |
26 |
27 |
68 |
--------------------------------------------------------------------------------
/app/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Hi {{ user.user_metadata.first_name }}
4 |
unauthenticated
5 |
6 | Nuxt3 + Supabase
7 |
8 | Starter Template
9 |
10 |
11 | Authentication template with email and password, using Supabase. If you want to a quick start to your next Nuxt3 app, please feel free to use this template.
12 |
13 |
14 |
15 | Log out
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Login
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Sign up
37 |
38 |
39 |
40 |
41 |
42 |
43 |
67 |
--------------------------------------------------------------------------------
/app/pages/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Sign in
4 |
31 |
32 |
Don’t have a SupaAuth account?
33 |
34 |
35 | Create new account
36 |
37 |
38 |
39 |
40 |
41 |
42 |
81 |
--------------------------------------------------------------------------------
/app/pages/new-password.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
New password
4 |
31 |
32 |
33 |
34 |
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ⚡ Supabase + Nuxt 4 Authentication Starter
2 |
3 | > A modern and secure authentication boilerplate built with the latest versions of **Nuxt 4** and **Supabase**. Provides out-of-the-box support for user registration and login functionality, ideal for rapidly initializing new full-stack projects.
4 |
5 | ## 🆕 Changelog
6 |
7 | - Migrated to **Nuxt 4** using the new `nuxt` package
8 | - Integrated **latest Supabase JavaScript client** (`@supabase/supabase-js`)
9 | - Switched to **pnpm** as the preferred package manager for faster, disk-efficient installs
10 |
11 | ## 🚀 Live Demonstrations
12 |
13 | | Deployment Platform | Live URL |
14 | | ------------------- | ------------------------------------------------------------------------ |
15 | | Vercel | [https://supaauth-nuxt4.vercel.app/](https://supaauth-nuxt4.vercel.app/) |
16 | | NuxtHub | [https://supaauth.nuxt.dev/](https://supaauth.nuxt.dev/) |
17 |
18 | ## 🖼️ UI Preview
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ## ✅ Key Features
27 |
28 | - 🔐 Email/password-based authentication powered by **Supabase Auth**
29 | - 💡 Seamless integration with **Nuxt 4 Composition API**
30 | - 🧱 Minimal and modular architecture, ideal for customization and scaling
31 | - 📱 Fully responsive design for both mobile and desktop
32 | - 🧼 Clean, maintainable codebase with best practices in mind
33 |
34 | ## ⚙️ Project Setup
35 |
36 | Ensure you have [Node.js](https://nodejs.org/) and [pnpm](https://pnpm.io) installed.
37 |
38 | ```bash
39 | # Install dependencies
40 | pnpm install
41 |
42 | # Start the development server
43 | pnpm dev
44 | ```
45 |
46 | ## 🔧 Supabase Configuration
47 |
48 | 1. Create a project via [supabase.com](https://supabase.com).
49 | 2. Go to **Project Settings → API** and retrieve your `SUPABASE_URL` and `SUPABASE_ANON_KEY`.
50 | 3. Create a `.env` file in your project root:
51 |
52 | ```env
53 | SUPABASE_URL=https://your-project-id.supabase.co
54 | SUPABASE_ANON_KEY=your-anon-key
55 | ```
56 |
57 | ## 📚 Documentation
58 |
59 | - [Nuxt 4 Documentation](https://nuxt.com/docs)
60 | - [Supabase Auth Documentation](https://supabase.com/docs/guides/auth)
61 | - [Supabase Client Reference](https://supabase.com/docs/reference/javascript/introduction)
62 |
63 | ---
64 |
65 | ## 🧑💻 Author
66 |
67 | **Sefa Bulak**
68 | 📫 Contact: [zckhtln@icloud.com](mailto:zckhtln@icloud.com)
69 | 🐙 GitHub: [github.com/zackhatlen](https://github.com/zackhatlen)
70 | 🐦 Twitter: [@ZHatlen](https://twitter.com/ZHatlen)
71 |
72 | ---
73 |
74 | ## 📝 License
75 |
76 | This project is released under the [MIT License](LICENSE).
77 |
--------------------------------------------------------------------------------
/app/pages/register.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Create an account
4 |
56 |
57 |
58 |
59 |
107 |
--------------------------------------------------------------------------------
/app/components/ErrorAlert.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
{{ errorMsg }}
22 |
30 |
31 |
32 |
33 |
34 |
35 |
42 |
43 |
97 |
--------------------------------------------------------------------------------
/app/components/SuccessAlert.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
{{ successMsg }}
22 |
30 |
31 |
32 |
33 |
34 |
35 |
42 |
43 |
97 |
--------------------------------------------------------------------------------
/app/assets/css/tailwind.postcss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | /** SuisseIntl **/
6 | @font-face {
7 | font-family: 'SuisseIntl';
8 | font-weight: 400;
9 | font-display: swap;
10 | src: url('../fonts/SuisseIntl-Regular.woff2') format('woff2');
11 | }
12 | @font-face {
13 | font-family: 'SuisseIntl';
14 | font-weight: 500;
15 | font-display: swap;
16 | src: url('../fonts/SuisseIntl-Medium.woff2') format('woff2');
17 | }
18 | @font-face {
19 | font-family: 'SuisseIntl';
20 | font-weight: 600;
21 | font-display: swap;
22 | src: url('../fonts/SuisseIntl-SemiBold.woff2') format('woff2');
23 | }
24 |
25 | body, html {
26 | @apply h-full overflow-hidden
27 | }
28 | body {
29 | @apply text-base m-0 leading-[1.6]
30 | }
31 | .buJnuC {
32 | @apply bg-fcfcfc text-12161e h-full;
33 | font-family: 'Inter', sans-serif;
34 | line-height: 1.6;
35 | }
36 | .fvfNpo {
37 | @apply bg-fcfcfc text-12161e h-screen overflow-y-auto relative
38 | }
39 | .uXyMu {
40 | @apply relative min-h-screen flex justify-center items-center box-border;
41 | padding: clamp(80px, 9vh, 200px) 0px
42 | }
43 | .fNAZQD {
44 | @apply relative flex-grow
45 | }
46 | .dgrFox {
47 | @apply max-w-75 w-full my-0 mx-auto py-0 px-5 box-border
48 | }
49 | .DaoRb {
50 | @apply grid gap-8 text-center
51 | }
52 | .jGQTZC {
53 | @apply grid gap-4 [&:not(:last-child)]:mb-6
54 | }
55 | .iJLvzO {
56 | @apply text-656976 bg-[var(--input-background,#f7f7f7)] block relative py-2.25 px-3 text-base cursor-text transition-all duration-200;
57 | box-shadow: inset 0 0 0 var(--input-border,1px) var(--input-border-color,#f1f1f2), 0 0 0 var(--input-outline,0px) var(--input-outline-color,transparent);
58 | border-radius: 5px;
59 | &:hover {
60 | --input-background: #f1f1f2;
61 | --input-border-color: #e3e3e5
62 | }
63 | &:focus-within {
64 | --input-background: #fff;
65 | --input-border-color: #5c89de;
66 | --input-outline: 2px;
67 | --input-outline-color: #5c89de26
68 | }
69 | }
70 | .iJLvzO input {
71 | @apply placeholder:transition-all placeholder:duration-200 placeholder:text-c2c3c7 hover:placeholder-91949b placeholder:focus-within:text-91949b
72 | }
73 | .fdCSlG {
74 | @apply relative flex items-center
75 | }
76 | .cmCuLh {
77 | @apply w-full block h-[22px] leading-[22px] m-0 p-0 text-inherit tracking-[-0.125px] appearance-none bg-[initial] border-none outline-none
78 | }
79 | .gZMQdu {
80 | box-shadow: 0 0 0 var(--button-outline,0px) var(--button-outline-color,#5c89de4d), inset 0 -1px 1px 0px var(--button-inner-shadow,rgba(38 38 44 / 15%)), inset 0 var(--button-shine-y,1px) 0 0 var(--button-shine,rgba(255 255 255 / 25%)), 0 1px 2px 0 var(--button-shadow,rgba(38 38 44 / 25%));
81 | @apply text-white bg-[var(--button-background,#5c89de)] table border-none relative font-semibold text-sm m-0 outline-none select-none whitespace-nowrap text-center appearance-none rounded-[5px] leading-5 tracking-[-0.125px] py-[10px] px-6 cursor-pointer transition scale-[var(--button-scale,1)] w-full duration-200;
82 | &:hover {
83 | --button-shadow: rgba(38 38 44 / 50%);
84 | --button-background: #3577da
85 | }
86 | &:active {
87 | --button-outline: 3px;
88 | --button-scale: 0.975
89 | }
90 | }
91 | .bjhGPG {
92 | @apply relative z-10 flex justify-center items-center gap-2 min-h-[1.25rem];
93 | transition: transform 0.2s ease 0s, opacity 0.2s ease 0s;
94 | &.loading {
95 | opacity: 0;
96 | transform: translateY(12px) scale(0.75);
97 | }
98 | }
99 | .fTZPOV {
100 | @apply no-underline transition-colors duration-200 text-xs font-medium m-0 leading-normal text-[#b3b5b9] hover:text-[#91949b]
101 | }
102 | .eSHwvX {
103 | @apply font-[inherit] m-0 leading-[1.4] tracking-[-0.125px] text-[#12161e] font-semibold text-xl
104 | }
105 | .dEDhcH {
106 | @apply m-0 leading-normal text-[#656976] text-sm font-medium
107 | }
108 | .lcqpaS {
109 | --button-border-radius: 5px;
110 | @apply text-[#656976] bg-[var(--button-background,#eeefef)] table border-none relative font-semibold text-sm m-0 outline-none select-none whitespace-nowrap text-center appearance-none rounded-[var(--button-border-radius)] leading-5 tracking-[-0.125px] py-[10px] px-6 cursor-pointer transition scale-[var(--button-scale,1)] w-full duration-300;
111 | &:before {
112 | @apply bg-white absolute inset-[1.34px] rounded-[calc(var(--button-border-radius)-1px)] shadow-[rgba(38,38,44,10%)_0_2px_4px];
113 | content: '';
114 | }
115 | &:hover {
116 | --button-background: #e3e3e5
117 | }
118 | &:active {
119 | --button-scale: 0.975
120 | }
121 | }
122 | .cyDNyc {
123 | @apply transition duration-200 m-0 absolute z-10 left-2/4 text-center translate-x-[-50%] bottom-6 text-[10px] max-w-[260px] text-[#c2c3c7]
124 | }
125 | .cyDNyc a {
126 | @apply no-underline transition duration-200 text-[#a3a5ab] hover:text-[#656976]
127 | }
128 | .gxYQch {
129 | @apply z-10 fixed left-0 top-0 right-0 p-6 box-border flex items-center justify-between transition-all duration-200
130 | }
131 | .jjabJD svg {
132 | --icon-color: currentColor;
133 | --icon-size: 20px;
134 | @apply flex-[0_0_20px]
135 | }
136 | .jXTFjD {
137 | @apply w-[var(--icon-size)] h-[var(--icon-size)]
138 | }
139 | .fvEvSO {
140 | @apply relative z-10 flex items-center gap-2 min-h-[20px] transition duration-200
141 | }
142 | .jjabJD {
143 | @apply text-[var(--button-color,#91949b)] relative text-sm font-[inherit] tracking-[-0.125px] py-[10px] pr-4 pl-3 font-semibold scale-[var(--button-scale,1)] transition duration-200 opacity-100;
144 | &:hover {
145 | --button-color: #656976
146 | }
147 | &:active {
148 | --button-scale: 0.975
149 | }
150 | &.router-link-exact-active {
151 | @apply pointer-events-none opacity-0
152 | }
153 | }
154 | .xxEKN {
155 | @apply text-left text-xs font-medium text-91949b
156 | }
157 | .bkFclS {
158 | @apply font-medium bg-none border-none inline
159 | cursor-pointer p-0 m-0 relative leading-5
160 | no-underline text-current;
161 | --line-background-size: 100%;
162 | --line-background-delay: 0.15s;
163 | --line-stroke-dashoffset: 46;
164 | --line-stroke-duration: 0.15s;
165 | --line-stroke-easing: linear;
166 | --line-stroke-delay: 0s;
167 | font-style: inherit;
168 | font-variant: inherit;
169 | font-stretch: inherit;
170 | font-size: inherit;
171 | font-family: inherit;
172 | font-optical-sizing: inherit;
173 | font-kerning: inherit;
174 | font-feature-settings: inherit;
175 | font-variation-settings: inherit;
176 | &:hover {
177 | --line-background-size: 0%;
178 | --line-background-delay: 0s;
179 | --line-stroke-dashoffset: 26;
180 | --line-stroke-duration: 0.3s;
181 | --line-stroke-easing: cubic-bezier(0.3,1.5,0.5,1);
182 | --line-stroke-delay: 0.195s;
183 | }
184 | & span {
185 | background-image: linear-gradient(0deg, currentcolor 0%, currentcolor 100%);
186 | background-position: 100% 100%;
187 | background-repeat: no-repeat;
188 | background-size: var(--line-background-size) 1px;
189 | transition: background-size 0.2s linear var(--line-background-delay);
190 | transform: translateZ(0px);
191 | line-height: 20px;
192 | }
193 | }
194 | .fVeafc {
195 | @apply text-base leading-5 font-semibold mb-3 text-232730;
196 | letter-spacing: -.01em;
197 | &:before {
198 | @apply w-2 h-4 mr-2 inline-block relative top-0.5;
199 | border-radius: 1px;
200 | background: linear-gradient(165deg,#cd4545 0%,#e97e7e 92%);
201 | content: '';
202 | }
203 | &.in::before {
204 | background: linear-gradient(165deg,#50cd45 0%,#82e97e 92%);
205 | }
206 | }
207 | .kKxhrq {
208 | @apply text-7xl text-12161e font-semibold mt-8;
209 | line-height: 4.5rem;
210 | letter-spacing: -.03em;
211 | }
212 | .kRTmDC {
213 | @apply text-2xl leading-8 text-575a64 mt-2;
214 | letter-spacing: -.01em;
215 | font-weight: inherit;
216 | }
217 | .uQxNj {
218 | @apply flex gap-3 mt-4;
219 | }
220 | .bQRHNT {
221 | @apply relative text-white rounded-lg px-4 h-9 m-px font-medium outline-none whitespace-nowrap
222 | cursor-pointer text-center leading-4 items-center justify-center inline-flex;
223 | background-color: rgba(17,26,74,1);
224 | box-shadow: 0 0 0 1px rgba(17,26,74,1),0 2px 4px rgba(0,0,0,0.25),0 1px 2px rgba(0,0,0,0.25);
225 | transition: box-shadow 0.2s ease 0s, background-color 0.2s ease 0s, border-color 0.2s ease 0s;
226 | min-height: 36px;
227 | &::after {
228 | @apply inset-0 absolute opacity-10 rounded-lg block;
229 | content: '';
230 | background-image: linear-gradient(180deg,rgba(255,255,255,1),rgba(255,255,255,0) 100%);
231 | transition: opacity 0.5s ease 0s;
232 | }
233 | &:hover {
234 | @apply cursor-pointer;
235 | .chevron, .stem {
236 | transform: translateX(0px);
237 | }
238 | .stem {
239 | stroke-dashoffset: 0;
240 | }
241 | &::after {
242 | opacity: 0.15;
243 | }
244 | }
245 | &:active {
246 | box-shadow: 0 0 0 1px rgba(17,26,74,1),0 1px 3px rgba(0,0,0,0.15),0 0 0 0 rgba(212,227,247,1);
247 | .chevron, .stem {
248 | transform: translateX(2px);
249 | }
250 | &::after {
251 | opacity: 0.05;
252 | }
253 | }
254 | }
255 | .fKlELC {
256 | @apply text-sm gap-1 items-center flex z-10 relative mt-0.5;
257 | transition: transform 0.2s ease 0s, opacity 0.2s ease 0s;
258 | &.loading {
259 | opacity: 0;
260 | transform: translateY(12px) scale(0.75);
261 | }
262 | }
263 | .taKtSf {
264 | @apply h-4 stroke-current inline-block -mr-1 mb-0.5;
265 | .chevron {
266 | transform: translateX(-3px);
267 | transition: transform 0.15s ease 0s;
268 | }
269 | .stem {
270 | stroke-dasharray: 10;
271 | stroke-dashoffset: 10;
272 | transform: translateX(-3px);
273 | transition: stroke-dashoffset 0.15s ease 0s, transform 0.15s ease 0s;
274 | }
275 | }
276 | .ieMfVH {
277 | @apply relative text-232730 rounded-lg px-4 h-9 m-px font-medium outline-none whitespace-nowrap
278 | cursor-pointer text-center leading-4 items-center justify-center inline-flex;
279 | background-color: rgba(255,255,255,0.5);
280 | backdrop-filter: blur(2px);
281 | box-shadow: 0 1px 1px rgba(255,255,255,0),0 0 0 1px rgba(87,90,100,0.12);
282 | transition: box-shadow 0.2s ease 0s, background-color 0.2s ease 0s, border-color 0.2s ease 0s;
283 | &:hover {
284 | background-color: rgba(255,255,255,1);
285 | box-shadow: 0 1px 1px rgba(0,0,0,0.15),0 0 0 1px rgba(87,90,100,0.2);
286 | }
287 | }
288 | .background-gradient {
289 | @apply absolute top-1/2 hue-rotate-0;
290 | width: 150vw;
291 | left: 80%;
292 | height: 1024px;
293 | transform: translate3d(-50%, -50%, 0px);
294 | -webkit-mask-image: radial-gradient(farthest-side at 50% 50%, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0));
295 | mask-image: radial-gradient(farthest-side at 50% 50%, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0));
296 | background-image: linear-gradient(90deg,rgba(126,167,233,1),rgba(148,239,183,1));
297 | animation-name: background-gradient-animation;
298 | animation-duration: 8s;
299 | animation-iteration-count: infinite;
300 | @keyframes background-gradient-animation {
301 | 0% {filter: hue-rotate(15deg)}
302 | 50% {filter: hue-rotate(-80deg)}
303 | 100% {filter: hue-rotate(15deg)}
304 | }
305 | }
306 | .background-gradient-pattern {
307 | @apply absolute inset-0 mix-blend-overlay;
308 | background-image: url('/img/guilloche.svg');
309 | background-size: 20px;
310 | }
311 |
312 |
313 | .gZMQdu svg,
314 | .ieMfVH svg {
315 | --icon-color: currentColor;
316 | --icon-size: 20px;
317 | flex: 0 0 20px;
318 | }
319 | .jjoFVh {
320 | @apply absolute left-1/2 top-1/2 opacity-0 block;
321 | margin: -10px 0px 0px -10px;
322 | transition: transform 0.2s ease 0s, opacity 0.2s ease 0s;
323 | transform: translateY(-12px) scale(0.75);
324 | width: var(--icon-size);
325 | height: var(--icon-size);
326 | opacity: 0;
327 | &.loading {
328 | transform: none;
329 | opacity: 1;
330 | }
331 | }
332 | .faEWLr {
333 | animation: 1s linear 0s infinite normal none running rotate;
334 | transform-origin: 50% 50%;
335 | @keyframes rotate {
336 | 0% {transform: rotate(0deg)}
337 | 100% {transform: rotate(360deg)}
338 | }
339 | }
340 | .VFMrX {
341 | stroke-dasharray: 38px;
342 | stroke-dashoffset: 114px;
343 | animation: 2s linear 0s infinite normal none running offset;
344 | @keyframes offset {
345 | 50% {stroke-dashoffset: 96px}
346 | }
347 | }
348 |
--------------------------------------------------------------------------------
/public/img/guilloche.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
25 |
--------------------------------------------------------------------------------