import clsx from "clsx"; /* Server-component-friendly skeleton primitives for route loading.tsx files. All shimmer comes from the `.skeleton` CSS class in globals.css (the existing `shimmer` keyframes) — no client JS, no framer-motion, and the global prefers-reduced-motion override collapses it to a static block. */ export interface SkeletonProps { className?: string; /** CSS width, e.g. "60%" or 120 (px) */ width?: string | number; /** CSS height, e.g. 16 (px) or "2rem" */ height?: string | number; } /** * Skeleton — a single shimmering placeholder block. * Size it via width/height props or Tailwind classes (h-4 w-32 …). */ export function Skeleton({ className, width, height }: SkeletonProps) { return (
); } export interface SkeletonTextProps { className?: string; /** number of text lines, default 3 */ lines?: number; /** width of the trailing line, default "60%" */ lastLineWidth?: string; } /** * SkeletonText — a stack of shimmering text lines; the last line is shorter * so the block reads like a paragraph. */ export function SkeletonText({ className, lines = 3, lastLineWidth = "60%", }: SkeletonTextProps) { return (
{Array.from({ length: lines }).map((_, i) => ( ))}
); } export interface SkeletonTableProps { className?: string; /** body rows, default 8 */ rows?: number; /** columns, default 6 */ columns?: number; } /** * SkeletonTable — header strip + hairline-separated rows; placeholder for * leaderboard-style tables while route data streams in. */ export function SkeletonTable({ className, rows = 8, columns = 6, }: SkeletonTableProps) { return (
{/* header */}
{Array.from({ length: columns }).map((_, i) => ( ))}
{/* rows */} {Array.from({ length: rows }).map((_, r) => (
{Array.from({ length: columns }).map((_, c) => ( ))}
))}
); } export interface SkeletonCardProps { className?: string; /** body text lines, default 2 */ lines?: number; } /** * SkeletonCard — hairline card with a heading line + body text placeholder. */ export function SkeletonCard({ className, lines = 2 }: SkeletonCardProps) { return (
); } export interface SkeletonStatsProps { className?: string; /** number of stat tiles, default 4 */ count?: number; } /** * SkeletonStats — grid of MetricCard-shaped stat tiles (big number + label). * Compose grid columns via className (e.g. "grid-cols-2 md:grid-cols-4"). */ export function SkeletonStats({ className, count = 4 }: SkeletonStatsProps) { return (
{Array.from({ length: count }).map((_, i) => (
))}
); }