{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "skeleton",
  "type": "registry:ui",
  "title": "Skeleton",
  "description": "Fluent 2-styled loading placeholder: a pulsing bg-accent block with rounded-md corners, resized/reshaped via className (line, avatar circle, card block).",
  "author": "graundtech <https://github.com/graundtech/fluent2-react-kit>",
  "registryDependencies": [
    "https://fluent2-react-kit.graund.io/r/utils.json"
  ],
  "files": [
    {
      "path": "components/ui/skeleton.tsx",
      "type": "registry:ui",
      "target": "components/ui/skeleton.tsx",
      "content": "import type { ComponentProps } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Skeleton — Fluent 2-styled, shadcn-API loading placeholder.\n *\n * A single `<div>` with no `cva` table (conventions §2 allows this for\n * leaf/static components — mirrors `Separator`): there is one visual\n * treatment, and every shape variation (line, avatar circle, button block,\n * card) is just a `className` override on width/height/radius, not a prop.\n *\n * **Fill color — `bg-secondary`, not `bg-accent`.** Current shadcn ships\n * `bg-accent animate-pulse rounded-md`\n * (https://ui.shadcn.com/docs/components/skeleton); this kit deliberately\n * swaps in `bg-secondary` after weighing all three themes against `tokens.css`:\n * - Dark theme: `--accent` and `--secondary` are the *same* hex (`#333333`,\n *   grey[20]) — no visual difference at all.\n * - Light theme: `--secondary` is grey[96] (`#f5f5f5`) vs. `--accent`'s\n *   grey[94] (`#f0f0f0`) — a hair lighter, i.e. marginally less contrast\n *   against the white `--background`. The delta (96 vs 94) is negligible; a\n *   skeleton still reads clearly as \"content pending\" at either value.\n * - High contrast (`.high-contrast`): the deciding case. `--accent` maps to\n *   the system `Highlight` keyword (selection semantics), so an inert\n *   placeholder painted in `bg-accent` lights up in the OS *selection* color\n *   and reads as \"this is selected\" — wrong. `--secondary` maps to\n *   `ButtonFace`, a neutral surface, so `bg-secondary` stays a plain neutral\n *   block in HC. A component can't theme-scope its own class (there's no\n *   `high-contrast:` variant to swap the fill only in that theme), so the\n *   right call is the token that stays neutral in *all three* themes —\n *   `--secondary`, not `--accent`.\n * `bg-muted` was also considered and rejected: it matches `--secondary` in\n * light mode but drops to grey[12] (`#1f1f1f`) in dark mode — visibly\n * dimmer than the `#242424` `--background` it sits on, i.e. *less* visible\n * exactly where contrast matters most.\n *\n * **Animation — the Fluent wave shimmer (`animate-shimmer`).** Fluent 2's\n * Skeleton uses a moving gradient \"wave\" sweep\n * (https://storybooks.fluentui.dev/react/?path=/docs/components-skeleton--docs),\n * not shadcn's opacity pulse. The keyframes live in the token layer\n * (`tokens.css` `--animate-shimmer`, sweeping `background-position` across a\n * 200%-wide gradient); this file paints the gradient — `--secondary` base\n * with an `--input`-grey crest, both theme-aware (the secondary/accent pair\n * was rejected for the crest because those two are the SAME hex in dark\n * mode, which would erase the wave exactly where it's hardest to see).\n *\n * **Reduced motion — `motion-reduce:animate-none`, full stop (not a slowed\n * animation).** This deliberately *contrasts* with `Spinner`, which slows to\n * `animation-duration: 3s` under `prefers-reduced-motion` instead of\n * stopping outright. The difference is what each animation communicates:\n * `Spinner`'s spin is the *only* signal that a background operation is still\n * in progress — freezing it would erase that information for a\n * reduced-motion user (its `role=\"status\"` region only helps assistive tech,\n * not sighted users). `Skeleton` carries no such information: it is a static\n * placeholder shape whether or not it pulses, and content either has loaded\n * or hasn't — nothing is lost by holding it still, and a persistently\n * animating region is exactly what `prefers-reduced-motion` asks apps to\n * avoid. `motion-reduce:animate-none` is a plain Tailwind utility, not a\n * `duration-*` token, so it isn't covered by the automatic duration collapse\n * `tokens.css` already applies — it has to be declared per animated\n * component (see `Spinner`'s doc comment for the same note).\n *\n * **Accessibility — no `aria-hidden` here, by design.** A lone `<Skeleton>`\n * is inert markup (a styled `<div>`, no text, no role) that assistive tech\n * already has nothing to announce for, so forcing `aria-hidden=\"true\"` on\n * every instance would be redundant — and current shadcn doesn't do it\n * either. Instead, wrap the *loading region* (the parent containing one or\n * more `Skeleton`s standing in for real content) in `aria-busy=\"true\"` —\n * optionally with `aria-live=\"polite\"` if the region should announce when\n * loading completes and real content replaces the skeletons. That's a\n * consumer-side decision (it depends on what the region becomes once\n * loaded), so it isn't baked into this component. Verified with axe: a\n * skeleton \"card\" composition (avatar circle + text lines) inside an\n * `aria-busy=\"true\"` wrapper produces no violations — see\n * `skeleton.test.tsx`.\n *\n * Server-safe: no `\"use client\"`, no hooks — the React import is type-only,\n * so this drops straight into an RSC tree.\n */\nfunction Skeleton({ className, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"skeleton\"\n      className={cn(\n        // Fluent wave: a 200%-wide gradient (secondary base, input-grey crest)\n        // swept by the token-layer shimmer keyframes. bg-secondary stays as\n        // the fallback fill (and the forced-colors surface, where gradients\n        // are stripped by forced-color-adjust).\n        \"animate-shimmer rounded-md bg-secondary motion-reduce:animate-none\",\n        \"bg-[linear-gradient(90deg,var(--secondary)_25%,var(--input)_50%,var(--secondary)_75%)] bg-[length:200%_100%]\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { Skeleton };\n"
    }
  ]
}
