aigent-team 0.1.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/LICENSE +21 -0
- package/README.md +253 -0
- package/dist/chunk-N3RYHWTR.js +267 -0
- package/dist/cli.js +576 -0
- package/dist/index.d.ts +234 -0
- package/dist/index.js +27 -0
- package/package.json +67 -0
- package/templates/shared/git-workflow.md +44 -0
- package/templates/shared/project-conventions.md +48 -0
- package/templates/teams/ba/agent.yaml +25 -0
- package/templates/teams/ba/references/acceptance-criteria.md +87 -0
- package/templates/teams/ba/references/api-contract-design.md +110 -0
- package/templates/teams/ba/references/requirements-analysis.md +83 -0
- package/templates/teams/ba/references/user-story-mapping.md +73 -0
- package/templates/teams/ba/skill.md +85 -0
- package/templates/teams/be/agent.yaml +34 -0
- package/templates/teams/be/conventions.md +102 -0
- package/templates/teams/be/references/api-design.md +91 -0
- package/templates/teams/be/references/async-processing.md +86 -0
- package/templates/teams/be/references/auth-security.md +58 -0
- package/templates/teams/be/references/caching.md +79 -0
- package/templates/teams/be/references/database.md +65 -0
- package/templates/teams/be/references/error-handling.md +106 -0
- package/templates/teams/be/references/observability.md +83 -0
- package/templates/teams/be/references/review-checklist.md +50 -0
- package/templates/teams/be/references/testing.md +100 -0
- package/templates/teams/be/review-checklist.md +54 -0
- package/templates/teams/be/skill.md +71 -0
- package/templates/teams/devops/agent.yaml +35 -0
- package/templates/teams/devops/conventions.md +133 -0
- package/templates/teams/devops/references/ci-cd.md +218 -0
- package/templates/teams/devops/references/cost-optimization.md +218 -0
- package/templates/teams/devops/references/disaster-recovery.md +199 -0
- package/templates/teams/devops/references/docker.md +237 -0
- package/templates/teams/devops/references/infrastructure-as-code.md +238 -0
- package/templates/teams/devops/references/kubernetes.md +397 -0
- package/templates/teams/devops/references/monitoring.md +224 -0
- package/templates/teams/devops/references/review-checklist.md +149 -0
- package/templates/teams/devops/references/security.md +225 -0
- package/templates/teams/devops/review-checklist.md +72 -0
- package/templates/teams/devops/skill.md +131 -0
- package/templates/teams/fe/agent.yaml +28 -0
- package/templates/teams/fe/conventions.md +80 -0
- package/templates/teams/fe/references/accessibility.md +92 -0
- package/templates/teams/fe/references/component-architecture.md +87 -0
- package/templates/teams/fe/references/css-styling.md +89 -0
- package/templates/teams/fe/references/forms.md +73 -0
- package/templates/teams/fe/references/performance.md +104 -0
- package/templates/teams/fe/references/review-checklist.md +51 -0
- package/templates/teams/fe/references/security.md +90 -0
- package/templates/teams/fe/references/state-management.md +117 -0
- package/templates/teams/fe/references/testing.md +112 -0
- package/templates/teams/fe/review-checklist.md +53 -0
- package/templates/teams/fe/skill.md +68 -0
- package/templates/teams/lead/agent.yaml +18 -0
- package/templates/teams/lead/references/cross-team-coordination.md +68 -0
- package/templates/teams/lead/references/quality-gates.md +64 -0
- package/templates/teams/lead/references/task-decomposition.md +69 -0
- package/templates/teams/lead/skill.md +83 -0
- package/templates/teams/qa/agent.yaml +32 -0
- package/templates/teams/qa/conventions.md +130 -0
- package/templates/teams/qa/references/ci-integration.md +337 -0
- package/templates/teams/qa/references/e2e-testing.md +292 -0
- package/templates/teams/qa/references/mocking.md +249 -0
- package/templates/teams/qa/references/performance-testing.md +288 -0
- package/templates/teams/qa/references/review-checklist.md +143 -0
- package/templates/teams/qa/references/security-testing.md +271 -0
- package/templates/teams/qa/references/test-data.md +275 -0
- package/templates/teams/qa/references/test-strategy.md +192 -0
- package/templates/teams/qa/review-checklist.md +53 -0
- package/templates/teams/qa/skill.md +131 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
## Component Architecture
|
|
2
|
+
|
|
3
|
+
- Use functional components exclusively. Class components are legacy — migrate when touching existing ones.
|
|
4
|
+
- Props interface is the component's contract. Design it like a public API:
|
|
5
|
+
- Use discriminated unions for variants: `type ButtonVariant = 'primary' | 'secondary' | 'ghost'` — not `isPrimary`, `isSecondary` booleans
|
|
6
|
+
- Required props first, optional with sensible defaults
|
|
7
|
+
- Callback props follow `onAction` naming: `onClick`, `onChange`, `onSubmit`
|
|
8
|
+
- Avoid `children` prop when the content structure is predictable — use named slots/props instead
|
|
9
|
+
- Co-locate everything for a component in one directory:
|
|
10
|
+
```
|
|
11
|
+
Button/
|
|
12
|
+
├── Button.tsx # Component
|
|
13
|
+
├── Button.test.tsx # Tests
|
|
14
|
+
├── Button.stories.tsx # Storybook
|
|
15
|
+
├── Button.module.css # Styles (if not using Tailwind)
|
|
16
|
+
└── index.ts # Public export (only export what consumers need)
|
|
17
|
+
```
|
|
18
|
+
- Component layers — enforce separation:
|
|
19
|
+
- **Primitives**: Design system atoms (Button, Input, Modal). No business logic. No API calls.
|
|
20
|
+
- **Composites**: Combine primitives for generic patterns (SearchBar, DataTable, FormField). Still no domain knowledge.
|
|
21
|
+
- **Features**: Domain-specific components (UserProfile, InvoiceList). Can fetch data, contain business logic.
|
|
22
|
+
|
|
23
|
+
## State Management
|
|
24
|
+
|
|
25
|
+
- **Local state** (`useState`): UI-only state (open/closed, active tab, form input). This is the default.
|
|
26
|
+
- **Server state** (React Query/TanStack Query): All data from APIs. Never store server data in useState/Zustand. React Query handles caching, revalidation, and deduplication.
|
|
27
|
+
- **Global client state** (Zustand/Jotai): Cross-component UI state (theme, sidebar collapsed, toast queue). Keep this minimal — if it comes from the server, it belongs in React Query.
|
|
28
|
+
- **URL state** (search params): Anything the user should be able to bookmark or share (filters, pagination, selected tab). Use `useSearchParams` or a URL state library.
|
|
29
|
+
- **Never** duplicate server state into client stores. This causes sync bugs that are extremely hard to debug in production.
|
|
30
|
+
|
|
31
|
+
## Performance Rules
|
|
32
|
+
|
|
33
|
+
- **Measure first, optimize second.** Don't add `useMemo`/`useCallback` prophylactically. Add them when React DevTools Profiler shows a measurable problem (>16ms render).
|
|
34
|
+
- Lazy load routes with `React.lazy()` + Suspense. Every route should be a separate chunk.
|
|
35
|
+
- Heavy components (charts, rich text editors, maps) get `dynamic(() => import(...), { ssr: false })` in Next.js.
|
|
36
|
+
- Images: Always use the framework's `<Image>` component. Set explicit `width`/`height` to prevent CLS. Use `priority` only for above-the-fold hero images.
|
|
37
|
+
- Fonts: Use `next/font` or `@fontsource`. Preload only the weights/subsets you actually use. `font-display: swap` always.
|
|
38
|
+
- Lists with >50 items must use virtualization (`react-window` or `@tanstack/react-virtual`). No exceptions.
|
|
39
|
+
- Avoid layout thrashing: batch DOM reads and writes. Never read `offsetHeight` inside a loop that modifies styles.
|
|
40
|
+
|
|
41
|
+
## CSS & Styling
|
|
42
|
+
|
|
43
|
+
- Tailwind is preferred. Use `@apply` sparingly — only in base component styles where utility classes become unreadable (>6 classes).
|
|
44
|
+
- If using CSS modules, follow BEM-like naming: `.card`, `.card__header`, `.card--highlighted`.
|
|
45
|
+
- Never use inline `style={{}}` for layout or spacing. Only for truly dynamic values (calculated positions, user-selected colors).
|
|
46
|
+
- Design tokens (colors, spacing, typography) come from the design system / Tailwind config. Never hardcode hex values or pixel sizes.
|
|
47
|
+
- Z-index scale: define in a central config. Use semantic names (dropdown: 100, modal: 200, toast: 300, tooltip: 400). Never use arbitrary z-index values.
|
|
48
|
+
|
|
49
|
+
## Forms
|
|
50
|
+
|
|
51
|
+
- Use React Hook Form + Zod for all forms. Define the Zod schema first — it serves as both validation and TypeScript type.
|
|
52
|
+
- Validate on blur for individual fields, on submit for the full form. Show inline errors immediately after blur.
|
|
53
|
+
- Disable submit button only during submission (not for validation). Show validation errors instead of hiding the submit action.
|
|
54
|
+
- Multi-step forms: persist partial state to sessionStorage or URL params. Users who accidentally navigate away should not lose data.
|
|
55
|
+
- File uploads: show progress, support drag-and-drop, validate file type/size client-side before upload.
|
|
56
|
+
|
|
57
|
+
## Error Handling
|
|
58
|
+
|
|
59
|
+
- Wrap feature sections with React Error Boundaries. A chart crashing should not take down the entire page.
|
|
60
|
+
- API errors: distinguish between 4xx (show user-actionable message) and 5xx (show generic "something went wrong" + retry).
|
|
61
|
+
- Network errors: detect offline state (`navigator.onLine` + `online`/`offline` events). Show persistent banner when offline. Queue mutations for retry.
|
|
62
|
+
- Race conditions: cancel in-flight requests on component unmount (`AbortController` in useEffect cleanup). Cancel previous search requests on new input (debounce + abort).
|
|
63
|
+
- Never show raw error messages to users. Map API error codes to human-readable messages. Log raw errors to monitoring (Sentry/DataDog).
|
|
64
|
+
|
|
65
|
+
## Testing
|
|
66
|
+
|
|
67
|
+
- Test from the user's perspective. Query by role (`getByRole('button', { name: 'Submit' })`), not by test ID.
|
|
68
|
+
- Test behavior, not implementation: "when user clicks submit, success message appears" — not "when submit handler is called, setState is invoked".
|
|
69
|
+
- Snapshot tests are banned for component output. They break on every style change and catch nothing meaningful. Use visual regression (Chromatic/Percy) instead.
|
|
70
|
+
- Test keyboard navigation for all interactive components.
|
|
71
|
+
- Mock network requests with MSW (Mock Service Worker), not by mocking fetch/axios directly.
|
|
72
|
+
- Every bug fix must include a test that would have caught the bug.
|
|
73
|
+
|
|
74
|
+
## Security
|
|
75
|
+
|
|
76
|
+
- Never use `dangerouslySetInnerHTML` without sanitizing with DOMPurify first.
|
|
77
|
+
- Never construct URLs from user input without validation. Use `URL` constructor and whitelist allowed origins.
|
|
78
|
+
- CSRF: ensure all mutation requests include the CSRF token. Framework should handle this — verify it's configured.
|
|
79
|
+
- Sensitive data (tokens, PII) must never appear in URL query params, localStorage, or client-side logs.
|
|
80
|
+
- Content Security Policy: work with DevOps to set strict CSP headers. Report violations to a monitoring endpoint.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Accessibility (WCAG 2.1 AA)
|
|
2
|
+
|
|
3
|
+
## Keyboard Navigation
|
|
4
|
+
|
|
5
|
+
Every interactive element must be operable via keyboard:
|
|
6
|
+
- **Tab**: Move focus to next interactive element
|
|
7
|
+
- **Shift+Tab**: Move focus backward
|
|
8
|
+
- **Enter/Space**: Activate buttons, links, checkboxes
|
|
9
|
+
- **Escape**: Close modals, dropdowns, tooltips
|
|
10
|
+
- **Arrow keys**: Navigate within lists, menus, tabs, radio groups
|
|
11
|
+
- **Home/End**: Jump to first/last item in list
|
|
12
|
+
|
|
13
|
+
**Focus management rules:**
|
|
14
|
+
- Focus order must match visual order (no `tabindex > 0`)
|
|
15
|
+
- Modals trap focus — Tab cycles within modal, cannot escape to background
|
|
16
|
+
- After modal closes, focus returns to the element that opened it
|
|
17
|
+
- After deleting an item, focus moves to next item or parent container
|
|
18
|
+
- Skip links: first Tab stop on page should be "Skip to main content"
|
|
19
|
+
|
|
20
|
+
**Visible focus indicator:**
|
|
21
|
+
```css
|
|
22
|
+
:focus-visible {
|
|
23
|
+
outline: 2px solid var(--focus-color);
|
|
24
|
+
outline-offset: 2px;
|
|
25
|
+
}
|
|
26
|
+
/* Never: *:focus { outline: none; } */
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## ARIA Attributes
|
|
30
|
+
|
|
31
|
+
**Use semantic HTML first** — ARIA is a last resort when HTML semantics are insufficient:
|
|
32
|
+
```html
|
|
33
|
+
<!-- GOOD: semantic HTML, no ARIA needed -->
|
|
34
|
+
<button>Submit</button>
|
|
35
|
+
<nav><ul>...</ul></nav>
|
|
36
|
+
|
|
37
|
+
<!-- BAD: div with ARIA role -->
|
|
38
|
+
<div role="button" tabindex="0" onclick="...">Submit</div>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Common ARIA patterns:**
|
|
42
|
+
- `aria-label`: Label for elements without visible text (icon buttons)
|
|
43
|
+
- `aria-labelledby`: Reference another element as label
|
|
44
|
+
- `aria-describedby`: Additional description (error messages, help text)
|
|
45
|
+
- `aria-expanded`: Toggle state for accordions, dropdowns
|
|
46
|
+
- `aria-live="polite"`: Announce dynamic content changes (toast, status)
|
|
47
|
+
- `aria-hidden="true"`: Hide decorative elements from screen readers
|
|
48
|
+
- `role="alert"`: Immediately announce urgent messages
|
|
49
|
+
|
|
50
|
+
**Dynamic content announcement:**
|
|
51
|
+
```tsx
|
|
52
|
+
// Status messages read by screen reader
|
|
53
|
+
<div aria-live="polite" aria-atomic="true">
|
|
54
|
+
{submitStatus === 'success' && 'Form submitted successfully'}
|
|
55
|
+
{submitStatus === 'error' && 'Submission failed. Please try again.'}
|
|
56
|
+
</div>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Color & Contrast
|
|
60
|
+
|
|
61
|
+
- Text contrast: minimum 4.5:1 ratio (3:1 for large text ≥ 24px)
|
|
62
|
+
- UI component contrast: minimum 3:1 against background
|
|
63
|
+
- Never rely on color alone to convey information — add icons, text, or patterns
|
|
64
|
+
- Test with color blindness simulators (Chrome DevTools → Rendering → Emulate vision deficiency)
|
|
65
|
+
|
|
66
|
+
## Touch Targets
|
|
67
|
+
|
|
68
|
+
- Minimum 44x44px for all interactive elements on mobile
|
|
69
|
+
- Adequate spacing between targets (minimum 8px gap)
|
|
70
|
+
- Inline links within text paragraphs are exempt but should have generous padding
|
|
71
|
+
|
|
72
|
+
## Testing
|
|
73
|
+
|
|
74
|
+
**Automated (catches ~30% of issues):**
|
|
75
|
+
- axe-core: `npm run test:a11y` or Storybook axe addon
|
|
76
|
+
- Lighthouse accessibility audit (target: 100 score)
|
|
77
|
+
- eslint-plugin-jsx-a11y for static analysis
|
|
78
|
+
|
|
79
|
+
**Manual (catches remaining ~70%):**
|
|
80
|
+
- **Keyboard test**: Unplug mouse, navigate entire feature with keyboard only
|
|
81
|
+
- **Screen reader test**: VoiceOver (Mac), NVDA (Windows), or TalkBack (Android)
|
|
82
|
+
- **Zoom test**: 200% browser zoom — no content clipped or overlapping
|
|
83
|
+
- **Reduced motion**: `prefers-reduced-motion` media query respected — no essential animations
|
|
84
|
+
|
|
85
|
+
## Common Mistakes
|
|
86
|
+
|
|
87
|
+
- `<div onClick>` instead of `<button>` — div has no keyboard support, no role, no focus
|
|
88
|
+
- Placeholder text as the only label — disappears on input, not announced by all screen readers
|
|
89
|
+
- Auto-playing video/audio without controls or mute option
|
|
90
|
+
- Form errors only shown on submit — show inline on blur
|
|
91
|
+
- Modal without focus trap — user tabs into background content
|
|
92
|
+
- Image carousel without pause control and keyboard navigation
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Component Architecture
|
|
2
|
+
|
|
3
|
+
## Props Interface Design
|
|
4
|
+
|
|
5
|
+
- Props interface is the component's contract. Design it like a public API:
|
|
6
|
+
- Use discriminated unions for variants: `type ButtonVariant = 'primary' | 'secondary' | 'ghost'` — not `isPrimary`, `isSecondary` booleans
|
|
7
|
+
- Required props first, optional with sensible defaults
|
|
8
|
+
- Callback props follow `onAction` naming: `onClick`, `onChange`, `onSubmit`
|
|
9
|
+
- Avoid `children` prop when the content structure is predictable — use named slots/props
|
|
10
|
+
- Use `React.forwardRef` if the component wraps a DOM element consumers may need to reference
|
|
11
|
+
- Spread remaining props onto the root element: `{...rest}` for flexibility (className, data-*, aria-*)
|
|
12
|
+
|
|
13
|
+
## Component File Structure
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Button/
|
|
17
|
+
├── Button.tsx # Component implementation
|
|
18
|
+
├── Button.test.tsx # Tests (behavior-based, not snapshot)
|
|
19
|
+
├── Button.stories.tsx # Storybook stories for all states
|
|
20
|
+
├── Button.module.css # Styles (if not using Tailwind)
|
|
21
|
+
└── index.ts # Public export only — never re-export internals
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Component Layers — Enforce Separation
|
|
25
|
+
|
|
26
|
+
**Primitives** (design system atoms):
|
|
27
|
+
- Button, Input, Modal, Tooltip, Badge, Avatar
|
|
28
|
+
- No business logic. No API calls. No domain knowledge.
|
|
29
|
+
- Accept generic props. Fully configurable via props.
|
|
30
|
+
|
|
31
|
+
**Composites** (domain-agnostic patterns):
|
|
32
|
+
- SearchBar, DataTable, FormField, Pagination, FileUpload
|
|
33
|
+
- Combine primitives for reusable UI patterns
|
|
34
|
+
- Still no domain knowledge — works in any project
|
|
35
|
+
|
|
36
|
+
**Features** (domain-specific):
|
|
37
|
+
- UserProfile, InvoiceList, CheckoutForm, DashboardWidget
|
|
38
|
+
- Can fetch data, contain business logic
|
|
39
|
+
- Use primitives and composites internally
|
|
40
|
+
|
|
41
|
+
**Rules**:
|
|
42
|
+
- Primitives never import composites or features
|
|
43
|
+
- Composites never import features
|
|
44
|
+
- Features can import anything below them
|
|
45
|
+
|
|
46
|
+
## Visual States — Every Component Must Handle
|
|
47
|
+
|
|
48
|
+
1. **Default**: Normal interactive state
|
|
49
|
+
2. **Loading**: Skeleton placeholder, not spinner (preserves layout, reduces CLS)
|
|
50
|
+
3. **Error**: Inline error message with retry action. Not just red text — explain what went wrong.
|
|
51
|
+
4. **Empty**: Meaningful empty state — illustration + call to action, not blank space
|
|
52
|
+
5. **Disabled**: Visually distinct, not interactive, cursor: not-allowed, aria-disabled
|
|
53
|
+
6. **Focused**: Visible focus ring (2px solid, high contrast). Never `outline: none` without replacement.
|
|
54
|
+
7. **Hover**: Subtle visual feedback. Must not be the only way to discover functionality.
|
|
55
|
+
|
|
56
|
+
## Composition Patterns
|
|
57
|
+
|
|
58
|
+
**Compound Components** (for complex UI with shared state):
|
|
59
|
+
```tsx
|
|
60
|
+
<Select>
|
|
61
|
+
<Select.Trigger>Choose option</Select.Trigger>
|
|
62
|
+
<Select.Content>
|
|
63
|
+
<Select.Item value="a">Option A</Select.Item>
|
|
64
|
+
<Select.Item value="b">Option B</Select.Item>
|
|
65
|
+
</Select.Content>
|
|
66
|
+
</Select>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Render Props** (for customizable rendering):
|
|
70
|
+
```tsx
|
|
71
|
+
<DataTable
|
|
72
|
+
data={users}
|
|
73
|
+
renderRow={(user) => <UserRow key={user.id} user={user} />}
|
|
74
|
+
/>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Slot Props** (named content areas):
|
|
78
|
+
```tsx
|
|
79
|
+
<Card
|
|
80
|
+
header={<h3>Title</h3>}
|
|
81
|
+
footer={<Button>Save</Button>}
|
|
82
|
+
>
|
|
83
|
+
Body content
|
|
84
|
+
</Card>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Prefer these over single mega-component with 20+ props.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# CSS & Styling
|
|
2
|
+
|
|
3
|
+
## Tailwind Conventions
|
|
4
|
+
|
|
5
|
+
- Tailwind is the default styling approach. Use utility classes directly in JSX.
|
|
6
|
+
- Use `@apply` sparingly — only in base component styles where utilities exceed 6 classes:
|
|
7
|
+
```css
|
|
8
|
+
/* OK: Complex base style */
|
|
9
|
+
.btn-primary { @apply px-4 py-2 rounded-lg font-medium text-white bg-blue-600 hover:bg-blue-700 focus-visible:ring-2; }
|
|
10
|
+
|
|
11
|
+
/* BAD: Simple style that should stay as utilities */
|
|
12
|
+
.my-margin { @apply mt-4; }
|
|
13
|
+
```
|
|
14
|
+
- **Never hardcode colors/spacing** — use Tailwind's design tokens:
|
|
15
|
+
```tsx
|
|
16
|
+
// BAD
|
|
17
|
+
<div style={{ color: '#3B82F6', padding: '16px' }}>
|
|
18
|
+
// GOOD
|
|
19
|
+
<div className="text-blue-500 p-4">
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Design Tokens
|
|
23
|
+
|
|
24
|
+
All visual values come from the design system / Tailwind config:
|
|
25
|
+
- Colors: `text-primary`, `bg-surface`, `border-muted` — defined in `tailwind.config`
|
|
26
|
+
- Spacing: Tailwind's scale (4, 8, 12, 16, 20, 24, 32, 40, 48, 64px)
|
|
27
|
+
- Typography: `text-sm`, `text-base`, `text-lg` — consistent scale
|
|
28
|
+
- Shadows: `shadow-sm`, `shadow-md`, `shadow-lg` — predefined elevation
|
|
29
|
+
- Radii: `rounded`, `rounded-lg`, `rounded-full` — consistent corner rounding
|
|
30
|
+
|
|
31
|
+
## Z-Index Scale
|
|
32
|
+
|
|
33
|
+
Define in Tailwind config, use semantic names:
|
|
34
|
+
```javascript
|
|
35
|
+
zIndex: {
|
|
36
|
+
dropdown: '100',
|
|
37
|
+
sticky: '200',
|
|
38
|
+
overlay: '300',
|
|
39
|
+
modal: '400',
|
|
40
|
+
toast: '500',
|
|
41
|
+
tooltip: '600',
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
**Never use arbitrary z-index** (`z-[9999]`). If you need a new level, add it to the scale.
|
|
45
|
+
|
|
46
|
+
## Responsive Design
|
|
47
|
+
|
|
48
|
+
- **Mobile-first**: Write base styles for mobile, add `md:` and `lg:` for larger screens
|
|
49
|
+
- **Breakpoints**: `sm` (640px), `md` (768px), `lg` (1024px), `xl` (1280px), `2xl` (1536px)
|
|
50
|
+
- **Test at**: 320px (small mobile), 375px (iPhone), 768px (iPad), 1024px (laptop), 1440px (desktop)
|
|
51
|
+
- Use `clamp()` for fluid typography: `font-size: clamp(1rem, 2.5vw, 1.5rem)`
|
|
52
|
+
- No horizontal scrollbar at any viewport width
|
|
53
|
+
|
|
54
|
+
## CSS Modules (if not using Tailwind)
|
|
55
|
+
|
|
56
|
+
- File naming: `Component.module.css`
|
|
57
|
+
- Class naming: BEM-like — `.card`, `.card__header`, `.card--highlighted`
|
|
58
|
+
- Import as object: `import styles from './Button.module.css'`
|
|
59
|
+
- Composition over nesting:
|
|
60
|
+
```css
|
|
61
|
+
.button { /* base */ }
|
|
62
|
+
.primary { composes: button; /* extends */ }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Dark Mode
|
|
66
|
+
|
|
67
|
+
- Use CSS variables for all colors:
|
|
68
|
+
```css
|
|
69
|
+
:root { --bg-primary: #ffffff; --text-primary: #111827; }
|
|
70
|
+
.dark { --bg-primary: #111827; --text-primary: #f9fafb; }
|
|
71
|
+
```
|
|
72
|
+
- Tailwind: `dark:bg-gray-900 dark:text-white`
|
|
73
|
+
- Test both modes for every component — screenshots in Storybook
|
|
74
|
+
- Respect `prefers-color-scheme` for default, allow user override
|
|
75
|
+
|
|
76
|
+
## Animation
|
|
77
|
+
|
|
78
|
+
- Respect `prefers-reduced-motion`:
|
|
79
|
+
```css
|
|
80
|
+
@media (prefers-reduced-motion: reduce) {
|
|
81
|
+
*, *::before, *::after {
|
|
82
|
+
animation-duration: 0.01ms !important;
|
|
83
|
+
transition-duration: 0.01ms !important;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
- Use CSS transitions for simple state changes (hover, focus, open/close)
|
|
88
|
+
- Use Framer Motion for complex animations (mount/unmount, layout, gestures)
|
|
89
|
+
- Animation should serve UX (guide attention, show relationships) — not decoration
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Forms
|
|
2
|
+
|
|
3
|
+
## Stack: React Hook Form + Zod
|
|
4
|
+
|
|
5
|
+
Define the Zod schema first — it serves as both validation and TypeScript type:
|
|
6
|
+
```typescript
|
|
7
|
+
const loginSchema = z.object({
|
|
8
|
+
email: z.string().email('Invalid email address'),
|
|
9
|
+
password: z.string().min(8, 'Password must be at least 8 characters'),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
type LoginForm = z.infer<typeof loginSchema>;
|
|
13
|
+
|
|
14
|
+
const { register, handleSubmit, formState: { errors } } = useForm<LoginForm>({
|
|
15
|
+
resolver: zodResolver(loginSchema),
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Validation UX
|
|
20
|
+
|
|
21
|
+
- **Field-level**: Validate on blur (when user leaves the field). Show error immediately.
|
|
22
|
+
- **Form-level**: Validate on submit. Scroll to first error. Focus the error field.
|
|
23
|
+
- **Inline errors**: Show directly below the field, not in a toast or alert banner.
|
|
24
|
+
- **Error text**: Explain what's wrong AND how to fix it: "Password must be at least 8 characters" — not just "Invalid password".
|
|
25
|
+
- **Submit button**: Never disable for validation. Show errors instead. Disable only during submission (loading state).
|
|
26
|
+
|
|
27
|
+
## Multi-step Forms
|
|
28
|
+
|
|
29
|
+
- Persist partial state to sessionStorage or URL params — users who navigate away should not lose data.
|
|
30
|
+
- Validate each step before proceeding to next.
|
|
31
|
+
- Show progress indicator (step 2 of 4).
|
|
32
|
+
- Allow navigation back to previous steps without losing data.
|
|
33
|
+
- Final step shows summary for review before submit.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// Persist form state to sessionStorage
|
|
37
|
+
const { watch, reset } = useForm({ defaultValues: loadFromSession() });
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const subscription = watch((data) => {
|
|
41
|
+
sessionStorage.setItem('checkout-form', JSON.stringify(data));
|
|
42
|
+
});
|
|
43
|
+
return () => subscription.unsubscribe();
|
|
44
|
+
}, [watch]);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## File Uploads
|
|
48
|
+
|
|
49
|
+
- Drag-and-drop zone with click-to-browse fallback
|
|
50
|
+
- Validate file type and size client-side before upload
|
|
51
|
+
- Show upload progress bar (not just spinner)
|
|
52
|
+
- Support multiple files if applicable
|
|
53
|
+
- Preview for images, file name + size for documents
|
|
54
|
+
- Cancel upload action
|
|
55
|
+
|
|
56
|
+
## Error Handling
|
|
57
|
+
|
|
58
|
+
- Network error: "Connection failed. Check your internet and try again." + retry button
|
|
59
|
+
- Server validation error (422): Map field errors to inline messages
|
|
60
|
+
- Timeout: "This is taking longer than expected. Please try again."
|
|
61
|
+
- Duplicate submission prevention: Disable submit on click, re-enable on error
|
|
62
|
+
|
|
63
|
+
## Accessibility
|
|
64
|
+
|
|
65
|
+
- Every input has a visible `<label>` element (not just placeholder)
|
|
66
|
+
- Error messages linked with `aria-describedby`:
|
|
67
|
+
```tsx
|
|
68
|
+
<input aria-describedby="email-error" aria-invalid={!!errors.email} />
|
|
69
|
+
{errors.email && <span id="email-error" role="alert">{errors.email.message}</span>}
|
|
70
|
+
```
|
|
71
|
+
- Required fields marked with `aria-required="true"` and visible indicator
|
|
72
|
+
- Form submission result announced with `aria-live="polite"`
|
|
73
|
+
- Tab order follows visual layout — label → input → error → next field
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Frontend Performance
|
|
2
|
+
|
|
3
|
+
## Core Web Vitals Targets
|
|
4
|
+
|
|
5
|
+
| Metric | Good | Needs Improvement | Poor |
|
|
6
|
+
|--------|------|-------------------|------|
|
|
7
|
+
| LCP (Largest Contentful Paint) | < 2.5s | 2.5s - 4s | > 4s |
|
|
8
|
+
| INP (Interaction to Next Paint) | < 200ms | 200ms - 500ms | > 500ms |
|
|
9
|
+
| CLS (Cumulative Layout Shift) | < 0.1 | 0.1 - 0.25 | > 0.25 |
|
|
10
|
+
|
|
11
|
+
## Performance Audit Procedure
|
|
12
|
+
|
|
13
|
+
1. **Measure baseline**: Lighthouse CI or `web-vitals` library. Record LCP, INP, CLS.
|
|
14
|
+
2. **Bundle analysis**: `npx next build --analyze` or `npx source-map-explorer`. Flag deps > 50KB.
|
|
15
|
+
3. **Render profiling**: React DevTools Profiler. Components re-rendering >2x per action = problem.
|
|
16
|
+
4. **Network waterfall**: DevTools Network tab. Sequential requests → parallelize. Unnecessary client fetches → move server-side.
|
|
17
|
+
5. **Memory check**: DevTools Memory tab during navigation. Growing RSS = leak. Look for detached DOM, uncleaned listeners.
|
|
18
|
+
6. **Fix → Re-measure → Compare** against baseline.
|
|
19
|
+
|
|
20
|
+
## Code Splitting
|
|
21
|
+
|
|
22
|
+
- **Route-based** (minimum): Every route = separate chunk via `React.lazy()` + Suspense
|
|
23
|
+
- **Component-level**: Heavy widgets (charts, editors, maps) via `dynamic(() => import(...), { ssr: false })`
|
|
24
|
+
- **Conditional**: Features behind feature flags loaded only when enabled
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// Route-level splitting
|
|
28
|
+
const Dashboard = lazy(() => import('./pages/Dashboard'));
|
|
29
|
+
|
|
30
|
+
// Component-level for heavy widgets
|
|
31
|
+
const Chart = dynamic(() => import('./components/Chart'), {
|
|
32
|
+
ssr: false,
|
|
33
|
+
loading: () => <ChartSkeleton />,
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Rendering Optimization
|
|
38
|
+
|
|
39
|
+
**When to use `useMemo` / `useCallback`:**
|
|
40
|
+
- Only when React DevTools Profiler shows a measurable problem (>16ms render)
|
|
41
|
+
- For expensive computations (sorting/filtering large arrays)
|
|
42
|
+
- For referential equality in dependency arrays of child components using `React.memo`
|
|
43
|
+
- **Never** prophylactically "just in case"
|
|
44
|
+
|
|
45
|
+
**Virtualization** — mandatory for lists > 50 items:
|
|
46
|
+
```typescript
|
|
47
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
48
|
+
// Renders only visible items + buffer. 10,000 items = ~20 DOM nodes.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Avoid layout thrashing:**
|
|
52
|
+
```typescript
|
|
53
|
+
// BAD: Read-write-read-write cycle forces reflow each time
|
|
54
|
+
items.forEach(item => {
|
|
55
|
+
const height = item.offsetHeight; // Read → forces reflow
|
|
56
|
+
item.style.height = height + 10 + 'px'; // Write
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// GOOD: Batch reads, then batch writes
|
|
60
|
+
const heights = items.map(item => item.offsetHeight);
|
|
61
|
+
items.forEach((item, i) => {
|
|
62
|
+
item.style.height = heights[i] + 10 + 'px';
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Images
|
|
67
|
+
|
|
68
|
+
- Always use framework `<Image>` component (Next.js, Nuxt, etc.)
|
|
69
|
+
- Set explicit `width` and `height` to prevent CLS
|
|
70
|
+
- Use `priority` only for above-the-fold hero images
|
|
71
|
+
- Lazy load all below-fold images (default behavior)
|
|
72
|
+
- Format: WebP/AVIF with JPEG fallback
|
|
73
|
+
- Responsive: use `sizes` attribute for art direction
|
|
74
|
+
|
|
75
|
+
## Fonts
|
|
76
|
+
|
|
77
|
+
- Use `next/font` or `@fontsource` for self-hosting
|
|
78
|
+
- Preload only the weights/subsets you use (Latin: ~15KB vs All: ~100KB+)
|
|
79
|
+
- `font-display: swap` always — show fallback font immediately
|
|
80
|
+
- Limit to 2 font families max
|
|
81
|
+
|
|
82
|
+
## Bundle Hygiene
|
|
83
|
+
|
|
84
|
+
- No dependency > 20KB gzipped without team discussion
|
|
85
|
+
- Check for duplicates: `npm ls <package>` — multiple versions = bundle bloat
|
|
86
|
+
- Import specific modules: `import { debounce } from 'lodash-es/debounce'` not `import _ from 'lodash'`
|
|
87
|
+
- Barrel files (`index.ts` re-exporting everything) kill tree-shaking — avoid in hot paths
|
|
88
|
+
|
|
89
|
+
## Streaming & Suspense
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
// Progressive loading with Suspense boundaries
|
|
93
|
+
<Suspense fallback={<HeaderSkeleton />}>
|
|
94
|
+
<Header />
|
|
95
|
+
</Suspense>
|
|
96
|
+
<Suspense fallback={<ContentSkeleton />}>
|
|
97
|
+
<MainContent /> {/* Streams as data arrives */}
|
|
98
|
+
</Suspense>
|
|
99
|
+
<Suspense fallback={<SidebarSkeleton />}>
|
|
100
|
+
<Sidebar /> {/* Independent loading */}
|
|
101
|
+
</Suspense>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Each Suspense boundary loads independently — user sees progressive content, not a blank page.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Frontend Review Checklist
|
|
2
|
+
|
|
3
|
+
### Component Design
|
|
4
|
+
- [ ] Props interface is minimal and hard to misuse (union types > boolean flags)
|
|
5
|
+
- [ ] Component has single responsibility — not mixing data fetching with presentation
|
|
6
|
+
- [ ] Ref forwarding implemented if component wraps a DOM element
|
|
7
|
+
- [ ] No barrel file re-exports that kill tree-shaking
|
|
8
|
+
|
|
9
|
+
### Rendering & Performance
|
|
10
|
+
- [ ] No unnecessary re-renders — checked with React DevTools Profiler
|
|
11
|
+
- [ ] `useMemo`/`useCallback` used only with measured justification
|
|
12
|
+
- [ ] Heavy components (>50KB) are lazy loaded
|
|
13
|
+
- [ ] Lists with >50 items use virtualization
|
|
14
|
+
- [ ] Images use framework `<Image>` component with explicit dimensions
|
|
15
|
+
- [ ] No layout thrashing (DOM reads inside style-modifying loops)
|
|
16
|
+
|
|
17
|
+
### Accessibility (WCAG 2.1 AA)
|
|
18
|
+
- [ ] All interactive elements reachable and operable via keyboard
|
|
19
|
+
- [ ] Focus management: logical order, modals trap focus, focus returns after close
|
|
20
|
+
- [ ] ARIA attributes correct: `role`, `aria-label`, `aria-expanded`, `aria-live`
|
|
21
|
+
- [ ] Color contrast meets 4.5:1 (text) / 3:1 (UI components)
|
|
22
|
+
- [ ] Touch targets minimum 44x44px on mobile
|
|
23
|
+
- [ ] Tested with screen reader or axe-core
|
|
24
|
+
|
|
25
|
+
### States & Error Handling
|
|
26
|
+
- [ ] All async operations handle: loading (skeleton), error (message + retry), empty, success
|
|
27
|
+
- [ ] React Error Boundary wraps the feature section
|
|
28
|
+
- [ ] Race conditions handled: useEffect cleanup cancels in-flight requests
|
|
29
|
+
- [ ] Stale closures checked in async callbacks
|
|
30
|
+
|
|
31
|
+
### Responsive & Visual
|
|
32
|
+
- [ ] Tested at 320px, 768px, 1024px, 1440px
|
|
33
|
+
- [ ] No horizontal scrollbar at any viewport
|
|
34
|
+
- [ ] Dark mode works (if supported)
|
|
35
|
+
|
|
36
|
+
### Security
|
|
37
|
+
- [ ] No `dangerouslySetInnerHTML` without DOMPurify
|
|
38
|
+
- [ ] User-generated content escaped
|
|
39
|
+
- [ ] No sensitive data in URL params or localStorage
|
|
40
|
+
- [ ] External URLs validated — no open redirect vectors
|
|
41
|
+
|
|
42
|
+
### Testing
|
|
43
|
+
- [ ] Tests query by role/label, not test ID
|
|
44
|
+
- [ ] Key user interactions and error cases covered
|
|
45
|
+
- [ ] No snapshot tests for component output
|
|
46
|
+
- [ ] Accessibility assertions included
|
|
47
|
+
|
|
48
|
+
### Bundle
|
|
49
|
+
- [ ] No new dependency > 20KB gzipped without discussion
|
|
50
|
+
- [ ] No duplicate packages
|
|
51
|
+
- [ ] Specific imports (not barrel file imports)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Frontend Security
|
|
2
|
+
|
|
3
|
+
## XSS Prevention
|
|
4
|
+
|
|
5
|
+
**Never use `dangerouslySetInnerHTML` without sanitization:**
|
|
6
|
+
```typescript
|
|
7
|
+
import DOMPurify from 'dompurify';
|
|
8
|
+
|
|
9
|
+
// GOOD: Sanitized
|
|
10
|
+
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userContent) }} />
|
|
11
|
+
|
|
12
|
+
// BAD: Raw user content
|
|
13
|
+
<div dangerouslySetInnerHTML={{ __html: userContent }} />
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**React auto-escapes JSX** — but watch for:
|
|
17
|
+
- `href={userInput}` — can execute `javascript:alert('xss')`. Validate URL scheme.
|
|
18
|
+
- `style={userControlledObject}` — can inject CSS expressions in older browsers.
|
|
19
|
+
- Template literals in `<script>` tags (SSR) — server-rendered user data must be escaped.
|
|
20
|
+
|
|
21
|
+
**URL validation:**
|
|
22
|
+
```typescript
|
|
23
|
+
function isValidUrl(input: string): boolean {
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(input);
|
|
26
|
+
return ['http:', 'https:'].includes(url.protocol);
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Authentication Token Handling
|
|
34
|
+
|
|
35
|
+
- Access tokens: store in memory (variable/React state). Lost on refresh = by design (use refresh flow).
|
|
36
|
+
- Refresh tokens: `httpOnly`, `Secure`, `SameSite=Strict` cookie. Never accessible to JavaScript.
|
|
37
|
+
- **Never store tokens in**: localStorage, sessionStorage, URL query params, cookies accessible to JS.
|
|
38
|
+
- Clear tokens on logout: revoke refresh token server-side, clear in-memory access token.
|
|
39
|
+
|
|
40
|
+
## CSRF Protection
|
|
41
|
+
|
|
42
|
+
- All mutation requests (POST, PUT, DELETE) must include CSRF token
|
|
43
|
+
- Framework handles this — verify it's configured:
|
|
44
|
+
- Next.js: use `next-csrf` or custom middleware
|
|
45
|
+
- SPA: include token from `X-CSRF-Token` header or cookie
|
|
46
|
+
- Same-origin policy + `SameSite=Strict` cookies provide baseline protection
|
|
47
|
+
|
|
48
|
+
## Content Security Policy (CSP)
|
|
49
|
+
|
|
50
|
+
Work with DevOps to set strict CSP headers:
|
|
51
|
+
```
|
|
52
|
+
Content-Security-Policy:
|
|
53
|
+
default-src 'self';
|
|
54
|
+
script-src 'self' 'nonce-{random}';
|
|
55
|
+
style-src 'self' 'unsafe-inline'; /* Tailwind needs this */
|
|
56
|
+
img-src 'self' data: https:;
|
|
57
|
+
connect-src 'self' https://api.example.com;
|
|
58
|
+
frame-ancestors 'none';
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- Report violations to a monitoring endpoint: `report-uri /csp-report`
|
|
62
|
+
- Start in report-only mode, then enforce after fixing violations
|
|
63
|
+
|
|
64
|
+
## Sensitive Data
|
|
65
|
+
|
|
66
|
+
- Never log PII (email, name, phone) to console in production
|
|
67
|
+
- Never put sensitive data in URL query params (visible in browser history, logs, analytics)
|
|
68
|
+
- Mask sensitive fields in error reports (Sentry: `beforeSend` hook)
|
|
69
|
+
- Clear sensitive form data from memory after submission
|
|
70
|
+
|
|
71
|
+
## Third-party Scripts
|
|
72
|
+
|
|
73
|
+
- Audit all third-party scripts — each is an XSS vector
|
|
74
|
+
- Load analytics/tracking via tag manager, not inline scripts
|
|
75
|
+
- Use `integrity` attribute for CDN scripts (SRI — Subresource Integrity)
|
|
76
|
+
- Sandbox iframes: `sandbox="allow-scripts allow-same-origin"`
|
|
77
|
+
|
|
78
|
+
## Open Redirect Prevention
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// BAD: Redirect to user-controlled URL
|
|
82
|
+
const returnUrl = searchParams.get('returnUrl');
|
|
83
|
+
router.push(returnUrl); // Could be https://evil.com
|
|
84
|
+
|
|
85
|
+
// GOOD: Validate against allowed paths
|
|
86
|
+
const returnUrl = searchParams.get('returnUrl');
|
|
87
|
+
if (returnUrl?.startsWith('/') && !returnUrl.startsWith('//')) {
|
|
88
|
+
router.push(returnUrl); // Relative path only
|
|
89
|
+
}
|
|
90
|
+
```
|