{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "skeleton",
  "title": "Skeleton",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-angular/components/skeleton/skeleton.component.ts",
      "content": "import { Component, Input, ViewEncapsulation, booleanAttribute, ChangeDetectionStrategy } from '@angular/core'\nimport { cn } from '@/lib/utils'\nimport { skeletonLoaderVariants, type SkeletonLoaderVariants } from './skeleton.variants'\n\nexport type SkeletonVariant =\n  'rectangular' | 'rounded' | 'circular' | 'text' | 'avatar' | 'image' | 'card' | 'table-row'\nexport type SkeletonLoaderVariant = NonNullable<SkeletonLoaderVariants['variant']>\n\nconst SKELETON_SHIMMER_STYLES = `\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`\n\nconst skeletonVariantClasses: Record<SkeletonVariant, 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-[120px]',\n  'table-row': 'rounded h-10 w-full',\n}\n\n/**\n * Angular port of UIPKGE Skeleton (React `Skeleton`). While `loading` the host is the\n * aria-hidden shimmer block (variant shape + width / height); with `loading=false` it steps\n * out of layout (`display: contents`) and renders its projected children only. The shimmer\n * keyframes React injects as a <style> ship unscoped (ViewEncapsulation.None) so they match\n * the host itself.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-skeleton, [ui-skeleton]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': 'loading ? \"\" : null',\n    '[attr.data-slot]': 'loading ? \"skeleton\" : null',\n    '[attr.aria-hidden]': 'loading ? \"true\" : null',\n    '[class]': 'hostClass',\n    '[style.width]': 'loading ? width : null',\n    '[style.height]': 'loading ? height : null',\n  },\n  template: `@if (!loading) {\n    <ng-content />\n  }`,\n  encapsulation: ViewEncapsulation.None,\n  styles: [SKELETON_SHIMMER_STYLES],\n})\nexport class UiSkeletonComponent {\n  @Input() variant: SkeletonVariant = 'rectangular'\n  @Input() width?: string\n  @Input() height?: string\n  @Input({ transform: booleanAttribute }) loading = true\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    if (!this.loading) return 'contents'\n    return cn('block skeleton-shimmer', skeletonVariantClasses[this.variant ?? 'rectangular'], this.className)\n  }\n}\n\n/**\n * React `SkeletonGroup`: a `space-y-2` stack. React's `tag` prop maps to the attribute form —\n * put `ui-skeleton-group` on the element you want (`<ul ui-skeleton-group>`).\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-skeleton-group, [ui-skeleton-group]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"skeleton-group\"',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiSkeletonGroupComponent {\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    return cn('block space-y-2', this.className)\n  }\n}\n\n/** React `SkeletonText`: `lines` shimmer bars with first / last line width tweaks. */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-skeleton-text, [ui-skeleton-text]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"skeleton-text\"',\n    '[attr.aria-hidden]': '\"true\"',\n    '[class]': 'hostClass',\n  },\n  template: `@for (width of lineWidths; track $index) {\n    <div class=\"skeleton-shimmer h-4 rounded\" [style.width]=\"width\"></div>\n  }`,\n  encapsulation: ViewEncapsulation.None,\n  styles: [SKELETON_SHIMMER_STYLES],\n})\nexport class UiSkeletonTextComponent {\n  @Input() lines = 3\n  @Input() lastLineWidth = '80%'\n  @Input() firstLineWidth = '100%'\n  @Input('class') className?: string\n\n  get lineWidths(): string[] {\n    return Array.from({ length: this.lines }, (_, i) => {\n      if (i === 0) return this.firstLineWidth\n      if (i === this.lines - 1) return this.lastLineWidth\n      return '100%'\n    })\n  }\n\n  get hostClass(): string {\n    return cn('block space-y-2', this.className)\n  }\n}\n\n/**\n * React `SkeletonLoader`: preset placeholder shapes (`skeletonLoaderVariants`). `rows > 1`\n * builds the composed presets (article, card, card-avatar, actions, table, list-item*) or\n * repeats the atom; `loading=false` renders the projected content instead.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-skeleton-loader, [ui-skeleton-loader]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"skeleton-loader\"',\n    class: 'block space-y-2',\n  },\n  template: `\n    @if (!loading) {\n      <ng-content />\n    } @else if (rows === 1) {\n      <div [class]=\"single\"></div>\n    } @else {\n      @switch (variant) {\n        @case ('article') {\n          <div [class]=\"v('heading', 'mb-4')\"></div>\n          <div [class]=\"v('text', 'mb-2')\"></div>\n          <div [class]=\"v('text', 'mb-2')\"></div>\n          <div [class]=\"v('text', 'w-3/4')\"></div>\n        }\n        @case ('card') {\n          <div [class]=\"v('image-large', 'mb-4')\"></div>\n          <div [class]=\"v('heading-small', 'mb-2')\"></div>\n          <div [class]=\"v('text', 'mb-2')\"></div>\n          <div [class]=\"v('text', 'w-1/2')\"></div>\n        }\n        @case ('card-avatar') {\n          <div class=\"mb-4 flex items-center gap-4\">\n            <div [class]=\"v('avatar-large')\"></div>\n            <div class=\"flex-1 space-y-2\">\n              <div [class]=\"v('heading-small')\"></div>\n              <div [class]=\"v('text', 'w-1/2')\"></div>\n            </div>\n          </div>\n        }\n        @case ('actions') {\n          <div class=\"flex gap-2\">\n            <div [class]=\"v('button')\"></div>\n            <div [class]=\"v('button')\"></div>\n          </div>\n        }\n        @case ('table') {\n          @for (r of rowList; track r) {\n            <div [class]=\"v('table-row', 'mb-2')\"></div>\n          }\n        }\n        @case ('list-item') {\n          @for (r of rowList; track r) {\n            <div class=\"mb-2 flex items-center gap-3\">\n              <div [class]=\"v('avatar-small')\"></div>\n              <div class=\"flex-1\"><div [class]=\"v('text')\"></div></div>\n            </div>\n          }\n        }\n        @case ('list-item-two-line') {\n          @for (r of rowList; track r) {\n            <div class=\"mb-2 flex items-center gap-3\">\n              <div [class]=\"v('avatar')\"></div>\n              <div class=\"flex-1 space-y-2\">\n                <div [class]=\"v('text')\"></div>\n                <div [class]=\"v('text', 'w-3/4')\"></div>\n              </div>\n            </div>\n          }\n        }\n        @case ('list-item-three-line') {\n          @for (r of rowList; track r) {\n            <div class=\"mb-2 flex items-start gap-3\">\n              <div [class]=\"v('avatar')\"></div>\n              <div class=\"flex-1 space-y-2\">\n                <div [class]=\"v('text')\"></div>\n                <div [class]=\"v('text')\"></div>\n                <div [class]=\"v('text', 'w-2/3')\"></div>\n              </div>\n            </div>\n          }\n        }\n        @default {\n          @for (r of rowList; track r) {\n            <div [class]=\"v(variant, 'mb-2')\"></div>\n          }\n        }\n      }\n    }\n    @if (boilerplate && !loading) {\n      <div class=\"bg-muted/50 absolute inset-0\"></div>\n    }\n  `,\n})\nexport class UiSkeletonLoaderComponent {\n  @Input() variant: SkeletonLoaderVariant = 'text'\n  @Input({ transform: booleanAttribute }) loading = true\n  @Input() rows = 1\n  @Input({ transform: booleanAttribute }) boilerplate = false\n  @Input('class') className?: string\n\n  get single(): string {\n    return cn(skeletonLoaderVariants({ variant: this.variant }), this.className)\n  }\n\n  get rowList(): number[] {\n    return Array.from({ length: this.rows }, (_, i) => i)\n  }\n\n  v(variant: SkeletonLoaderVariant, extra?: string): string {\n    return cn(skeletonLoaderVariants({ variant }), extra)\n  }\n}\n\nexport { skeletonLoaderVariants, type SkeletonLoaderVariants }\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/skeleton/skeleton.component.ts"
    },
    {
      "path": "packages/registry-angular/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/app/components/ui/skeleton/skeleton.variants.ts"
    },
    {
      "path": "packages/registry-angular/components/skeleton/index.ts",
      "content": "export {\n  UiSkeletonComponent,\n  UiSkeletonTextComponent,\n  UiSkeletonGroupComponent,\n  UiSkeletonLoaderComponent,\n  skeletonLoaderVariants,\n  type SkeletonLoaderVariants,\n  type SkeletonVariant,\n  type SkeletonLoaderVariant,\n} from './skeleton.component'\n",
      "type": "registry:ui",
      "target": "~/src/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"
  ]
}