{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "skeleton",
  "title": "Skeleton",
  "description": "Zero-CLS loading placeholder using screentone dot fill and stepped 12fps cadence across block, line, circle, metric, and panel variants.",
  "registryDependencies": [
    "https://frame12.dev/r/frame12-styles.json"
  ],
  "files": [
    {
      "path": "ui/skeleton.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\n\nexport type SkeletonVariant = 'block' | 'line' | 'circle' | 'metric' | 'panel';\nexport type SkeletonDensity = 'comfortable' | 'compact';\n\nexport interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {\n  variant?: SkeletonVariant;\n  width?: number | string;\n  height?: number | string;\n  animated?: boolean;\n  density?: SkeletonDensity;\n  label?: string;\n}\n\nexport const Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(function Skeleton(\n  {\n    variant = 'block',\n    width,\n    height,\n    animated = true,\n    density = 'comfortable',\n    label,\n    role: userRole,\n    'aria-hidden': userAriaHidden,\n    'aria-label': userAriaLabel,\n    'aria-busy': userAriaBusy,\n    style,\n    className = '',\n    children,\n    ...rest\n  },\n  ref,\n) {\n  const isCompact = density === 'compact';\n  const hasLabel = Boolean(label || userAriaLabel);\n  const resolvedRole = userRole ?? (hasLabel ? 'status' : undefined);\n  const resolvedAriaBusy = userAriaBusy ?? (hasLabel ? 'true' : undefined);\n  const resolvedAriaHidden =\n    userAriaHidden !== undefined ? userAriaHidden : hasLabel ? undefined : 'true';\n\n  const resolvedStyle: React.CSSProperties = {\n    ...(width !== undefined\n      ? { width: typeof width === 'number' ? `${width}px` : width }\n      : undefined),\n    ...(height !== undefined\n      ? { height: typeof height === 'number' ? `${height}px` : height }\n      : undefined),\n    ...style,\n  };\n\n  const srText = label ?? userAriaLabel;\n\n  return (\n    <div\n      ref={ref}\n      role={resolvedRole}\n      aria-busy={resolvedAriaBusy}\n      aria-label={srText}\n      aria-hidden={resolvedAriaHidden}\n      data-variant={variant}\n      data-animated={animated ? 'true' : 'false'}\n      data-density={isCompact ? 'compact' : undefined}\n      style={resolvedStyle}\n      className={`f12-skeleton f12-skeleton-${variant}${animated ? ' f12-skeleton-animated' : ''}${isCompact ? ' f12-skeleton-compact' : ''}${className ? ` ${className}` : ''}`}\n      {...rest}\n    >\n      {children ?? (srText ? <span className=\"f12-sr-only\">{srText}</span> : null)}\n    </div>\n  );\n});\n\nexport interface SkeletonTextProps extends React.HTMLAttributes<HTMLDivElement> {\n  lines?: number;\n  lastLineWidth?: number | string;\n  animated?: boolean;\n  density?: SkeletonDensity;\n  label?: string;\n}\n\nexport const SkeletonText = React.forwardRef<HTMLDivElement, SkeletonTextProps>(\n  function SkeletonText(\n    {\n      lines = 3,\n      lastLineWidth = '65%',\n      animated = true,\n      density = 'comfortable',\n      label,\n      role: userRole,\n      'aria-hidden': userAriaHidden,\n      'aria-label': userAriaLabel,\n      'aria-busy': userAriaBusy,\n      className = '',\n      ...rest\n    },\n    ref,\n  ) {\n    const isCompact = density === 'compact';\n    const safeLines = Math.max(1, lines);\n    const hasLabel = Boolean(label || userAriaLabel);\n    const resolvedRole = userRole ?? (hasLabel ? 'status' : undefined);\n    const resolvedAriaBusy = userAriaBusy ?? (hasLabel ? 'true' : undefined);\n    const resolvedAriaHidden =\n      userAriaHidden !== undefined ? userAriaHidden : hasLabel ? undefined : 'true';\n\n    const srText = label ?? userAriaLabel;\n\n    return (\n      <div\n        ref={ref}\n        role={resolvedRole}\n        aria-busy={resolvedAriaBusy}\n        aria-label={srText}\n        aria-hidden={resolvedAriaHidden}\n        data-density={isCompact ? 'compact' : undefined}\n        className={`f12-skeleton-text${isCompact ? ' f12-skeleton-text-compact' : ''}${className ? ` ${className}` : ''}`}\n        {...rest}\n      >\n        {srText && <span className=\"f12-sr-only\">{srText}</span>}\n        {Array.from({ length: safeLines }, (_, idx) => {\n          const isLast = safeLines > 1 && idx === safeLines - 1;\n          return (\n            <Skeleton\n              key={idx}\n              variant=\"line\"\n              width={isLast ? lastLineWidth : '100%'}\n              animated={animated}\n              density={density}\n              aria-hidden=\"true\"\n            />\n          );\n        })}\n      </div>\n    );\n  },\n);\n",
      "type": "registry:ui",
      "target": "components/frame12/skeleton.tsx"
    }
  ],
  "type": "registry:ui"
}