# Button Component Implementation Plan ## 1. TypeScript Type Definitions ### Discriminated Union Variant Type ```typescript type ButtonVariant = | 'primary' | 'outline-gold' | 'outline-dark' | 'outline-light' | 'ghost' | 'circular' | 'icon-edit' | 'icon-delete' | 'file-upload'; ``` ### Props Interface (Discriminated Union via Generics) ```typescript type TButtonBaseProps = { /** * Button variant — controls visual style and behavior. */ variant: ButtonVariant; /** * Button size: 'small' | 'medium' | 'large'. * Maps to the design specs: primary/outline-gold=large, outline-dark/outline-light/ghost=medium, * circular/icon-edit/icon-delete=small, file-upload=medium. */ size?: 'small' | 'medium' | 'large'; /** * Whether the button is disabled. */ disabled?: boolean; /** * Click handler. */ onClick?: (e: React.MouseEvent) => void; /** * Optional HTML button type attribute. */ type?: 'button' | 'submit' | 'reset'; /** * Optional className for external styling overrides. */ className?: string; /** * Optional aria-label for accessibility. */ 'aria-label'?: string; }; type TTextButtonProps = TButtonBaseProps & { /** * Button text content. Required for text-based variants. */ children: React.ReactNode; }; type TIconButtonProps = TButtonBaseProps & { /** * Icon children only — no text. For circular, icon-edit, icon-delete variants. */ children: React.ReactNode; variant: 'circular' | 'icon-edit' | 'icon-delete'; }; type TFileUploadProps = TButtonBaseProps & { children: React.ReactNode; variant: 'file-upload'; /** * File accept attribute (e.g., 'image/*', '.pdf'). */ accept?: string; }; type TButtonProps = TTextButtonProps | TIconButtonProps | TFileUploadProps; ``` **Rationale:** - Using a union of `TButtonProps` variants enforces at the type level that icon-only variants (`circular`, `icon-edit`, `icon-delete`) are distinct from text variants. This prevents developers from passing text to icon-only buttons. - `size` is optional — the component maps the variant to the correct size automatically, but allows override for flexibility. - `TTextButtonProps` is the broadest type; `TIconButtonProps` and `TFileUploadProps` narrow the `variant` field. TypeScript will correctly narrow `children` and `variant` when discriminated. --- ## 2. Component Structure (JSX) ```tsx const Button: FC = ({ variant, size, disabled = false, onClick, type = 'button', className = '', 'aria-label': ariaLabel, children, accept, }) => { // Auto-size mapping: variant → size const resolvedSize = size ?? variantToSize[variant]; // Build class name: base + variant + size + state const classes = [ 'Button', `Button--${variant}`, `Button--${resolvedSize}`, disabled && 'Button--disabled', className, ] .filter(Boolean) .join(' '); // File-upload variant renders as a label wrapping an input if (variant === 'file-upload') { return ( ); } return ( ); }; ``` **Key Design Decisions:** - **`