{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "skeleton-variants",
  "type": "registry:ui",
  "title": "Skeleton Variants",
  "description": "Single source of truth for the `<Skeleton variant>` discriminated union. Every entry maps to a Tailwind shape class set that paints a placeholder matching the corresponding primitive / pattern / template.",
  "author": "ofri-peretz <https://github.com/ofri-peretz>",
  "categories": [
    "feedback",
    "primitive"
  ],
  "dependencies": [],
  "registryDependencies": [
    "https://ds.interlace.tools/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/skeleton-variants.ts",
      "target": "components/ui/skeleton-variants.ts",
      "type": "registry:ui",
      "content": "export const SKELETON_VARIANTS = [\n  // ── Generic shapes (no specific primitive — reusable) ────────────────\n  'rect',\n  'circle',\n  'text',\n  'paragraph',\n\n  // ── Primitive-shaped (matches a primitive's resting silhouette) ──────\n  'avatar',\n  'badge',\n  'breadcrumb',\n  'button',\n  'card',\n  'code-block',\n  'input',\n  'menu',\n  'pagination',\n  'prose',\n  'tabs',\n  'tag',\n  'toc',\n\n  // ── Form family (Phase 1.1) ──────────────────────────────────────────\n  // One variant per form primitive so a loading form reserves the exact\n  // resting silhouette of the control it replaces — CLS=0 (R23). The\n  // coverage assertion in `skeleton-variant-coverage-lock.test.ts` pins\n  // this list to the primitives directory, so a new form control cannot\n  // ship without its placeholder.\n  'checkbox',\n  'form',\n  'label',\n  'number-field',\n  'radio-group',\n  'select',\n  'slider',\n  'switch',\n  'textarea',\n\n  // ── Pattern-shaped (matches a pattern's full layout) ─────────────────\n  'article-card',\n  'author-byline',\n  // A table's loading state is the one most often left as a centred spinner,\n  // which reserves nothing: the body arrives, the page grows by 300px, and\n  // whatever the reader was aiming at moves. Composite (header row + body\n  // rows) — see skeleton.tsx.\n  'data-table',\n  'newsletter-form',\n  'page-header',\n  'prev-next-post',\n  'stat-card',\n\n  // ── Chart-shaped (Phase 5) ───────────────────────────────────────────\n  // A loading chart is the most common state a data surface has — the data\n  // is always in flight on first paint — and it is the one most often left\n  // as a spinner, which reserves nothing and guarantees a layout shift the\n  // moment the series arrives. Each variant reserves the real silhouette:\n  // `chart` an axis + plot box, `metric-table` a header row plus rows,\n  // `sparkline` the exact inline 90×22 cell so a table does not reflow.\n  'chart',\n  'metric-table',\n  'sparkline',\n\n  // ── Absence-vocabulary shapes (Phase 10.2 / 10.3) ────────────────────\n  // Both reserve the resting silhouette of a surface whose data is ALWAYS\n  // in flight on first paint. `stat-strip` is a composite (label + value\n  // pairs across a grid); `meter` is a label row plus a track, and its\n  // `count` prop is what a RankedBarList reserves rows with.\n  'meter',\n  'stat-strip',\n\n  // ── Template-shaped (matches a template's full-page layout) ──────────\n  // Added by Phase 4 when the templates land.\n] as const;\n\nexport type SkeletonVariant = (typeof SKELETON_VARIANTS)[number];\n\n/**\n * Shape class map. Each variant gets the Tailwind utility chain that\n * paints its silhouette. Values use DS tokens (`rounded-md` / `h-9` /\n * `gap-md`) — no raw px / hex. The Skeleton root always adds\n * `animate-pulse bg-muted` so these only need to declare layout.\n *\n * For COMPOSITE variants (card, article-card, author-byline, ...) the\n * class string is the OUTER container; the Skeleton component reads a\n * separate `<X>InnerSkeleton` JSX block per variant — see\n * `./skeleton.tsx`.\n */\nexport const SKELETON_VARIANT_CLASSES: Record<SkeletonVariant, string> = {\n  // Generic\n  rect: 'h-4 w-full rounded-md',\n  circle: 'size-9 rounded-full',\n  text: 'h-4 w-2/3 rounded-sm',\n  paragraph: 'h-4 w-full rounded-sm',\n\n  // Primitive-shaped\n  avatar: 'size-9 rounded-full',\n  badge: 'h-5 w-16 rounded-full',\n  breadcrumb: 'h-5 w-full rounded-sm',\n  button: 'h-9 w-24 rounded-md',\n  card: 'h-32 w-full rounded-lg',\n  'code-block': 'h-40 w-full rounded-md',\n  input: 'h-9 w-full rounded-md',\n  // Overlay/nav surfaces (wave 1.2). `menu` matches the DropdownMenu /\n  // ContextMenu popup silhouette; `pagination` the nav row; `tabs` the\n  // list + panel; `toc` the indented heading rail.\n  // max-w-full: `menu` is the only variant with a fixed inline size. Without\n  // the cap its 224px cannot shrink (flex items floor at min-content), so the\n  // placeholder overflows any slot narrower than itself — e.g. a 375px phone.\n  menu: 'h-40 w-56 max-w-full rounded-md border',\n  pagination: 'h-9 w-full rounded-md',\n  prose: 'h-4 w-full rounded-sm',\n  tabs: 'h-32 w-full rounded-lg',\n  tag: 'h-5 w-12 rounded-full',\n  toc: 'h-40 w-full rounded-md',\n\n  // Form family — silhouettes match the resting control geometry exactly.\n  //   checkbox / radio dot   size-4   (16px, per checkbox.tsx / radio-group.tsx)\n  //   switch track           h-5 w-8  (the 1.15rem track rounds to h-5 here)\n  //   select trigger / input h-9      (36px control height)\n  //   slider rail            h-2      (per slider.tsx SliderTrack)\n  //   textarea md            min-h-2xl (per textarea.tsx size=md)\n  checkbox: 'size-4 rounded-sm',\n  label: 'h-4 w-24 rounded-sm',\n  'number-field': 'h-9 w-32 rounded-md',\n  select: 'h-9 w-full rounded-md',\n  slider: 'h-2 w-full rounded-full',\n  switch: 'h-5 w-8 rounded-full',\n  textarea: 'min-h-2xl w-full rounded-md',\n  // Composite (inner JSX in skeleton.tsx): a field group / option list.\n  form: 'w-full rounded-md',\n  'radio-group': 'w-full rounded-md',\n\n  // Pattern-shaped (outer; composite inner rendered in skeleton.tsx)\n  'article-card': 'h-72 w-full rounded-xl',\n  'author-byline': 'h-12 w-full rounded-md',\n  // Bordered like the real DataTable scroll box, so the swap changes the\n  // contents of the frame and not the frame itself.\n  'data-table': 'w-full rounded-md border border-border',\n  'newsletter-form': 'h-32 w-full rounded-md',\n  'page-header': 'h-20 w-full rounded-md',\n  // Transparent + unrounded on purpose: this variant paints TWO cards side\n  // by side, so the shape lives entirely in the composite body. A filled\n  // root would draw a third block behind them.\n  'prev-next-post': 'h-24 w-full bg-transparent',\n  'stat-card': 'h-24 w-full rounded-lg',\n\n  // Chart-shaped. `chart` matches the TimeSeries default drawing height\n  // (220 user units ≈ h-56) so the swap is CLS-neutral; `metric-table` is a\n  // composite (header + rows) rendered in skeleton.tsx; `sparkline` reserves\n  // the exact inline cell so a metric arriving mid-window does not reflow the\n  // column around it.\n  chart: 'h-56 w-full rounded-md',\n  'metric-table': 'w-full rounded-md',\n  // Composites (inner JSX in skeleton.tsx). Both are transparent at the root:\n  // the shape lives entirely in the body, and a filled root would paint a\n  // block behind the parts.\n  meter: 'w-full bg-transparent',\n  'stat-strip': 'w-full bg-transparent',\n  sparkline: 'inline-block h-[22px] w-[90px] rounded-sm align-middle',\n};\n\n/**\n * @interlace/ui — Skeleton variant catalogue.\n *\n * Single source of truth for the `<Skeleton variant>` discriminated union.\n * Every entry maps to a Tailwind shape class set that paints a placeholder\n * matching the corresponding primitive / pattern / template.\n *\n * Adding a new variant:\n *   1. Add the name to `SKELETON_VARIANTS` below (alphabetic within its\n *      category block).\n *   2. Add the matching shape class set to `SKELETON_VARIANT_CLASSES`.\n *   3. The TypeScript `SkeletonVariant` union and the lock test\n *      (`skeleton-variant-coverage-lock.test.ts`) pick the new value up\n *      automatically.\n *\n * The lock test ALSO asserts: every `<Skeleton variant=\"x\">` call site in\n * the codebase resolves to a value in this array. Typos and renames fail\n * the PR at CI time, complementing the dev-time TypeScript check.\n */\n\n// @interlace/skeleton-variants v1.7.0 — Interlace design system.\n// Docs, props and live preview: https://ds.interlace.tools/c/skeleton-variants\n// What changed since: https://ds.interlace.tools/c/skeleton-variants#history\n// Generated banner — keep it, the upgrade diff reads this version.\n"
    }
  ],
  "meta": {
    "tier": "primitive",
    "client": false,
    "minViewport": null,
    "loading": false,
    "version": "1.7.0",
    "since": "1.0.0"
  },
  "docs": "## @interlace/skeleton-variants\n\nInstalled to `components/ui/skeleton-variants.ts`.\n\n```tsx\nimport { /* … */ } from '@/components/ui/skeleton-variants';\n```\n\nProps, a11y contract, live preview and source: https://ds.interlace.tools/c/skeleton-variants\n\nRequires the `@interlace/theme` CSS baseline (installed automatically as a registry dependency)."
}
