{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "spinner",
  "title": "Spinner",
  "type": "registry:ui",
  "description": "Lightweight loading indicator — circular spinner with three sizes and tone variants. Use inside buttons (replacing the icon when an action is in flight) or as a centered page loader.",
  "categories": [
    "feedback"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/spinner/spinner.ts",
      "content": "import { LitElement, css } from 'lit'\nimport { Loader2 } from 'lucide'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { icon } from '../../lib/icon'\nimport { spinnerVariants, type SpinnerVariants } from './spinner.variants'\n\ntype Size = NonNullable<SpinnerVariants['size']>\n\n/**\n * <uip-spinner> — the registry Spinner (a spinning Loader2) as a web component.\n *\n * Class string is React's `spinnerVariants` verbatim. The icon is the same\n * Loader2 node lucide-react renders, so the `lucide lucide-loader-circle`\n * classes match. `role=\"status\"` + `aria-label=\"Loading\"` like React.\n *\n * React's `className` (size/color overrides) → style `part=\"base\"` from the\n * host, e.g. `class=\"[&::part(base)]:size-8 [&::part(base)]:text-primary\"`.\n */\nexport class UipSpinner extends LitElement {\n  // :host display can't be a utility class (the host has no template of its own).\n  static styles = [tailwind, css`:host { display: inline-flex; }`]\n\n  static properties = {\n    size: { reflect: true },\n  }\n\n  size: Size = 'default'\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'spinner')\n  }\n\n  protected updated() {\n    // icon() takes no extra attributes; stamp React's data/ARIA attributes and\n    // the styling part onto the rendered svg.\n    const svg = this.renderRoot.querySelector('svg')\n    if (svg) {\n      svg.setAttribute('part', 'base')\n      svg.setAttribute('data-uipkge', '')\n      svg.setAttribute('data-slot', 'spinner')\n      svg.setAttribute('role', 'status')\n      svg.setAttribute('aria-label', 'Loading')\n    }\n  }\n\n  render() {\n    return icon(Loader2, 'loader-circle', spinnerVariants({ size: this.size }))\n  }\n}\n\ncustomElements.get('uip-spinner') || customElements.define('uip-spinner', UipSpinner)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-spinner': UipSpinner\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/spinner/spinner.ts"
    },
    {
      "path": "packages/elements-lit/src/components/spinner/spinner.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 the package\n * `index.ts`) so consuming Vue SFCs can import without creating a circular\n * dependency through the index. See card.variants.ts for the canonical\n * example + the SSR symptom that motivated the split.\n */\n\n// motion-safe: honor prefers-reduced-motion (static glyph when reduced).\nexport const spinnerVariants = cva('motion-safe:animate-spin text-muted-foreground', {\n  variants: {\n    size: {\n      default: 'size-6',\n      sm: 'size-4',\n      lg: 'size-8',\n      icon: 'size-4',\n    },\n  },\n  defaultVariants: {\n    size: 'default',\n  },\n})\n\nexport type SpinnerVariants = VariantProps<typeof spinnerVariants>\n",
      "type": "registry:file",
      "target": "src/uip/components/spinner/spinner.variants.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
