{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "skeleton",
  "title": "Skeleton",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/skeleton/Skeleton.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {\n    variant?: 'rectangular' | 'rounded' | 'circular' | 'text' | 'avatar' | 'image' | 'card' | 'table-row'\n    width?: string\n    height?: string\n    loading?: boolean\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  const variantClasses: Record<NonNullable<SkeletonProps['variant']>, string> = {\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-30',\n    'table-row': 'rounded h-10 w-full',\n  }\n\n  let {\n    class: className,\n    variant = 'rectangular',\n    width,\n    height,\n    loading = true,\n    children,\n    ref = $bindable(null),\n    ...restProps\n  }: SkeletonProps = $props()\n\n  const variantStyle = $derived(\n    width || height ? `${width ? `width:${width};` : ''}${height ? `height:${height};` : ''}` : undefined,\n  )\n</script>\n\n{#if loading}\n  <div\n    bind:this={ref}\n    data-uipkge=\"\"\n    data-slot=\"skeleton\"\n    aria-hidden=\"true\"\n    class={cn('skeleton-shimmer', variantClasses[variant ?? 'rectangular'], className)}\n    style={variantStyle}\n    {...restProps}\n  ></div>\n{:else}\n  {@render children?.()}\n{/if}\n\n<style>\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": "~/src/lib/components/ui/skeleton/Skeleton.svelte"
    },
    {
      "path": "packages/registry-svelte/components/skeleton/SkeletonGroup.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface SkeletonGroupProps extends HTMLAttributes<HTMLElement> {\n    tag?: string\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, tag = 'div', children, ...restProps }: SkeletonGroupProps = $props()\n</script>\n\n<svelte:element this={tag} data-uipkge=\"\" data-slot=\"skeleton-group\" class={cn('space-y-2', className)} {...restProps}>\n  {@render children?.()}\n</svelte:element>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/skeleton/SkeletonGroup.svelte"
    },
    {
      "path": "packages/registry-svelte/components/skeleton/SkeletonLoader.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n  import type { SkeletonLoaderVariants } from './skeleton.variants'\n\n  export interface SkeletonLoaderProps extends HTMLAttributes<HTMLDivElement> {\n    variant?: SkeletonLoaderVariants['variant']\n    loading?: boolean\n    rows?: number\n    boilerplate?: boolean\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n  import { skeletonLoaderVariants } from './skeleton.variants'\n\n  let {\n    class: className,\n    variant = 'text',\n    loading = true,\n    rows = 1,\n    boilerplate = false,\n    children,\n    ref = $bindable(null),\n    ...restProps\n  }: SkeletonLoaderProps = $props()\n\n  const rowIndexes = $derived(Array.from({ length: rows }, (_, i) => i))\n</script>\n\n<div bind:this={ref} data-uipkge=\"\" data-slot=\"skeleton-loader\" class=\"space-y-2\" {...restProps}>\n  {#if rows === 1}\n    {#if loading}\n      <div class={cn(skeletonLoaderVariants({ variant }), className)}></div>\n    {:else}\n      {@render children?.()}\n    {/if}\n  {:else if loading}\n    {#if variant === 'article'}\n      <div class={cn(skeletonLoaderVariants({ variant: 'heading' }), 'mb-4')}></div>\n      <div class={cn(skeletonLoaderVariants({ variant: 'text' }), 'mb-2')}></div>\n      <div class={cn(skeletonLoaderVariants({ variant: 'text' }), 'mb-2')}></div>\n      <div class={cn(skeletonLoaderVariants({ variant: 'text' }), 'w-3/4')}></div>\n    {:else if variant === 'card'}\n      <div class={cn(skeletonLoaderVariants({ variant: 'image-large' }), 'mb-4')}></div>\n      <div class={cn(skeletonLoaderVariants({ variant: 'heading-small' }), 'mb-2')}></div>\n      <div class={cn(skeletonLoaderVariants({ variant: 'text' }), 'mb-2')}></div>\n      <div class={cn(skeletonLoaderVariants({ variant: 'text' }), 'w-1/2')}></div>\n    {:else if variant === 'card-avatar'}\n      <div class=\"mb-4 flex items-center gap-4\">\n        <div class={cn(skeletonLoaderVariants({ variant: 'avatar-large' }))}></div>\n        <div class=\"flex-1 space-y-2\">\n          <div class={cn(skeletonLoaderVariants({ variant: 'heading-small' }))}></div>\n          <div class={cn(skeletonLoaderVariants({ variant: 'text' }), 'w-1/2')}></div>\n        </div>\n      </div>\n    {:else if variant === 'actions'}\n      <div class=\"flex gap-2\">\n        <div class={cn(skeletonLoaderVariants({ variant: 'button' }))}></div>\n        <div class={cn(skeletonLoaderVariants({ variant: 'button' }))}></div>\n      </div>\n    {:else if variant === 'table'}\n      {#each rowIndexes as i (i)}\n        <div class={cn(skeletonLoaderVariants({ variant: 'table-row' }), 'mb-2')}></div>\n      {/each}\n    {:else if variant === 'list-item'}\n      {#each rowIndexes as i (i)}\n        <div class=\"mb-2 flex items-center gap-3\">\n          <div class={cn(skeletonLoaderVariants({ variant: 'avatar-small' }))}></div>\n          <div class=\"flex-1\">\n            <div class={cn(skeletonLoaderVariants({ variant: 'text' }))}></div>\n          </div>\n        </div>\n      {/each}\n    {:else if variant === 'list-item-two-line'}\n      {#each rowIndexes as i (i)}\n        <div class=\"mb-2 flex items-center gap-3\">\n          <div class={cn(skeletonLoaderVariants({ variant: 'avatar' }))}></div>\n          <div class=\"flex-1 space-y-2\">\n            <div class={cn(skeletonLoaderVariants({ variant: 'text' }))}></div>\n            <div class={cn(skeletonLoaderVariants({ variant: 'text' }), 'w-3/4')}></div>\n          </div>\n        </div>\n      {/each}\n    {:else if variant === 'list-item-three-line'}\n      {#each rowIndexes as i (i)}\n        <div class=\"mb-2 flex items-start gap-3\">\n          <div class={cn(skeletonLoaderVariants({ variant: 'avatar' }))}></div>\n          <div class=\"flex-1 space-y-2\">\n            <div class={cn(skeletonLoaderVariants({ variant: 'text' }))}></div>\n            <div class={cn(skeletonLoaderVariants({ variant: 'text' }))}></div>\n            <div class={cn(skeletonLoaderVariants({ variant: 'text' }), 'w-2/3')}></div>\n          </div>\n        </div>\n      {/each}\n    {:else}\n      {#each rowIndexes as i (i)}\n        <div class={cn(skeletonLoaderVariants({ variant }), 'mb-2')}></div>\n      {/each}\n    {/if}\n  {:else}\n    {@render children?.()}\n  {/if}\n\n  {#if boilerplate && !loading}\n    <div class=\"bg-muted/50 absolute inset-0\"></div>\n  {/if}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/skeleton/SkeletonLoader.svelte"
    },
    {
      "path": "packages/registry-svelte/components/skeleton/SkeletonText.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface SkeletonTextProps extends HTMLAttributes<HTMLDivElement> {\n    lines?: number\n    lastLineWidth?: string\n    firstLineWidth?: string\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let {\n    class: className,\n    lines = 3,\n    lastLineWidth = '80%',\n    firstLineWidth = '100%',\n    ref = $bindable(null),\n    ...restProps\n  }: SkeletonTextProps = $props()\n\n  const lineWidths = $derived(\n    Array.from({ length: lines }, (_, i) => {\n      if (i === 0) return firstLineWidth\n      if (i === lines - 1) return lastLineWidth\n      return '100%'\n    }),\n  )\n</script>\n\n<div\n  bind:this={ref}\n  data-uipkge=\"\"\n  data-slot=\"skeleton-text\"\n  aria-hidden=\"true\"\n  class={cn('space-y-2', className)}\n  {...restProps}\n>\n  {#each lineWidths as width, i (i)}\n    <div class=\"skeleton-shimmer h-4 rounded\" style=\"width:{width}\"></div>\n  {/each}\n</div>\n\n<style>\n  /* Match Skeleton.svelte shimmer so text lines don't flash a different motion language. */\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": "~/src/lib/components/ui/skeleton/SkeletonText.svelte"
    },
    {
      "path": "packages/registry-svelte/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": "~/src/lib/components/ui/skeleton/skeleton.variants.ts"
    },
    {
      "path": "packages/registry-svelte/components/skeleton/index.ts",
      "content": "export { default as Skeleton, type SkeletonProps } from './Skeleton.svelte'\nexport { default as SkeletonText, type SkeletonTextProps } from './SkeletonText.svelte'\nexport { default as SkeletonGroup, type SkeletonGroupProps } from './SkeletonGroup.svelte'\nexport { default as SkeletonLoader, type SkeletonLoaderProps } from './SkeletonLoader.svelte'\n\n// Re-export variant API from the sibling file (kept separate to avoid the\n// SkeletonLoader.svelte <-> index.ts circular import that broke dev SSR).\nexport { skeletonLoaderVariants, type SkeletonLoaderVariants } from './skeleton.variants'\n",
      "type": "registry:ui",
      "target": "~/src/lib/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"
  ]
}