mirror of
https://github.com/daffy0208/ai-dev-standards.git
synced 2026-09-14 20:49:49 +08:00
e5da92f751
Comprehensive design resources for building aesthetically pleasing, accessible websites. ## Phase 3 Overview: 75 New Resources - 35 UI components (overlays, inputs, layouts, utilities) - 12 integrations (media, icons, fonts, design tools) - 11 tools (image optimization, color utilities, accessibility) - 14 MCPs (AI design automation, Figma sync, documentation) - 3 skills (figma-developer, asset-manager, prototype-designer) ## Phase 3.1: Image Management Suite Integrations (3): - Cloudinary: CDN with URL-based transformations, face detection - Imgix: Real-time image processing via URL parameters - Unsplash: Stock photo API with search and collections Components (6): - Image: Optimized lazy-loading with srcset/sizes, blur placeholder - Gallery: Lightbox gallery with grid/masonry layout, keyboard navigation - Carousel: Touch-enabled carousel with autoplay, indicators - ImageUpload: Drag-and-drop uploader with preview and validation - Avatar: User avatar with fallback initials and status indicator - MediaViewer: Full-screen media viewer with zoom/pan Tools (3): - image-optimizer-tool: Sharp-based optimization (compression, resizing) - responsive-image-generator-tool: Generate srcset and sizes attributes - webp-converter-tool: Convert images to WebP format ## Phase 3.2: Icon & Font Systems Integrations (5): - Heroicons: 500+ icons by Tailwind team (outline/solid variants) - Lucide: 1000+ beautiful open-source icons - Font Awesome: Thousands of icons with multiple styles - Google Fonts: 1400+ fonts with pairing suggestions - Adobe Fonts: Premium web font service integration Components (2): - Icon: Universal icon component supporting all libraries - IconPicker: Icon search and selection interface with previews MCPs (2): - icon-library-mcp: Icon search, management, and SVG generation - font-optimizer-mcp: Font subsetting, optimization, and loading Tools (2): - font-pairing-tool: AI-powered font combination suggestions - type-scale-generator-tool: Generate modular typography scales ## Phase 3.3-3.5: Advanced UI Components (35 components) All with TypeScript, Tailwind CSS + CVA, WCAG 2.1 AA accessibility, full keyboard navigation. Overlays & Dialogs (5): - Tooltip: 4 placements, portal rendering, ARIA describedby - Popover: Floating popover with click/hover triggers - Accordion: Collapsible sections with single/multi-expand modes - Drawer: Slide-out panel from 4 sides with focus trap - Dialog: Accessible modal with backdrop and ESC handling Advanced Inputs (5): - Slider: Range slider with marks, tooltips, dual handles - ColorPicker: HEX/RGB/HSL modes with presets and alpha - DatePicker: Calendar picker with range selection, min/max - TimePicker: 12/24hr formats with keyboard input - FileUpload: Drag-and-drop with preview and validation Feedback & Navigation (6): - Progress: Linear/circular with determinate/indeterminate states - Badge: Notification badge with dot/count variants - Alert: Message alerts (info, success, warning, error) - Breadcrumb: Breadcrumb navigation with custom separators - Stepper: Step-by-step progress indicator - Rating: Star rating with half-star support Layout Components (10): - Grid: CSS Grid wrapper with responsive columns - Container: Max-width container with responsive padding - Stack: Flexbox stack (vertical/horizontal) with spacing - Spacer: Flexible spacing component - Divider: Visual divider (horizontal/vertical) - AspectRatio: Maintain aspect ratio wrapper - Center: Center content horizontally and vertically - Switch: Toggle switch with ARIA switch pattern - Checkbox: Checkbox with indeterminate state - Radio: Radio button with RadioGroup Enhanced Existing (9): - Pagination: Intelligent page number display with ellipsis - Select: Enhanced with search and multi-select - Tabs: Compound component with controlled/uncontrolled modes - Table: Sortable with ARIA table semantics - Skeleton: Loading placeholder (enhanced version) - Button: Added loading states and variants - Card: Enhanced with composable sections - Input: Added password toggle and validation states - Modal: Enhanced with focus trap and body scroll lock ## Phase 3.6: AI & Design Tool MCPs (7 MCPs) - image-generator-mcp: DALL-E, Midjourney, Stable Diffusion integration - svg-generator-mcp: Programmatic SVG generation with templates - illustration-generator-mcp: AI-powered custom illustrations - figma-sync-mcp: Figma-to-code with token extraction - design-handoff-mcp: Automated design handoff workflows - theme-builder-mcp: Generate complete themes from base colors - dark-mode-converter-mcp: Light-to-dark palette transformation ## Phase 3.7: Design Tool Integrations (4 integrations + 3 skills) Integrations: - Figma: Complete REST API client with token extraction - Sketch: Sketch API integration for design assets - Adobe XD: Adobe XD API for prototypes and specs - Canva: Canva API for template and asset management Skills: - figma-developer: Figma-to-code workflows and automation - asset-manager: Asset organization, optimization, brand libraries - prototype-designer: Interactive prototypes and user testing ## Phase 3.8: Design Tools & Utilities (6 tools + 5 MCPs) Tools: - color-palette-generator-tool: Generate harmonious palettes with WCAG - contrast-checker-tool: WCAG 2.1 AA/AAA contrast validation - color-scheme-tool: Complementary, analogous, triadic schemes - breakpoint-analyzer-tool: Analyze responsive breakpoints - color-blindness-simulator-tool: Simulate deuteranopia, protanopia, tritanopia - touch-target-validator-tool: Validate 44px minimum touch targets MCPs: - storybook-generator-mcp: Auto-generate Storybook stories - props-documenter-mcp: Component props documentation - typography-analyzer-mcp: Typography analysis and optimization - asset-library-mcp: Design asset library management - responsive-preview-mcp: Multi-device preview generation ## Registry Updates (Phase 3.9) Updated all META registries with Phase 3 resources: - component-registry.json: 35 → 70 components (+35) - integration-registry.json: 13 → 25 integrations (+12) - tool-registry.json: 13 → 24 tools (+11) - mcp-registry.json: 34 → 48 MCPs (+14) - skill-registry.json: 42 → 45 skills (+3) - .claude/CLAUDE.md: Updated resource counts (150 → 216) - README.md: Updated resource counts New Categories: - Components: media (6) - Integrations: media-services (3), icon-libraries (3), font-services (2), design-tools (4) - Tools: media-tools (3), design-tools (8) - MCPs: design category expanded (2 → 16) ## Also Included - 7 comprehensive playbooks (RAG, multi-agent, MCP dev, component dev, monitoring, performance, security) - 4 observability tools (logger, metrics, tracer, error-tracker) - 4 workflow orchestration components (task-queue, workflow-orchestrator, state-manager, event-bus) - 3 MCP server handler components (tool-handler, resource-handler, prompt-handler) - Phase 2 CLI components and tools (auth, forms, errors, feedback) Total Resources: 216 (was 150) All resources discoverable via CLI and relationship mapping system. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
18 KiB
18 KiB
Component Development Playbook
Comprehensive guide for creating reusable, accessible, and well-tested components.
Table of Contents
- Overview
- Component Design Principles
- TypeScript Patterns
- Accessibility Guidelines
- Testing Components
- Documentation
- Storybook Setup
- Component Library Publishing
- Common Pitfalls
- Troubleshooting
Overview
Building reusable components requires careful attention to API design, accessibility, performance, and developer experience.
Component Types
- Presentational - Pure UI, no business logic
- Container - Connect to state/data
- Layout - Structure and positioning
- Utility - Shared functionality (hooks, contexts)
Quality Checklist
- TypeScript types for all props
- Full accessibility support (ARIA, keyboard)
- Comprehensive tests (unit, integration, visual)
- Documentation with examples
- Performance optimized (memoization, lazy loading)
- Mobile responsive
- Dark mode support
- Error boundaries
Component Design Principles
1. Single Responsibility
// ❌ Bad: Component does too much
function UserDashboard() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Fetch user
// Fetch posts
// Handle errors
}, []);
return (
<div>
{/* User profile */}
{/* Posts list */}
{/* Analytics */}
{/* Settings */}
</div>
);
}
// ✅ Good: Separated concerns
function UserDashboard() {
return (
<DashboardLayout>
<UserProfile />
<PostsList />
<Analytics />
<Settings />
</DashboardLayout>
);
}
2. Composability
// ✅ Composable Card component
interface CardProps {
children: React.ReactNode;
className?: string;
}
function Card({ children, className }: CardProps) {
return (
<div className={`card ${className || ''}`}>
{children}
</div>
);
}
function CardHeader({ children }: { children: React.ReactNode }) {
return <div className="card-header">{children}</div>;
}
function CardBody({ children }: { children: React.ReactNode }) {
return <div className="card-body">{children}</div>;
}
function CardFooter({ children }: { children: React.ReactNode }) {
return <div className="card-footer">{children}</div>;
}
// Compose as needed
Card.Header = CardHeader;
Card.Body = CardBody;
Card.Footer = CardFooter;
// Usage
<Card>
<Card.Header>
<h2>Title</h2>
</Card.Header>
<Card.Body>
<p>Content</p>
</Card.Body>
<Card.Footer>
<Button>Action</Button>
</Card.Footer>
</Card>
3. Controlled vs Uncontrolled
// Controlled component
interface ControlledInputProps {
value: string;
onChange: (value: string) => void;
}
function ControlledInput({ value, onChange }: ControlledInputProps) {
return (
<input
value={value}
onChange={(e) => onChange(e.target.value)}
/>
);
}
// Uncontrolled component
interface UncontrolledInputProps {
defaultValue?: string;
onBlur?: (value: string) => void;
}
function UncontrolledInput({ defaultValue, onBlur }: UncontrolledInputProps) {
const inputRef = useRef<HTMLInputElement>(null);
return (
<input
ref={inputRef}
defaultValue={defaultValue}
onBlur={() => onBlur?.(inputRef.current?.value || '')}
/>
);
}
// Hybrid: Support both patterns
interface FlexibleInputProps {
value?: string;
defaultValue?: string;
onChange?: (value: string) => void;
}
function FlexibleInput({ value, defaultValue, onChange }: FlexibleInputProps) {
const [internalValue, setInternalValue] = useState(defaultValue || '');
const isControlled = value !== undefined;
const currentValue = isControlled ? value : internalValue;
const handleChange = (newValue: string) => {
if (!isControlled) {
setInternalValue(newValue);
}
onChange?.(newValue);
};
return (
<input
value={currentValue}
onChange={(e) => handleChange(e.target.value)}
/>
);
}
TypeScript Patterns
Proper Prop Types
import React from 'react';
// Basic props
interface ButtonProps {
children: React.ReactNode;
variant?: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
loading?: boolean;
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
}
// Extend HTML attributes
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'danger';
loading?: boolean;
}
// Generic component
interface SelectProps<T> {
options: T[];
value: T | null;
onChange: (value: T) => void;
getOptionLabel: (option: T) => string;
getOptionValue: (option: T) => string | number;
}
function Select<T>({
options,
value,
onChange,
getOptionLabel,
getOptionValue
}: SelectProps<T>) {
return (
<select
value={value ? getOptionValue(value) : ''}
onChange={(e) => {
const selected = options.find(
opt => getOptionValue(opt).toString() === e.target.value
);
if (selected) onChange(selected);
}}
>
{options.map(option => (
<option key={getOptionValue(option)} value={getOptionValue(option)}>
{getOptionLabel(option)}
</option>
))}
</select>
);
}
// Discriminated unions
type ButtonProps =
| {
variant: 'link';
href: string;
onClick?: never;
}
| {
variant?: 'primary' | 'secondary';
href?: never;
onClick: () => void;
};
Polymorphic Components
type PolymorphicProps<E extends React.ElementType> = {
as?: E;
} & React.ComponentPropsWithoutRef<E>;
function Text<E extends React.ElementType = 'span'>({
as,
children,
...props
}: PolymorphicProps<E>) {
const Component = as || 'span';
return <Component {...props}>{children}</Component>;
}
// Usage
<Text>Default span</Text>
<Text as="p">Paragraph</Text>
<Text as="h1">Heading</Text>
<Text as="a" href="/link">Link</Text>
Accessibility Guidelines
ARIA Attributes
function Modal({
isOpen,
onClose,
title,
children
}: {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
}) {
useEffect(() => {
if (isOpen) {
// Trap focus
const previouslyFocused = document.activeElement as HTMLElement;
return () => {
previouslyFocused?.focus();
};
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
className="modal-overlay"
onClick={onClose}
>
<div
className="modal-content"
onClick={(e) => e.stopPropagation()}
>
<h2 id="modal-title">{title}</h2>
<button
aria-label="Close modal"
onClick={onClose}
>
×
</button>
{children}
</div>
</div>
);
}
Keyboard Navigation
function Dropdown({
trigger,
items,
onSelect
}: {
trigger: React.ReactNode;
items: Array<{ id: string; label: string }>;
onSelect: (id: string) => void;
}) {
const [isOpen, setIsOpen] = useState(false);
const [focusedIndex, setFocusedIndex] = useState(0);
const handleKeyDown = (e: React.KeyboardEvent) => {
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
setFocusedIndex((prev) => (prev + 1) % items.length);
break;
case 'ArrowUp':
e.preventDefault();
setFocusedIndex((prev) => (prev - 1 + items.length) % items.length);
break;
case 'Enter':
case ' ':
e.preventDefault();
onSelect(items[focusedIndex].id);
setIsOpen(false);
break;
case 'Escape':
setIsOpen(false);
break;
}
};
return (
<div onKeyDown={handleKeyDown}>
<button
aria-haspopup="true"
aria-expanded={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
{trigger}
</button>
{isOpen && (
<ul role="menu">
{items.map((item, index) => (
<li
key={item.id}
role="menuitem"
tabIndex={index === focusedIndex ? 0 : -1}
onClick={() => onSelect(item.id)}
>
{item.label}
</li>
))}
</ul>
)}
</div>
);
}
Screen Reader Support
function ProgressBar({
value,
max = 100,
label
}: {
value: number;
max?: number;
label: string;
}) {
const percentage = (value / max) * 100;
return (
<div
role="progressbar"
aria-valuenow={value}
aria-valuemin={0}
aria-valuemax={max}
aria-label={label}
aria-valuetext={`${percentage.toFixed(0)}%`}
>
<div
className="progress-fill"
style={{ width: `${percentage}%` }}
/>
<span className="sr-only">{label}: {percentage.toFixed(0)}%</span>
</div>
);
}
Testing Components
Unit Tests with Vitest + React Testing Library
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';
describe('Button', () => {
it('renders children', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', async () => {
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click me</Button>);
await userEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('is disabled when disabled prop is true', () => {
render(<Button disabled>Click me</Button>);
expect(screen.getByText('Click me')).toBeDisabled();
});
it('shows loading spinner when loading', () => {
render(<Button loading>Click me</Button>);
expect(screen.getByRole('status')).toBeInTheDocument();
});
it('applies variant classes', () => {
const { rerender } = render(<Button variant="primary">Primary</Button>);
expect(screen.getByText('Primary')).toHaveClass('btn-primary');
rerender(<Button variant="danger">Danger</Button>);
expect(screen.getByText('Danger')).toHaveClass('btn-danger');
});
});
Accessibility Tests
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
describe('Accessibility', () => {
it('should not have accessibility violations', async () => {
const { container } = render(
<Button onClick={() => {}}>Click me</Button>
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it('has correct ARIA attributes', () => {
render(<Button loading>Loading</Button>);
const button = screen.getByRole('button');
expect(button).toHaveAttribute('aria-busy', 'true');
});
it('supports keyboard navigation', async () => {
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click me</Button>);
const button = screen.getByRole('button');
button.focus();
await userEvent.keyboard('{Enter}');
expect(handleClick).toHaveBeenCalled();
handleClick.mockClear();
await userEvent.keyboard('{ }'); // Space
expect(handleClick).toHaveBeenCalled();
});
});
Integration Tests
describe('Form Integration', () => {
it('submits form with valid data', async () => {
const handleSubmit = vi.fn();
render(
<Form onSubmit={handleSubmit}>
<Input name="email" label="Email" type="email" />
<Input name="password" label="Password" type="password" />
<Button type="submit">Submit</Button>
</Form>
);
await userEvent.type(screen.getByLabelText('Email'), 'test@example.com');
await userEvent.type(screen.getByLabelText('Password'), 'password123');
await userEvent.click(screen.getByText('Submit'));
await waitFor(() => {
expect(handleSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123'
});
});
});
it('shows validation errors', async () => {
render(
<Form>
<Input name="email" label="Email" type="email" required />
<Button type="submit">Submit</Button>
</Form>
);
await userEvent.click(screen.getByText('Submit'));
expect(await screen.findByText('Email is required')).toBeInTheDocument();
});
});
Documentation
Component Documentation Template
/**
* Button component for user interactions
*
* @example
* ```tsx
* <Button variant="primary" onClick={handleClick}>
* Click me
* </Button>
* ```
*
* @example Loading state
* ```tsx
* <Button loading>Processing...</Button>
* ```
*/
export function Button({
children,
variant = 'primary',
size = 'md',
disabled = false,
loading = false,
onClick,
...props
}: ButtonProps) {
// Implementation
}
README Template
# Button Component
Versatile button component with multiple variants and states.
## Installation
```bash
npm install @yourorg/components
```
## Usage
```tsx
import { Button } from '@yourorg/components';
function App() {
return (
<Button variant="primary" onClick={handleClick}>
Click me
</Button>
);
}
```
## Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'primary' \| 'secondary' \| 'danger'` | `'primary'` | Visual style variant |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Button size |
| `disabled` | `boolean` | `false` | Disable the button |
| `loading` | `boolean` | `false` | Show loading spinner |
| `onClick` | `(event: MouseEvent) => void` | - | Click handler |
## Examples
### Primary Button
```tsx
<Button variant="primary">Primary</Button>
```
### Loading State
```tsx
<Button loading>Processing...</Button>
```
### Sizes
```tsx
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
```
## Accessibility
- Full keyboard support (Enter/Space)
- ARIA attributes for screen readers
- Focus management
- High contrast mode support
## Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Storybook Setup
Install Storybook
npx storybook@latest init
Write Stories
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger']
},
size: {
control: 'select',
options: ['sm', 'md', 'lg']
}
}
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
children: 'Button',
variant: 'primary'
}
};
export const Secondary: Story = {
args: {
children: 'Button',
variant: 'secondary'
}
};
export const Loading: Story = {
args: {
children: 'Loading',
loading: true
}
};
export const Disabled: Story = {
args: {
children: 'Disabled',
disabled: true
}
};
// Interactive story
export const AllVariants: Story = {
render: () => (
<div style={{ display: 'flex', gap: '1rem' }}>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
</div>
)
};
Component Library Publishing
Package Configuration
{
"name": "@yourorg/components",
"version": "1.0.0",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
"files": ["dist"],
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"test": "vitest",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
Rollup Configuration
// rollup.config.js
import typescript from '@rollup/plugin-typescript';
import postcss from 'rollup-plugin-postcss';
export default {
input: 'src/index.ts',
output: [
{
file: 'dist/index.js',
format: 'cjs'
},
{
file: 'dist/index.esm.js',
format: 'esm'
}
],
external: ['react', 'react-dom'],
plugins: [
typescript({ tsconfig: './tsconfig.json' }),
postcss({ extract: true, minimize: true })
]
};
Common Pitfalls
- Not memoizing expensive computations → Use
useMemo - Missing key props in lists → Always provide unique keys
- Not handling loading/error states → Show appropriate UI
- Poor accessibility → Follow ARIA guidelines
- No TypeScript types → Provide full type coverage
- Inconsistent API → Follow established patterns
Troubleshooting
Issue: Component Re-renders Too Often
Solution: Use React.memo and useCallback
const MemoizedComponent = React.memo(function Component({ data }) {
return <div>{data}</div>;
});
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
Issue: Accessibility Warnings
Solution: Add proper ARIA attributes and labels
Related Resources
Skills
- frontend-builder
- design-system-architect
- accessibility-engineer
Components
/COMPONENTS/ui-components/- UI component library
Last Updated: 2025-10-27 Version: 1.0.0