{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "skeleton-text",
  "title": "Text skeleton",
  "description": "Styled skeleton: the Text set. 5 decorative skeleton variations (LineSkeleton, ParagraphSkeleton, HeadingSkeleton, MixedSkeleton, CaptionSkeleton) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/skeleton-text.tsx",
      "content": "\"use client\"\n\nimport type * as React from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Text skeleton family: 5 text-line layouts. The difference is not in the ANIMATION but in the STRUCTURE - the line count, the line height and realistic varying widths. The widths are picked by index from a FIXED list (NO Math.random, deterministic SSR). Colour comes ONLY from tokens. Skeletons are decorative: the root is aria-hidden. The pulse stops under reduced motion. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\ntype Props = React.ComponentProps<\"div\"> & { size?: StyledSize }\n\n/* Shared calm pulse (framer-motion, NO keyframes). */\nfunction Block({ className, delay = 0 }: { className?: string; delay?: number }) {\n  const reduce = useReducedMotion()\n  return (\n    <motion.span\n      className={cn(\"block rounded-md bg-secondary\", className)}\n      animate={reduce ? undefined : { opacity: [0.5, 1, 0.5] }}\n      transition={\n        reduce ? undefined : { duration: 1.6, ease: \"easeInOut\", repeat: Infinity, delay }\n      }\n    />\n  )\n}\n\n/* Govde satiri yuksekligi. */\nconst line: Record<StyledSize, string> = {\n  sm: \"h-2.5\",\n  md: \"h-3\",\n  lg: \"h-3.5\",\n  xl: \"h-4\",\n}\n\n/* Baslik cubugu yuksekligi. */\nconst head: Record<StyledSize, string> = {\n  sm: \"h-4\",\n  md: \"h-5\",\n  lg: \"h-6\",\n  xl: \"h-8\",\n}\n\nconst gap: Record<StyledSize, string> = {\n  sm: \"gap-2\",\n  md: \"gap-2.5\",\n  lg: \"gap-3\",\n  xl: \"gap-3.5\",\n}\n\n/* A realistic paragraph rhythm: a fixed width cycle with a short last line. */\nconst widths = [\"w-full\", \"w-11/12\", \"w-full\", \"w-10/12\", \"w-11/12\", \"w-9/12\"]\n\nconst root = \"flex w-full flex-col\"\n\n/* Line: tek metin satiri. */\nexport function LineSkeleton({ className, size = \"md\", ...props }: Props) {\n  return (\n    <div\n      data-slot=\"styled-skeleton\"\n      aria-hidden=\"true\"\n      className={cn(root, className)}\n      {...props}\n    >\n      <Block className={cn(\"w-full\", line[size])} />\n    </div>\n  )\n}\n\n/* Paragraph: an n-line block whose last line is cut short. */\nexport function ParagraphSkeleton({\n  className,\n  size = \"md\",\n  lines = 4,\n  ...props\n}: Props & { lines?: number }) {\n  return (\n    <div\n      data-slot=\"styled-skeleton\"\n      aria-hidden=\"true\"\n      className={cn(root, gap[size], className)}\n      {...props}\n    >\n      {Array.from({ length: lines }, (_, i) => (\n        <Block\n          key={i}\n          delay={i * 0.12}\n          className={cn(line[size], i === lines - 1 ? \"w-3/5\" : widths[i % widths.length])}\n        />\n      ))}\n    </div>\n  )\n}\n\n/* Heading: kalin baslik cubugu + altinda tek destek satiri. */\nexport function HeadingSkeleton({ className, size = \"md\", ...props }: Props) {\n  return (\n    <div\n      data-slot=\"styled-skeleton\"\n      aria-hidden=\"true\"\n      className={cn(root, gap[size], className)}\n      {...props}\n    >\n      <Block className={cn(\"w-2/3 rounded-lg\", head[size])} />\n      <Block className={cn(\"w-2/5\", line[size])} delay={0.12} />\n    </div>\n  )\n}\n\n/* Mixed: baslik + paragraf + kisa alt not, tek blokta gercekci makale ritmi. */\nexport function MixedSkeleton({ className, size = \"md\", ...props }: Props) {\n  return (\n    <div\n      data-slot=\"styled-skeleton\"\n      aria-hidden=\"true\"\n      className={cn(root, gap[size], className)}\n      {...props}\n    >\n      <Block className={cn(\"w-1/2 rounded-lg\", head[size])} />\n      {widths.slice(0, 3).map((w, i) => (\n        <Block key={w} delay={(i + 1) * 0.12} className={cn(w, line[size])} />\n      ))}\n      <Block className={cn(\"w-1/4\", line[size])} delay={0.48} />\n    </div>\n  )\n}\n\n/* Caption: tek kisa alt yazi satiri (yari opak, ikincil metin gibi). */\nexport function CaptionSkeleton({ className, size = \"md\", ...props }: Props) {\n  return (\n    <div\n      data-slot=\"styled-skeleton\"\n      aria-hidden=\"true\"\n      className={cn(root, className)}\n      {...props}\n    >\n      <Block\n        className={cn(\n          \"w-2/5 bg-[color-mix(in_oklab,var(--color-secondary)_70%,transparent)]\",\n          line[size]\n        )}\n      />\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/skeleton-text.tsx"
    }
  ],
  "categories": [
    "styled",
    "skeleton"
  ],
  "type": "registry:component"
}