Please use Desktop to view and interact with components
Components300msspring
AI Glow Input
Input field with dynamic glow that pulses based on typing speed - smart motion feedback
inputaiglowtypingsearchsmartfeedback
Useto navigate between components
Preview
Code
TypeScript + React
'use client'
import { useState, useRef, useEffect } from 'react'
import { motion, useMotionValue, useSpring } from 'framer-motion'
export function AIGlowInput() {
const [value, setValue] = useState('')
const [glowIntensity, setGlowIntensity] = useState(0.3)
const [isFocused, setIsFocused] = useState(false)
const lastTypingTime = useRef(Date.now())
const typingSpeed = useMotionValue(0)
const glowIntensityMotion = useMotionValue(0.3)
const glowColor = '#8b5cf6'
const springIntensity = useSpring(glowIntensityMotion, { stiffness: 400, damping: 30 })
useEffect(() => {
const unsubscribe = springIntensity.on('change', (latest) => {
setGlowIntensity(latest as number)
})
return () => unsubscribe()
}, [springIntensity])
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value
setValue(newValue)
const now = Date.now()
const timeSinceLastType = now - lastTypingTime.current
lastTypingTime.current = now
const speed = Math.min(1, 1000 / (timeSinceLastType || 1))
typingSpeed.set(speed)
const baseIntensity = 0.3
const dynamicIntensity = baseIntensity + (speed * 0.7)
glowIntensityMotion.set(dynamicIntensity)
if (timeSinceLastType > 500) {
glowIntensityMotion.set(baseIntensity)
}
}
const handleFocus = () => {
setIsFocused(true)
glowIntensityMotion.set(0.6)
}
const handleBlur = () => {
setIsFocused(false)
if (!value) {
glowIntensityMotion.set(0.3)
} else {
glowIntensityMotion.set(0.4)
}
}
const hexToRgb = (hex: string) => {
const result = /^#?([a-fd]{2})([a-fd]{2})([a-fd]{2})$/i.exec(hex)
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: { r: 139, g: 92, b: 246 }
}
const rgb = hexToRgb(glowColor)
return (
<div className="relative w-full max-w-md">
<div className="relative">
<motion.div
style={{
background: `radial-gradient(circle,
rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${glowIntensity}) 0%,
rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${glowIntensity * 0.5}) 50%,
transparent 100%)`,
filter: 'blur(20px)',
}}
className="absolute inset-0 pointer-events-none"
animate={{
scale: isFocused ? [1, 1.1, 1] : 1,
}}
transition={{
duration: 2,
repeat: isFocused ? Infinity : 0,
ease: 'easeInOut',
}}
/>
<input
type="text"
value={value}
onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder="Ask AI anything..."
className="relative w-full px-6 py-4 rounded-2xl border-2 bg-background/50 backdrop-blur-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-0 transition-all duration-300"
style={{
borderColor: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${glowIntensity})`,
boxShadow: `0 0 ${glowIntensity * 40}px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${glowIntensity * 0.5})`,
}}
/>
<motion.div
className="absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none"
animate={{
opacity: [0.5, 1, 0.5],
}}
transition={{
duration: 2,
repeat: Infinity,
ease: 'easeInOut',
}}
>
<svg
className="w-5 h-5"
viewBox="0 0 24 24"
fill="none"
stroke={`rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${glowIntensity})`}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M12 2L2 7l10 5 10-5-10-5z" />
<path d="M2 17l10 5 10-5" />
<path d="M2 12l10 5 10-5" />
</svg>
</motion.div>
{value && isFocused && (
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="absolute -bottom-8 left-0 text-xs text-muted-foreground"
>
<motion.span
animate={{
opacity: [0.4, 1, 0.4],
}}
transition={{
duration: 1,
repeat: Infinity,
ease: 'easeInOut',
}}
>
{glowIntensity > 0.8 ? '⚡ Fast typing!' : glowIntensity > 0.5 ? '🎯 Active' : '✨ Ready'}
</motion.span>
</motion.div>
)}
{isFocused && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute inset-0 rounded-2xl pointer-events-none"
style={{
background: `linear-gradient(135deg,
transparent 0%,
rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.1) 50%,
transparent 100%)`,
}}
/>
)}
</div>
<div className="absolute inset-0 overflow-hidden rounded-2xl pointer-events-none">
{Array.from({ length: 5 }).map((_, i) => (
<motion.div
key={i}
className="absolute rounded-full"
style={{
background: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.2)`,
width: '4px',
height: '4px',
left: `${Math.random() * 100}%`,
top: `${Math.random() * 100}%`,
}}
animate={{
y: [0, -20, 0],
opacity: [0, 0.5, 0],
}}
transition={{
duration: 3,
repeat: Infinity,
delay: i * 0.6,
ease: 'easeInOut',
}}
/>
))}
</div>
</div>
)
}
How to Use
- 1Install Framer Motion:
npm install framer-motion - 2Copy the code from above
- 3Paste it into your project and customize as needed
- 4Colors are customizable via Tailwind CSS classes. The default theme uses dark mode colors defined in your globals.css file