{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "icon-box",
  "title": "Icon Box",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-angular/components/icon-box/icon-box.component.ts",
      "content": "import {\n  Component,\n  Directive,\n  Input,\n  OnChanges,\n  OnDestroy,\n  TemplateRef,\n  ViewContainerRef,\n  inject,\n  ChangeDetectionStrategy,\n} from '@angular/core'\nimport { cn } from '@/lib/utils'\n\nexport type IconBoxVariant =\n  'primary' | 'muted' | 'outline' | 'solid' | 'subtle' | 'destructive' | 'success' | 'warning' | 'ghost' | 'custom'\nexport type IconBoxSize = '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'\nexport type IconBoxShape = 'rounded' | 'circle' | 'square'\nexport type IconStackVariant = 'primary' | 'muted' | 'destructive' | 'success' | 'warning'\nexport type IconStackSize = 'sm' | 'md' | 'lg' | 'xl'\n\nconst VARIANT_CLASSES: Record<IconBoxVariant, string> = {\n  primary: 'bg-primary/10 text-primary',\n  muted: 'bg-muted text-muted-foreground',\n  outline: 'border border-border bg-background text-foreground shadow-xs',\n  solid: 'bg-foreground text-background shadow-xs',\n  subtle: 'bg-accent text-accent-foreground',\n  destructive: 'bg-destructive/10 text-destructive',\n  success: 'bg-success/15 text-success',\n  warning: 'bg-warning/15 text-warning',\n  ghost: 'text-muted-foreground hover:bg-accent hover:text-foreground',\n  custom: '',\n}\n\nconst SHAPE_CLASSES: Record<IconBoxShape, string> = {\n  rounded: 'rounded-lg',\n  circle: 'rounded-full',\n  square: 'rounded-none',\n}\n\nconst SIZE_CLASSES: Record<IconBoxSize, string> = {\n  '2xs': 'size-6 p-1',\n  xs: 'size-7 p-1.5',\n  sm: 'size-8 p-1.5',\n  md: 'size-9 p-2',\n  lg: 'size-11 p-2.5',\n  xl: 'size-14 p-3.5',\n}\n\nconst ICON_SIZES: Record<IconBoxSize, string> = {\n  '2xs': 'size-3',\n  xs: 'size-3.5',\n  sm: 'size-4',\n  md: 'size-4.5',\n  lg: 'size-6',\n  xl: 'size-7',\n}\n\nconst STACK_SIZES: Record<IconStackSize, { root: string; base: string; icon: string }> = {\n  sm: { root: 'size-10', base: 'size-8 rounded-lg', icon: 'size-4' },\n  md: { root: 'size-14', base: 'size-11 rounded-xl', icon: 'size-5' },\n  lg: { root: 'size-18', base: 'size-14 rounded-2xl', icon: 'size-7' },\n  xl: { root: 'size-24', base: 'size-18 rounded-3xl', icon: 'size-9' },\n}\n\nconst STACK_VARIANTS: Record<IconStackVariant, { back: string; front: string; text: string }> = {\n  primary: {\n    back: 'bg-primary/20 border-primary/30',\n    front: 'bg-background border-primary/20 shadow-primary/10',\n    text: 'text-primary',\n  },\n  muted: {\n    back: 'bg-muted border-border',\n    front: 'bg-card border-border shadow-black/5',\n    text: 'text-muted-foreground',\n  },\n  destructive: {\n    back: 'bg-destructive/20 border-destructive/30',\n    front: 'bg-background border-destructive/20 shadow-destructive/10',\n    text: 'text-destructive',\n  },\n  success: {\n    back: 'bg-success/20 border-success/30',\n    front: 'bg-background border-success/20 shadow-success/10',\n    text: 'text-success',\n  },\n  warning: {\n    back: 'bg-warning/20 border-warning/30',\n    front: 'bg-background border-warning/20 shadow-warning/10',\n    text: 'text-warning',\n  },\n}\n\n/**\n * Stamps an icon template and styles its root element(s) the way React styles the `icon`\n * component it receives (`className` + aria-hidden), so consumers pass a bare svg.\n */\n@Directive({ selector: '[uiIconBoxIcon]', standalone: true })\nexport class UiIconBoxIconDirective implements OnChanges, OnDestroy {\n  @Input('uiIconBoxIcon') template: TemplateRef<unknown> | null | undefined = null\n  @Input() iconClass?: string\n  private readonly vcr = inject(ViewContainerRef)\n\n  ngOnChanges(): void {\n    this.vcr.clear()\n    if (!this.template) return\n    const view = this.vcr.createEmbeddedView(this.template)\n    view.detectChanges()\n    for (const node of view.rootNodes) {\n      if (!(node instanceof Element)) continue\n      node.setAttribute('class', cn(node.getAttribute('class') ?? '', this.iconClass))\n      node.setAttribute('aria-hidden', 'true')\n    }\n  }\n\n  ngOnDestroy(): void {\n    this.vcr.clear()\n  }\n}\n\n/**\n * Icon container with calibrated sizes, surface variants and shapes (React `IconBox`). Pass the\n * icon as a template (`<ng-template #bell><svg …/></ng-template>` + `[icon]=\"bell\"`), which gets\n * the size-matched icon class, or project your own content instead.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-icon-box, [ui-icon-box]',\n  standalone: true,\n  imports: [UiIconBoxIconDirective],\n  host: {\n    'data-uipkge': '',\n    'data-slot': 'icon-box',\n    '[attr.data-variant]': 'variant',\n    '[attr.data-size]': 'size',\n    '[attr.data-shape]': 'shape',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />\n    @if (icon) {\n      <ng-container [uiIconBoxIcon]=\"icon\" [iconClass]=\"iconClassMerged\" />\n    }`,\n})\nexport class UiIconBoxComponent {\n  /** Optional icon template rendered inside the box (e.g. a Lucide svg). */\n  @Input() icon?: TemplateRef<unknown> | null\n  @Input() variant: IconBoxVariant = 'primary'\n  @Input() shape: IconBoxShape = 'rounded'\n  @Input() size: IconBoxSize = 'md'\n  @Input() iconClass?: string\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    return cn(\n      'inline-flex shrink-0 items-center justify-center transition-colors',\n      VARIANT_CLASSES[this.variant],\n      SHAPE_CLASSES[this.shape],\n      SIZE_CLASSES[this.size],\n      this.className,\n    )\n  }\n\n  get iconClassMerged(): string {\n    return cn(ICON_SIZES[this.size], this.iconClass)\n  }\n}\n\n/** 2.5D layered icon for empty states and feature hero cards (React `IconStack`). */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-icon-stack, [ui-icon-stack]',\n  standalone: true,\n  imports: [UiIconBoxIconDirective],\n  host: {\n    'data-uipkge': '',\n    'data-slot': 'icon-stack',\n    '[attr.data-variant]': 'variant',\n    '[attr.data-size]': 'size',\n    '[class]': 'hostClass',\n  },\n  template: `\n    <div aria-hidden=\"true\" [class]=\"backClass\"></div>\n    <div [class]=\"frontClass\">\n      <ng-content />\n      @if (icon) {\n        <ng-container [uiIconBoxIcon]=\"icon\" [iconClass]=\"iconClassMerged\" />\n      }\n    </div>\n  `,\n})\nexport class UiIconStackComponent {\n  /** Optional icon template rendered on the front tile. */\n  @Input() icon?: TemplateRef<unknown> | null\n  @Input() variant: IconStackVariant = 'primary'\n  @Input() size: IconStackSize = 'md'\n  @Input() iconClass?: string\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    return cn(\n      'relative inline-flex items-center justify-center select-none',\n      STACK_SIZES[this.size].root,\n      this.className,\n    )\n  }\n\n  get backClass(): string {\n    return cn(\n      'absolute inset-0 m-auto rotate-6 border transition-transform duration-300',\n      STACK_SIZES[this.size].base,\n      STACK_VARIANTS[this.variant].back,\n    )\n  }\n\n  get frontClass(): string {\n    return cn(\n      'relative z-10 flex items-center justify-center border shadow-md transition-transform duration-300 group-hover:-translate-y-0.5',\n      STACK_SIZES[this.size].base,\n      STACK_VARIANTS[this.variant].front,\n      STACK_VARIANTS[this.variant].text,\n    )\n  }\n\n  get iconClassMerged(): string {\n    return cn(STACK_SIZES[this.size].icon, this.iconClass)\n  }\n}\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/icon-box/icon-box.component.ts"
    },
    {
      "path": "packages/registry-angular/components/icon-box/index.ts",
      "content": "export { UiIconBoxComponent, UiIconBoxIconDirective, UiIconStackComponent } from './icon-box.component'\nexport type { IconBoxShape, IconBoxSize, IconBoxVariant, IconStackSize, IconStackVariant } from './icon-box.component'\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/icon-box/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Icon container primitive with calibrated sizes (2xs to xl), surface variants (solid, outline, subtle, primary, muted), and 2.5D layered IconStack for empty states and feature hero cards.",
  "categories": [
    "data-display"
  ]
}