{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"levitating-field-input","type":"registry:component","title":"Levitating Field Input","description":"A real text input whose typed words hang above the field and cast a soft shadow on its lit shelf; focus lifts them higher and submitting lands them.","dependencies":[],"registryDependencies":[],"files":[{"path":"components/viberdy/levitating-field-input.tsx","content":"\"use client\";\n\nimport { useId } from \"react\";\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\n\n/**\n * LevitatingFieldInput — a real text input whose words hang above the field and cast a shadow on its shelf.\n *\n * The input is lifted a few pixels inside a pale field with a lit shelf\n * along its bottom edge, and its text throws a soft shadow down onto the\n * shelf (offset and blur grow with the height), so the typed words read as\n * floating. The caret, selection, autofill and every input attribute stay\n * native. Focus raises the words about 4px more and lights a faint wedge\n * above the field; `dropped` (set it when the form submits) lands them on\n * the shelf with one small settle. It is CSS only: one registered custom\n * property drives the lift, the shadow and the light. Label, hint and error\n * are wired with htmlFor, aria-describedby and aria-invalid. Reduced motion\n * makes every change instant.\n *\n * Part of the Attention kit: a style of anomalies, familiar things made wrong\n * (fish in dry air, a stone that breaks upward, a shadow that runs toward its\n * lamp). One subject, one motivated light, one broken rule, and everything\n * else calm. Cut byte for byte from the kit's one site (AttentionSite, a\n * demo studio called Aerium). Paste it as its own file: it carries its own\n * copy of every helper it uses and has no dependencies.\n */\n\n/** The kit's type: Instrument Sans (through --font-instrument) over Geist. */\nconst A_SANS = 'var(--font-instrument, \"Instrument Sans\"), \"Geist\", system-ui, sans-serif';\n\n/** The field's CSS: one registered number (--a-h, the text's height in px) drives the lift, the shadow and the light. */\nconst A_FIELD_CSS =\n  \"@property --a-h{syntax:'<number>';inherits:true;initial-value:5}\" +\n  \".a-field{--a-h:var(--a-rest,5);transition:--a-h .24s cubic-bezier(.2,.7,.2,1)}\" +\n  \".a-field:focus-within{--a-h:var(--a-up,9)}\" +\n  \".a-field[data-dropped]{--a-h:0;animation:a-drop .3s cubic-bezier(.5,0,.75,0)}\" +\n  \"@keyframes a-drop{70%{--a-h:0}85%{--a-h:.7}100%{--a-h:0}}\" +\n  \".a-field input{transform:translate3d(0,calc(-1px * var(--a-h)),0);text-shadow:0 calc(1px + 1.5px * var(--a-h)) calc(0.6px + 0.9px * var(--a-h)) rgba(10,14,18,calc(0.36 - 0.018 * var(--a-h)))}\" +\n  \".a-field input::placeholder{color:rgba(10,14,18,.42);text-shadow:none}\" +\n  \".a-field .a-wedge{opacity:0;transition:opacity .3s}\" +\n  \".a-field:focus-within .a-wedge{opacity:1}\" +\n  \"@media (prefers-reduced-motion:reduce){.a-field,.a-field .a-wedge{transition:none}.a-field[data-dropped]{animation:none}}\";\n\nexport interface LevitatingFieldInputProps extends Omit<ComponentPropsWithoutRef<\"input\">, \"className\" | \"style\" | \"children\" | \"size\"> {\n  /** The visible label. */\n  label: string;\n  /** A short hint under the field. */\n  hint?: string;\n  /** An error under the field (sets aria-invalid). */\n  error?: string;\n  /** How high the words hang at rest, 2-10px (focus lifts them about 4px more). */\n  lift?: number;\n  /** Settle the words onto the shelf (set it when the form submits). */\n  dropped?: boolean;\n  className?: string;\n  style?: CSSProperties;\n}\n\n/**\n * LevitatingFieldInput — a real text input whose words hang above the field\n * and cast a soft shadow on its lit shelf; focus lifts them, `dropped` lands them.\n */\nexport function LevitatingFieldInput({ label, hint, error, lift = 5, dropped = false, id, className = \"\", style, ...input }: LevitatingFieldInputProps) {\n  const auto = useId();\n  const fid = id ?? \"a-field-\" + auto;\n  const rest = Math.min(10, Math.max(2, Number.isFinite(lift) ? lift : 5));\n  const hintId = hint ? fid + \"-hint\" : undefined;\n  const errId = error ? fid + \"-error\" : undefined;\n  const described = [input[\"aria-describedby\"], hintId, errId].filter(Boolean).join(\" \") || undefined;\n  return (\n    <div\n      className={\"a-field flex flex-col gap-2 \" + className}\n      data-dropped={dropped ? \"\" : undefined}\n      style={{ \"--a-rest\": rest, \"--a-up\": rest + 4, fontFamily: A_SANS, ...style } as CSSProperties}\n    >\n      <style>{A_FIELD_CSS}</style>\n      <label htmlFor={fid} className=\"font-mono text-[10px] leading-[1.6] tracking-[0.14em] text-[#0a0e12]/72 uppercase\">\n        {label}\n      </label>\n      <div className=\"relative rounded-[2px]\" style={{ background: \"rgba(250,252,253,0.72)\", boxShadow: \"inset 0 0 0 1px \" + (error ? \"rgba(150,40,30,0.55)\" : \"rgba(10,14,18,0.14)\"), backdropFilter: \"blur(10px)\" }}>\n        <span\n          aria-hidden=\"true\"\n          className=\"a-wedge pointer-events-none absolute inset-x-[8%] -top-[34px] h-[34px]\"\n          style={{ background: \"linear-gradient(180deg, rgba(255,255,255,0), rgba(255,255,255,0.5))\", clipPath: \"polygon(22% 0, 78% 0, 100% 100%, 0 100%)\" }}\n        />\n        <input\n          id={fid}\n          {...input}\n          aria-invalid={error ? true : input[\"aria-invalid\"]}\n          aria-describedby={described}\n          className=\"relative z-[1] block w-full bg-transparent px-3.5 pt-3.5 pb-3 text-[16px] leading-[1.3] text-[#0a0e12] caret-[#0a0e12] outline-none\"\n        />\n        <span\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-x-0 bottom-0 h-[7px] rounded-b-[2px]\"\n          style={{ background: \"linear-gradient(180deg, rgba(10,14,18,0) 0, rgba(10,14,18,0.05) 5px, rgba(255,255,255,0.9) 6px, rgba(255,255,255,0.9) 7px)\" }}\n        />\n        <span aria-hidden=\"true\" className=\"pointer-events-none absolute inset-0 rounded-[2px] [.a-field:focus-within_&]:shadow-[inset_0_0_0_2px_#0a0e12]\" />\n      </div>\n      {hint && (\n        <p id={hintId} className=\"m-0 text-[12px] leading-[1.45] text-[#0a0e12]/70\">\n          {hint}\n        </p>\n      )}\n      {error && (\n        <p id={errId} className=\"m-0 text-[12px] leading-[1.45] text-[#7a1f16]\">\n          {error}\n        </p>\n      )}\n    </div>\n  );\n}\n","type":"registry:component","target":"components/viberdy/levitating-field-input.tsx"}],"meta":{"tier":"free","tags":["input","form","text field","levitation","shadow","css","anomaly","attention","light mode"],"href":"https://viberdy.dev/library/levitating-field-input","aiPrompt":"Add \"LevitatingFieldInput\" to this app: a real text input whose typed words hang a few pixels above the field and cast a soft shadow onto a lit shelf along its bottom edge. The anomaly is that the words float.\n\nHow it works (CSS only, no animation loop):\n- A registered custom property `--a-h` (`@property`, a number) is the words' height in px: the rest height comes from `lift` (default 5, clamped 2-10) and focus raises it by 4, with a 0.24 s transition.\n- The input is moved up by `--a-h` px, and its text-shadow falls down onto the shelf: offset 1 + 1.5h px, blur 0.6 + 0.9h px, opacity 0.36 - 0.018h. The placeholder has no shadow.\n- The field is a pale, slightly blurred plate with a hairline and a 7px shelf at the bottom (a soft dark band under a bright line). Focus also fades in a faint wedge of light above the field and draws a 2px dark inset ring.\n- `dropped` lands the words: `--a-h` goes to 0 with one small settle (a 0.3 s keyframe that reaches 0, rises to 0.7 and returns). Set it while a form submits.\n\nProps: `label` (required, wired with htmlFor), `hint`, `error` (sets aria-invalid; hint and error are linked through aria-describedby), `lift`, `dropped`, `className`, `style`, and every other input attribute (name, type, autoComplete, required, disabled and so on), passed to the input.\n\nUse it inside a form that has a real submit handler. Reduced motion makes every change instant.","license":"https://viberdy.dev/license"}}