{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"shimmer-loader","type":"registry:ui","title":"Shimmer Loader","description":"Skeleton blocks that share one soft light sweep, plus shimmering text for “Thinking…” states","dependencies":[],"files":[{"path":"motion/shimmer-loader/shimmer-loader.tsx","type":"registry:ui","content":"\"use client\";\n\nimport type { CSSProperties, ReactNode } from \"react\";\n\n/*\n * Shimmer Loader – loading states with a soft light sweep, in a single self-contained file.\n * Skeleton draws placeholder blocks; the highlight is pinned to the viewport (background-attachment: fixed),\n * so every skeleton on the page shares one sweep that travels across them together. ShimmerText runs a\n * highlight band through the letters of a label, like an AI \"Thinking…\" state. The styles are injected below.\n */\n\nexport type SkeletonProps = {\n  /** Width – a number is px, a string any CSS length; defaults to 100% */\n  width?: number | string;\n  /** Height – a number is px, a string any CSS length; defaults to 12px */\n  height?: number | string;\n  /** Corner radius; defaults to 6px */\n  radius?: number | string;\n  /** Round shape, e.g. for avatars – uses width for both sides */\n  circle?: boolean;\n  className?: string;\n  style?: CSSProperties;\n};\n\nexport type ShimmerTextProps = {\n  children: ReactNode;\n  /** Seconds for one pass of the highlight */\n  duration?: number;\n  className?: string;\n};\n\nconst len = (v: number | string | undefined) => (typeof v === \"number\" ? `${v}px` : v);\n\nconst Style = () => (\n  <style href=\"ui-shimmer-loader\" precedence=\"default\">\n    {css}\n  </style>\n);\n\nexport function Skeleton({ width, height, radius, circle, className, style }: SkeletonProps) {\n  const w = len(width);\n  return (\n    <>\n      <Style />\n      <span\n        className={[\"ui-shm\", className].filter(Boolean).join(\" \")}\n        aria-hidden=\"true\"\n        style={{\n          flexShrink: circle || typeof width === \"number\" ? 0 : undefined,\n          width: w,\n          height: circle ? (w ?? len(height)) : len(height),\n          borderRadius: circle ? \"50%\" : len(radius),\n          ...style,\n        }}\n      />\n    </>\n  );\n}\n\nexport function ShimmerText({ children, duration = 2, className }: ShimmerTextProps) {\n  return (\n    <>\n      <Style />\n      <span\n        className={[\"ui-shm-text\", className].filter(Boolean).join(\" \")}\n        style={{ \"--ui-shm-text-dur\": `${duration}s` } as CSSProperties}\n      >\n        {children}\n      </span>\n    </>\n  );\n}\n\n/* ---------- Styles ---------- */\n\nconst css = /* css */ `\n.ui-shm,\n.ui-shm-text {\n  --ui-shm-base: #ececec;\n  --ui-shm-glow: rgba(255, 255, 255, 0.9);\n  --ui-shm-text: #a3a3a3;\n  --ui-shm-text-glow: #ffffff;\n  --ui-shm-dur: 2.2s;\n}\n\n/* Skeleton: one fixed gradient for the whole viewport, so all blocks sweep together */\n.ui-shm {\n  display: block;\n  /* Fixed sizes keep their size in a row; a % width shrinks to the room that's left */\n  flex: 0 1 auto;\n  min-width: 0;\n  width: 100%;\n  height: 12px;\n  max-width: 100%;\n  border-radius: 6px;\n  background-color: var(--ui-shm-base);\n  background-image: linear-gradient(\n    100deg,\n    transparent 0%,\n    transparent 30%,\n    var(--ui-shm-glow) 50%,\n    transparent 70%,\n    transparent 100%\n  );\n  background-size: 150vw 100%;\n  background-repeat: repeat-x;\n  background-attachment: fixed;\n  animation: ui-shm-sweep var(--ui-shm-dur) linear infinite;\n}\n@keyframes ui-shm-sweep {\n  from { background-position: -50vw 0; }\n  to { background-position: 100vw 0; }\n}\n\n/* Text: a highlight band runs through the letters */\n.ui-shm-text {\n  display: inline-block;\n  color: var(--ui-shm-text);\n  background-image: linear-gradient(\n    90deg,\n    var(--ui-shm-text) 0%,\n    var(--ui-shm-text) 40%,\n    var(--ui-shm-text-glow) 50%,\n    var(--ui-shm-text) 60%,\n    var(--ui-shm-text) 100%\n  );\n  background-size: 300% 100%;\n  background-position: 100% 0;\n  -webkit-background-clip: text;\n  background-clip: text;\n  -webkit-text-fill-color: transparent;\n  animation: ui-shm-text var(--ui-shm-text-dur, 2s) linear infinite;\n}\n@keyframes ui-shm-text {\n  from { background-position: 100% 0; }\n  to { background-position: 0% 0; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .ui-shm { animation: none; background-image: none; }\n  .ui-shm-text { animation: none; background-image: none; -webkit-text-fill-color: currentColor; }\n}\n\n/* Dark mode: follows a .dark class or data-theme=\"dark\" on any ancestor, e.g. <html> */\n:where(.dark, [data-theme=\"dark\"]) .ui-shm,\n:where(.dark, [data-theme=\"dark\"]) .ui-shm-text {\n  --ui-shm-base: #2f2f33;\n  --ui-shm-glow: rgba(255, 255, 255, 0.08);\n  --ui-shm-text: #6e6e73;\n  --ui-shm-text-glow: #f5f5f5;\n}\n`;\n"}]}