{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "form",
  "title": "Form",
  "author": "AlgorizeTech",
  "description": "Form scaffolding — wrapper, field, error and submit — with a shared color theme and a glitch error variant. Use it to keep field spacing and validation styling consistent across a form.",
  "dependencies": [],
  "registryDependencies": [
    "https://avaraui.com/r/input.json",
    "utils"
  ],
  "files": [
    {
      "path": "src/components/custom/blocks/FormBlock.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { getThemeStyles } from \"./form-theme\";\n\n// ==========================================\n// DYNAMIC THEME ENGINE\n// ==========================================\nexport type FormColor = \"indigo\" | \"rose\" | \"emerald\" | \"zinc\" | string;\n\n// ==========================================\n// FORM WRAPPER (High-Fidelity UI)\n// ==========================================\nexport interface FormWrapperProps {\n  children: ReactNode;\n  color?: FormColor;\n  title?: string;\n  description?: string;\n  className?: string;\n  onSubmit?: (e: React.FormEvent) => void;\n}\n\nexport function FormWrapper({\n  children,\n  color = \"primary\",\n  title,\n  description,\n  className,\n  onSubmit,\n}: FormWrapperProps) {\n  const { style } = getThemeStyles(color);\n\n  return (\n    <div style={style} className={cn(\"relative group w-full\", className)}>\n      {/* Animated Slim Glowing Border */}\n      <div className=\"absolute -inset-[1px] rounded-2xl overflow-hidden pointer-events-none opacity-50 group-hover:opacity-100 transition-opacity duration-500\">\n        <div className=\"absolute inset-[-100%] bg-[conic-gradient(from_0deg,transparent_70%,var(--theme-color)_100%)] animate-[spin_3s_linear_infinite]\" />\n      </div>\n\n      {/* Inner Container */}\n      <div className=\"relative bg-card text-card-foreground border border-border rounded-2xl p-6 sm:p-10 h-full w-full z-10\">\n        {/* Header */}\n        {(title || description) && (\n          <div className=\"mb-8 border-b border-border pb-6\">\n            {title && (\n              <h2 className=\"text-2xl font-bold tracking-tight text-foreground\">\n                {title}\n              </h2>\n            )}\n            {description && (\n              <p className=\"text-sm text-muted-foreground mt-2\">\n                {description}\n              </p>\n            )}\n          </div>\n        )}\n\n        {/* Form Body */}\n        {onSubmit ? (\n          <form onSubmit={onSubmit} className=\"flex flex-col gap-6\">\n            {children}\n          </form>\n        ) : (\n          <div className=\"flex flex-col gap-6\">{children}</div>\n        )}\n      </div>\n    </div>\n  );\n}\n\n// ==========================================\n// FORM ERROR (With Toggleable Glitch Effect)\n// ==========================================\nexport function FormError({\n  message,\n  variant = \"glitch\",\n}: {\n  message?: string;\n  variant?: \"default\" | \"glitch\";\n}) {\n  if (!message) return null;\n\n  if (variant === \"default\") {\n    return (\n      <div className=\"mt-1 flex items-center gap-1.5 overflow-hidden\">\n        <div className=\"w-1 h-3 bg-red-500 shrink-0\" />\n        <span className=\"text-red-500 font-semibold text-xs tracking-wider uppercase\">\n          {message}\n        </span>\n      </div>\n    );\n  }\n\n  return (\n    <>\n      <style>{`\n        @keyframes glitch-anim-1 {\n          0% { clip-path: inset(20% 0 80% 0); transform: translate(-2px, 1px); }\n          20% { clip-path: inset(60% 0 10% 0); transform: translate(2px, -1px); }\n          40% { clip-path: inset(40% 0 50% 0); transform: translate(-2px, 2px); }\n          60% { clip-path: inset(80% 0 5% 0); transform: translate(2px, -2px); }\n          80% { clip-path: inset(10% 0 70% 0); transform: translate(-1px, 1px); }\n          100% { clip-path: inset(30% 0 50% 0); transform: translate(1px, -1px); }\n        }\n        @keyframes glitch-anim-2 {\n          0% { clip-path: inset(10% 0 60% 0); transform: translate(2px, -1px); }\n          20% { clip-path: inset(80% 0 5% 0); transform: translate(-2px, 2px); }\n          40% { clip-path: inset(30% 0 20% 0); transform: translate(2px, -2px); }\n          60% { clip-path: inset(70% 0 10% 0); transform: translate(-1px, 1px); }\n          80% { clip-path: inset(20% 0 50% 0); transform: translate(1px, -1px); }\n          100% { clip-path: inset(50% 0 30% 0); transform: translate(-2px, 1px); }\n        }\n        .glitch-text {\n          position: relative;\n          display: inline-block;\n          font-weight: 700;\n          color: #ef4444; /* red-500 */\n        }\n        .glitch-text::before, .glitch-text::after {\n          content: attr(data-text);\n          position: absolute;\n          top: 0;\n          left: 0;\n          width: 100%;\n          height: 100%;\n          background: transparent;\n        }\n        .glitch-text::before {\n          left: 1px;\n          text-shadow: -1px 0 rgba(255, 0, 0, 0.7);\n          animation: glitch-anim-1 2.5s infinite linear alternate-reverse;\n        }\n        .glitch-text::after {\n          left: -1px;\n          text-shadow: 1px 0 rgba(0, 0, 255, 0.7);\n          animation: glitch-anim-2 3s infinite linear alternate-reverse;\n        }\n      `}</style>\n      <div className=\"mt-1 flex items-center gap-1.5 overflow-hidden\">\n        <div className=\"w-1 h-3 bg-red-500 shrink-0\" />\n        <span\n          className=\"glitch-text text-xs tracking-wider uppercase\"\n          data-text={message}\n        >\n          {message}\n        </span>\n      </div>\n    </>\n  );\n}\n\n// ==========================================\n// FORM FIELD WRAPPER\n// ==========================================\nexport function FormField({\n  label,\n  error,\n  errorVariant = \"glitch\",\n  children,\n}: {\n  label?: string;\n  error?: string;\n  errorVariant?: \"default\" | \"glitch\";\n  children: ReactNode;\n}) {\n  return (\n    <div className=\"flex flex-col gap-1.5 w-full\">\n      {label && (\n        <label\n          className={cn(\n            \"text-sm font-semibold\",\n            error ? \"text-red-500\" : \"text-foreground\",\n          )}\n        >\n          {label}\n        </label>\n      )}\n      {children}\n      <FormError message={error} variant={errorVariant} />\n    </div>\n  );\n}\n\n// ==========================================\n// SUBMIT BUTTON (Matches Theme)\n// ==========================================\nexport function FormSubmit({\n  children,\n  isSubmitting = false,\n}: {\n  children: ReactNode;\n  isSubmitting?: boolean;\n}) {\n  return (\n    <button\n      type=\"submit\"\n      disabled={isSubmitting}\n      className=\"relative w-full overflow-hidden rounded-xl font-semibold text-[var(--theme-color-foreground)] shadow-lg transition-transform active:scale-95 disabled:opacity-70 disabled:cursor-not-allowed group mt-2\"\n    >\n      <div className=\"absolute inset-0 bg-[var(--theme-color)] transition-opacity group-hover:opacity-90\" />\n      <div className=\"relative px-6 py-3.5 flex items-center justify-center gap-2 w-full h-full\">\n        {isSubmitting ? (\n          <div className=\"w-5 h-5 border-2 border-[var(--theme-color-foreground)]/30 border-t-[var(--theme-color-foreground)] rounded-full animate-spin\" />\n        ) : (\n          children\n        )}\n      </div>\n    </button>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/custom/blocks/FormBlock.tsx"
    },
    {
      "path": "src/components/custom/blocks/form-theme.ts",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nconst hexToRgb = (hex: string) => {\n  let cleanHex = hex.replace(/^#/, \"\");\n  if (cleanHex.length === 3)\n    cleanHex = cleanHex\n      .split(\"\")\n      .map((x) => x + x)\n      .join(\"\");\n  const result = /^([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(cleanHex);\n  return result\n    ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}`\n    : \"99, 102, 241\";\n};\n\nexport const getThemeStyles = (color: string) => {\n  const isHex = color && color.startsWith(\"#\");\n  const rgb = isHex ? hexToRgb(color) : \"99, 102, 241\"; // Default indigo rgb\n\n  // Map standard colors to tailwind variables if not hex\n  const colorMap: Record<string, string> = {\n    primary: \"var(--primary)\",\n    indigo: \"#4f46e5\",\n    rose: \"#e11d48\",\n    emerald: \"#10b981\",\n    zinc: \"#18181b\",\n  };\n\n  const finalColor = isHex ? color : colorMap[color] || colorMap.primary;\n  // \"primary\" tracks the site theme (can be light or dark), so its\n  // button text must track --primary-foreground instead of a fixed white.\n  const finalForeground =\n    !isHex && color === \"primary\" ? \"var(--primary-foreground)\" : \"#ffffff\";\n\n  return {\n    style: {\n      \"--theme-color\": finalColor,\n      \"--theme-color-rgb\": isHex ? rgb : hexToRgb(finalColor),\n      \"--theme-color-foreground\": finalForeground,\n    } as React.CSSProperties,\n  };\n};\n",
      "type": "registry:component",
      "target": "components/custom/blocks/form-theme.ts"
    }
  ],
  "docs": "Named exports: FormWrapper, FormField, FormError, FormSubmit, getThemeStyles (from form-theme.ts, which installs alongside). Layout and styling only — bring your own state, via react-hook-form or otherwise. FormSubmit takes `isSubmitting` to render its pending state.",
  "categories": [
    "forms"
  ],
  "type": "registry:component"
}
