{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "floating-label-input",
  "title": "Floating Label Input",
  "description": "A text input whose label rests inside the field like a placeholder, then shrinks and floats up onto the top border the moment the field is focused or has a value (the Material \"outlined\" floating-label pattern). Use it anywhere you want a compact, self-labelling field: a login or sign-up form (email, password), a settings or profile form, a contact or checkout form, a search or filter panel, or any dense form where separate labels above every input would waste vertical space. Common asks it answers: \"floating label input\", \"animated / material label\", \"label that moves up on focus\", \"placeholder that turns into a label\", \"outlined text field\", \"MUI TextField for shadcn\". shadcn/ui ships no floating-label field — you'd otherwise wire the placeholder-shown CSS onto its Input by hand; this packages it and keeps it a real, accessible input. The float is driven purely by CSS (`:placeholder-shown` / `:focus` on the peer input) with no JS state, so it works for controlled or uncontrolled inputs, survives browser autofill, and is correct before hydration with no first-focus jump. The label is a genuine `<label htmlFor>` (not a fake overlay): the `id` defaults to a stable React.useId() value so the association always holds and clicking the label focuses the input. It forwards a ref to the underlying <input>, so every native prop works unchanged — `type` (email/password/tel/url…), `name`, `value`/`onChange`, `required`, `disabled`, `autoComplete`, and form libraries like react-hook-form. Pass `error` to show an invalid state (destructive border, ring, and label, plus `aria-invalid`). Styled with shadcn tokens (border-input, ring, background, muted-foreground, destructive) for automatic light/dark theming, and ships with zero dependencies beyond your cn util.",
  "files": [
    {
      "path": "registry/ui/floating-label-input.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface FloatingLabelInputProps\n  extends Omit<React.ComponentPropsWithoutRef<\"input\">, \"placeholder\"> {\n  /**\n   * The label text. It sits inside the field like a placeholder while the\n   * input is empty and unfocused, then shrinks and floats up onto the top\n   * border once the field is focused or has a value. It doubles as the\n   * accessible `<label>` (associated via `htmlFor`), so a separate label is\n   * not needed.\n   */\n  label: string\n  /** Marks the field invalid: destructive border/ring/label and `aria-invalid`. */\n  error?: boolean\n  /** Class for the wrapping element (the input itself takes `className`). */\n  containerClassName?: string\n}\n\n/**\n * A text input whose label animates from a resting placeholder position into a\n * floating label on the field's top border when the input is focused or filled\n * (the Material \"outlined\" pattern). shadcn/ui ships no floating-label field —\n * this packages one that stays a real, accessible input.\n *\n * The float is driven purely by CSS `:placeholder-shown`/`:focus` on the peer\n * input (no JS state), so it works for controlled or uncontrolled inputs, for\n * autofill, and before hydration. The label is a genuine `<label htmlFor>`; the\n * `id` defaults to a stable `React.useId()` value so the association always\n * holds. The ref is forwarded to the underlying `<input>`, and every native\n * input prop (`type`, `name`, `value`/`onChange`, `disabled`, `required`,\n * `autoComplete`, …) works unchanged. Styled with shadcn tokens for automatic\n * light/dark theming; zero dependencies beyond your `cn` util.\n */\nexport const FloatingLabelInput = React.forwardRef<\n  HTMLInputElement,\n  FloatingLabelInputProps\n>(function FloatingLabelInput(\n  { label, error = false, id, className, containerClassName, disabled, ...props },\n  ref\n) {\n  const generatedId = React.useId()\n  const inputId = id ?? generatedId\n\n  return (\n    <div className={cn(\"relative\", containerClassName)}>\n      <input\n        id={inputId}\n        ref={ref}\n        // A non-empty placeholder is required for `:placeholder-shown` to\n        // track emptiness; the space is invisible and the label stands in for it.\n        placeholder=\" \"\n        disabled={disabled}\n        aria-invalid={error || undefined}\n        className={cn(\n          \"peer h-11 w-full rounded-md border bg-transparent px-3 text-sm text-foreground shadow-sm transition-colors placeholder:text-transparent focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50\",\n          error\n            ? \"border-destructive focus-visible:border-destructive focus-visible:ring-destructive/40\"\n            : \"border-input focus-visible:border-ring focus-visible:ring-ring\",\n          className\n        )}\n        {...props}\n      />\n      <label\n        htmlFor={inputId}\n        className={cn(\n          // Resting state: centered like a placeholder.\n          \"pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 bg-background px-1 text-sm text-muted-foreground transition-all\",\n          // Floated state (focused OR has a value): small, on the top border.\n          \"peer-focus:top-0 peer-focus:-translate-y-1/2 peer-focus:text-xs peer-[:not(:placeholder-shown)]:top-0 peer-[:not(:placeholder-shown)]:-translate-y-1/2 peer-[:not(:placeholder-shown)]:text-xs\",\n          error\n            ? \"peer-focus:text-destructive\"\n            : \"peer-focus:text-foreground\",\n          \"peer-disabled:opacity-50\"\n        )}\n      >\n        {label}\n      </label>\n    </div>\n  )\n})\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}