Forms
GlowInput
A field whose border lights up under the pointer, then glows all round in a gradient once it has focus.
Download
- React
- Tailwind CSS v4
components/ui/glow-input.tsx
"use client";
import { useId, type CSSProperties, type InputHTMLAttributes, type PointerEvent, type ReactNode } from "react";
type GlowInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "className"> & {
/** Field name. Shown above the box. */
label: string;
/** Keep the label for screen readers only. */
hideLabel?: boolean;
/** Icon at the start of the box (an svg works best). */
icon?: ReactNode;
/** Small help text under the field. */
hint?: string;
/** Two glow colours: [start, end]. */
colors?: [string, string];
/** Wrapper look (width etc.). */
className?: string;
};
/* GlowInput: the border lights up under the pointer as you hover, and the whole edge glows in a gradient once the field has focus. */
export function GlowInput({
label,
hideLabel = false,
icon,
hint,
colors = ["#a78bfa", "#22d3ee"],
className = "w-full max-w-sm",
id,
...rest
}: GlowInputProps) {
const autoId = useId();
const inputId = id ?? autoId;
const hintId = `${inputId}-hint`;
const track = (e: PointerEvent<HTMLDivElement>) => {
const r = e.currentTarget.getBoundingClientRect();
e.currentTarget.style.setProperty("--gx", `${e.clientX - r.left}px`);
e.currentTarget.style.setProperty("--gy", `${e.clientY - r.top}px`);
};
const fade = "transition-opacity duration-300 motion-reduce:transition-none";
const gradient = "linear-gradient(90deg, var(--g1), var(--g2))";
return (
<div className={className}>
<label htmlFor={inputId} className={hideLabel ? "sr-only" : "mb-2 block text-sm font-medium text-white/80"}>
{label}
</label>
<div
onPointerMove={track}
className="group relative rounded-xl p-px"
style={{ "--g1": colors[0], "--g2": colors[1] } as CSSProperties}
>
<div aria-hidden className={`pointer-events-none absolute -inset-1 rounded-2xl opacity-0 blur-lg group-focus-within:opacity-40 ${fade}`} style={{ background: gradient }} />
<div aria-hidden className="absolute inset-0 rounded-xl bg-white/12" />
<div
aria-hidden
className={`absolute inset-0 rounded-xl opacity-0 group-hover:opacity-100 group-focus-within:opacity-0 ${fade}`}
style={{ background: "radial-gradient(140px circle at var(--gx, 50%) var(--gy, 50%), var(--g1), transparent 70%)" }}
/>
<div aria-hidden className={`absolute inset-0 rounded-xl opacity-0 group-focus-within:opacity-100 ${fade}`} style={{ background: gradient }} />
<div className="relative flex items-center gap-2.5 rounded-[11px] bg-[#0f0d1c] px-3.5">
{icon && (
<span aria-hidden className="grid size-[18px] shrink-0 place-items-center text-white/40 transition-colors duration-300 group-focus-within:text-[var(--g1)] [&>svg]:size-full">
{icon}
</span>
)}
<input
id={inputId}
aria-describedby={hint ? hintId : undefined}
className="h-12 w-full min-w-0 bg-transparent text-base text-white outline-none placeholder:text-white/30"
{...rest}
/>
</div>
</div>
{hint && (
<p id={hintId} className="mt-1.5 px-1 text-xs text-white/45">
{hint}
</p>
)}
</div>
);
}
1. Paste the Code tab into
components/ui/glow-input.tsx. The styles are Tailwind CSS v4 classes.2. Use it. This is the preview above:
app/glow-input-demo.tsx
"use client"; import { Mail } from "lucide-react"; import { GlowInput } from "@/components/ui/glow-input"; export default function GlowInputExample() { return ( <div className="grid h-full place-items-center bg-[#0b0a14] p-5"> <form className="w-full max-w-sm" onSubmit={(e) => e.preventDefault()}> <GlowInput label="Get the newsletter" type="email" name="email" autoComplete="email" placeholder="you@example.com" icon={<Mail />} hint="One email a month. Unsubscribe any time." className="w-full" /> </form> </div> ); }
Props
| Name | Type | Default | What it does |
|---|---|---|---|
| label | string | — | Field name above the box. |
| hideLabel | boolean | false | Keep the label for screen readers only. |
| icon | ReactNode | — | Icon at the start of the box. |
| hint | string | — | Help text under the field. |
| colors | [string, string] | ["#a78bfa", "#22d3ee"] | Glow gradient start and end. |
| ...input props | InputHTMLAttributes | — | type, name, placeholder, value, onChange… all pass to the input. |
| className | string | "w-full max-w-sm" | Wrapper look. |