{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "rating",
  "type": "registry:ui",
  "title": "Rating",
  "description": "Interactive star rating primitive with hover preview and keyboard support.",
  "dependencies": [
    "clsx",
    "tailwind-merge",
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/ui/rating.tsx",
      "content": "import * as React from 'react'\nimport { Star } from 'lucide-react'\nimport { cn } from '@/lib/utils'\n\nexport interface RatingProps {\n  value?: number\n  defaultValue?: number\n  count?: number\n  onChange?: (value: number) => void\n  readOnly?: boolean\n  size?: 'sm' | 'md' | 'lg'\n  className?: string\n  ariaLabel?: string\n}\n\nexport function Rating({\n  value,\n  defaultValue = 0,\n  count = 5,\n  onChange,\n  readOnly = false,\n  size = 'md',\n  className,\n  ariaLabel = 'Rating',\n}: RatingProps) {\n  const isControlled = value !== undefined\n  const [internal, setInternal] = React.useState(defaultValue)\n  const [hovered, setHovered] = React.useState(0)\n  const current = isControlled ? (value as number) : internal\n\n  const displayed = hovered || current\n\n  const handleSelect = (index: number) => {\n    if (readOnly) return\n    const next = index + 1\n    if (!isControlled) setInternal(next)\n    onChange?.(next)\n  }\n\n  const sizeClass = {\n    sm: 'h-5 w-5',\n    md: 'h-7 w-7',\n    lg: 'h-9 w-9',\n  }[size]\n\n  return (\n    <div\n      role=\"radiogroup\"\n      aria-label={ariaLabel}\n      aria-readonly={readOnly || undefined}\n      className={cn('inline-flex items-center gap-1', className)}\n    >\n      {Array.from({ length: count }).map((_, index) => {\n        const filled = index < displayed\n        return (\n          <button\n            key={index}\n            type=\"button\"\n            role=\"radio\"\n            aria-checked={index < current}\n            aria-label={`${index + 1} star${index === 0 ? '' : 's'}${readOnly ? '' : ` out of ${count}`}`}\n            disabled={readOnly}\n            onClick={() => handleSelect(index)}\n            onMouseEnter={() => !readOnly && setHovered(index + 1)}\n            onMouseLeave={() => setHovered(0)}\n            className={cn(\n              'transition-transform cursor-pointer disabled:cursor-default',\n              !readOnly && 'hover:scale-125 active:scale-95',\n              'focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2'\n            )}\n          >\n            <Star\n              className={cn(\n                sizeClass,\n                'transition-colors',\n                filled ? 'fill-rk-primary stroke-rk-ink stroke-2' : 'fill-rk-surface stroke-rk-ink stroke-2'\n              )}\n              aria-hidden=\"true\"\n            />\n          </button>\n        )\n      })}\n    </div>\n  )\n}\n",
      "type": "registry:ui",
      "target": "components/ui/rating.tsx"
    }
  ]
}