{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "skeleton",
  "title": "Skeleton",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/skeleton/Skeleton.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n  variant?: 'rectangular' | 'rounded' | 'circular' | 'text' | 'avatar' | 'image' | 'card' | 'table-row'\n  width?: string\n  height?: string\n  loading?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  variant: 'rectangular',\n  loading: true,\n})\n\nconst variantClasses = {\n  rectangular: '',\n  rounded: 'rounded-md',\n  circular: 'rounded-full',\n  text: 'rounded h-4 w-full',\n  avatar: 'rounded-full size-10',\n  image: 'rounded-lg size-24',\n  card: 'rounded-xl size-full min-h-[120px]',\n  'table-row': 'rounded h-10 w-full',\n}\n\nconst variantStyles = computed(() => {\n  const base: Record<string, string> = {}\n  if (props.width) base.width = props.width\n  if (props.height) base.height = props.height\n  return Object.keys(base).length > 0 ? base : undefined\n})\n</script>\n\n<template>\n  <div\n    v-if=\"loading\"\n    data-uipkge\n    data-slot=\"skeleton\"\n    :class=\"cn('skeleton-shimmer', variantClasses[variant || 'rectangular'], props.class)\"\n    :style=\"variantStyles\"\n  />\n  <slot v-else />\n</template>\n\n<style scoped>\n/* Slow left-to-right gradient sweep. Per NN/g, slow shimmer reads as\n   faster loading than a pulse. 1.8s loop is the documented sweet spot. */\n.skeleton-shimmer {\n  background: linear-gradient(\n    90deg,\n    color-mix(in srgb, var(--muted) 100%, transparent) 0%,\n    color-mix(in srgb, var(--muted) 60%, var(--foreground) 8%) 50%,\n    color-mix(in srgb, var(--muted) 100%, transparent) 100%\n  );\n  background-size: 200% 100%;\n  animation: skeleton-shimmer 1.8s linear infinite;\n}\n@keyframes skeleton-shimmer {\n  from {\n    background-position: 200% 0;\n  }\n  to {\n    background-position: -200% 0;\n  }\n}\n@media (prefers-reduced-motion: reduce) {\n  .skeleton-shimmer {\n    animation: none;\n    background: var(--muted);\n  }\n}\n</style>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/skeleton/Skeleton.vue"
    },
    {
      "path": "packages/registry-vue/components/skeleton/SkeletonGroup.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n  tag?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  tag: 'div',\n})\n</script>\n\n<template>\n  <component :is=\"props.tag\" data-uipkge data-slot=\"skeleton-group\" :class=\"cn('space-y-2', props.class)\">\n    <slot />\n  </component>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/skeleton/SkeletonGroup.vue"
    },
    {
      "path": "packages/registry-vue/components/skeleton/SkeletonLoader.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { skeletonLoaderVariants } from './skeleton.variants'\n\nconst props = withDefaults(\n  defineProps<{\n    class?: HTMLAttributes['class']\n    variant?:\n      | 'text'\n      | 'chip'\n      | 'chip-icon'\n      | 'article'\n      | 'avatar'\n      | 'avatar-small'\n      | 'avatar-large'\n      | 'heading'\n      | 'heading-medium'\n      | 'heading-small'\n      | 'image'\n      | 'image-small'\n      | 'image-large'\n      | 'card'\n      | 'card-avatar'\n      | 'actions'\n      | 'table'\n      | 'table-row'\n      | 'button'\n      | 'button-icon'\n      | 'badge'\n      | 'tab'\n      | 'date-picker'\n      | 'list-item'\n      | 'list-item-two-line'\n      | 'list-item-three-line'\n    loading?: boolean\n    rows?: number\n    boilerplate?: boolean\n  }>(),\n  {\n    variant: 'text',\n    loading: true,\n    rows: 1,\n    boilerplate: false,\n  },\n)\n</script>\n\n<template>\n  <div data-uipkge data-slot=\"skeleton-loader\" class=\"space-y-2\">\n    <!-- Single item -->\n    <template v-if=\"rows === 1\">\n      <div v-if=\"loading\" :class=\"cn(skeletonLoaderVariants({ variant }), props.class)\" />\n      <slot v-else />\n    </template>\n\n    <!-- Multiple rows -->\n    <template v-else>\n      <template v-if=\"loading\">\n        <!-- Article variant with multiple elements -->\n        <template v-if=\"variant === 'article'\">\n          <div :class=\"cn(skeletonLoaderVariants({ variant: 'heading' }), 'mb-4')\" />\n          <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }), 'mb-2')\" />\n          <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }), 'mb-2')\" />\n          <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }), 'w-3/4')\" />\n        </template>\n\n        <!-- Card variant -->\n        <template v-else-if=\"variant === 'card'\">\n          <div :class=\"cn(skeletonLoaderVariants({ variant: 'image-large' }), 'mb-4')\" />\n          <div :class=\"cn(skeletonLoaderVariants({ variant: 'heading-small' }), 'mb-2')\" />\n          <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }), 'mb-2')\" />\n          <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }), 'w-1/2')\" />\n        </template>\n\n        <!-- Card avatar variant -->\n        <template v-else-if=\"variant === 'card-avatar'\">\n          <div class=\"mb-4 flex items-center gap-4\">\n            <div :class=\"cn(skeletonLoaderVariants({ variant: 'avatar-large' }))\" />\n            <div class=\"flex-1 space-y-2\">\n              <div :class=\"cn(skeletonLoaderVariants({ variant: 'heading-small' }))\" />\n              <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }), 'w-1/2')\" />\n            </div>\n          </div>\n        </template>\n\n        <!-- Actions variant -->\n        <template v-else-if=\"variant === 'actions'\">\n          <div class=\"flex gap-2\">\n            <div :class=\"cn(skeletonLoaderVariants({ variant: 'button' }))\" />\n            <div :class=\"cn(skeletonLoaderVariants({ variant: 'button' }))\" />\n          </div>\n        </template>\n\n        <!-- Table variant -->\n        <template v-else-if=\"variant === 'table'\">\n          <div v-for=\"i in rows\" :key=\"i\" :class=\"cn(skeletonLoaderVariants({ variant: 'table-row' }), 'mb-2')\" />\n        </template>\n\n        <!-- List item variants -->\n        <template v-else-if=\"variant === 'list-item'\">\n          <div v-for=\"i in rows\" :key=\"i\" class=\"mb-2 flex items-center gap-3\">\n            <div :class=\"cn(skeletonLoaderVariants({ variant: 'avatar-small' }))\" />\n            <div class=\"flex-1\">\n              <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }))\" />\n            </div>\n          </div>\n        </template>\n\n        <!-- List item two line -->\n        <template v-else-if=\"variant === 'list-item-two-line'\">\n          <div v-for=\"i in rows\" :key=\"i\" class=\"mb-2 flex items-center gap-3\">\n            <div :class=\"cn(skeletonLoaderVariants({ variant: 'avatar' }))\" />\n            <div class=\"flex-1 space-y-2\">\n              <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }))\" />\n              <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }), 'w-3/4')\" />\n            </div>\n          </div>\n        </template>\n\n        <!-- List item three line -->\n        <template v-else-if=\"variant === 'list-item-three-line'\">\n          <div v-for=\"i in rows\" :key=\"i\" class=\"mb-2 flex items-start gap-3\">\n            <div :class=\"cn(skeletonLoaderVariants({ variant: 'avatar' }))\" />\n            <div class=\"flex-1 space-y-2\">\n              <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }))\" />\n              <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }))\" />\n              <div :class=\"cn(skeletonLoaderVariants({ variant: 'text' }), 'w-2/3')\" />\n            </div>\n          </div>\n        </template>\n\n        <!-- Default: repeat rows -->\n        <template v-else>\n          <div v-for=\"i in rows\" :key=\"i\" :class=\"cn(skeletonLoaderVariants({ variant }), 'mb-2')\" />\n        </template>\n      </template>\n      <slot v-else />\n    </template>\n\n    <!-- Boilerplate mode: dim the content -->\n    <div v-if=\"boilerplate && !loading\" class=\"bg-muted/50 absolute inset-0\" />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/skeleton/SkeletonLoader.vue"
    },
    {
      "path": "packages/registry-vue/components/skeleton/SkeletonText.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n  lines?: number\n  lastLineWidth?: string\n  firstLineWidth?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  lines: 3,\n  lastLineWidth: '80%',\n  firstLineWidth: '100%',\n})\n\nconst lineWidths = computed(() => {\n  return Array.from({ length: props.lines }, (_, i) => {\n    if (i === 0) return props.firstLineWidth\n    if (i === props.lines - 1) return props.lastLineWidth\n    return '100%'\n  })\n})\n</script>\n\n<template>\n  <div data-uipkge data-slot=\"skeleton-text\" :class=\"cn('space-y-2', props.class)\">\n    <div v-for=\"(width, i) in lineWidths\" :key=\"i\" class=\"bg-primary/10 h-4 animate-pulse rounded\" :style=\"{ width }\" />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/skeleton/SkeletonText.vue"
    },
    {
      "path": "packages/registry-vue/components/skeleton/skeleton.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than inline in the\n * SFC) so `SkeletonLoader.vue` can import them without the cva runtime\n * coupling that breaks SSR when several `<SkeletonLoader>` instances\n * render before the module graph fully resolves. Sibling pattern to\n * `card/card.variants.ts`.\n */\nexport const skeletonLoaderVariants = cva('bg-muted animate-pulse rounded', {\n  variants: {\n    variant: {\n      text: 'h-4 w-full',\n      chip: 'h-8 w-24 rounded-full',\n      'chip-icon': 'h-8 w-8 rounded-full',\n      article: '',\n      avatar: 'size-12 rounded-full',\n      'avatar-small': 'size-8 rounded-full',\n      'avatar-large': 'size-16 rounded-full',\n      heading: 'h-6 w-3/4',\n      'heading-medium': 'h-5 w-1/2',\n      'heading-small': 'h-4 w-1/3',\n      image: 'aspect-video w-full rounded-lg',\n      'image-small': 'h-32 w-32 rounded-lg',\n      'image-large': 'h-64 w-full rounded-lg',\n      card: '',\n      'card-avatar': '',\n      actions: '',\n      table: 'h-4 w-full',\n      'table-row': 'h-12 w-full',\n      button: 'h-10 w-24 rounded-md',\n      'button-icon': 'h-10 w-10 rounded-md',\n      badge: 'h-6 w-16 rounded-full',\n      tab: 'h-8 w-24 rounded-md',\n      'date-picker': 'h-10 w-full',\n      'list-item': 'h-16 w-full',\n      'list-item-two-line': 'h-20 w-full',\n      'list-item-three-line': 'h-24 w-full',\n    },\n  },\n  defaultVariants: {\n    variant: 'text',\n  },\n})\n\nexport type SkeletonLoaderVariants = VariantProps<typeof skeletonLoaderVariants>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/skeleton/skeleton.variants.ts"
    },
    {
      "path": "packages/registry-vue/components/skeleton/index.ts",
      "content": "export { default as Skeleton } from './Skeleton.vue'\nexport { default as SkeletonText } from './SkeletonText.vue'\nexport { default as SkeletonGroup } from './SkeletonGroup.vue'\nexport { default as SkeletonLoader } from './SkeletonLoader.vue'\n\n// Re-export variant API from the sibling file (kept separate to avoid the\n// SkeletonLoader.vue <-> index.ts circular import that broke dev SSR).\nexport { skeletonLoaderVariants, type SkeletonLoaderVariants } from './skeleton.variants'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/skeleton/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Animated placeholder rectangles for loading states — drop one in shape of the content that’s about to render. Variants for text lines, avatars, rounded rectangles, and circles.",
  "categories": [
    "feedback"
  ]
}