← Back
UI

Floating Label

Material design input where the label transitions up and shrinks on focus or fill.

Minimum 8 characters

floating label · focus state · smooth transition

FloatingLabel.tsx78 lines
"use client";

import { useState } from "react";

function FloatField({
  label,
  type = "text",
  hint,
}: {
  label: string;
  type?: string;
  hint?: string;
}) {
  const [focused, setFocused] = useState(false);
  const [value, setValue] = useState("");
  const raised = focused || value.length > 0;

  return (
    <div className="relative">
      <input
        type={type}
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onFocus={() => setFocused(true)}
        onBlur={() => setFocused(false)}
        className={`w-full px-4 pt-6 pb-2.5 rounded-2xl border-2 bg-white dark:bg-zinc-900 text-sm text-zinc-900 dark:text-zinc-50 outline-none transition-colors duration-200 ${
          focused
            ? "border-violet-500"
            : "border-zinc-200 dark:border-zinc-700"
        }`}
      />
      <label
        className="absolute left-4 pointer-events-none transition-all duration-200 origin-left font-medium"
        style={{
          top: raised ? "8px" : "50%",
          transform: raised ? "none" : "translateY(-50%)",
          fontSize: raised ? "10px" : "14px",
          color: focused ? "#8b5cf6" : raised ? "#a1a1aa" : "#a1a1aa",
          letterSpacing: raised ? "0.05em" : undefined,
        }}
      >
        {label}
      </label>
      {hint && (
        <p className="mt-1 text-[11px] text-zinc-400 dark:text-zinc-600 px-1">{hint}</p>
      )}
    </div>
  );
}

export default function FloatingLabel() {
  const [submitted, setSubmitted] = useState(false);

  return (
    <div className="w-full max-w-sm mx-auto flex flex-col gap-4 py-8">
      <FloatField label="Full name" />
      <FloatField label="Email address" type="email" />
      <FloatField label="Password" type="password" hint="Minimum 8 characters" />
      <FloatField label="Company (optional)" />

      <button
        onClick={() => { setSubmitted(true); setTimeout(() => setSubmitted(false), 2000); }}
        className={`mt-2 w-full py-3 rounded-2xl font-semibold text-sm transition-all duration-300 ${
          submitted
            ? "bg-emerald-500 text-white"
            : "bg-violet-600 hover:bg-violet-700 text-white"
        }`}
      >
        {submitted ? "✓ Submitted" : "Create account"}
      </button>

      <p className="text-center text-[10px] font-mono text-zinc-400 dark:text-zinc-600">
        floating label · focus state · smooth transition
      </p>
    </div>
  );
}

More in UI