/** * Sample Components — Production-Ready React Components * * This file demonstrates how to build type-safe, accessible React components * using the design token system. * * Location: {project_path}/skills/frontend-design/examples/typescript/sample-components.tsx * * All components include: * - Full TypeScript type safety * - Complete state coverage (default/hover/active/focus/disabled/loading/error) * - Accessibility (ARIA labels, keyboard navigation) * - Responsive design * - Token-based styling */ import React, { useState, forwardRef, InputHTMLAttributes, ButtonHTMLAttributes } from 'react'; import { cn } from './utils'; // Utility for classname merging // ============================================ // BUTTON COMPONENT // ============================================ interface ButtonProps extends ButtonHTMLAttributes { variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger'; size?: 'sm' | 'md' | 'lg'; isLoading?: boolean; leftIcon?: React.ReactNode; rightIcon?: React.ReactNode; } export const Button = forwardRef( ( { variant = 'primary', size = 'md', isLoading = false, disabled, leftIcon, rightIcon, children, className, ...props }, ref ) => { const baseClasses = 'btn'; const variantClasses = { primary: 'btn-primary', secondary: 'btn-secondary', outline: 'btn-outline', ghost: 'btn-ghost', danger: 'btn-danger', }; const sizeClasses = { sm: 'btn-sm', md: '', lg: 'btn-lg', }; return ( ); } ); Button.displayName = 'Button'; // Usage Example: /* */ // ============================================ // INPUT COMPONENT // ============================================ interface InputProps extends InputHTMLAttributes { label?: string; error?: string; helperText?: string; size?: 'sm' | 'md' | 'lg'; leftIcon?: React.ReactNode; rightIcon?: React.ReactNode; } export const Input = forwardRef( ( { label, error, helperText, size = 'md', leftIcon, rightIcon, className, id, required, ...props }, ref ) => { const inputId = id || `input-${Math.random().toString(36).substr(2, 9)}`; const errorId = error ? `${inputId}-error` : undefined; const helperId = helperText ? `${inputId}-helper` : undefined; const sizeClasses = { sm: 'input-sm', md: '', lg: 'input-lg', }; return (
{label && ( )}
{leftIcon && (
{leftIcon}
)} {rightIcon && (
{rightIcon}
)}
{error && ( {error} )} {!error && helperText && ( {helperText} )}
); } ); Input.displayName = 'Input'; // Usage Example: /* } /> */ // ============================================ // CARD COMPONENT // ============================================ interface CardProps { children: React.ReactNode; className?: string; interactive?: boolean; onClick?: () => void; } export function Card({ children, className, interactive = false, onClick }: CardProps) { return (
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick?.(); } } : undefined } > {children}
); } export function CardHeader({ children, className }: { children: React.ReactNode; className?: string }) { return
{children}
; } export function CardTitle({ children, className }: { children: React.ReactNode; className?: string }) { return

{children}

; } export function CardDescription({ children, className }: { children: React.ReactNode; className?: string }) { return

{children}

; } export function CardBody({ children, className }: { children: React.ReactNode; className?: string }) { return
{children}
; } export function CardFooter({ children, className }: { children: React.ReactNode; className?: string }) { return
{children}
; } // Usage Example: /* Project Overview Track your project's progress

Your project is 75% complete

console.log('Clicked')}> Click me! */ // ============================================ // BADGE COMPONENT // ============================================ interface BadgeProps { children: React.ReactNode; variant?: 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'outline'; className?: string; } export function Badge({ children, variant = 'primary', className }: BadgeProps) { const variantClasses = { primary: 'badge-primary', secondary: 'badge-secondary', success: 'badge-success', warning: 'badge-warning', danger: 'badge-danger', outline: 'badge-outline', }; return ( {children} ); } // Usage Example: /* Active Pending Failed */ // ============================================ // ALERT COMPONENT // ============================================ interface AlertProps { children: React.ReactNode; variant?: 'info' | 'success' | 'warning' | 'danger'; title?: string; onClose?: () => void; className?: string; } export function Alert({ children, variant = 'info', title, onClose, className }: AlertProps) { const variantClasses = { info: 'alert-info', success: 'alert-success', warning: 'alert-warning', danger: 'alert-danger', }; const icons = { info: ( ), success: ( ), warning: ( ), danger: ( ), }; return (
{icons[variant]}
{title &&
{title}
}
{children}
{onClose && ( )}
); } // Usage Example: /* Your changes have been saved successfully. console.log('Closed')}> Failed to save changes. Please try again. */ // ============================================ // MODAL COMPONENT // ============================================ interface ModalProps { isOpen: boolean; onClose: () => void; children: React.ReactNode; title?: string; className?: string; } export function Modal({ isOpen, onClose, children, title, className }: ModalProps) { if (!isOpen) return null; return (
e.stopPropagation()} role="dialog" aria-modal="true" aria-labelledby={title ? 'modal-title' : undefined} > {title && (
)} {children}
); } export function ModalBody({ children, className }: { children: React.ReactNode; className?: string }) { return
{children}
; } export function ModalFooter({ children, className }: { children: React.ReactNode; className?: string }) { return
{children}
; } // Usage Example: /* function Example() { const [isOpen, setIsOpen] = useState(false); return ( <> setIsOpen(false)} title="Confirm Action">

Are you sure you want to proceed with this action?

); } */ // ============================================ // SKELETON LOADING COMPONENT // ============================================ interface SkeletonProps { className?: string; variant?: 'text' | 'title' | 'avatar' | 'card' | 'rect'; width?: string; height?: string; } export function Skeleton({ className, variant = 'text', width, height }: SkeletonProps) { const variantClasses = { text: 'skeleton-text', title: 'skeleton-title', avatar: 'skeleton-avatar', card: 'skeleton-card', rect: '', }; const style: React.CSSProperties = {}; if (width) style.width = width; if (height) style.height = height; return (
); } // Usage Example: /* // Loading card // Loading list
{[1, 2, 3].map((i) => (
))}
*/ // ============================================ // EMPTY STATE COMPONENT // ============================================ interface EmptyStateProps { icon?: React.ReactNode; title: string; description?: string; action?: React.ReactNode; className?: string; } export function EmptyState({ icon, title, description, action, className }: EmptyStateProps) { return (
{icon &&
{icon}
}

{title}

{description &&

{description}

} {action &&
{action}
}
); } // Usage Example: /* } title="No projects yet" description="Get started by creating your first project" action={ } /> */ // ============================================ // ERROR STATE COMPONENT // ============================================ interface ErrorStateProps { title: string; message: string; onRetry?: () => void; onGoBack?: () => void; className?: string; } export function ErrorState({ title, message, onRetry, onGoBack, className }: ErrorStateProps) { return (

{title}

{message}

{onGoBack && ( )} {onRetry && ( )}
); } // Usage Example: /* refetch()} onGoBack={() => navigate('/')} /> */ // ============================================ // AVATAR COMPONENT // ============================================ interface AvatarProps { src?: string; alt?: string; fallback?: string; size?: 'sm' | 'md' | 'lg'; className?: string; } export function Avatar({ src, alt, fallback, size = 'md', className }: AvatarProps) { const [imageError, setImageError] = useState(false); const sizeClasses = { sm: 'avatar-sm', md: '', lg: 'avatar-lg', }; const showFallback = !src || imageError; const initials = fallback ? fallback .split(' ') .map((n) => n[0]) .join('') .toUpperCase() .slice(0, 2) : '?'; return (
{showFallback ? ( {initials} ) : ( {alt setImageError(true)} /> )}
); } // Usage Example: /* // Falls back to initials */