Copy URL or Code
Paste to your AI coding assistant and customize:
Done. Your AI handles the rest.
Fully customizable. 5 distinct effects: split underlines, sliding icons, 3D flips, ripple shadows, and splitting borders. Mix and match for your forms.
"use client";
import React, { useState, useId } from "react";
export interface SplitUnderlineInputProps {
label?: string;
value?: string;
onChange?: (value: string) => void;
type?: "text" | "email" | "password" | "tel" | "url";
lineColor?: string;
textColor?: string;
labelColor?: string;
className?: string;
disabled?: boolean;
required?: boolean;
name?: string;
}
export default function SplitUnderlineInput({
label = "Enter text",
value: controlledValue,
onChange,
type = "text",
lineColor = "#6a7989",
textColor = "#333",
labelColor = "#6a7989",
className = "",
disabled = false,
required = false,
name,
}: SplitUnderlineInputProps) {
const [internalValue, setInternalValue] = useState("");
const [isFocused, setIsFocused] = useState(false);
const id = useId();
const value = controlledValue ?? internalValue;
const isFilled = value.length > 0;
const isActive = isFocused || isFilled;
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
if (onChange) onChange(newValue);
else setInternalValue(newValue);
};
return (
<span className={`relative inline-block w-full ${className}`} style={{ marginTop: "3em", marginBottom: "1em" }}>
<input
id={id} type={type} name={name} value={value} onChange={handleChange}
onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)}
disabled={disabled} required={required}
className="w-full bg-transparent outline-none border-none"
style={{ padding: "0.4em 0.25em", fontSize: "1.25rem", color: textColor }}
/>
<label htmlFor={id} className="absolute left-0 w-full text-left pointer-events-none" style={{ top: 0, bottom: 0, color: labelColor }}>
<span className="absolute left-0 w-full" style={{ top: 0, height: "3px", backgroundColor: lineColor, transition: "transform 0.3s ease", transform: isActive ? "translate3d(0, -0.5em, 0)" : "translate3d(0, 0, 0)", zIndex: -1 }} />
<span className="absolute left-0 w-full" style={{ bottom: 0, height: "3px", backgroundColor: lineColor, transition: "transform 0.3s ease", transform: isActive ? "translate3d(0, 0.5em, 0)" : "translate3d(0, 0, 0)", zIndex: -1 }} />
<span className="inline-block" style={{ transition: "transform 0.3s ease", transform: isActive ? "translate3d(0, -90%, 0)" : "translate3d(0, 0, 0)", fontSize: "1rem" }}>{label}</span>
</label>
</span>
);
}"use client";
import React, { useState, useId } from "react";
export interface IconSlideInputProps {
label?: string;
value?: string;
onChange?: (value: string) => void;
type?: "text" | "email" | "password" | "tel" | "url";
icon?: React.ReactNode;
iconBgColor?: string;
iconColor?: string;
inputBgColor?: string;
textColor?: string;
className?: string;
disabled?: boolean;
required?: boolean;
name?: string;
}
const DefaultUserIcon = () => (
<svg viewBox="0 0 24 24" fill="currentColor" style={{ width: "1.25em", height: "1.25em" }}>
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
</svg>
);
export default function IconSlideInput({
label = "Enter text",
value: controlledValue,
onChange,
type = "text",
icon = <DefaultUserIcon />,
iconBgColor = "#899dda",
iconColor = "#fff",
inputBgColor = "#fff",
textColor = "#333",
className = "",
disabled = false,
required = false,
name,
}: IconSlideInputProps) {
const [internalValue, setInternalValue] = useState("");
const [isFocused, setIsFocused] = useState(false);
const id = useId();
const value = controlledValue ?? internalValue;
const isFilled = value.length > 0;
const isActive = isFocused || isFilled;
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
if (onChange) onChange(newValue);
else setInternalValue(newValue);
};
return (
<span className={`relative inline-block w-full overflow-hidden ${className}`} style={{ backgroundColor: inputBgColor, borderRadius: "4px" }}>
<input
id={id} type={type} name={name} value={value} onChange={handleChange}
onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)}
disabled={disabled} required={required} placeholder={isActive ? label : ""}
className="w-full bg-transparent outline-none border-none"
style={{ padding: "0.85em 0.85em 0.85em 3.5em", fontSize: "1rem", color: textColor, transition: "transform 0.3s ease", transform: isActive ? "translate3d(0, 0, 0)" : "translate3d(1em, 0, 0)" }}
/>
<label htmlFor={id} className="absolute top-0 left-0 h-full pointer-events-none flex items-center justify-center" style={{ width: "3.5em", color: iconColor }}>
<span className="absolute top-0 left-0 h-full" style={{ width: "3.5em", backgroundColor: iconBgColor, transformOrigin: "0% 50%", transition: "transform 0.3s ease", transform: isActive ? "scale3d(0.8, 1, 1)" : "scale3d(1, 1, 1)", zIndex: 0 }} />
<span className="relative z-10" style={{ transformOrigin: "0% 50%", transition: "transform 0.3s ease", transform: isActive ? "scale3d(0.7, 0.7, 1)" : "scale3d(1, 1, 1)" }}>{icon}</span>
<span className="sr-only">{label}</span>
</label>
</span>
);
}"use client";
import React, { useState, useId } from "react";
export interface FlipLabelInputProps {
label?: string;
value?: string;
onChange?: (value: string) => void;
type?: "text" | "email" | "password" | "tel" | "url";
panelColor?: string;
accentColor?: string;
labelColor?: string;
inputTextColor?: string;
className?: string;
disabled?: boolean;
required?: boolean;
name?: string;
}
export default function FlipLabelInput({
label = "Enter text",
value: controlledValue,
onChange,
type = "text",
panelColor = "#c5564a",
accentColor = "#ad473c",
labelColor = "#b04b40",
inputTextColor = "#f5f5f5",
className = "",
disabled = false,
required = false,
name,
}: FlipLabelInputProps) {
const [internalValue, setInternalValue] = useState("");
const [isFocused, setIsFocused] = useState(false);
const id = useId();
const value = controlledValue ?? internalValue;
const isFilled = value.length > 0;
const isActive = isFocused || isFilled;
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
if (onChange) onChange(newValue);
else setInternalValue(newValue);
};
return (
<span className={`relative inline-block w-full ${className}`} style={{ perspective: "1000px", height: "4em" }}>
<input
id={id} type={type} name={name} value={value} onChange={handleChange}
onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)}
disabled={disabled} required={required}
className="absolute inset-0 w-full bg-transparent outline-none border-none z-10"
style={{ padding: "0.75em", fontSize: "1rem", color: inputTextColor, opacity: isActive ? 1 : 0, transition: "opacity 0.3s ease", transitionDelay: isActive ? "0.3s" : "0s" }}
/>
<label htmlFor={id} className="absolute inset-0 w-full text-left" style={{ color: labelColor, pointerEvents: isActive ? "none" : "auto" }}>
<span className="absolute left-0 w-full" style={{ bottom: "100%", height: "4em", backgroundColor: panelColor, transformOrigin: "50% 100%", transition: "transform 0.3s ease", transform: isActive ? "perspective(1000px) rotate3d(1, 0, 0, 0deg)" : "perspective(1000px) rotate3d(1, 0, 0, 90deg)" }} />
<span className="absolute left-0 w-full" style={{ top: 0, height: "0.25em", backgroundColor: accentColor, transformOrigin: "50% 0%", transition: "transform 0.3s ease", transform: isActive ? "perspective(1000px) rotate3d(1, 0, 0, -90deg)" : "perspective(1000px) rotate3d(1, 0, 0, 0deg)" }} />
<span className="inline-block" style={{ padding: "0.75em 0", fontSize: "1rem", opacity: isActive ? 0 : 1, transition: "opacity 0.2s ease" }}>{label}</span>
</label>
</span>
);
}"use client";
import React, { useState, useId, useRef, useEffect } from "react";
export interface RippleFocusInputProps {
label?: string;
value?: string;
onChange?: (value: string) => void;
type?: "text" | "email" | "password" | "tel" | "url";
focusColor?: string;
rippleColor?: string;
textColor?: string;
labelColor?: string;
bgColor?: string;
className?: string;
disabled?: boolean;
required?: boolean;
name?: string;
}
export default function RippleFocusInput({
label = "Enter text",
value: controlledValue,
onChange,
type = "text",
focusColor = "#eca29b",
rippleColor = "rgba(199, 152, 157, 0.6)",
textColor = "#333",
labelColor = "#eca29b",
bgColor = "#fff",
className = "",
disabled = false,
required = false,
name,
}: RippleFocusInputProps) {
const [internalValue, setInternalValue] = useState("");
const [isFocused, setIsFocused] = useState(false);
const [isAnimating, setIsAnimating] = useState(false);
const id = useId();
const animationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const value = controlledValue ?? internalValue;
const isFilled = value.length > 0;
const isActive = isFocused || isFilled;
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
if (onChange) onChange(newValue);
else setInternalValue(newValue);
};
const handleFocus = () => {
setIsFocused(true);
setIsAnimating(true);
if (animationTimeoutRef.current) clearTimeout(animationTimeoutRef.current);
animationTimeoutRef.current = setTimeout(() => setIsAnimating(false), 300);
};
useEffect(() => {
return () => { if (animationTimeoutRef.current) clearTimeout(animationTimeoutRef.current); };
}, []);
return (
<span className={`relative inline-block w-full ${className}`}>
<input
id={id} type={type} name={name} value={value} onChange={handleChange}
onFocus={handleFocus} onBlur={() => setIsFocused(false)}
disabled={disabled} required={required} placeholder={isActive ? "" : label}
className="w-full outline-none border-none rounded"
style={{ padding: "0.75em", fontSize: "1rem", color: textColor, backgroundColor: bgColor, boxShadow: isFocused ? `0px 0px 0px 2px ${focusColor}` : "0px 0px 0px 2px transparent", transition: "box-shadow 0.3s ease" }}
/>
<label htmlFor={id} className="absolute left-0 w-full text-left pointer-events-none" style={{ top: 0, padding: "0.75em 0.15em", color: labelColor, opacity: isActive ? 0 : 1, transition: "opacity 0.2s ease" }}>
<span className="absolute inset-0" style={{ zIndex: -1, borderRadius: "4px", boxShadow: isAnimating ? `0px 0px 100px 50px ${rippleColor}` : `0px 0px 0px 0px ${rippleColor}`, opacity: isAnimating ? 0 : 1, animation: isAnimating ? "ripple-expand 0.3s forwards" : "none" }} />
<span className="opacity-0">{label}</span>
</label>
{isActive && <span className="absolute text-xs pointer-events-none" style={{ top: "-1.5em", left: "0.15em", color: focusColor }}>{label}</span>}
<style jsx>{`@keyframes ripple-expand { 0% { box-shadow: 0px 0px 0px 0px ${rippleColor}; opacity: 1; } 100% { box-shadow: 0px 0px 100px 50px ${rippleColor}; opacity: 0; } }`}</style>
</span>
);
}"use client";
import React, { useState, useId } from "react";
export interface SplitBorderInputProps {
label?: string;
value?: string;
onChange?: (value: string) => void;
type?: "text" | "email" | "password" | "tel" | "url";
borderColor?: string;
labelColor?: string;
textColor?: string;
borderWidth?: number;
className?: string;
disabled?: boolean;
required?: boolean;
name?: string;
}
export default function SplitBorderInput({
label = "Enter text",
value: controlledValue,
onChange,
type = "text",
borderColor = "#747981",
labelColor = "#df6589",
textColor = "#9196A1",
borderWidth = 3,
className = "",
disabled = false,
required = false,
name,
}: SplitBorderInputProps) {
const [internalValue, setInternalValue] = useState("");
const [isFocused, setIsFocused] = useState(false);
const [hasAnimated, setHasAnimated] = useState(false);
const id = useId();
const value = controlledValue ?? internalValue;
const isFilled = value.length > 0;
const isActive = isFocused || isFilled;
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
if (onChange) onChange(newValue);
else setInternalValue(newValue);
};
const handleFocus = () => {
setIsFocused(true);
if (!hasAnimated) setHasAnimated(true);
};
return (
<span className={`relative inline-block w-full max-w-xs ${className}`} style={{ height: "5em" }}>
<input
id={id} type={type} name={name} value={value} onChange={handleChange}
onFocus={handleFocus} onBlur={() => setIsFocused(false)}
disabled={disabled} required={required}
className="absolute inset-0 w-full bg-transparent outline-none border-none text-center z-10"
style={{ padding: "0.75em", fontSize: "1rem", color: textColor, opacity: isActive ? 1 : 0, transition: "opacity 0.3s ease", transitionDelay: isActive ? "0.3s" : "0s" }}
/>
<label htmlFor={id} className="absolute inset-0 w-full pointer-events-none" style={{ color: labelColor }}>
<span className="absolute top-0 left-0 h-full" style={{ width: "50%", borderTop: `${borderWidth}px solid ${borderColor}`, borderBottom: `${borderWidth}px solid ${borderColor}`, borderLeft: `${borderWidth}px solid ${borderColor}`, transition: "transform 0.3s ease", transform: isActive ? "translate3d(-10%, 0, 0)" : "translate3d(0, 0, 0)" }} />
<span className="absolute top-0 left-1/2 h-full" style={{ width: "50%", borderTop: `${borderWidth}px solid ${borderColor}`, borderBottom: `${borderWidth}px solid ${borderColor}`, borderRight: `${borderWidth}px solid ${borderColor}`, transition: "transform 0.3s ease", transform: isActive ? "translate3d(10%, 0, 0)" : "translate3d(0, 0, 0)" }} />
<span className="absolute w-full text-center" style={{ top: "50%", left: 0, transform: "translateY(-50%)", fontSize: "1rem", animation: isActive && hasAnimated ? "label-drop 0.3s forwards" : "none", opacity: isActive && hasAnimated ? undefined : isActive ? 0 : 1 }}>{label}</span>
</label>
<style jsx>{`@keyframes label-drop { 0% { opacity: 1; transform: translateY(-50%) scale(1); } 50% { opacity: 0; transform: translateY(-50%) scale(0.3); } 51% { opacity: 0; transform: translateY(200%) scale(0.3); } 100% { opacity: 1; transform: translateY(200%) scale(1); } }`}</style>
</span>
);
}// Import the effect you want
import SplitUnderlineInput from "@/components/SplitUnderlineInput";
import IconSlideInput from "@/components/IconSlideInput";
import FlipLabelInput from "@/components/FlipLabelInput";
import RippleFocusInput from "@/components/RippleFocusInput";
import SplitBorderInput from "@/components/SplitBorderInput";
// Example form using different effects
export function ContactForm() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
return (
<form className="space-y-6 p-8">
<SplitUnderlineInput
label="Your Name"
value={name}
onChange={setName}
/>
<RippleFocusInput
label="Email Address"
type="email"
value={email}
onChange={setEmail}
focusColor="#3b82f6"
/>
<button type="submit">Submit</button>
</form>
);
}