{ "name": "shimmer", "type": "registry:ui", "registryDependencies": [], "dependencies": [ "motion" ], "devDependencies": [], "tailwind": {}, "cssVars": { "light": {}, "dark": {} }, "files": [ { "path": "shimmer.tsx", "content": "// components/core/shimmer-skeleton.tsx\n'use client';\n\nimport * as React from 'react';\nimport { motion } from 'motion/react';\nimport { cn } from '@/lib/utils';\n\nexport type ShimmerVariant = 'line' | 'avatar' | 'card' | 'rect' | 'list';\n\nexport type ShimmerProps = {\n /** Which visual variant to render. */\n variant?: ShimmerVariant;\n\n /** Number of repeated items (useful for lists). */\n count?: number;\n\n /** Width as Tailwind friendly string or number (px). Applied to top-level when relevant. */\n width?: string | number;\n\n /** Height as Tailwind friendly string or number (px). Applied to top-level when relevant. */\n height?: string | number;\n\n /** Rounded corners style. */\n rounded?: 'none' | 'sm' | 'md' | 'lg' | 'full';\n\n /** Tailwind color for base (e.g. 'bg-zinc-200 dark:bg-zinc-700') - accepts classes. */\n baseClassName?: string;\n\n /** Tailwind color for shimmer highlight (applied via gradient stop). */\n highlightClassName?: string;\n\n /** Animation speed multiplier (1 = default). */\n speed?: number;\n\n /** Gap between items when count > 1 (px). */\n gap?: number;\n\n /** Custom className on each item. */\n className?: string;\n\n /** When true, makes element accessible for screen readers with label. */\n ariaLabel?: string | null;\n\n /** If true, use reduced-motion friendly static style. */\n reduceMotion?: boolean;\n};\n\nconst ROUND_MAP: Record = {\n none: 'rounded-none',\n sm: 'rounded-sm',\n md: 'rounded-md',\n lg: 'rounded-lg',\n full: 'rounded-full',\n};\n\nfunction pxOrString(v?: string | number) {\n if (v === undefined) return undefined;\n return typeof v === 'number' ? `${v}px` : v;\n}\n\n/**\n * Shimmer/Skeleton component\n *\n * - Presentational, repeatable, accessible\n * - Use variant + count to produce multi-line lists/cards etc\n */\nexport default function Shimmer({\n variant = 'line',\n count = 1,\n width,\n height,\n rounded = 'md',\n baseClassName = 'bg-zinc-200 dark:bg-zinc-700',\n // highlightClassName = 'bg-white/70',\n speed = 1,\n gap = 12,\n className,\n ariaLabel = null,\n reduceMotion = false,\n}: ShimmerProps) {\n // animation duration in seconds\n const duration = Math.max(0.6, 1.6 / Math.max(0.25, speed));\n\n const itemStyle: React.CSSProperties = {\n width: pxOrString(width),\n height: pxOrString(height),\n marginBottom: count > 1 ? `${gap}px` : undefined,\n };\n\n // helper render single item depending on variant\n const renderItem = (i: number) => {\n const roundedCls = ROUND_MAP[rounded] ?? ROUND_MAP.md;\n const commonInner = (\n \n {/* shimmer gradient overlay */}\n {!reduceMotion ? (\n \n ) : null}\n {/* A subtle overlay stripe for depth */}\n
\n
\n );\n\n switch (variant) {\n case 'avatar':\n return (\n 0 ? 'mt-[12px]' : '')}\n style={itemStyle}\n >\n \n
\n \n \n
\n \n );\n case 'card':\n return (\n \n
\n {commonInner}\n
\n
\n \n \n \n
\n \n );\n case 'rect':\n return (\n \n {commonInner}\n \n );\n case 'list':\n return (\n
\n \n
\n \n \n
\n
\n
\n );\n case 'line':\n default:\n return (\n
\n
\n
\n );\n }\n };\n\n // container role/aria\n const roleProps = ariaLabel\n ? { role: 'status', 'aria-label': ariaLabel }\n : { 'aria-hidden': true };\n\n // render repeated items\n const items = Array.from({ length: Math.max(1, count) }, (_, i) =>\n renderItem(i)\n );\n\n return (\n
\n {variant === 'list' ? (\n
{items}
\n ) : (\n
{items}
\n )}\n
\n );\n}\n", "type": "registry:ui" } ] }