Password Input
nexvyn/password-inputFreeComponent
A password input with an animated eye toggle that follows your cursor and blinks periodically.
Live preview
live · rendered by the registry
Rendered by nexvyn on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ui.nexvyn.dev/r/password-input.jsonSource
1'use client'23import { forwardRef, useEffect, useId, useRef, useState, type InputHTMLAttributes } from 'react'4import { motion, useMotionValue, useReducedMotion, useSpring } from 'motion/react'5import { cn } from '@/lib/utils'6import { playHoverSound, playClickSound } from '@/lib/sound'78const EYE_OPEN_TOP = 'M1 12 C1 12 5 4 12 4 C19 4 23 12 23 12'9const EYE_CLOSED_TOP = 'M1 12 C1 12 5 12 12 12 C19 12 23 12 23 12'10const EYE_OPEN_BOTTOM = 'M1 12 C1 12 5 20 12 20 C19 20 23 12 23 12'11const EYE_CLOSED_BOTTOM = 'M1 12 C1 12 5 12 12 12 C19 12 23 12 23 12'12const CLIP_OPEN = 'M1 12 C1 12 5 4 12 4 C19 4 23 12 23 12 C23 12 19 20 12 20 C5 20 1 12 1 12Z'13const CLIP_CLOSED = 'M1 12 C1 12 5 12 12 12 C19 12 23 12 23 12 C23 12 19 12 12 12 C5 12 1 12 1 12Z'1415const PUPIL_RX = 516const PUPIL_RY = 3.51718export interface PasswordInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {19 label?: string20 showLabel?: string21 hideLabel?: string22 containerClassName?: string23 labelClassName?: string24}2526export const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(27 (28 {29 label = 'Password',30 showLabel = 'Show password',31 hideLabel = 'Hide password',32 containerClassName,33 labelClassName,34 className,35 id: idProp,36 ...props37 },38 ref,39 ) => {40 const [visible, setVisible] = useState(false)41 const [blinking, setBlinking] = useState(false)42 const eyeRef = useRef<SVGSVGElement | null>(null)43 const reactId = useId()44 const id = idProp ?? reactId45 const prefersReducedMotion = useReducedMotion()4647 const px = useMotionValue(0)48 const py = useMotionValue(0)49 const sx = useSpring(px, { stiffness: 350, damping: 28, mass: 0.4 })50 const sy = useSpring(py, { stiffness: 350, damping: 28, mass: 0.4 })5152 useEffect(() => {53 if (typeof window === 'undefined' || prefersReducedMotion) return54 const handle = (e: MouseEvent) => {55 const el = eyeRef.current56 if (!el) return57 const rect = el.getBoundingClientRect()58 const cx = rect.left + rect.width / 259 const cy = rect.top + rect.height / 260 const dx = e.clientX - cx61 const dy = e.clientY - cy62 const dist = Math.hypot(dx, dy) || 163 const nx = dx / dist64 const ny = dy / dist65 const reach = Math.min(1, dist / 240)66 px.set(nx * PUPIL_RX * reach)67 py.set(ny * PUPIL_RY * reach)68 }69 window.addEventListener('mousemove', handle)70// … truncated
What it pulls in
npm packages
Files it writes
More from nexvyn
All 33 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Badgebadge | nexvyn | Components | Free | 2 deps · 2 files | |
| Bars Themebars-theme | nexvyn | Components | Free | 1 dep · 3 files | |
| Bounce Sidebarbounce-sidebar | nexvyn | Components | Free | 1 dep · 2 files | |
| Breadcrumbsbreadcrumbs | nexvyn | Components | Free | 1 dep · 2 files | |
| Checkboxcheckbox | nexvyn | Components | Free | 1 dep · 3 files | |
| Clipboard Fieldclipboard-field | nexvyn | Components | Free | no deps · 2 files | |
| Color Pickercolor-picker | nexvyn | Components | Free | no deps · 16 files | |
| Comboboxcombobox | nexvyn | Components | Free | 1 dep · 2 files |
