Velocity UI
Loading…
Menu

Glassmorphic Auth Card

Frosted-glass authentication card with clean Login/Register transitions and light/dark readiness.

Installation

Add this component to your project using the CLI:

terminal
npx vui-registry-cli-v1 add glassmorphic-auth-card

Source Code

glassmorphic-auth-card.tsx
 'use client'
 
 import React, { useRef, useState } from 'react'
 import { motion, AnimatePresence, useMotionValue, useSpring, useTransform } from 'framer-motion'
 
 export default function GlassmorphicAuthCard() {
  const [mode, setMode] = useState<'login' | 'register'>('login')

  return (
    <div className="w-full grid place-items-center py-10">
      <div
        className="relative w-full max-w-[400px] rounded-[32px] border border-foreground/10 dark:border-white/10 bg-background/70 dark:bg-white/5 backdrop-blur-[40px] shadow-[0_30px_80px_-20px_rgba(0,0,0,0.15)] overflow-hidden"
      >
        <div className="absolute inset-0 pointer-events-none">
             <div className="absolute top-0 left-1/2 -translate-x-1/2 w-full h-1/2 bg-gradient-to-b from-white/10 to-transparent opacity-50 blur-3xl" />
        </div>

        <div className="relative px-8 pt-8 pb-6 flex items-center justify-between z-10">
          <div className="flex items-center gap-3">
            <div className="w-8 h-8 rounded-xl bg-gradient-to-br from-foreground/10 to-foreground/5 dark:from-white/20 dark:to-white/5 border border-foreground/10 dark:border-white/10 grid place-items-center shadow-inner">
              <div className="w-3 h-3 rounded-[3px] bg-foreground dark:bg-white shadow-[0_0_10px_rgba(255,255,255,0.35)]" />
            </div>
            <span className="text-sm font-medium tracking-widest text-foreground dark:text-white/90">VELOCITY</span>
          </div>
          <div className="relative flex items-center gap-1 bg-foreground/5 dark:bg-black/20 border border-foreground/10 dark:border-white/5 rounded-full p-1 backdrop-blur-md">
            <motion.div
              layoutId="authMode"
              className={`absolute inset-y-1 rounded-full ${mode === 'login' ? 'left-1 w-[calc(50%-4px)]' : 'left-[50%] w-[calc(50%-4px)]'} bg-foreground/10 dark:bg-white/10 shadow-sm border border-foreground/10 dark:border-white/5`}
              transition={{ type: 'spring', bounce: 0.15, duration: 0.5 }}
            />
            <button
              type="button"
              onClick={() => setMode('login')}
              className={`relative z-10 px-4 py-1.5 rounded-full text-xs font-medium transition-colors ${mode === 'login' ? 'text-foreground dark:text-white' : 'text-foreground/60 dark:text-white/60 hover:text-foreground/80 dark:hover:text-white/80'}`}
            >
              Login
            </button>
            <button
              type="button"
              onClick={() => setMode('register')}
              className={`relative z-10 px-4 py-1.5 rounded-full text-xs font-medium transition-colors ${mode === 'register' ? 'text-foreground dark:text-white' : 'text-foreground/60 dark:text-white/60 hover:text-foreground/80 dark:hover:text-white/80'}`}
            >
              Register
            </button>
          </div>
        </div>

        <div className="p-8 pt-2 relative z-10">
          <AnimatePresence mode="wait">
            {mode === 'login' ? (
              <motion.form
                key="login"
                initial={{ opacity: 0, scale: 0.95, filter: 'blur(10px)' }}
                animate={{ opacity: 1, scale: 1, filter: 'blur(0px)' }}
                exit={{ opacity: 0, scale: 0.95, filter: 'blur(10px)' }}
                transition={{ type: 'spring', stiffness: 300, damping: 30 }}
                className="space-y-6"
                onSubmit={(e) => e.preventDefault()}
              >
                <div className="space-y-4">
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-muted-foreground ml-1">Email</label>
                    <input
                      type="email"
                      className="w-full rounded-2xl bg-foreground/5 dark:bg-black/20 border border-foreground/15 dark:border-white/5 px-4 py-3 text-sm text-foreground dark:text-white placeholder:text-muted-foreground outline-none focus:border-foreground/25 dark:focus:border-white/20 focus:bg-background/80 dark:focus:bg-black/30 transition-all shadow-inner"
                      placeholder="you@velocity.ui"
                    />
                  </div>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-muted-foreground ml-1">Password</label>
                    <input
                      type="password"
                      className="w-full rounded-2xl bg-foreground/5 dark:bg-black/20 border border-foreground/15 dark:border-white/5 px-4 py-3 text-sm text-foreground dark:text-white placeholder:text-muted-foreground outline-none focus:border-foreground/25 dark:focus:border-white/20 focus:bg-background/80 dark:focus:bg-black/30 transition-all shadow-inner"
                      placeholder="••••••••"
                    />
                  </div>
                </div>

                <div className="flex items-center justify-between px-1">
                  <label className="inline-flex items-center gap-2 text-xs text-muted-foreground cursor-pointer hover:text-foreground transition-colors">
                    <input type="checkbox" className="w-3.5 h-3.5 rounded border-foreground/20 bg-foreground/5 checked:bg-foreground accent-foreground dark:border-white/20 dark:bg-white/5 dark:checked:bg-white transition-all" />
                    Remember me
                  </label>
                  <button type="button" className="text-xs text-muted-foreground hover:text-foreground transition-colors">
                    Forgot password?
                  </button>
                </div>

                <motion.button
                  type="submit"
                  whileHover={{ scale: 1.02 }}
                  whileTap={{ scale: 0.98 }}
                  className="w-full rounded-2xl bg-gradient-to-br from-foreground/95 to-foreground/80 dark:from-white dark:to-white/80 hover:to-foreground/90 dark:hover:to-white/90 border border-foreground/10 dark:border-white/10 text-background dark:text-black font-medium text-sm py-3.5 shadow-[0_0_20px_-5px_rgba(0,0,0,0.08)] transition-all"
                >
                  Sign In
                </motion.button>

                <div className="grid grid-cols-2 gap-3">
                  <button type="button" className="h-11 rounded-2xl bg-foreground/5 dark:bg-white/5 hover:bg-foreground/10 dark:hover:bg-white/10 border border-foreground/10 dark:border-white/5 text-muted-foreground hover:text-foreground dark:text-white/70 dark:hover:text-white text-xs font-medium transition-all flex items-center justify-center gap-2 group">
                    <svg className="w-4 h-4 opacity-60 group-hover:opacity-100 transition-opacity" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
                    GitHub
                  </button>
                  <button type="button" className="h-11 rounded-2xl bg-foreground/5 dark:bg-white/5 hover:bg-foreground/10 dark:hover:bg-white/10 border border-foreground/10 dark:border-white/5 text-muted-foreground hover:text-foreground dark:text-white/70 dark:hover:text-white text-xs font-medium transition-all flex items-center justify-center gap-2 group">
                    <svg className="w-4 h-4 opacity-60 group-hover:opacity-100 transition-opacity" viewBox="0 0 24 24" fill="currentColor"><path d="M12.545,10.239v3.821h5.445c-0.712,2.315-2.647,3.972-5.445,3.972c-3.332,0-6.033-2.701-6.033-6.032s2.701-6.032,6.033-6.032c1.498,0,2.866,0.549,3.921,1.453l2.814-2.814C17.503,2.988,15.139,2,12.545,2C7.021,2,2.543,6.477,2.543,12s4.478,10,10.002,10c8.396,0,10.249-7.85,9.426-11.748L12.545,10.239z"/></svg>
                    Google
                  </button>
                </div>
              </motion.form>
            ) : (
              <motion.form
                key="register"
                initial={{ opacity: 0, scale: 0.95, filter: 'blur(10px)' }}
                animate={{ opacity: 1, scale: 1, filter: 'blur(0px)' }}
                exit={{ opacity: 0, scale: 0.95, filter: 'blur(10px)' }}
                transition={{ type: 'spring', stiffness: 300, damping: 30 }}
                className="space-y-6"
                onSubmit={(e) => e.preventDefault()}
              >
                <div className="space-y-4">
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-muted-foreground ml-1">Full Name</label>
                    <input
                      type="text"
                      className="w-full rounded-2xl bg-foreground/5 dark:bg-black/20 border border-foreground/15 dark:border-white/5 px-4 py-3 text-sm text-foreground dark:text-white placeholder:text-muted-foreground outline-none focus:border-foreground/25 dark:focus:border-white/20 focus:bg-background/80 dark:focus:bg-black/30 transition-all shadow-inner"
                      placeholder="John Doe"
                    />
                  </div>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-muted-foreground ml-1">Email</label>
                    <input
                      type="email"
                      className="w-full rounded-2xl bg-foreground/5 dark:bg-black/20 border border-foreground/15 dark:border-white/5 px-4 py-3 text-sm text-foreground dark:text-white placeholder:text-muted-foreground outline-none focus:border-foreground/25 dark:focus:border-white/20 focus:bg-background/80 dark:focus:bg-black/30 transition-all shadow-inner"
                      placeholder="you@velocity.ui"
                    />
                  </div>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-muted-foreground ml-1">Password</label>
                    <input
                      type="password"
                      className="w-full rounded-2xl bg-foreground/5 dark:bg-black/20 border border-foreground/15 dark:border-white/5 px-4 py-3 text-sm text-foreground dark:text-white placeholder:text-muted-foreground outline-none focus:border-foreground/25 dark:focus:border-white/20 focus:bg-background/80 dark:focus:bg-black/30 transition-all shadow-inner"
                      placeholder="Create a password"
                    />
                  </div>
                </div>

                <motion.button
                  type="submit"
                  whileHover={{ scale: 1.02 }}
                  whileTap={{ scale: 0.98 }}
                  className="w-full rounded-2xl bg-gradient-to-br from-foreground/95 to-foreground/80 dark:from-white dark:to-white/80 hover:to-foreground/90 dark:hover:to-white/90 border border-foreground/10 dark:border-white/10 text-background dark:text-black font-medium text-sm py-3.5 shadow-[0_0_20px_-5px_rgba(0,0,0,0.08)] transition-all"
                >
                  Create Account
                </motion.button>

                <div className="grid grid-cols-2 gap-3">
                  <button type="button" className="h-11 rounded-2xl bg-white/5 hover:bg-white/10 border border-white/5 text-white/70 hover:text-white text-xs font-medium transition-all flex items-center justify-center gap-2 group">
                    <svg className="w-4 h-4 opacity-60 group-hover:opacity-100 transition-opacity" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
                    GitHub
                  </button>
                  <button type="button" className="h-11 rounded-2xl bg-white/5 hover:bg-white/10 border border-white/5 text-white/70 hover:text-white text-xs font-medium transition-all flex items-center justify-center gap-2 group">
                    <svg className="w-4 h-4 opacity-60 group-hover:opacity-100 transition-opacity" viewBox="0 0 24 24" fill="currentColor"><path d="M12.545,10.239v3.821h5.445c-0.712,2.315-2.647,3.972-5.445,3.972c-3.332,0-6.033-2.701-6.033-6.032s2.701-6.032,6.033-6.032c1.498,0,2.866,0.549,3.921,1.453l2.814-2.814C17.503,2.988,15.139,2,12.545,2C7.021,2,2.543,6.477,2.543,12s4.478,10,10.002,10c8.396,0,10.249-7.85,9.426-11.748L12.545,10.239z"/></svg>
                    Google
                  </button>
                </div>
              </motion.form>
            )}
          </AnimatePresence>
        </div>

        <div className="absolute inset-0 z-0 pointer-events-none">
          <div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_0%,rgba(255,255,255,0.05),transparent_70%)]" />
          <div className="absolute bottom-0 left-0 right-0 h-1/3 bg-gradient-to-t from-black/40 to-transparent" />
        </div>
      </div>
    </div>
  )
}
 

Props

Component property reference.

NameTypeDefaultDescription
variant'login' | 'register''login'Initial view to display.
onSubmit(data: any) => void-Submit handler callback.
classNamestring-Additional CSS classes.
Context Worth Keeping In Orbit

Most components here are inspired by outstanding libraries and creators in the ecosystem. I don’t claim to be the original author — this is my space for learning, rebuilding, and understanding great work at a deeper level.

I’m still a student of the craft, constantly studying the best and translating what I learn through my own perspective. Every piece reflects curiosity, respect for the community, and small creative touches that feel true to me.

I’ve done my best to credit inspirations properly. If anything is missing or inaccurate, I truly appreciate a message so it can be corrected with care.