@intentsolutions/blueprint 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +117 -75
- package/dist/cli.js.map +1 -1
- package/dist/core/index.d.ts +62 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +137 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/interview/analyzer.d.ts +39 -0
- package/dist/interview/analyzer.d.ts.map +1 -0
- package/dist/interview/analyzer.js +353 -0
- package/dist/interview/analyzer.js.map +1 -0
- package/dist/interview/engine.d.ts +71 -0
- package/dist/interview/engine.d.ts.map +1 -0
- package/dist/interview/engine.js +194 -0
- package/dist/interview/engine.js.map +1 -0
- package/dist/interview/index.d.ts +9 -0
- package/dist/interview/index.d.ts.map +1 -0
- package/dist/interview/index.js +8 -0
- package/dist/interview/index.js.map +1 -0
- package/dist/interview/questions.d.ts +22 -0
- package/dist/interview/questions.d.ts.map +1 -0
- package/dist/interview/questions.js +353 -0
- package/dist/interview/questions.js.map +1 -0
- package/dist/interview/types.d.ts +84 -0
- package/dist/interview/types.d.ts.map +1 -0
- package/dist/interview/types.js +5 -0
- package/dist/interview/types.js.map +1 -0
- package/dist/mcp/index.d.ts +7 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +241 -0
- package/dist/mcp/index.js.map +1 -0
- package/package.json +30 -10
- package/templates/core/01_prd.md +465 -0
- package/templates/core/02_adr.md +432 -0
- package/templates/core/03_generate_tasks.md +418 -0
- package/templates/core/04_process_task_list.md +430 -0
- package/templates/core/05_market_research.md +483 -0
- package/templates/core/06_architecture.md +561 -0
- package/templates/core/07_competitor_analysis.md +462 -0
- package/templates/core/08_personas.md +367 -0
- package/templates/core/09_user_journeys.md +385 -0
- package/templates/core/10_user_stories.md +582 -0
- package/templates/core/11_acceptance_criteria.md +687 -0
- package/templates/core/12_qa_gate.md +737 -0
- package/templates/core/13_risk_register.md +605 -0
- package/templates/core/14_project_brief.md +477 -0
- package/templates/core/15_brainstorming.md +653 -0
- package/templates/core/16_frontend_spec.md +1479 -0
- package/templates/core/17_test_plan.md +878 -0
- package/templates/core/18_release_plan.md +994 -0
- package/templates/core/19_operational_readiness.md +1100 -0
- package/templates/core/20_metrics_dashboard.md +1375 -0
- package/templates/core/21_postmortem.md +1122 -0
- package/templates/core/22_playtest_usability.md +1624 -0
|
@@ -0,0 +1,1479 @@
|
|
|
1
|
+
# 🎨 Frontend Technical Specification
|
|
2
|
+
|
|
3
|
+
**Metadata**
|
|
4
|
+
- Last Updated: {{DATE}}
|
|
5
|
+
- Maintainer: AI-Dev Toolkit
|
|
6
|
+
- Related Docs: 01_prd.md, 07_architecture.md, 12_test_plan.md, 19_design_doc.md
|
|
7
|
+
|
|
8
|
+
> **🎯 Executive Summary**
|
|
9
|
+
> A comprehensive frontend technical specification defining the user interface architecture, component library, user experience patterns, and implementation standards. This document serves as the definitive guide for frontend development teams to deliver consistent, accessible, and performant user experiences.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 🚀 1. Frontend Architecture Overview
|
|
14
|
+
|
|
15
|
+
### 1.1 Architecture Philosophy
|
|
16
|
+
**Design Principles:**
|
|
17
|
+
- **Component-First Architecture:** Reusable, modular components as building blocks
|
|
18
|
+
- **Atomic Design System:** Hierarchical component organization (atoms → molecules → organisms)
|
|
19
|
+
- **Performance-Driven:** Optimized for loading speed and runtime performance
|
|
20
|
+
- **Accessibility-First:** WCAG 2.1 AA compliance by default
|
|
21
|
+
- **Mobile-First Responsive:** Progressive enhancement from mobile to desktop
|
|
22
|
+
|
|
23
|
+
### 1.2 Technology Stack
|
|
24
|
+
| Category | Technology | Version | Justification |
|
|
25
|
+
|----------|------------|---------|---------------|
|
|
26
|
+
| **Core Framework** | React | 18.2+ | Component ecosystem, performance, community |
|
|
27
|
+
| **Build Tool** | Vite | 4.5+ | Fast HMR, optimized builds, ES modules |
|
|
28
|
+
| **Language** | TypeScript | 5.0+ | Type safety, developer experience, maintainability |
|
|
29
|
+
| **Styling** | Tailwind CSS | 3.3+ | Utility-first, consistent design tokens |
|
|
30
|
+
| **State Management** | Zustand | 4.4+ | Simple, lightweight, TypeScript-friendly |
|
|
31
|
+
| **Routing** | React Router | 6.15+ | Standard routing solution, nested routes |
|
|
32
|
+
| **Testing** | Vitest + RTL | Latest | Fast testing, Jest-compatible API |
|
|
33
|
+
| **Linting** | ESLint + Prettier | Latest | Code quality, consistent formatting |
|
|
34
|
+
|
|
35
|
+
### 1.3 Project Structure
|
|
36
|
+
```
|
|
37
|
+
src/
|
|
38
|
+
├── components/ # Reusable UI components
|
|
39
|
+
│ ├── atoms/ # Basic building blocks (Button, Input, etc.)
|
|
40
|
+
│ ├── molecules/ # Component combinations (SearchBox, Card, etc.)
|
|
41
|
+
│ ├── organisms/ # Complex sections (Header, Sidebar, etc.)
|
|
42
|
+
│ └── templates/ # Page layouts and structure
|
|
43
|
+
├── pages/ # Route components and page logic
|
|
44
|
+
├── hooks/ # Custom React hooks
|
|
45
|
+
├── stores/ # State management (Zustand stores)
|
|
46
|
+
├── services/ # API calls and external integrations
|
|
47
|
+
├── utils/ # Helper functions and utilities
|
|
48
|
+
├── types/ # TypeScript type definitions
|
|
49
|
+
├── assets/ # Static assets (images, icons, fonts)
|
|
50
|
+
├── styles/ # Global styles and Tailwind config
|
|
51
|
+
└── tests/ # Test utilities and mocks
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 1.4 Performance Targets
|
|
55
|
+
| Metric | Target | Measurement Tool | Validation |
|
|
56
|
+
|--------|--------|------------------|------------|
|
|
57
|
+
| **First Contentful Paint** | <1.5s | Lighthouse | CI/CD checks |
|
|
58
|
+
| **Largest Contentful Paint** | <2.5s | Lighthouse | Performance budget |
|
|
59
|
+
| **Total Blocking Time** | <300ms | Lighthouse | Core Web Vitals |
|
|
60
|
+
| **Cumulative Layout Shift** | <0.1 | Lighthouse | Visual stability |
|
|
61
|
+
| **Bundle Size** | <500KB gzipped | Bundle analyzer | Build-time checks |
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 📱 2. Target Platforms & Device Support
|
|
66
|
+
|
|
67
|
+
### 2.1 Device Matrix
|
|
68
|
+
| Device Category | Screen Size | Viewport | Priority | Support Level |
|
|
69
|
+
|-----------------|-------------|----------|----------|---------------|
|
|
70
|
+
| **Mobile Phones** | 320-768px | Portrait/Landscape | Primary | Full support |
|
|
71
|
+
| **Tablets** | 768-1024px | Portrait/Landscape | Secondary | Full support |
|
|
72
|
+
| **Laptops** | 1024-1440px | Landscape | Primary | Full support |
|
|
73
|
+
| **Desktops** | 1440px+ | Landscape | Secondary | Full support |
|
|
74
|
+
| **Large Displays** | 1920px+ | Landscape | Tertiary | Basic support |
|
|
75
|
+
|
|
76
|
+
### 2.2 Browser Support Matrix
|
|
77
|
+
| Browser | Version | Market Share | Support Level | Testing Priority |
|
|
78
|
+
|---------|---------|--------------|---------------|------------------|
|
|
79
|
+
| **Chrome** | Last 2 versions | 65% | Full | Primary |
|
|
80
|
+
| **Safari** | Last 2 versions | 20% | Full | Primary |
|
|
81
|
+
| **Firefox** | Last 2 versions | 8% | Full | Secondary |
|
|
82
|
+
| **Edge** | Last 2 versions | 5% | Full | Secondary |
|
|
83
|
+
| **Samsung Internet** | Latest | 2% | Basic | Tertiary |
|
|
84
|
+
|
|
85
|
+
### 2.3 Accessibility Standards
|
|
86
|
+
#### WCAG 2.1 AA Compliance Requirements
|
|
87
|
+
- **Perceivable:** Color contrast 4.5:1, scalable text, alt text for images
|
|
88
|
+
- **Operable:** Keyboard navigation, no seizure triggers, sufficient time limits
|
|
89
|
+
- **Understandable:** Readable text, predictable functionality, input assistance
|
|
90
|
+
- **Robust:** Valid markup, assistive technology compatibility
|
|
91
|
+
|
|
92
|
+
#### Assistive Technology Support
|
|
93
|
+
- **Screen Readers:** NVDA, JAWS, VoiceOver, TalkBack
|
|
94
|
+
- **Keyboard Navigation:** Full functionality without mouse
|
|
95
|
+
- **Voice Control:** Dragon NaturallySpeaking, voice recognition
|
|
96
|
+
- **Magnification:** ZoomText, built-in browser zoom
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 🧩 3. Component Library & Design System
|
|
101
|
+
|
|
102
|
+
### 3.1 Design Token System
|
|
103
|
+
#### Color Palette
|
|
104
|
+
```typescript
|
|
105
|
+
const colors = {
|
|
106
|
+
// Primary brand colors
|
|
107
|
+
primary: {
|
|
108
|
+
50: '#eff6ff',
|
|
109
|
+
100: '#dbeafe',
|
|
110
|
+
500: '#3b82f6', // Main brand color
|
|
111
|
+
600: '#2563eb',
|
|
112
|
+
900: '#1e3a8a'
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// Semantic colors
|
|
116
|
+
success: { 500: '#10b981', 600: '#059669' },
|
|
117
|
+
warning: { 500: '#f59e0b', 600: '#d97706' },
|
|
118
|
+
error: { 500: '#ef4444', 600: '#dc2626' },
|
|
119
|
+
|
|
120
|
+
// Neutral grays
|
|
121
|
+
gray: {
|
|
122
|
+
50: '#f9fafb',
|
|
123
|
+
100: '#f3f4f6',
|
|
124
|
+
500: '#6b7280',
|
|
125
|
+
800: '#1f2937',
|
|
126
|
+
900: '#111827'
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### Typography Scale
|
|
132
|
+
```typescript
|
|
133
|
+
const typography = {
|
|
134
|
+
fontFamily: {
|
|
135
|
+
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
136
|
+
mono: ['JetBrains Mono', 'Menlo', 'monospace']
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
fontSize: {
|
|
140
|
+
xs: ['0.75rem', { lineHeight: '1rem' }],
|
|
141
|
+
sm: ['0.875rem', { lineHeight: '1.25rem' }],
|
|
142
|
+
base: ['1rem', { lineHeight: '1.5rem' }],
|
|
143
|
+
lg: ['1.125rem', { lineHeight: '1.75rem' }],
|
|
144
|
+
xl: ['1.25rem', { lineHeight: '1.75rem' }],
|
|
145
|
+
'2xl': ['1.5rem', { lineHeight: '2rem' }],
|
|
146
|
+
'3xl': ['1.875rem', { lineHeight: '2.25rem' }]
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Spacing & Layout
|
|
152
|
+
```typescript
|
|
153
|
+
const spacing = {
|
|
154
|
+
px: '1px',
|
|
155
|
+
0: '0',
|
|
156
|
+
1: '0.25rem', // 4px
|
|
157
|
+
2: '0.5rem', // 8px
|
|
158
|
+
4: '1rem', // 16px
|
|
159
|
+
6: '1.5rem', // 24px
|
|
160
|
+
8: '2rem', // 32px
|
|
161
|
+
12: '3rem', // 48px
|
|
162
|
+
16: '4rem' // 64px
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 3.2 Atomic Component Library
|
|
167
|
+
|
|
168
|
+
#### Atoms (Basic Elements)
|
|
169
|
+
**Button Component**
|
|
170
|
+
```typescript
|
|
171
|
+
interface ButtonProps {
|
|
172
|
+
variant: 'primary' | 'secondary' | 'outline' | 'ghost';
|
|
173
|
+
size: 'sm' | 'md' | 'lg';
|
|
174
|
+
disabled?: boolean;
|
|
175
|
+
loading?: boolean;
|
|
176
|
+
icon?: ReactNode;
|
|
177
|
+
children: ReactNode;
|
|
178
|
+
onClick?: () => void;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Usage patterns:
|
|
182
|
+
<Button variant="primary" size="md">Save Changes</Button>
|
|
183
|
+
<Button variant="outline" icon={<PlusIcon />}>Add Item</Button>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Input Component**
|
|
187
|
+
```typescript
|
|
188
|
+
interface InputProps {
|
|
189
|
+
type: 'text' | 'email' | 'password' | 'number';
|
|
190
|
+
label: string;
|
|
191
|
+
placeholder?: string;
|
|
192
|
+
error?: string;
|
|
193
|
+
required?: boolean;
|
|
194
|
+
disabled?: boolean;
|
|
195
|
+
value: string;
|
|
196
|
+
onChange: (value: string) => void;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Usage:
|
|
200
|
+
<Input
|
|
201
|
+
label="Email Address"
|
|
202
|
+
type="email"
|
|
203
|
+
required
|
|
204
|
+
error={formErrors.email}
|
|
205
|
+
value={email}
|
|
206
|
+
onChange={setEmail}
|
|
207
|
+
/>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Badge Component**
|
|
211
|
+
```typescript
|
|
212
|
+
interface BadgeProps {
|
|
213
|
+
variant: 'default' | 'success' | 'warning' | 'error';
|
|
214
|
+
size: 'sm' | 'md';
|
|
215
|
+
children: ReactNode;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Usage:
|
|
219
|
+
<Badge variant="success">Active</Badge>
|
|
220
|
+
<Badge variant="warning" size="sm">Pending</Badge>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Molecules (Component Combinations)
|
|
224
|
+
**Search Bar**
|
|
225
|
+
```typescript
|
|
226
|
+
interface SearchBarProps {
|
|
227
|
+
placeholder: string;
|
|
228
|
+
value: string;
|
|
229
|
+
onChange: (value: string) => void;
|
|
230
|
+
onSearch: (query: string) => void;
|
|
231
|
+
suggestions?: string[];
|
|
232
|
+
loading?: boolean;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Features:
|
|
236
|
+
// - Real-time search suggestions
|
|
237
|
+
// - Keyboard navigation (↑↓ arrows, Enter, Escape)
|
|
238
|
+
// - Debounced input for performance
|
|
239
|
+
// - Clear button when populated
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Form Field**
|
|
243
|
+
```typescript
|
|
244
|
+
interface FormFieldProps {
|
|
245
|
+
label: string;
|
|
246
|
+
required?: boolean;
|
|
247
|
+
error?: string;
|
|
248
|
+
hint?: string;
|
|
249
|
+
children: ReactNode;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Combines label, input, error message, and hint text
|
|
253
|
+
// Manages ARIA relationships and accessibility
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Data Table Cell**
|
|
257
|
+
```typescript
|
|
258
|
+
interface TableCellProps {
|
|
259
|
+
sortable?: boolean;
|
|
260
|
+
align: 'left' | 'center' | 'right';
|
|
261
|
+
width?: string;
|
|
262
|
+
children: ReactNode;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Features:
|
|
266
|
+
// - Sortable headers with visual indicators
|
|
267
|
+
// - Responsive column hiding
|
|
268
|
+
// - Cell content truncation with tooltips
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### Organisms (Complex Sections)
|
|
272
|
+
**Navigation Header**
|
|
273
|
+
```typescript
|
|
274
|
+
interface HeaderProps {
|
|
275
|
+
user?: UserProfile;
|
|
276
|
+
navigation: NavigationItem[];
|
|
277
|
+
searchEnabled?: boolean;
|
|
278
|
+
notificationsEnabled?: boolean;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Features:
|
|
282
|
+
// - Responsive navigation (hamburger menu on mobile)
|
|
283
|
+
// - User profile dropdown
|
|
284
|
+
// - Global search functionality
|
|
285
|
+
// - Notification center integration
|
|
286
|
+
// - Breadcrumb support
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Data Table**
|
|
290
|
+
```typescript
|
|
291
|
+
interface DataTableProps<T> {
|
|
292
|
+
data: T[];
|
|
293
|
+
columns: ColumnDefinition<T>[];
|
|
294
|
+
pagination?: PaginationConfig;
|
|
295
|
+
sorting?: SortingConfig;
|
|
296
|
+
filtering?: FilterConfig;
|
|
297
|
+
loading?: boolean;
|
|
298
|
+
emptyState?: ReactNode;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Features:
|
|
302
|
+
// - Server-side and client-side pagination
|
|
303
|
+
// - Multi-column sorting
|
|
304
|
+
// - Advanced filtering with facets
|
|
305
|
+
// - Row selection and bulk actions
|
|
306
|
+
// - Virtualization for large datasets
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 3.3 Component Development Standards
|
|
310
|
+
|
|
311
|
+
#### Component Template
|
|
312
|
+
```typescript
|
|
313
|
+
// ComponentName.tsx
|
|
314
|
+
import React from 'react';
|
|
315
|
+
import { cn } from '@/utils/classNames';
|
|
316
|
+
|
|
317
|
+
interface ComponentNameProps {
|
|
318
|
+
/** Required prop description */
|
|
319
|
+
requiredProp: string;
|
|
320
|
+
/** Optional prop with default */
|
|
321
|
+
optionalProp?: boolean;
|
|
322
|
+
/** Event handler */
|
|
323
|
+
onEvent?: (data: EventData) => void;
|
|
324
|
+
/** Additional CSS classes */
|
|
325
|
+
className?: string;
|
|
326
|
+
/** Children elements */
|
|
327
|
+
children?: React.ReactNode;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* ComponentName - Brief description of component purpose
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* <ComponentName requiredProp="value">
|
|
335
|
+
* Content here
|
|
336
|
+
* </ComponentName>
|
|
337
|
+
*/
|
|
338
|
+
export const ComponentName: React.FC<ComponentNameProps> = ({
|
|
339
|
+
requiredProp,
|
|
340
|
+
optionalProp = false,
|
|
341
|
+
onEvent,
|
|
342
|
+
className,
|
|
343
|
+
children,
|
|
344
|
+
...props
|
|
345
|
+
}) => {
|
|
346
|
+
// Component logic here
|
|
347
|
+
|
|
348
|
+
return (
|
|
349
|
+
<div
|
|
350
|
+
className={cn('base-styles', className)}
|
|
351
|
+
{...props}
|
|
352
|
+
>
|
|
353
|
+
{children}
|
|
354
|
+
</div>
|
|
355
|
+
);
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
ComponentName.displayName = 'ComponentName';
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
#### Testing Requirements
|
|
362
|
+
```typescript
|
|
363
|
+
// ComponentName.test.tsx
|
|
364
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
365
|
+
import { ComponentName } from './ComponentName';
|
|
366
|
+
|
|
367
|
+
describe('ComponentName', () => {
|
|
368
|
+
it('renders with required props', () => {
|
|
369
|
+
render(<ComponentName requiredProp="test" />);
|
|
370
|
+
expect(screen.getByRole('component')).toBeInTheDocument();
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('handles events correctly', () => {
|
|
374
|
+
const handleEvent = jest.fn();
|
|
375
|
+
render(<ComponentName requiredProp="test" onEvent={handleEvent} />);
|
|
376
|
+
|
|
377
|
+
fireEvent.click(screen.getByRole('button'));
|
|
378
|
+
expect(handleEvent).toHaveBeenCalledWith(expectedData);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('meets accessibility standards', async () => {
|
|
382
|
+
const { container } = render(<ComponentName requiredProp="test" />);
|
|
383
|
+
const results = await axe(container);
|
|
384
|
+
expect(results).toHaveNoViolations();
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## 🎨 4. User Experience Patterns & Guidelines
|
|
392
|
+
|
|
393
|
+
### 4.1 Interaction Patterns
|
|
394
|
+
|
|
395
|
+
#### Loading States
|
|
396
|
+
```typescript
|
|
397
|
+
// Progressive loading patterns
|
|
398
|
+
const LoadingStates = {
|
|
399
|
+
// Skeleton screens for content areas
|
|
400
|
+
skeleton: <SkeletonLoader />,
|
|
401
|
+
|
|
402
|
+
// Spinners for actions
|
|
403
|
+
spinner: <Spinner size="sm" />,
|
|
404
|
+
|
|
405
|
+
// Progress bars for uploads/downloads
|
|
406
|
+
progress: <ProgressBar value={progress} max={100} />,
|
|
407
|
+
|
|
408
|
+
// Shimmer effect for images
|
|
409
|
+
shimmer: <ImageShimmer />
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
// Implementation guidelines:
|
|
413
|
+
// - Show skeletons immediately (<100ms)
|
|
414
|
+
// - Display progress for operations >2 seconds
|
|
415
|
+
// - Provide cancel options for long operations
|
|
416
|
+
// - Show success/error feedback after completion
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
#### Error Handling
|
|
420
|
+
```typescript
|
|
421
|
+
interface ErrorBoundaryState {
|
|
422
|
+
hasError: boolean;
|
|
423
|
+
error?: Error;
|
|
424
|
+
errorInfo?: ErrorInfo;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Error display patterns:
|
|
428
|
+
// 1. Inline errors for form validation
|
|
429
|
+
// 2. Toast notifications for temporary issues
|
|
430
|
+
// 3. Error pages for critical failures
|
|
431
|
+
// 4. Retry mechanisms for network errors
|
|
432
|
+
|
|
433
|
+
const ErrorPatterns = {
|
|
434
|
+
validation: <InlineError message="Email is required" />,
|
|
435
|
+
network: <Toast type="error" retry={retryAction} />,
|
|
436
|
+
critical: <ErrorPage code={500} />,
|
|
437
|
+
notFound: <NotFoundPage />
|
|
438
|
+
};
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
#### Form Interactions
|
|
442
|
+
```typescript
|
|
443
|
+
// Form UX patterns and validation
|
|
444
|
+
const FormPatterns = {
|
|
445
|
+
// Real-time validation for critical fields
|
|
446
|
+
realtimeValidation: ['email', 'password', 'username'],
|
|
447
|
+
|
|
448
|
+
// Validation timing
|
|
449
|
+
validateOnBlur: true,
|
|
450
|
+
validateOnSubmit: true,
|
|
451
|
+
showErrorsAfterFirstSubmit: true,
|
|
452
|
+
|
|
453
|
+
// Success patterns
|
|
454
|
+
successFeedback: 'toast', // 'toast' | 'inline' | 'redirect'
|
|
455
|
+
|
|
456
|
+
// Auto-save for long forms
|
|
457
|
+
autoSave: {
|
|
458
|
+
enabled: true,
|
|
459
|
+
interval: 30000, // 30 seconds
|
|
460
|
+
indicator: <AutoSaveIndicator />
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### 4.2 Navigation Patterns
|
|
466
|
+
|
|
467
|
+
#### Page Navigation
|
|
468
|
+
```typescript
|
|
469
|
+
interface NavigationConfig {
|
|
470
|
+
// Breadcrumb navigation
|
|
471
|
+
breadcrumbs: {
|
|
472
|
+
enabled: boolean;
|
|
473
|
+
maxItems: number;
|
|
474
|
+
showHome: boolean;
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// Tab navigation
|
|
478
|
+
tabs: {
|
|
479
|
+
scrollable: boolean;
|
|
480
|
+
persistState: boolean;
|
|
481
|
+
lazyLoad: boolean;
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// Sidebar navigation
|
|
485
|
+
sidebar: {
|
|
486
|
+
collapsible: boolean;
|
|
487
|
+
persistState: boolean;
|
|
488
|
+
mobileOverlay: boolean;
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Navigation hierarchy:
|
|
493
|
+
// Level 1: Main navigation (header)
|
|
494
|
+
// Level 2: Section navigation (sidebar)
|
|
495
|
+
// Level 3: Page navigation (tabs)
|
|
496
|
+
// Level 4: Content navigation (breadcrumbs)
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
#### Modal & Overlay Patterns
|
|
500
|
+
```typescript
|
|
501
|
+
interface ModalPatterns {
|
|
502
|
+
// Modal sizing
|
|
503
|
+
sizes: 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
504
|
+
|
|
505
|
+
// Interaction patterns
|
|
506
|
+
closeOnOverlayClick: boolean;
|
|
507
|
+
closeOnEscape: boolean;
|
|
508
|
+
trapFocus: boolean;
|
|
509
|
+
|
|
510
|
+
// Animation preferences
|
|
511
|
+
animationType: 'fade' | 'slide' | 'scale';
|
|
512
|
+
duration: number;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Modal usage guidelines:
|
|
516
|
+
// - Use sparingly, prefer inline editing
|
|
517
|
+
// - Always provide clear close affordances
|
|
518
|
+
// - Maintain focus management
|
|
519
|
+
// - Support keyboard navigation
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### 4.3 Data Visualization Patterns
|
|
523
|
+
|
|
524
|
+
#### Table Interactions
|
|
525
|
+
```typescript
|
|
526
|
+
interface TableUXPatterns {
|
|
527
|
+
// Selection patterns
|
|
528
|
+
selection: {
|
|
529
|
+
type: 'single' | 'multiple' | 'none';
|
|
530
|
+
showCheckboxes: boolean;
|
|
531
|
+
selectOnRowClick: boolean;
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
// Sorting and filtering
|
|
535
|
+
sorting: {
|
|
536
|
+
multiColumn: boolean;
|
|
537
|
+
persistState: boolean;
|
|
538
|
+
showIndicators: boolean;
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
// Pagination
|
|
542
|
+
pagination: {
|
|
543
|
+
type: 'numbered' | 'loadMore' | 'infinite';
|
|
544
|
+
pageSize: number;
|
|
545
|
+
showSizeSelector: boolean;
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
#### Chart and Graph Guidelines
|
|
551
|
+
```typescript
|
|
552
|
+
interface ChartPatterns {
|
|
553
|
+
// Accessibility
|
|
554
|
+
accessibility: {
|
|
555
|
+
altText: string;
|
|
556
|
+
dataTable: boolean; // Provide table alternative
|
|
557
|
+
keyboardNavigation: boolean;
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
// Responsive behavior
|
|
561
|
+
responsive: {
|
|
562
|
+
minHeight: number;
|
|
563
|
+
aspectRatio: string;
|
|
564
|
+
hideElementsOnMobile: string[];
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// Color and styling
|
|
568
|
+
colorPalette: 'categorical' | 'sequential' | 'diverging';
|
|
569
|
+
colorBlindSafe: boolean;
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
575
|
+
## 🔧 5. State Management Architecture
|
|
576
|
+
|
|
577
|
+
### 5.1 State Organization Strategy
|
|
578
|
+
```typescript
|
|
579
|
+
// Global state structure using Zustand
|
|
580
|
+
interface AppState {
|
|
581
|
+
// User authentication and profile
|
|
582
|
+
auth: AuthState;
|
|
583
|
+
|
|
584
|
+
// Application-wide UI state
|
|
585
|
+
ui: UIState;
|
|
586
|
+
|
|
587
|
+
// Feature-specific state stores
|
|
588
|
+
features: {
|
|
589
|
+
dashboard: DashboardState;
|
|
590
|
+
settings: SettingsState;
|
|
591
|
+
notifications: NotificationState;
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// State management principles:
|
|
596
|
+
// 1. Keep state as local as possible
|
|
597
|
+
// 2. Use global state for truly shared data
|
|
598
|
+
// 3. Separate UI state from business logic
|
|
599
|
+
// 4. Implement optimistic updates where appropriate
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
### 5.2 Store Patterns
|
|
603
|
+
```typescript
|
|
604
|
+
// Authentication store example
|
|
605
|
+
interface AuthState {
|
|
606
|
+
user: User | null;
|
|
607
|
+
token: string | null;
|
|
608
|
+
isLoading: boolean;
|
|
609
|
+
error: string | null;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
interface AuthActions {
|
|
613
|
+
login: (credentials: LoginCredentials) => Promise<void>;
|
|
614
|
+
logout: () => void;
|
|
615
|
+
refreshToken: () => Promise<void>;
|
|
616
|
+
updateProfile: (updates: ProfileUpdates) => Promise<void>;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const useAuthStore = create<AuthState & AuthActions>((set, get) => ({
|
|
620
|
+
// Initial state
|
|
621
|
+
user: null,
|
|
622
|
+
token: localStorage.getItem('token'),
|
|
623
|
+
isLoading: false,
|
|
624
|
+
error: null,
|
|
625
|
+
|
|
626
|
+
// Actions
|
|
627
|
+
login: async (credentials) => {
|
|
628
|
+
set({ isLoading: true, error: null });
|
|
629
|
+
try {
|
|
630
|
+
const response = await authAPI.login(credentials);
|
|
631
|
+
set({
|
|
632
|
+
user: response.user,
|
|
633
|
+
token: response.token,
|
|
634
|
+
isLoading: false
|
|
635
|
+
});
|
|
636
|
+
localStorage.setItem('token', response.token);
|
|
637
|
+
} catch (error) {
|
|
638
|
+
set({
|
|
639
|
+
error: error.message,
|
|
640
|
+
isLoading: false
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
|
|
645
|
+
logout: () => {
|
|
646
|
+
localStorage.removeItem('token');
|
|
647
|
+
set({ user: null, token: null, error: null });
|
|
648
|
+
}
|
|
649
|
+
}));
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
### 5.3 Data Fetching Patterns
|
|
653
|
+
```typescript
|
|
654
|
+
// React Query integration for server state
|
|
655
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
656
|
+
|
|
657
|
+
// Query patterns
|
|
658
|
+
const useUsers = (filters?: UserFilters) => {
|
|
659
|
+
return useQuery({
|
|
660
|
+
queryKey: ['users', filters],
|
|
661
|
+
queryFn: () => userAPI.getUsers(filters),
|
|
662
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
663
|
+
cacheTime: 10 * 60 * 1000, // 10 minutes
|
|
664
|
+
});
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
// Mutation patterns with optimistic updates
|
|
668
|
+
const useUpdateUser = () => {
|
|
669
|
+
const queryClient = useQueryClient();
|
|
670
|
+
|
|
671
|
+
return useMutation({
|
|
672
|
+
mutationFn: userAPI.updateUser,
|
|
673
|
+
onMutate: async (updatedUser) => {
|
|
674
|
+
// Optimistic update
|
|
675
|
+
await queryClient.cancelQueries(['users']);
|
|
676
|
+
const previousUsers = queryClient.getQueryData(['users']);
|
|
677
|
+
|
|
678
|
+
queryClient.setQueryData(['users'], (old: User[]) =>
|
|
679
|
+
old.map(user =>
|
|
680
|
+
user.id === updatedUser.id ? { ...user, ...updatedUser } : user
|
|
681
|
+
)
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
return { previousUsers };
|
|
685
|
+
},
|
|
686
|
+
onError: (err, updatedUser, context) => {
|
|
687
|
+
// Rollback on error
|
|
688
|
+
queryClient.setQueryData(['users'], context?.previousUsers);
|
|
689
|
+
},
|
|
690
|
+
onSettled: () => {
|
|
691
|
+
// Refetch to ensure consistency
|
|
692
|
+
queryClient.invalidateQueries(['users']);
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
};
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
## 🎯 6. Performance Optimization Strategy
|
|
701
|
+
|
|
702
|
+
### 6.1 Bundle Optimization
|
|
703
|
+
```typescript
|
|
704
|
+
// Vite configuration for optimal bundling
|
|
705
|
+
export default defineConfig({
|
|
706
|
+
build: {
|
|
707
|
+
// Code splitting strategy
|
|
708
|
+
rollupOptions: {
|
|
709
|
+
output: {
|
|
710
|
+
manualChunks: {
|
|
711
|
+
// Vendor chunks
|
|
712
|
+
vendor: ['react', 'react-dom'],
|
|
713
|
+
ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
|
|
714
|
+
|
|
715
|
+
// Feature-based chunks
|
|
716
|
+
dashboard: ['./src/pages/Dashboard'],
|
|
717
|
+
settings: ['./src/pages/Settings'],
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
},
|
|
721
|
+
|
|
722
|
+
// Compression and minification
|
|
723
|
+
minify: 'terser',
|
|
724
|
+
terserOptions: {
|
|
725
|
+
compress: {
|
|
726
|
+
drop_console: true,
|
|
727
|
+
drop_debugger: true
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
|
|
732
|
+
// Development optimizations
|
|
733
|
+
server: {
|
|
734
|
+
hmr: {
|
|
735
|
+
overlay: false // Disable error overlay in dev
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### 6.2 Runtime Performance Patterns
|
|
742
|
+
```typescript
|
|
743
|
+
// React performance optimization patterns
|
|
744
|
+
|
|
745
|
+
// 1. Memoization
|
|
746
|
+
const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
|
|
747
|
+
const processedData = useMemo(() => {
|
|
748
|
+
return heavyDataProcessing(data);
|
|
749
|
+
}, [data]);
|
|
750
|
+
|
|
751
|
+
const handleUpdate = useCallback((id: string) => {
|
|
752
|
+
onUpdate(id);
|
|
753
|
+
}, [onUpdate]);
|
|
754
|
+
|
|
755
|
+
return <div>{/* render logic */}</div>;
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
// 2. Virtual scrolling for large lists
|
|
759
|
+
import { FixedSizeList as List } from 'react-window';
|
|
760
|
+
|
|
761
|
+
const VirtualizedTable = ({ items }) => (
|
|
762
|
+
<List
|
|
763
|
+
height={600}
|
|
764
|
+
itemCount={items.length}
|
|
765
|
+
itemSize={50}
|
|
766
|
+
itemData={items}
|
|
767
|
+
>
|
|
768
|
+
{({ index, style, data }) => (
|
|
769
|
+
<div style={style}>
|
|
770
|
+
<TableRow data={data[index]} />
|
|
771
|
+
</div>
|
|
772
|
+
)}
|
|
773
|
+
</List>
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
// 3. Lazy loading with Suspense
|
|
777
|
+
const LazyDashboard = lazy(() => import('./Dashboard'));
|
|
778
|
+
|
|
779
|
+
const App = () => (
|
|
780
|
+
<Suspense fallback={<PageSkeleton />}>
|
|
781
|
+
<Routes>
|
|
782
|
+
<Route path="/dashboard" element={<LazyDashboard />} />
|
|
783
|
+
</Routes>
|
|
784
|
+
</Suspense>
|
|
785
|
+
);
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
### 6.3 Image and Asset Optimization
|
|
789
|
+
```typescript
|
|
790
|
+
// Image optimization strategies
|
|
791
|
+
const OptimizedImage = ({ src, alt, ...props }) => {
|
|
792
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
793
|
+
|
|
794
|
+
return (
|
|
795
|
+
<div className="relative">
|
|
796
|
+
{!isLoaded && <ImageSkeleton />}
|
|
797
|
+
<img
|
|
798
|
+
src={src}
|
|
799
|
+
alt={alt}
|
|
800
|
+
loading="lazy"
|
|
801
|
+
onLoad={() => setIsLoaded(true)}
|
|
802
|
+
className={cn(
|
|
803
|
+
'transition-opacity duration-300',
|
|
804
|
+
isLoaded ? 'opacity-100' : 'opacity-0'
|
|
805
|
+
)}
|
|
806
|
+
{...props}
|
|
807
|
+
/>
|
|
808
|
+
</div>
|
|
809
|
+
);
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
// Asset preloading for critical resources
|
|
813
|
+
const preloadCriticalAssets = () => {
|
|
814
|
+
const link = document.createElement('link');
|
|
815
|
+
link.rel = 'preload';
|
|
816
|
+
link.as = 'font';
|
|
817
|
+
link.href = '/fonts/inter-var.woff2';
|
|
818
|
+
link.crossOrigin = 'anonymous';
|
|
819
|
+
document.head.appendChild(link);
|
|
820
|
+
};
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
---
|
|
824
|
+
|
|
825
|
+
## ♿ 7. Accessibility Implementation
|
|
826
|
+
|
|
827
|
+
### 7.1 Semantic HTML Structure
|
|
828
|
+
```typescript
|
|
829
|
+
// Proper semantic structure
|
|
830
|
+
const AccessibleForm = () => (
|
|
831
|
+
<form>
|
|
832
|
+
<fieldset>
|
|
833
|
+
<legend>Personal Information</legend>
|
|
834
|
+
|
|
835
|
+
<div className="form-group">
|
|
836
|
+
<label htmlFor="firstName">
|
|
837
|
+
First Name <span aria-label="required">*</span>
|
|
838
|
+
</label>
|
|
839
|
+
<input
|
|
840
|
+
id="firstName"
|
|
841
|
+
type="text"
|
|
842
|
+
required
|
|
843
|
+
aria-describedby="firstName-error"
|
|
844
|
+
aria-invalid={hasError}
|
|
845
|
+
/>
|
|
846
|
+
{hasError && (
|
|
847
|
+
<div id="firstName-error" role="alert">
|
|
848
|
+
First name is required
|
|
849
|
+
</div>
|
|
850
|
+
)}
|
|
851
|
+
</div>
|
|
852
|
+
</fieldset>
|
|
853
|
+
</form>
|
|
854
|
+
);
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
### 7.2 ARIA Implementation Patterns
|
|
858
|
+
```typescript
|
|
859
|
+
// Common ARIA patterns
|
|
860
|
+
const AccessibleModal = ({ isOpen, onClose, children }) => {
|
|
861
|
+
const modalRef = useRef<HTMLDivElement>(null);
|
|
862
|
+
|
|
863
|
+
// Focus management
|
|
864
|
+
useEffect(() => {
|
|
865
|
+
if (isOpen) {
|
|
866
|
+
modalRef.current?.focus();
|
|
867
|
+
}
|
|
868
|
+
}, [isOpen]);
|
|
869
|
+
|
|
870
|
+
return (
|
|
871
|
+
<div
|
|
872
|
+
ref={modalRef}
|
|
873
|
+
role="dialog"
|
|
874
|
+
aria-modal="true"
|
|
875
|
+
aria-labelledby="modal-title"
|
|
876
|
+
aria-describedby="modal-description"
|
|
877
|
+
tabIndex={-1}
|
|
878
|
+
>
|
|
879
|
+
<h2 id="modal-title">Modal Title</h2>
|
|
880
|
+
<div id="modal-description">
|
|
881
|
+
{children}
|
|
882
|
+
</div>
|
|
883
|
+
<button onClick={onClose} aria-label="Close modal">
|
|
884
|
+
×
|
|
885
|
+
</button>
|
|
886
|
+
</div>
|
|
887
|
+
);
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
// Accessible data table
|
|
891
|
+
const AccessibleTable = ({ data, columns }) => (
|
|
892
|
+
<table role="table" aria-label="User data">
|
|
893
|
+
<thead>
|
|
894
|
+
<tr>
|
|
895
|
+
{columns.map(column => (
|
|
896
|
+
<th
|
|
897
|
+
key={column.key}
|
|
898
|
+
scope="col"
|
|
899
|
+
aria-sort={getSortDirection(column.key)}
|
|
900
|
+
>
|
|
901
|
+
{column.label}
|
|
902
|
+
</th>
|
|
903
|
+
))}
|
|
904
|
+
</tr>
|
|
905
|
+
</thead>
|
|
906
|
+
<tbody>
|
|
907
|
+
{data.map((row, index) => (
|
|
908
|
+
<tr key={row.id}>
|
|
909
|
+
{columns.map(column => (
|
|
910
|
+
<td key={column.key}>
|
|
911
|
+
{row[column.key]}
|
|
912
|
+
</td>
|
|
913
|
+
))}
|
|
914
|
+
</tr>
|
|
915
|
+
))}
|
|
916
|
+
</tbody>
|
|
917
|
+
</table>
|
|
918
|
+
);
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
### 7.3 Keyboard Navigation
|
|
922
|
+
```typescript
|
|
923
|
+
// Keyboard navigation implementation
|
|
924
|
+
const useKeyboardNavigation = (items: NavItem[]) => {
|
|
925
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
926
|
+
|
|
927
|
+
const handleKeyDown = useCallback((event: KeyboardEvent) => {
|
|
928
|
+
switch (event.key) {
|
|
929
|
+
case 'ArrowDown':
|
|
930
|
+
event.preventDefault();
|
|
931
|
+
setActiveIndex(prev => (prev + 1) % items.length);
|
|
932
|
+
break;
|
|
933
|
+
|
|
934
|
+
case 'ArrowUp':
|
|
935
|
+
event.preventDefault();
|
|
936
|
+
setActiveIndex(prev => (prev - 1 + items.length) % items.length);
|
|
937
|
+
break;
|
|
938
|
+
|
|
939
|
+
case 'Enter':
|
|
940
|
+
case ' ':
|
|
941
|
+
event.preventDefault();
|
|
942
|
+
items[activeIndex].onClick();
|
|
943
|
+
break;
|
|
944
|
+
|
|
945
|
+
case 'Escape':
|
|
946
|
+
event.preventDefault();
|
|
947
|
+
// Close menu or modal
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
}, [items, activeIndex]);
|
|
951
|
+
|
|
952
|
+
useEffect(() => {
|
|
953
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
954
|
+
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
955
|
+
}, [handleKeyDown]);
|
|
956
|
+
|
|
957
|
+
return { activeIndex };
|
|
958
|
+
};
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
---
|
|
962
|
+
|
|
963
|
+
## 🧪 8. Testing Strategy & Implementation
|
|
964
|
+
|
|
965
|
+
### 8.1 Testing Pyramid Structure
|
|
966
|
+
```typescript
|
|
967
|
+
// Unit tests (70% of tests)
|
|
968
|
+
// Component testing with React Testing Library
|
|
969
|
+
describe('Button Component', () => {
|
|
970
|
+
it('renders with correct text', () => {
|
|
971
|
+
render(<Button>Click me</Button>);
|
|
972
|
+
expect(screen.getByRole('button')).toHaveTextContent('Click me');
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
it('calls onClick when clicked', () => {
|
|
976
|
+
const handleClick = jest.fn();
|
|
977
|
+
render(<Button onClick={handleClick}>Click me</Button>);
|
|
978
|
+
|
|
979
|
+
fireEvent.click(screen.getByRole('button'));
|
|
980
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
it('is accessible', async () => {
|
|
984
|
+
const { container } = render(<Button>Click me</Button>);
|
|
985
|
+
const results = await axe(container);
|
|
986
|
+
expect(results).toHaveNoViolations();
|
|
987
|
+
});
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
// Integration tests (20% of tests)
|
|
991
|
+
// Testing component interactions
|
|
992
|
+
describe('Search Functionality', () => {
|
|
993
|
+
it('searches and displays results', async () => {
|
|
994
|
+
const mockSearch = jest.fn().mockResolvedValue(['result1', 'result2']);
|
|
995
|
+
|
|
996
|
+
render(<SearchPage onSearch={mockSearch} />);
|
|
997
|
+
|
|
998
|
+
const searchInput = screen.getByLabelText('Search');
|
|
999
|
+
fireEvent.change(searchInput, { target: { value: 'test query' } });
|
|
1000
|
+
fireEvent.click(screen.getByRole('button', { name: 'Search' }));
|
|
1001
|
+
|
|
1002
|
+
await waitFor(() => {
|
|
1003
|
+
expect(screen.getByText('result1')).toBeInTheDocument();
|
|
1004
|
+
});
|
|
1005
|
+
});
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
// E2E tests (10% of tests)
|
|
1009
|
+
// Critical user journeys with Playwright
|
|
1010
|
+
test('User can complete signup flow', async ({ page }) => {
|
|
1011
|
+
await page.goto('/signup');
|
|
1012
|
+
|
|
1013
|
+
await page.fill('[data-testid="email"]', 'user@example.com');
|
|
1014
|
+
await page.fill('[data-testid="password"]', 'SecurePassword123');
|
|
1015
|
+
await page.click('[data-testid="submit"]');
|
|
1016
|
+
|
|
1017
|
+
await expect(page).toHaveURL('/dashboard');
|
|
1018
|
+
await expect(page.getByText('Welcome')).toBeVisible();
|
|
1019
|
+
});
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
### 8.2 Visual Regression Testing
|
|
1023
|
+
```typescript
|
|
1024
|
+
// Storybook integration for visual testing
|
|
1025
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
1026
|
+
|
|
1027
|
+
const meta: Meta<typeof Button> = {
|
|
1028
|
+
title: 'Components/Button',
|
|
1029
|
+
component: Button,
|
|
1030
|
+
parameters: {
|
|
1031
|
+
docs: {
|
|
1032
|
+
description: {
|
|
1033
|
+
component: 'Primary button component for user actions.'
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
export default meta;
|
|
1040
|
+
type Story = StoryObj<typeof meta>;
|
|
1041
|
+
|
|
1042
|
+
export const Primary: Story = {
|
|
1043
|
+
args: {
|
|
1044
|
+
variant: 'primary',
|
|
1045
|
+
children: 'Primary Button'
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
export const Loading: Story = {
|
|
1050
|
+
args: {
|
|
1051
|
+
variant: 'primary',
|
|
1052
|
+
loading: true,
|
|
1053
|
+
children: 'Loading...'
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
// Visual regression with Chromatic
|
|
1058
|
+
// Automatically captures screenshots of all stories
|
|
1059
|
+
// Compares against baseline images
|
|
1060
|
+
// Flags visual changes for review
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
### 8.3 Performance Testing
|
|
1064
|
+
```typescript
|
|
1065
|
+
// Performance testing with testing-library
|
|
1066
|
+
import { render } from '@testing-library/react';
|
|
1067
|
+
import { profiler } from '@testing-library/react-hooks';
|
|
1068
|
+
|
|
1069
|
+
describe('Performance Tests', () => {
|
|
1070
|
+
it('renders large list efficiently', () => {
|
|
1071
|
+
const largeDataset = Array.from({ length: 1000 }, (_, i) => ({
|
|
1072
|
+
id: i,
|
|
1073
|
+
name: `Item ${i}`
|
|
1074
|
+
}));
|
|
1075
|
+
|
|
1076
|
+
const startTime = performance.now();
|
|
1077
|
+
render(<LargeList data={largeDataset} />);
|
|
1078
|
+
const endTime = performance.now();
|
|
1079
|
+
|
|
1080
|
+
expect(endTime - startTime).toBeLessThan(100); // 100ms threshold
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
it('handles rapid state updates', async () => {
|
|
1084
|
+
const { rerender } = render(<Counter initialValue={0} />);
|
|
1085
|
+
|
|
1086
|
+
const startTime = performance.now();
|
|
1087
|
+
for (let i = 0; i < 100; i++) {
|
|
1088
|
+
rerender(<Counter initialValue={i} />);
|
|
1089
|
+
}
|
|
1090
|
+
const endTime = performance.now();
|
|
1091
|
+
|
|
1092
|
+
expect(endTime - startTime).toBeLessThan(50);
|
|
1093
|
+
});
|
|
1094
|
+
});
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
---
|
|
1098
|
+
|
|
1099
|
+
## 🚀 9. Build & Deployment Pipeline
|
|
1100
|
+
|
|
1101
|
+
### 9.1 Development Workflow
|
|
1102
|
+
```yaml
|
|
1103
|
+
# .github/workflows/frontend-ci.yml
|
|
1104
|
+
name: Frontend CI/CD
|
|
1105
|
+
|
|
1106
|
+
on:
|
|
1107
|
+
push:
|
|
1108
|
+
branches: [main, develop]
|
|
1109
|
+
pull_request:
|
|
1110
|
+
branches: [main]
|
|
1111
|
+
|
|
1112
|
+
jobs:
|
|
1113
|
+
test:
|
|
1114
|
+
runs-on: ubuntu-latest
|
|
1115
|
+
steps:
|
|
1116
|
+
- uses: actions/checkout@v3
|
|
1117
|
+
|
|
1118
|
+
- name: Setup Node.js
|
|
1119
|
+
uses: actions/setup-node@v3
|
|
1120
|
+
with:
|
|
1121
|
+
node-version: '18'
|
|
1122
|
+
cache: 'npm'
|
|
1123
|
+
|
|
1124
|
+
- name: Install dependencies
|
|
1125
|
+
run: npm ci
|
|
1126
|
+
|
|
1127
|
+
- name: Run linting
|
|
1128
|
+
run: npm run lint
|
|
1129
|
+
|
|
1130
|
+
- name: Run type checking
|
|
1131
|
+
run: npm run type-check
|
|
1132
|
+
|
|
1133
|
+
- name: Run tests
|
|
1134
|
+
run: npm run test:coverage
|
|
1135
|
+
|
|
1136
|
+
- name: Run accessibility tests
|
|
1137
|
+
run: npm run test:a11y
|
|
1138
|
+
|
|
1139
|
+
- name: Build application
|
|
1140
|
+
run: npm run build
|
|
1141
|
+
|
|
1142
|
+
- name: Run Lighthouse CI
|
|
1143
|
+
run: npm run lighthouse:ci
|
|
1144
|
+
|
|
1145
|
+
build-and-deploy:
|
|
1146
|
+
needs: test
|
|
1147
|
+
runs-on: ubuntu-latest
|
|
1148
|
+
if: github.ref == 'refs/heads/main'
|
|
1149
|
+
steps:
|
|
1150
|
+
- name: Deploy to staging
|
|
1151
|
+
run: npm run deploy:staging
|
|
1152
|
+
|
|
1153
|
+
- name: Run E2E tests
|
|
1154
|
+
run: npm run test:e2e
|
|
1155
|
+
|
|
1156
|
+
- name: Deploy to production
|
|
1157
|
+
run: npm run deploy:production
|
|
1158
|
+
```
|
|
1159
|
+
|
|
1160
|
+
### 9.2 Environment Configuration
|
|
1161
|
+
```typescript
|
|
1162
|
+
// Environment-specific configurations
|
|
1163
|
+
interface EnvironmentConfig {
|
|
1164
|
+
apiBaseUrl: string;
|
|
1165
|
+
cdnUrl: string;
|
|
1166
|
+
analytics: {
|
|
1167
|
+
googleAnalytics?: string;
|
|
1168
|
+
mixpanel?: string;
|
|
1169
|
+
};
|
|
1170
|
+
features: {
|
|
1171
|
+
enableBetaFeatures: boolean;
|
|
1172
|
+
enableAnalytics: boolean;
|
|
1173
|
+
enableErrorReporting: boolean;
|
|
1174
|
+
};
|
|
1175
|
+
performance: {
|
|
1176
|
+
enableServiceWorker: boolean;
|
|
1177
|
+
enableCodeSplitting: boolean;
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const configs: Record<string, EnvironmentConfig> = {
|
|
1182
|
+
development: {
|
|
1183
|
+
apiBaseUrl: 'http://localhost:3000/api',
|
|
1184
|
+
cdnUrl: 'http://localhost:3000',
|
|
1185
|
+
analytics: {},
|
|
1186
|
+
features: {
|
|
1187
|
+
enableBetaFeatures: true,
|
|
1188
|
+
enableAnalytics: false,
|
|
1189
|
+
enableErrorReporting: false
|
|
1190
|
+
},
|
|
1191
|
+
performance: {
|
|
1192
|
+
enableServiceWorker: false,
|
|
1193
|
+
enableCodeSplitting: false
|
|
1194
|
+
}
|
|
1195
|
+
},
|
|
1196
|
+
|
|
1197
|
+
production: {
|
|
1198
|
+
apiBaseUrl: 'https://api.example.com',
|
|
1199
|
+
cdnUrl: 'https://cdn.example.com',
|
|
1200
|
+
analytics: {
|
|
1201
|
+
googleAnalytics: 'GA_TRACKING_ID',
|
|
1202
|
+
mixpanel: 'MIXPANEL_TOKEN'
|
|
1203
|
+
},
|
|
1204
|
+
features: {
|
|
1205
|
+
enableBetaFeatures: false,
|
|
1206
|
+
enableAnalytics: true,
|
|
1207
|
+
enableErrorReporting: true
|
|
1208
|
+
},
|
|
1209
|
+
performance: {
|
|
1210
|
+
enableServiceWorker: true,
|
|
1211
|
+
enableCodeSplitting: true
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
```
|
|
1216
|
+
|
|
1217
|
+
### 9.3 Monitoring & Analytics
|
|
1218
|
+
```typescript
|
|
1219
|
+
// Error monitoring setup
|
|
1220
|
+
import * as Sentry from '@sentry/react';
|
|
1221
|
+
|
|
1222
|
+
Sentry.init({
|
|
1223
|
+
dsn: process.env.VITE_SENTRY_DSN,
|
|
1224
|
+
environment: process.env.NODE_ENV,
|
|
1225
|
+
integrations: [
|
|
1226
|
+
new Sentry.BrowserTracing(),
|
|
1227
|
+
new Sentry.Replay()
|
|
1228
|
+
],
|
|
1229
|
+
tracesSampleRate: 0.1,
|
|
1230
|
+
replaysSessionSampleRate: 0.1,
|
|
1231
|
+
replaysOnErrorSampleRate: 1.0
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1234
|
+
// Performance monitoring
|
|
1235
|
+
const performanceObserver = new PerformanceObserver((list) => {
|
|
1236
|
+
list.getEntries().forEach((entry) => {
|
|
1237
|
+
if (entry.entryType === 'largest-contentful-paint') {
|
|
1238
|
+
analytics.track('performance.lcp', {
|
|
1239
|
+
value: entry.startTime,
|
|
1240
|
+
page: window.location.pathname
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
});
|
|
1245
|
+
|
|
1246
|
+
performanceObserver.observe({ entryTypes: ['largest-contentful-paint'] });
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
---
|
|
1250
|
+
|
|
1251
|
+
## 📚 10. Documentation & Style Guide
|
|
1252
|
+
|
|
1253
|
+
### 10.1 Component Documentation Standards
|
|
1254
|
+
```typescript
|
|
1255
|
+
/**
|
|
1256
|
+
* Button component for user interactions
|
|
1257
|
+
*
|
|
1258
|
+
* @example
|
|
1259
|
+
* ```tsx
|
|
1260
|
+
* <Button variant="primary" onClick={handleClick}>
|
|
1261
|
+
* Save Changes
|
|
1262
|
+
* </Button>
|
|
1263
|
+
* ```
|
|
1264
|
+
*
|
|
1265
|
+
* @example
|
|
1266
|
+
* ```tsx
|
|
1267
|
+
* <Button variant="outline" loading={isLoading}>
|
|
1268
|
+
* Submit
|
|
1269
|
+
* </Button>
|
|
1270
|
+
* ```
|
|
1271
|
+
*/
|
|
1272
|
+
export interface ButtonProps {
|
|
1273
|
+
/** Visual style variant */
|
|
1274
|
+
variant: 'primary' | 'secondary' | 'outline' | 'ghost';
|
|
1275
|
+
|
|
1276
|
+
/** Size of the button */
|
|
1277
|
+
size: 'sm' | 'md' | 'lg';
|
|
1278
|
+
|
|
1279
|
+
/** Whether button is disabled */
|
|
1280
|
+
disabled?: boolean;
|
|
1281
|
+
|
|
1282
|
+
/** Shows loading spinner and disables interaction */
|
|
1283
|
+
loading?: boolean;
|
|
1284
|
+
|
|
1285
|
+
/** Icon to display alongside text */
|
|
1286
|
+
icon?: ReactNode;
|
|
1287
|
+
|
|
1288
|
+
/** Button content */
|
|
1289
|
+
children: ReactNode;
|
|
1290
|
+
|
|
1291
|
+
/** Click event handler */
|
|
1292
|
+
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
1293
|
+
|
|
1294
|
+
/** Additional CSS classes */
|
|
1295
|
+
className?: string;
|
|
1296
|
+
}
|
|
1297
|
+
```
|
|
1298
|
+
|
|
1299
|
+
### 10.2 Code Style Guidelines
|
|
1300
|
+
```typescript
|
|
1301
|
+
// ESLint configuration
|
|
1302
|
+
module.exports = {
|
|
1303
|
+
extends: [
|
|
1304
|
+
'@typescript-eslint/recommended',
|
|
1305
|
+
'plugin:react/recommended',
|
|
1306
|
+
'plugin:react-hooks/recommended',
|
|
1307
|
+
'plugin:jsx-a11y/recommended'
|
|
1308
|
+
],
|
|
1309
|
+
rules: {
|
|
1310
|
+
// Enforce consistent naming
|
|
1311
|
+
'@typescript-eslint/naming-convention': [
|
|
1312
|
+
'error',
|
|
1313
|
+
{
|
|
1314
|
+
selector: 'interface',
|
|
1315
|
+
format: ['PascalCase'],
|
|
1316
|
+
suffix: ['Props', 'State', 'Config']
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
selector: 'typeAlias',
|
|
1320
|
+
format: ['PascalCase']
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
selector: 'function',
|
|
1324
|
+
format: ['camelCase', 'PascalCase']
|
|
1325
|
+
}
|
|
1326
|
+
],
|
|
1327
|
+
|
|
1328
|
+
// React-specific rules
|
|
1329
|
+
'react/prop-types': 'off',
|
|
1330
|
+
'react/react-in-jsx-scope': 'off',
|
|
1331
|
+
'react/display-name': 'error',
|
|
1332
|
+
|
|
1333
|
+
// Accessibility rules
|
|
1334
|
+
'jsx-a11y/no-autofocus': 'warn',
|
|
1335
|
+
'jsx-a11y/click-events-have-key-events': 'error'
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
|
|
1339
|
+
// Prettier configuration
|
|
1340
|
+
module.exports = {
|
|
1341
|
+
semi: true,
|
|
1342
|
+
trailingComma: 'es5',
|
|
1343
|
+
singleQuote: true,
|
|
1344
|
+
printWidth: 80,
|
|
1345
|
+
tabWidth: 2,
|
|
1346
|
+
useTabs: false
|
|
1347
|
+
};
|
|
1348
|
+
```
|
|
1349
|
+
|
|
1350
|
+
### 10.3 Design System Documentation
|
|
1351
|
+
```markdown
|
|
1352
|
+
# Design System Guidelines
|
|
1353
|
+
|
|
1354
|
+
## Color Usage
|
|
1355
|
+
- Primary colors for main actions and branding
|
|
1356
|
+
- Semantic colors (success, warning, error) for status indication
|
|
1357
|
+
- Neutral grays for text and backgrounds
|
|
1358
|
+
- Ensure 4.5:1 contrast ratio for accessibility
|
|
1359
|
+
|
|
1360
|
+
## Typography Hierarchy
|
|
1361
|
+
- H1: Page titles (32px, font-weight: 700)
|
|
1362
|
+
- H2: Section headings (24px, font-weight: 600)
|
|
1363
|
+
- H3: Subsection headings (20px, font-weight: 600)
|
|
1364
|
+
- Body: Regular text (16px, font-weight: 400)
|
|
1365
|
+
- Caption: Secondary text (14px, font-weight: 400)
|
|
1366
|
+
|
|
1367
|
+
## Spacing System
|
|
1368
|
+
- Use consistent spacing units (4px base)
|
|
1369
|
+
- Component padding: 16px, 24px, 32px
|
|
1370
|
+
- Section margins: 32px, 48px, 64px
|
|
1371
|
+
- Maintain visual rhythm and alignment
|
|
1372
|
+
```
|
|
1373
|
+
|
|
1374
|
+
---
|
|
1375
|
+
|
|
1376
|
+
## 📋 11. Maintenance & Support Guidelines
|
|
1377
|
+
|
|
1378
|
+
### 11.1 Dependency Management
|
|
1379
|
+
```json
|
|
1380
|
+
{
|
|
1381
|
+
"scripts": {
|
|
1382
|
+
"deps:check": "npm outdated",
|
|
1383
|
+
"deps:update": "npm update",
|
|
1384
|
+
"deps:audit": "npm audit",
|
|
1385
|
+
"deps:fix": "npm audit fix"
|
|
1386
|
+
},
|
|
1387
|
+
"engines": {
|
|
1388
|
+
"node": ">=18.0.0",
|
|
1389
|
+
"npm": ">=8.0.0"
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
```
|
|
1393
|
+
|
|
1394
|
+
### 11.2 Browser Support Updates
|
|
1395
|
+
```typescript
|
|
1396
|
+
// Browser support matrix review process
|
|
1397
|
+
const browserSupportReview = {
|
|
1398
|
+
frequency: 'quarterly',
|
|
1399
|
+
metrics: [
|
|
1400
|
+
'browser usage analytics',
|
|
1401
|
+
'support ticket volume',
|
|
1402
|
+
'feature compatibility',
|
|
1403
|
+
'performance impact'
|
|
1404
|
+
],
|
|
1405
|
+
decisionCriteria: {
|
|
1406
|
+
dropSupport: 'usage < 1% for 6 months',
|
|
1407
|
+
addSupport: 'usage > 5% or critical users',
|
|
1408
|
+
maintainSupport: 'usage > 1% or business requirement'
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
### 11.3 Component Library Versioning
|
|
1414
|
+
```typescript
|
|
1415
|
+
// Semantic versioning for component library
|
|
1416
|
+
interface ComponentVersion {
|
|
1417
|
+
major: number; // Breaking changes
|
|
1418
|
+
minor: number; // New features, backward compatible
|
|
1419
|
+
patch: number; // Bug fixes, backward compatible
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// Breaking change examples:
|
|
1423
|
+
// - Removing props or changing prop types
|
|
1424
|
+
// - Changing component behavior significantly
|
|
1425
|
+
// - Removing components
|
|
1426
|
+
|
|
1427
|
+
// Minor change examples:
|
|
1428
|
+
// - Adding new optional props
|
|
1429
|
+
// - Adding new components
|
|
1430
|
+
// - Enhancing existing functionality
|
|
1431
|
+
|
|
1432
|
+
// Patch change examples:
|
|
1433
|
+
// - Bug fixes
|
|
1434
|
+
// - Performance improvements
|
|
1435
|
+
// - Documentation updates
|
|
1436
|
+
```
|
|
1437
|
+
|
|
1438
|
+
---
|
|
1439
|
+
|
|
1440
|
+
## 📞 12. Team Contacts & Resources
|
|
1441
|
+
|
|
1442
|
+
### Frontend Team Structure
|
|
1443
|
+
| Role | Name | Email | Phone | Specialization |
|
|
1444
|
+
|------|------|-------|-------|---------------|
|
|
1445
|
+
| **Frontend Architect** | _[Name]_ | _[Email]_ | _[Phone]_ | Architecture, performance, standards |
|
|
1446
|
+
| **Senior Frontend Developer** | _[Name]_ | _[Email]_ | _[Phone]_ | Component library, React expertise |
|
|
1447
|
+
| **UI/UX Developer** | _[Name]_ | _[Email]_ | _[Phone]_ | Design systems, accessibility |
|
|
1448
|
+
| **Frontend DevOps** | _[Name]_ | _[Email]_ | _[Phone]_ | Build tools, deployment, monitoring |
|
|
1449
|
+
|
|
1450
|
+
### External Resources
|
|
1451
|
+
- **Design System Consultants:** _[Contact for design system expertise]_
|
|
1452
|
+
- **Accessibility Auditors:** _[Contact for WCAG compliance reviews]_
|
|
1453
|
+
- **Performance Specialists:** _[Contact for performance optimization]_
|
|
1454
|
+
- **Security Reviewers:** _[Contact for frontend security assessment]_
|
|
1455
|
+
|
|
1456
|
+
### Development Tools & Platforms
|
|
1457
|
+
- **Code Repository:** GitHub - _[Repository URL]_
|
|
1458
|
+
- **Design System:** Storybook - _[Storybook URL]_
|
|
1459
|
+
- **Performance Monitoring:** Lighthouse CI, Core Web Vitals
|
|
1460
|
+
- **Error Tracking:** Sentry - _[Sentry project URL]_
|
|
1461
|
+
- **Analytics:** Google Analytics, Mixpanel
|
|
1462
|
+
- **Documentation:** _[Documentation platform URL]_
|
|
1463
|
+
|
|
1464
|
+
---
|
|
1465
|
+
|
|
1466
|
+
**Document Control:**
|
|
1467
|
+
- **Version:** 1.0
|
|
1468
|
+
- **Last Review:** 2025-09-16
|
|
1469
|
+
- **Next Review:** _[Date + 30 days]_
|
|
1470
|
+
- **Approval Status:** Draft/Under Review/Approved
|
|
1471
|
+
- **Digital Signature:** _[Electronic signature block]_
|
|
1472
|
+
|
|
1473
|
+
**Template Usage Notes:**
|
|
1474
|
+
- Replace all placeholders marked with _[brackets]_
|
|
1475
|
+
- Update 2025-09-16 with current date
|
|
1476
|
+
- Customize technology stack based on project requirements
|
|
1477
|
+
- Scale component library complexity based on application size
|
|
1478
|
+
- Regular updates ensure alignment with modern frontend practices
|
|
1479
|
+
- Maintain compatibility with accessibility standards and performance benchmarks
|