import type React from "react" import { cn } from "../../utils/cn" interface UnifiedSkeletonProps extends React.HTMLAttributes { className?: string variant?: 'default' | 'text' | 'circular' | 'rectangular' width?: string | number height?: string | number /** * Disable animation for users who prefer reduced motion */ animate?: boolean /** * Accessibility label for screen readers */ 'aria-label'?: string } /** * UnifiedSkeleton Component * * Base skeleton component with consistent styling across the application. * * Design Specifications: * - Color: #2A2A2A base with #3A3A3A pulse highlight * - Animation: 1.5s pulse duration, ease-in-out timing * - Responsive: Mobile-first approach with proper scaling * - Accessibility: ARIA labels and reduced motion support * * @param variant - Shape variant: default (rounded), text (rounded-sm), circular, rectangular * @param width - Custom width (defaults to full width) * @param height - Custom height (defaults to auto based on variant) * @param animate - Enable/disable animation (respects prefers-reduced-motion) * @param aria-label - Accessibility label for screen readers */ export function UnifiedSkeleton({ className, variant = 'default', width, height, animate = true, 'aria-label': ariaLabel, ...props }: UnifiedSkeletonProps) { const baseClasses = "bg-ods-skeleton" const variantClasses = { default: "rounded-md", text: "rounded-sm", circular: "rounded-full", rectangular: "rounded-none" } const defaultHeights = { default: "h-4", text: "h-4", circular: "h-12 w-12", rectangular: "h-4" } const animationClasses = animate ? "animate-pulse motion-reduce:animate-none" : "" const style: React.CSSProperties = { width: width, height: height, } return (
) } /** * Text skeleton variants for consistent typography loading */ export const TextSkeleton = { /** * Large heading skeleton (h1, h2) */ Heading: ({ className, ...props }: Omit) => ( ), /** * Medium heading skeleton (h3, h4) */ Subheading: ({ className, ...props }: Omit) => ( ), /** * Body text skeleton */ Body: ({ className, ...props }: Omit) => ( ), /** * Small text skeleton (captions, metadata) */ Caption: ({ className, ...props }: Omit) => ( ), } /** * Interactive element skeletons */ export const InteractiveSkeleton = { /** * Button skeleton with proper touch targets */ Button: ({ className, ...props }: Omit) => ( ), /** * Input field skeleton */ Input: ({ className, ...props }: Omit) => ( ), /** * Filter chip skeleton */ Chip: ({ className, ...props }: Omit) => ( ), } /** * Media skeletons for images and icons */ export const MediaSkeleton = { /** * Avatar/profile image skeleton */ Avatar: ({ size = 'md', className, ...props }: Omit & { size?: 'sm' | 'md' | 'lg' }) => { const sizeClasses = { sm: "h-8 w-8", md: "h-12 w-12", lg: "h-16 w-16" } return ( ) }, /** * Card image skeleton (1200×630 OG aspect — matches entity-cards default * so loading state never causes a height shift when card data arrives) */ CardImage: ({ className, ...props }: Omit) => ( ), /** * Icon skeleton */ Icon: ({ size = 'md', className, ...props }: Omit & { size?: 'sm' | 'md' | 'lg' }) => { const sizeClasses = { sm: "h-4 w-4", md: "h-6 w-6", lg: "h-8 w-8" } return ( ) }, }