Skip to Content
📚 MyStoryFlow Docs — Your guide to preserving family stories
Components

Component Library

MyStoryFlow uses a comprehensive component library built with React, TypeScript, and Tailwind CSS. Components are organized in the /packages/ui package and shared across all applications.

Design System

Design Principles

  • Accessibility First - WCAG 2.1 AA compliance
  • Mobile Responsive - Touch-friendly and responsive design
  • Consistent Branding - Cohesive visual identity
  • Performance Optimized - Lightweight and fast components

Color Palette

/* Primary Colors - Warm Orange/Amber */ --primary-50: #fffbeb; --primary-100: #fef3c7; --primary-600: #d97706; --primary-800: #92400e; /* Secondary Colors - Teal/Blue */ --secondary-50: #f0fdfa; --secondary-600: #0d9488; --secondary-800: #115e59; /* Neutral Colors */ --gray-50: #f9fafb; --gray-600: #4b5563; --gray-900: #111827; ``` --> ### Typography ```css /* Font Families */ --font-sans: 'Inter', system-ui, sans-serif; --font-serif: 'Crimson Pro', Georgia, serif; /* Font Sizes */ --text-xs: 0.75rem; /* 12px */ --text-sm: 0.875rem; /* 14px */ --text-base: 1rem; /* 16px */ --text-lg: 1.125rem; /* 18px */ --text-xl: 1.25rem; /* 20px */ --text-2xl: 1.5rem; /* 24px */ ``` --> ## Core Components ### Button Versatile button component with multiple variants and sizes. ```tsx import { Button } from '@mystoryflow/ui' // Basic usage <Button>Click me</Button> // Variants <Button variant="primary">Primary</Button> <Button variant="secondary">Secondary</Button> <Button variant="outline">Outline</Button> <Button variant="ghost">Ghost</Button> <Button variant="destructive">Delete</Button> // Sizes <Button size="sm">Small</Button> <Button size="md">Medium</Button> <Button size="lg">Large</Button> // States <Button loading>Loading...</Button> <Button disabled>Disabled</Button> // With icons <Button> <Plus className="h-4 w-4 mr-2" /> Add Story </Button> ``` --> **Props:** - `variant`: `primary` | `secondary` | `outline` | `ghost` | `destructive` - `size`: `sm` | `md` | `lg` - `loading`: boolean - `disabled`: boolean - `onClick`: function - `children`: ReactNode ### Card Container component for grouping related content. ```tsx import { Card } from '@mystoryflow/ui' // Basic card <Card> <h3>Card Title</h3> <p>Card content goes here.</p> </Card> // With padding variants <Card padding="sm">Small padding</Card> <Card padding="lg">Large padding</Card> // With hover effects <Card hoverable> <p>Hover over me</p> </Card> // Interactive card <Card clickable onClick={() => console.log('Card clicked')} > <p>Click me</p> </Card> ``` --> **Props:** - `padding`: `none` | `sm` | `md` | `lg` - `hoverable`: boolean - `clickable`: boolean - `onClick`: function - `className`: string - `children`: ReactNode ### Input Form input component with validation and error states. ```tsx import { Input } from '@mystoryflow/ui' // Basic input <Input placeholder="Enter your name" value={name} onChange={(e) => setName(e.target.value)} /> // With label <Input label="Email Address" type="email" required /> // With error state <Input label="Password" type="password" error="Password must be at least 8 characters" /> // Different sizes <Input size="sm" placeholder="Small input" /> <Input size="lg" placeholder="Large input" /> // With icons <Input placeholder="Search stories..." leftIcon={<Search className="h-4 w-4" />} /> ``` --> **Props:** - `type`: HTML input types - `label`: string - `placeholder`: string - `value`: string - `onChange`: function - `error`: string - `required`: boolean - `disabled`: boolean - `size`: `sm` | `md` | `lg` - `leftIcon`: ReactNode - `rightIcon`: ReactNode ### Modal Overlay component for dialogs and forms. ```tsx import { Modal } from '@mystoryflow/ui' <Modal isOpen={isOpen} onClose={() => setIsOpen(false)} title="Create New Story" > <p>Modal content goes here.</p> <div className="flex gap-2 mt-4"> <Button onClick={() => setIsOpen(false)}> Cancel </Button> <Button variant="primary"> Create Story </Button> </div> </Modal> // Different sizes <Modal size="sm">Small modal</Modal> <Modal size="lg">Large modal</Modal> <Modal size="full">Full screen modal</Modal> // Without close button <Modal isOpen={isOpen} onClose={() => setIsOpen(false)} hideCloseButton > <p>No close button</p> </Modal> ``` --> **Props:** - `isOpen`: boolean - `onClose`: function - `title`: string - `size`: `sm` | `md` | `lg` | `full` - `hideCloseButton`: boolean - `children`: ReactNode ## Form Components ### TextArea Multi-line text input for longer content. ```tsx import { TextArea } from '@mystoryflow/ui' <TextArea label="Story Content" placeholder="Tell your story..." rows={6} value={content} onChange={(e) => setContent(e.target.value)} /> // With character count <TextArea label="Description" maxLength={500} showCharacterCount /> // Auto-resize <TextArea placeholder="Auto-resizing textarea" autoResize minRows={3} maxRows={10} /> ``` --> ### Select Dropdown selection component. ```tsx import { Select } from '@mystoryflow/ui' <Select label="Relationship Type" value={relationship} onChange={setRelationship} options={[ { value: 'parent', label: 'Parent' }, { value: 'grandparent', label: 'Grandparent' }, { value: 'spouse', label: 'Spouse' }, { value: 'other', label: 'Other Family Member' } ]} /> // With search <Select label="Story Category" searchable placeholder="Search categories..." options={categories} /> // Multi-select <Select label="Tags" multiple value={selectedTags} onChange={setSelectedTags} options={tagOptions} /> ``` --> ### FileUpload File upload component with drag-and-drop support. ```tsx import { FileUpload } from '@mystoryflow/ui' <FileUpload accept="image/*" maxSize={5 * 1024 * 1024} // 5MB onUpload={(files) => handleUpload(files)} > <p>Drag photos here or click to browse</p> </FileUpload> // Multiple files <FileUpload multiple accept="image/*,audio/*" onUpload={handleMultipleFiles} > <p>Upload photos and audio files</p> </FileUpload> // With preview <FileUpload accept="image/*" showPreview onUpload={handleImageUpload} /> ``` --> ## Navigation Components ### AppHeader Main application header with navigation and user menu. ```tsx import { AppHeader } from '@mystoryflow/ui' <AppHeader user={currentUser} active="campaigns" onLogout={handleLogout} /> // With custom navigation items <AppHeader user={currentUser} navigationItems={[ { href: '/campaigns', label: 'Campaigns', icon: BookOpen }, { href: '/stories', label: 'Stories', icon: FileText }, { href: '/books', label: 'Books', icon: Book } ]} /> ``` --> ### Breadcrumb Navigation breadcrumb component. ```tsx import { Breadcrumb } from '@mystoryflow/ui' ;<Breadcrumb items={[ { href: '/campaigns', label: 'Campaigns' }, { href: '/campaigns/123', label: "Dad's Stories" }, { label: 'Story Details' }, ]} /> ``` --> ### Tabs Tab navigation component. ```tsx import { Tabs } from '@mystoryflow/ui' ;<Tabs value={activeTab} onChange={setActiveTab} tabs={[ { value: 'stories', label: 'Stories', count: 8 }, { value: 'family', label: 'Family', count: 5 }, { value: 'settings', label: 'Settings' }, ]} > <TabPanel value="stories"> <StoriesList /> </TabPanel> <TabPanel value="family"> <FamilyList /> </TabPanel> <TabPanel value="settings"> <CampaignSettings /> </TabPanel> </Tabs> ``` --> ## Data Display Components ### Table Data table with sorting, filtering, and pagination. ```tsx import { Table } from '@mystoryflow/ui' <Table columns={[ { key: 'title', label: 'Story Title', sortable: true }, { key: 'category', label: 'Category' }, { key: 'wordCount', label: 'Words', sortable: true }, { key: 'createdAt', label: 'Created', sortable: true } ]} data={stories} onSort={handleSort} pagination={{ page: currentPage, pageSize: 10, total: totalStories, onChange: setCurrentPage }} /> // With row actions <Table columns={columns} data={data} actions={[ { label: 'Edit', onClick: handleEdit }, { label: 'Delete', onClick: handleDelete, variant: 'destructive' } ]} /> ``` --> ### Badge Small status and category indicators. ```tsx import { Badge } from '@mystoryflow/ui' // Status badges <Badge variant="success">Completed</Badge> <Badge variant="warning">In Progress</Badge> <Badge variant="error">Failed</Badge> // Category badges <Badge variant="primary">Childhood</Badge> <Badge variant="secondary">Career</Badge> // With icons <Badge variant="success"> <CheckCircle className="h-3 w-3 mr-1" /> Complete </Badge> ``` --> ### ProgressBar Visual progress indicators. ```tsx import { ProgressBar } from '@mystoryflow/ui' // Basic progress bar <ProgressBar value={65} max={100} /> // With label <ProgressBar value={8} max={12} label="Stories completed" showPercentage /> // Different sizes <ProgressBar value={75} size="sm" /> <ProgressBar value={75} size="lg" /> // Different colors <ProgressBar value={90} variant="success" /> <ProgressBar value={45} variant="warning" /> ``` --> ## Feedback Components ### Toast Notification messages for user feedback. ```tsx import { toast } from '@mystoryflow/ui' // Success message toast.success('Story saved successfully!') // Error message toast.error('Failed to save story') // Info message toast.info('Campaign shared with family') // Custom toast toast.custom( <div className="flex items-center gap-2"> <BookOpen className="h-5 w-5" /> <span>Book is ready to order!</span> </div> ) ``` --> ### Alert Contextual feedback and information messages. ```tsx import { Alert } from '@mystoryflow/ui' // Info alert <Alert variant="info"> <Info className="h-4 w-4" /> <AlertTitle>Campaign Tips</AlertTitle> <AlertDescription> Aim for 10-12 stories to create a meaningful book. </AlertDescription> </Alert> // Warning alert <Alert variant="warning"> <AlertTriangle className="h-4 w-4" /> <AlertTitle>Storage Almost Full</AlertTitle> <AlertDescription> You've used 90% of your storage. Consider upgrading. </AlertDescription> </Alert> // Error alert <Alert variant="error"> <XCircle className="h-4 w-4" /> <AlertTitle>Upload Failed</AlertTitle> <AlertDescription> The file size is too large. Please choose a smaller file. </AlertDescription> </Alert> ``` --> ### Loading Loading states and spinners. ```tsx import { Loading, Spinner } from '@mystoryflow/ui' // Full page loading <Loading /> // Inline spinner <Spinner size="sm" /> <Spinner size="lg" /> // Loading with message <Loading message="Creating your book..." /> // Skeleton loading <div className="space-y-4"> <Skeleton className="h-4 w-full" /> <Skeleton className="h-4 w-3/4" /> <Skeleton className="h-4 w-1/2" /> </div> ``` --> ## MyStoryFlow-Specific Components ### CampaignCard Display campaign information in a card format. ```tsx import { CampaignCard } from '@mystoryflow/ui' ;<CampaignCard campaign={{ id: '123', title: "Dad's Life Stories", storyteller_name: 'Robert Johnson', relationship_type: 'parent', story_count: 8, goal_stories: 12, word_count: 15000, status: 'active', }} onView={() => navigate(`/campaigns/123`)} onEdit={() => navigate(`/campaigns/123/settings`)} /> ``` --> ### StoryEditor Rich text editor for story content. ```tsx import { StoryEditor } from '@mystoryflow/ui' ;<StoryEditor value={storyContent} onChange={setStoryContent} placeholder="Start writing your story..." onSave={handleSave} autoSave wordCount /> ``` --> ### BookPreview Preview component for book design. ```tsx import { BookPreview } from '@mystoryflow/ui' ;<BookPreview book={{ title: "Dad's Stories", stories: stories, template: 'classic', coverImage: '/cover.jpg', }} onPageChange={setCurrentPage} showPageNumbers /> ``` --> ### FamilyInvite Component for inviting family members. ```tsx import { FamilyInvite } from '@mystoryflow/ui' ;<FamilyInvite campaignId="123" onInviteSent={(email) => toast.success(`Invitation sent to ${email}`)} onError={(error) => toast.error(error)} /> ``` --> ## Utility Components ### Container Responsive container with max-width constraints. ```tsx import { Container } from '@mystoryflow/ui' <Container> <h1>Page Content</h1> </Container> // Different sizes <Container size="sm">Small container</Container> <Container size="lg">Large container</Container> <Container size="full">Full width</Container> ``` --> ### Grid Responsive grid system. ```tsx import { Grid, GridItem } from '@mystoryflow/ui' <Grid cols={3} gap={4}> <GridItem>Item 1</GridItem> <GridItem>Item 2</GridItem> <GridItem>Item 3</GridItem> </Grid> // Responsive grid <Grid cols={{ sm: 1, md: 2, lg: 3 }}> <GridItem>Responsive item</GridItem> </Grid> ``` --> ### Stack Vertical and horizontal stacking component. ```tsx import { Stack } from '@mystoryflow/ui' // Vertical stack <Stack direction="vertical" spacing={4}> <div>Item 1</div> <div>Item 2</div> <div>Item 3</div> </Stack> // Horizontal stack <Stack direction="horizontal" spacing={2} align="center"> <Button>Cancel</Button> <Button variant="primary">Save</Button> </Stack> ``` --> ## Hooks ### useToast Hook for managing toast notifications. ```tsx import { useToast } from '@mystoryflow/ui' function MyComponent() { const { toast } = useToast() const handleSave = async () => { try { await saveStory() toast.success('Story saved!') } catch (error) { toast.error('Failed to save story') } } } ``` --> ### useModal Hook for managing modal state. ```tsx import { useModal } from '@mystoryflow/ui' function MyComponent() { const { isOpen, open, close } = useModal() return ( <> <Button onClick={open}>Open Modal</Button> <Modal isOpen={isOpen} onClose={close}> <p>Modal content</p> </Modal> </> ) } ``` --> ### useLocalStorage Hook for persisting state in localStorage. ```tsx import { useLocalStorage } from '@mystoryflow/ui' function MyComponent() { const [preferences, setPreferences] = useLocalStorage('user-preferences', { theme: 'light', autoSave: true, }) return ( <div> <p>Theme: {preferences.theme}</p> <Button onClick={() => setPreferences({ ...preferences, theme: 'dark' })}> Switch to Dark </Button> </div> ) } ``` --> ## Development ### Adding New Components ```bash # Create new component mkdir packages/ui/src/components/MyComponent cd packages/ui/src/components/MyComponent # Create component files touch index.tsx touch MyComponent.tsx touch MyComponent.test.tsx touch MyComponent.stories.tsx ``` --> ### Component Template ```tsx // MyComponent.tsx import React from 'react' import { cn } from '../../lib/utils' interface MyComponentProps { children: React.ReactNode variant?: 'default' | 'primary' | 'secondary' size?: 'sm' | 'md' | 'lg' className?: string } export const MyComponent = React.forwardRef<HTMLDivElement, MyComponentProps>( ( { children, variant = 'default', size = 'md', className, ...props }, ref ) => { return ( <div ref={ref} className={cn( 'base-styles', { 'variant-styles': variant === 'primary', 'size-styles': size === 'lg', }, className )} {...props} > {children} </div> ) } ) MyComponent.displayName = 'MyComponent' ``` --> ### Testing Components ```tsx // MyComponent.test.tsx import { render, screen } from '@testing-library/react' import { MyComponent } from './MyComponent' describe('MyComponent', () => { it('renders children correctly', () => { render(<MyComponent>Test content</MyComponent>) expect(screen.getByText('Test content')).toBeInTheDocument() }) it('applies variant classes', () => { render(<MyComponent variant="primary">Content</MyComponent>) expect(screen.getByText('Content')).toHaveClass('variant-styles') }) }) ``` --> --- For more information on component development and contribution guidelines, see the [Development Guide](/development).