{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "textarea",
  "type": "registry:ui",
  "title": "Textarea",
  "description": "@interlace/ui — textarea primitive (shadcn-compatible).",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "theme"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/textarea.tsx",
      "target": "components/ui/textarea.tsx",
      "type": "registry:ui",
      "content": "/**\n * @interlace/ui — Textarea\n *\n * Multi-line text input. A pure surface primitive with no controlled-state\n * machinery: the native `<textarea>` already owns selection, IME, undo/redo,\n * `onChange` etc., so we add structure (size + tone) without wrapping a\n * single native behaviour. Server component — no hooks, no Base UI client\n * surface required. Inherits the DS font from `preflight.css` (rule #3).\n *\n * ## Anatomy\n *\n *   <textarea data-slot=\"textarea\" data-min-viewport=\"320\">\n *     <!-- user content -->\n *   </textarea>\n *\n * ## MIN_VIEWPORT — 320\n *\n * Form controls are the LAST thing that may degrade on a narrow screen; a\n * sign-in / contact / search form must work on a 320 CSS-px iPhone SE. The\n * `sm` size at min-h 64px clears the WCAG 2.5.5 target-size floor on that\n * viewport (TARGET_SIZE_AA in DESIGN_PRINCIPLES #11).\n *\n * | Rule | Concept                          | Where in this file                                          |\n * | ---- | -------------------------------- | ----------------------------------------------------------- |\n * | R4   | Extends native el + VariantProps | `React.ComponentProps<'textarea'> & VariantProps<...>`      |\n * | R6   | data-slot on the part            | `data-slot=\"textarea\"` + data-size / data-tone              |\n * | R7   | className merged + ...rest       | `cn(textareaVariants(...), className)` + `{...props}`       |\n * | R8   | No `isXxx`; enums for >2 states  | `size` / `tone` / `resize` are enums                        |\n * | R9   | Native onChange stays native     | `onChange` / `onFocus` etc. pass through `{...props}`       |\n * | R14  | Declares min viewport            | `data-min-viewport={String(MIN_VIEWPORT)}` + exported const |\n * | R18  | Tailwind only                    | zero inline `style`; cva classes only                       |\n * | R19  | Tokens only                      | padding from `--spacing-*`, size from `--text-*`, radius `--radius-md`, colors via semantic tokens |\n * | R20  | AA contrast                      | `border-input` / `aria-invalid:border-destructive`, placeholder uses `--muted-foreground` (preflight) |\n * | R25  | Server component                 | no hooks → no `'use client'`                                |\n * | R26  | A11y from native el              | `<textarea>` owns focus, keyboard, ARIA, label association  |\n */\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\n\nimport { cn } from '@/lib/utils';\n\n/**\n * Minimum viable viewport (CSS px) for this primitive. Below it, the\n * preflight contract draws a dev-mode outline; in prod the component still\n * renders. Exported so consumers / tests can read it.\n */\nexport const MIN_VIEWPORT = 320 as const;\n\nconst textareaVariants = cva(\n  // Base — surface, border, radius, focus + invalid rings, disabled.\n  [\n    'block w-full min-w-0 border border-input bg-transparent',\n    'rounded-md shadow-xs outline-none transition-[color,box-shadow]',\n    'placeholder:text-muted-foreground',\n    'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n    'aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40',\n    'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',\n    'dark:bg-input/30',\n  ].join(' '),\n  {\n    variants: {\n      /**\n       * Padding + type-scale density. min-h is grounded in the\n       * `--spacing-xl` scale (xl=64px, 2xl=96px; lg doubles xl via calc\n       * to stay token-anchored — no raw px literal).\n       */\n      size: {\n        sm: 'px-sm py-xs text-ui-sm min-h-xl',\n        md: 'px-sm py-xs text-ui min-h-2xl',\n        lg: 'px-md py-sm text-body min-h-[calc(var(--spacing-xl)*2)]',\n      },\n      /** Border tone. `invalid` is an explicit prop AND a fallback for `aria-invalid`. */\n      tone: {\n        default: '',\n        invalid: 'border-destructive',\n      },\n      /** Resize affordance. Vertical-only by default; opt out for fixed-height fields. */\n      resize: {\n        y: 'resize-y',\n        none: 'resize-none',\n      },\n    },\n    defaultVariants: {\n      size: 'md',\n      tone: 'default',\n      resize: 'y',\n    },\n  },\n);\n\ninterface TextareaProps\n  extends\n    Omit<React.ComponentProps<'textarea'>, 'size'>,\n    VariantProps<typeof textareaVariants> {}\n\n/** Multi-line text input. Server component (no hooks). */\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n  ({ className, size, tone, resize, ...props }, ref) => {\n    return (\n      // This primitive doesn't own accessible naming; consumers pair it\n      // with <Label htmlFor> (or their own aria-label) one level up, same\n      // as shadcn's Textarea.\n      // eslint-disable-next-line react-a11y/control-has-associated-label, react-a11y/no-missing-aria-labels\n      <textarea\n        ref={ref}\n        data-slot=\"textarea\"\n        data-min-viewport={String(MIN_VIEWPORT)}\n        data-size={size ?? undefined}\n        data-tone={tone ?? undefined}\n        className={cn(textareaVariants({ size, tone, resize }), className)}\n        {...props}\n      />\n    );\n  },\n);\nTextarea.displayName = 'Textarea';\n\nexport { Textarea, textareaVariants };\nexport type { TextareaProps };\n"
    }
  ]
}
