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).