├── 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 | 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 | 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 | 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 | 6 | 7 | 63 | -------------------------------------------------------------------------------- /app/pages/forgot-password.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 68 | -------------------------------------------------------------------------------- /app/pages/index.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 67 | -------------------------------------------------------------------------------- /app/pages/login.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 81 | -------------------------------------------------------------------------------- /app/pages/new-password.vue: -------------------------------------------------------------------------------- 1 | 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 | 58 | 59 | 107 | -------------------------------------------------------------------------------- /app/components/ErrorAlert.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 42 | 43 | 97 | -------------------------------------------------------------------------------- /app/components/SuccessAlert.vue: -------------------------------------------------------------------------------- 1 | 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 | --------------------------------------------------------------------------------