{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "skeleton",
  "type": "registry:ui",
  "title": "Skeleton",
  "description": "A React skeleton loader for Next.js and Tailwind CSS: placeholder blocks while content loads, with a calm sheen that sweeps every skeleton in step.",
  "author": "FewTone <https://fewtone.com>",
  "dependencies": [
    "motion@^14.0.0"
  ],
  "registryDependencies": [
    "https://ui.fewtone.com/r/tokens.json",
    "https://ui.fewtone.com/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/fewtone/ui/skeleton.tsx",
      "type": "registry:ui",
      "content": "import { SkeletonSheen } from \"@/components/ui/skeleton-sheen\";\nimport { cn } from \"@/lib/utils\";\nimport type { ComponentProps } from \"react\";\n\n/*\n * A placeholder block while content loads (owner's roadmap, 2026-10-16), with shadcn's API: one `div` you shape\n * with classes (`h-4 w-40`, `size-10 rounded-full`).\n * - A muted block with a calm sheen sweeping across it: one neutral hue, a step lighter, never a colour. The sheen\n *   lives in SkeletonSheen, a small client part, so Skeleton stays a server component, which is where loading\n *   states usually render (loading.tsx, Suspense fallbacks).\n * - Every skeleton on the page sweeps in step, however late it mounted (see skeleton-sheen.tsx).\n * - With reduced motion it's a still, muted block.\n * - Hidden from screen readers (aria-hidden): a grey block says nothing. Mark the region that's loading with\n *   aria-busy=\"true\" and a visually hidden \"Loading…\" instead (the demos do).\n */\n\nfunction Skeleton({ className, children, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"skeleton\"\n      aria-hidden\n      className={cn(\"relative overflow-hidden rounded-md bg-muted\", className)}\n      {...props}\n    >\n      {children}\n      <SkeletonSheen />\n    </div>\n  );\n}\n\nexport { Skeleton };\n"
    },
    {
      "path": "registry/fewtone/ui/skeleton-sheen.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { duration, ease } from \"@/lib/motion\";\nimport { useReducedMotion } from \"motion/react\";\nimport { useEffect, useRef } from \"react\";\n\n/*\n * The light sweeping across a Skeleton. The only client part of Skeleton, so Skeleton stays server-safe.\n * - How: a band (a gradient one step lighter than the block, fading out at both sides) sits just outside the\n *   block and slides across it. Only `translate` moves, so the browser composites it on its own.\n * - In step: every sheen counts from the page's start (startTime 0 on the document timeline), so skeletons that\n *   mount at different moments still sweep together, as one loading surface.\n * - The pace: a 1.5s sweep, then a 0.5s rest, on ease.inOut. Calm, not a flicker.\n * - With reduced motion nothing moves: a still, muted block. Before hydration it's still too; the sweep starts\n *   once React runs.\n * - The Web Animations API directly, rather than motion: motion doesn't expose an animation's start time, and\n *   that's what keeps every sheen in step.\n */\n\nconst period = duration.slower * 4 * 1000;\n/** The share of each period spent sweeping; the rest is a pause */\nconst sweep = 0.75;\n\nfunction SkeletonSheen() {\n  const reduce = useReducedMotion() ?? false;\n  const ref = useRef<HTMLSpanElement>(null);\n\n  useEffect(() => {\n    const node = ref.current;\n    if (!node || reduce || typeof node.animate !== \"function\") return;\n    // From the start of the line in either writing direction\n    const [from, to] = getComputedStyle(node).direction === \"rtl\" ? [\"100%\", \"-100%\"] : [\"-100%\", \"100%\"];\n    const animation = node.animate(\n      [\n        { translate: from, easing: `cubic-bezier(${ease.inOut.join(\", \")})` },\n        { translate: to, offset: sweep },\n        { translate: to },\n      ],\n      { duration: period, iterations: Number.POSITIVE_INFINITY },\n    );\n    animation.startTime = 0;\n    return () => animation.cancel();\n  }, [reduce]);\n\n  return (\n    <span\n      ref={ref}\n      aria-hidden\n      data-slot=\"skeleton-sheen\"\n      className=\"pointer-events-none absolute inset-0 -translate-x-full bg-linear-to-r from-transparent via-background/60 to-transparent rtl:translate-x-full dark:via-foreground/5\"\n    />\n  );\n}\n\nexport { SkeletonSheen };\n"
    }
  ],
  "categories": [
    "feedback"
  ],
  "docs": "Usage and examples: https://ui.fewtone.com/components/skeleton"
}
