class-ai-agent 1.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/.claude/CLAUDE.md +155 -0
- package/.claude/agents/backend.md +395 -0
- package/.claude/agents/code-reviewer.md +110 -0
- package/.claude/agents/copywriter-seo.md +236 -0
- package/.claude/agents/frontend.md +384 -0
- package/.claude/agents/project-manager.md +201 -0
- package/.claude/agents/qa.md +221 -0
- package/.claude/agents/security-auditor.md +143 -0
- package/.claude/agents/systems-architect.md +211 -0
- package/.claude/agents/test-engineer.md +123 -0
- package/.claude/agents/ui-ux-designer.md +210 -0
- package/.claude/commands/build.md +132 -0
- package/.claude/commands/debug.md +242 -0
- package/.claude/commands/deploy.md +40 -0
- package/.claude/commands/fix-issue.md +42 -0
- package/.claude/commands/plan.md +125 -0
- package/.claude/commands/review.md +50 -0
- package/.claude/commands/simplify.md +222 -0
- package/.claude/commands/spec.md +95 -0
- package/.claude/commands/test.md +214 -0
- package/.claude/references/accessibility-checklist.md +174 -0
- package/.claude/references/performance-checklist.md +150 -0
- package/.claude/references/security-checklist.md +94 -0
- package/.claude/references/testing-patterns.md +183 -0
- package/.claude/rules/api-conventions.md +79 -0
- package/.claude/rules/clean-code.md +205 -0
- package/.claude/rules/code-style.md +86 -0
- package/.claude/rules/database.md +60 -0
- package/.claude/rules/error-handling.md +92 -0
- package/.claude/rules/git-workflow.md +77 -0
- package/.claude/rules/monitoring.md +311 -0
- package/.claude/rules/naming-conventions.md +260 -0
- package/.claude/rules/project-structure.md +65 -0
- package/.claude/rules/security.md +90 -0
- package/.claude/rules/system-design.md +162 -0
- package/.claude/rules/tech-stack.md +456 -0
- package/.claude/rules/testing.md +104 -0
- package/.claude/settings.json +14 -0
- package/.claude/skills/code-review/SKILL.md +208 -0
- package/.claude/skills/deploy/SKILL.md +68 -0
- package/.claude/skills/deploy/deploy.md +735 -0
- package/.claude/skills/incremental-implementation/SKILL.md +210 -0
- package/.claude/skills/security-review/SKILL.md +71 -0
- package/.claude/skills/tdd/SKILL.md +217 -0
- package/.cursor/CURSOR.md +112 -0
- package/.cursor/agents/backend.md +395 -0
- package/.cursor/agents/code-reviewer.md +110 -0
- package/.cursor/agents/copywriter-seo.md +236 -0
- package/.cursor/agents/frontend.md +384 -0
- package/.cursor/agents/project-manager.md +201 -0
- package/.cursor/agents/qa.md +221 -0
- package/.cursor/agents/security-auditor.md +143 -0
- package/.cursor/agents/systems-architect.md +211 -0
- package/.cursor/agents/test-engineer.md +123 -0
- package/.cursor/agents/ui-ux-designer.md +210 -0
- package/.cursor/commands/build.md +132 -0
- package/.cursor/commands/debug.md +242 -0
- package/.cursor/commands/deploy.md +40 -0
- package/.cursor/commands/fix-issue.md +42 -0
- package/.cursor/commands/plan.md +125 -0
- package/.cursor/commands/review.md +50 -0
- package/.cursor/commands/simplify.md +222 -0
- package/.cursor/commands/spec.md +95 -0
- package/.cursor/commands/test.md +214 -0
- package/.cursor/references/accessibility-checklist.md +174 -0
- package/.cursor/references/performance-checklist.md +150 -0
- package/.cursor/references/security-checklist.md +94 -0
- package/.cursor/references/testing-patterns.md +183 -0
- package/.cursor/rules/api-conventions.mdc +85 -0
- package/.cursor/rules/clean-code.mdc +211 -0
- package/.cursor/rules/code-style.mdc +92 -0
- package/.cursor/rules/cursor-overview.mdc +35 -0
- package/.cursor/rules/database.mdc +66 -0
- package/.cursor/rules/error-handling.mdc +98 -0
- package/.cursor/rules/git-workflow.mdc +83 -0
- package/.cursor/rules/monitoring.mdc +317 -0
- package/.cursor/rules/naming-conventions.mdc +266 -0
- package/.cursor/rules/project-structure.mdc +71 -0
- package/.cursor/rules/security.mdc +95 -0
- package/.cursor/rules/system-design.mdc +168 -0
- package/.cursor/rules/tech-stack.mdc +462 -0
- package/.cursor/rules/testing.mdc +110 -0
- package/.cursor/settings.json +8 -0
- package/.cursor/skills/code-review/SKILL.md +208 -0
- package/.cursor/skills/deploy/SKILL.md +68 -0
- package/.cursor/skills/deploy/deploy.md +735 -0
- package/.cursor/skills/incremental-implementation/SKILL.md +210 -0
- package/.cursor/skills/security-review/SKILL.md +71 -0
- package/.cursor/skills/tdd/SKILL.md +217 -0
- package/AGENTS.md +11 -0
- package/README.md +405 -0
- package/bin/class-ai-agent.cjs +176 -0
- package/package.json +38 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spec
|
|
3
|
+
description: Spec before code — structured PRD creation for new features
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /spec — Specification-Driven Development
|
|
7
|
+
|
|
8
|
+
> "Plan the work, then work the plan."
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
Create a comprehensive specification document **before** writing any code. This ensures alignment on requirements, constraints, and acceptance criteria.
|
|
13
|
+
|
|
14
|
+
## Workflow
|
|
15
|
+
|
|
16
|
+
### Phase 1: Discovery (Ask Questions)
|
|
17
|
+
|
|
18
|
+
Before generating a spec, gather requirements by asking:
|
|
19
|
+
|
|
20
|
+
**Scope**
|
|
21
|
+
- What is the objective of this feature?
|
|
22
|
+
- Who are the target users?
|
|
23
|
+
- What problem does this solve?
|
|
24
|
+
|
|
25
|
+
**Features**
|
|
26
|
+
- What are the core features (MVP)?
|
|
27
|
+
- What are the acceptance criteria for each?
|
|
28
|
+
- What is explicitly out of scope?
|
|
29
|
+
|
|
30
|
+
**Technical**
|
|
31
|
+
- Any tech stack preferences or constraints?
|
|
32
|
+
- Integration points with existing systems?
|
|
33
|
+
- Performance requirements?
|
|
34
|
+
|
|
35
|
+
### Phase 2: Generate Specification
|
|
36
|
+
|
|
37
|
+
After discovery, produce `SPEC.md` with these sections:
|
|
38
|
+
|
|
39
|
+
```markdown
|
|
40
|
+
# Feature: [Name]
|
|
41
|
+
|
|
42
|
+
## Objective
|
|
43
|
+
[1-2 sentences describing the goal]
|
|
44
|
+
|
|
45
|
+
## Target Users
|
|
46
|
+
[Who will use this and why]
|
|
47
|
+
|
|
48
|
+
## Core Features
|
|
49
|
+
1. [Feature A] — [Acceptance criteria]
|
|
50
|
+
2. [Feature B] — [Acceptance criteria]
|
|
51
|
+
3. [Feature C] — [Acceptance criteria]
|
|
52
|
+
|
|
53
|
+
## Out of Scope
|
|
54
|
+
- [What we're NOT building in this iteration]
|
|
55
|
+
|
|
56
|
+
## Technical Approach
|
|
57
|
+
- Tech stack decisions
|
|
58
|
+
- Data models
|
|
59
|
+
- API contracts (if applicable)
|
|
60
|
+
- Integration points
|
|
61
|
+
|
|
62
|
+
## Code Style
|
|
63
|
+
- Follow rules in `.claude/rules/`
|
|
64
|
+
- [Any feature-specific conventions]
|
|
65
|
+
|
|
66
|
+
## Testing Strategy
|
|
67
|
+
- Unit tests for: [areas]
|
|
68
|
+
- Integration tests for: [areas]
|
|
69
|
+
- E2E tests for: [critical paths]
|
|
70
|
+
|
|
71
|
+
## Boundaries
|
|
72
|
+
### Always Do
|
|
73
|
+
- [Non-negotiables]
|
|
74
|
+
|
|
75
|
+
### Ask First
|
|
76
|
+
- [Decisions requiring approval]
|
|
77
|
+
|
|
78
|
+
### Never Do
|
|
79
|
+
- [Hard constraints]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Phase 3: Review & Confirm
|
|
83
|
+
|
|
84
|
+
- Present the spec to the user
|
|
85
|
+
- Confirm before proceeding to `/plan`
|
|
86
|
+
- Save as `SPEC.md` in project root or `docs/specs/[feature].md`
|
|
87
|
+
|
|
88
|
+
## Output
|
|
89
|
+
|
|
90
|
+
- `SPEC.md` — The specification document
|
|
91
|
+
- Clear alignment on what to build
|
|
92
|
+
|
|
93
|
+
## Next Step
|
|
94
|
+
|
|
95
|
+
After spec is approved, run `/plan` to decompose into tasks.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test
|
|
3
|
+
description: Write tests using TDD patterns — tests are proof of correctness
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /test — Test-Driven Development
|
|
7
|
+
|
|
8
|
+
> "Tests are proof, not afterthought."
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
Write tests that prove code works correctly. Use the RED-GREEN-REFACTOR cycle for new features and the Prove-It pattern for bug fixes.
|
|
13
|
+
|
|
14
|
+
## For New Features: RED-GREEN-REFACTOR
|
|
15
|
+
|
|
16
|
+
### RED: Write Failing Test First
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
describe('UserService.findById', () => {
|
|
20
|
+
it('should return user when found', async () => {
|
|
21
|
+
// Arrange
|
|
22
|
+
const userId = 'user-123';
|
|
23
|
+
|
|
24
|
+
// Act
|
|
25
|
+
const user = await userService.findById(userId);
|
|
26
|
+
|
|
27
|
+
// Assert
|
|
28
|
+
expect(user).toBeDefined();
|
|
29
|
+
expect(user.id).toBe(userId);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should throw NotFoundError when user does not exist', async () => {
|
|
33
|
+
await expect(userService.findById('non-existent'))
|
|
34
|
+
.rejects.toThrow(NotFoundError);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Run tests — **they should fail** (proves nothing is implemented yet).
|
|
40
|
+
|
|
41
|
+
### GREEN: Write Minimal Code to Pass
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
async findById(id: string): Promise<User> {
|
|
45
|
+
const user = await this.db.user.findUnique({ where: { id } });
|
|
46
|
+
if (!user) throw new NotFoundError('User not found');
|
|
47
|
+
return user;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Run tests — **they should pass**.
|
|
52
|
+
|
|
53
|
+
### REFACTOR: Improve While Green
|
|
54
|
+
|
|
55
|
+
Clean up code while keeping tests passing:
|
|
56
|
+
- Better naming
|
|
57
|
+
- Extract helpers
|
|
58
|
+
- Remove duplication
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## For Bug Fixes: Prove-It Pattern
|
|
63
|
+
|
|
64
|
+
### Step 1: Write Test That Reproduces the Bug
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
it('should not duplicate items when adding to cart twice', async () => {
|
|
68
|
+
// This test should FAIL with the current buggy code
|
|
69
|
+
await cart.addItem('product-1', 1);
|
|
70
|
+
await cart.addItem('product-1', 1);
|
|
71
|
+
|
|
72
|
+
expect(cart.items).toHaveLength(1);
|
|
73
|
+
expect(cart.items[0].quantity).toBe(2);
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Step 2: Verify Test Fails
|
|
78
|
+
|
|
79
|
+
Run the test — confirm it **fails** (proving the bug exists).
|
|
80
|
+
|
|
81
|
+
### Step 3: Fix the Bug
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
async addItem(productId: string, quantity: number) {
|
|
85
|
+
const existing = this.items.find(i => i.productId === productId);
|
|
86
|
+
if (existing) {
|
|
87
|
+
existing.quantity += quantity; // Fix: increment instead of duplicate
|
|
88
|
+
} else {
|
|
89
|
+
this.items.push({ productId, quantity });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Step 4: Verify Test Passes
|
|
95
|
+
|
|
96
|
+
Run the test — confirm it **passes** (proving the fix works).
|
|
97
|
+
|
|
98
|
+
### Step 5: Run Full Suite
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm test # Ensure no regressions
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Test Pyramid
|
|
107
|
+
|
|
108
|
+
| Level | Percentage | Speed | Scope |
|
|
109
|
+
|-------|------------|-------|-------|
|
|
110
|
+
| **Unit** | 80% | ms | Single function, no I/O |
|
|
111
|
+
| **Integration** | 15% | seconds | API + DB, component interactions |
|
|
112
|
+
| **E2E** | 5% | minutes | Full user flows |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Writing Good Tests
|
|
117
|
+
|
|
118
|
+
### Arrange-Act-Assert Pattern
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
it('should calculate total with discount', () => {
|
|
122
|
+
// Arrange — setup
|
|
123
|
+
const cart = new Cart();
|
|
124
|
+
cart.addItem({ price: 100 });
|
|
125
|
+
cart.applyDiscount(10);
|
|
126
|
+
|
|
127
|
+
// Act — execute
|
|
128
|
+
const total = cart.calculateTotal();
|
|
129
|
+
|
|
130
|
+
// Assert — verify
|
|
131
|
+
expect(total).toBe(90);
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### DAMP Over DRY
|
|
136
|
+
|
|
137
|
+
Tests should be **Descriptive And Meaningful Phrases**. Each test reads independently:
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
// ✅ DAMP — clear and independent
|
|
141
|
+
it('should reject password shorter than 8 characters', () => {
|
|
142
|
+
const result = validatePassword('short');
|
|
143
|
+
expect(result.valid).toBe(false);
|
|
144
|
+
expect(result.error).toBe('Password must be at least 8 characters');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// ❌ Too DRY — requires reading shared setup
|
|
148
|
+
it('should reject short password', () => {
|
|
149
|
+
expect(validate(shortPassword)).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Descriptive Test Names
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
// ✅ Good — describes behavior
|
|
157
|
+
'should return 404 when user is not found'
|
|
158
|
+
'should increment quantity when adding existing product'
|
|
159
|
+
'should send welcome email after registration'
|
|
160
|
+
|
|
161
|
+
// ❌ Bad — vague
|
|
162
|
+
'works correctly'
|
|
163
|
+
'handles error'
|
|
164
|
+
'test user'
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### One Behavior Per Test
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
// ✅ Good — focused
|
|
171
|
+
it('should validate email format', () => { ... });
|
|
172
|
+
it('should require email field', () => { ... });
|
|
173
|
+
|
|
174
|
+
// ❌ Bad — testing multiple things
|
|
175
|
+
it('should validate email', () => {
|
|
176
|
+
expect(validate('')).toBe(false); // required
|
|
177
|
+
expect(validate('bad')).toBe(false); // format
|
|
178
|
+
expect(validate('a@b.c')).toBe(true); // valid
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Test Doubles Preference
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
1. Real implementations (best)
|
|
188
|
+
2. In-memory fakes (test DB, fake filesystem)
|
|
189
|
+
3. Stubs (canned responses)
|
|
190
|
+
4. Mocks (verify interactions — use sparingly)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Anti-Patterns to Avoid
|
|
196
|
+
|
|
197
|
+
| Anti-Pattern | Problem | Fix |
|
|
198
|
+
|--------------|---------|-----|
|
|
199
|
+
| Testing internals | Breaks on refactor | Test inputs/outputs only |
|
|
200
|
+
| Flaky tests | Erodes trust | Use deterministic data, isolate state |
|
|
201
|
+
| Over-mocking | False confidence | Prefer real implementations |
|
|
202
|
+
| Snapshot abuse | Large diffs ignored | Use for rare cases only |
|
|
203
|
+
| Shared state | Tests affect each other | Reset state in beforeEach |
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Verification Checklist
|
|
208
|
+
|
|
209
|
+
- [ ] All new behaviors have tests
|
|
210
|
+
- [ ] Bug fixes have reproduction tests
|
|
211
|
+
- [ ] Test names describe behavior
|
|
212
|
+
- [ ] No skipped or disabled tests
|
|
213
|
+
- [ ] Coverage maintained or improved
|
|
214
|
+
- [ ] Full test suite passes
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Accessibility Checklist (WCAG 2.1 AA)
|
|
2
|
+
|
|
3
|
+
> Ensure your application is usable by everyone.
|
|
4
|
+
|
|
5
|
+
## Keyboard Navigation
|
|
6
|
+
|
|
7
|
+
- [ ] All interactive elements focusable with Tab
|
|
8
|
+
- [ ] Focus order is logical (follows visual order)
|
|
9
|
+
- [ ] Focus indicator visible (never `outline: none`)
|
|
10
|
+
- [ ] Skip link to main content
|
|
11
|
+
- [ ] No keyboard traps
|
|
12
|
+
- [ ] Custom components work with Enter/Space
|
|
13
|
+
|
|
14
|
+
```jsx
|
|
15
|
+
// ✅ Good: Custom button with keyboard support
|
|
16
|
+
<div
|
|
17
|
+
role="button"
|
|
18
|
+
tabIndex={0}
|
|
19
|
+
onClick={handleClick}
|
|
20
|
+
onKeyDown={(e) => {
|
|
21
|
+
if (e.key === 'Enter' || e.key === ' ') handleClick();
|
|
22
|
+
}}
|
|
23
|
+
>
|
|
24
|
+
Click me
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
// ✅ Better: Use native button
|
|
28
|
+
<button onClick={handleClick}>Click me</button>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Screen Readers
|
|
32
|
+
|
|
33
|
+
### Semantic HTML
|
|
34
|
+
- [ ] Use semantic elements (`<nav>`, `<main>`, `<article>`, etc.)
|
|
35
|
+
- [ ] Headings in logical order (h1 → h2 → h3)
|
|
36
|
+
- [ ] Lists use `<ul>`, `<ol>`, `<dl>`
|
|
37
|
+
- [ ] Tables have `<th>` with scope
|
|
38
|
+
|
|
39
|
+
### ARIA
|
|
40
|
+
- [ ] Images have alt text (empty `alt=""` for decorative)
|
|
41
|
+
- [ ] Form inputs have labels
|
|
42
|
+
- [ ] Buttons have accessible names
|
|
43
|
+
- [ ] Dynamic content announced (aria-live)
|
|
44
|
+
- [ ] Modals trap focus and have aria-modal
|
|
45
|
+
|
|
46
|
+
```jsx
|
|
47
|
+
// ✅ Good: Accessible form
|
|
48
|
+
<label htmlFor="email">Email</label>
|
|
49
|
+
<input id="email" type="email" aria-describedby="email-hint" />
|
|
50
|
+
<span id="email-hint">We'll never share your email</span>
|
|
51
|
+
|
|
52
|
+
// ✅ Good: Accessible icon button
|
|
53
|
+
<button aria-label="Close menu">
|
|
54
|
+
<CloseIcon aria-hidden="true" />
|
|
55
|
+
</button>
|
|
56
|
+
|
|
57
|
+
// ✅ Good: Live region for updates
|
|
58
|
+
<div aria-live="polite" aria-atomic="true">
|
|
59
|
+
{notification}
|
|
60
|
+
</div>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Visual Design
|
|
64
|
+
|
|
65
|
+
### Color
|
|
66
|
+
- [ ] Color contrast ratio ≥ 4.5:1 (text)
|
|
67
|
+
- [ ] Color contrast ratio ≥ 3:1 (large text, UI components)
|
|
68
|
+
- [ ] Information not conveyed by color alone
|
|
69
|
+
- [ ] Links distinguishable from text (underline or contrast)
|
|
70
|
+
|
|
71
|
+
### Text
|
|
72
|
+
- [ ] Text resizable to 200% without loss
|
|
73
|
+
- [ ] Line height ≥ 1.5
|
|
74
|
+
- [ ] No text in images (except logos)
|
|
75
|
+
|
|
76
|
+
### Motion
|
|
77
|
+
- [ ] Respect `prefers-reduced-motion`
|
|
78
|
+
- [ ] No flashing content (> 3 flashes/second)
|
|
79
|
+
- [ ] Animations can be paused
|
|
80
|
+
|
|
81
|
+
```css
|
|
82
|
+
/* Respect motion preferences */
|
|
83
|
+
@media (prefers-reduced-motion: reduce) {
|
|
84
|
+
* {
|
|
85
|
+
animation-duration: 0.01ms !important;
|
|
86
|
+
transition-duration: 0.01ms !important;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Forms
|
|
92
|
+
|
|
93
|
+
- [ ] All inputs have visible labels
|
|
94
|
+
- [ ] Required fields marked
|
|
95
|
+
- [ ] Error messages associated with inputs
|
|
96
|
+
- [ ] Autocomplete attributes used
|
|
97
|
+
- [ ] Form validation accessible
|
|
98
|
+
|
|
99
|
+
```jsx
|
|
100
|
+
// ✅ Accessible error message
|
|
101
|
+
<input
|
|
102
|
+
id="email"
|
|
103
|
+
aria-invalid={hasError}
|
|
104
|
+
aria-describedby={hasError ? 'email-error' : undefined}
|
|
105
|
+
/>
|
|
106
|
+
{hasError && (
|
|
107
|
+
<span id="email-error" role="alert">
|
|
108
|
+
Please enter a valid email address
|
|
109
|
+
</span>
|
|
110
|
+
)}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Interactive Components
|
|
114
|
+
|
|
115
|
+
### Buttons & Links
|
|
116
|
+
- [ ] Buttons for actions, links for navigation
|
|
117
|
+
- [ ] Link text is descriptive (not "click here")
|
|
118
|
+
- [ ] Disabled state communicated
|
|
119
|
+
|
|
120
|
+
### Modals/Dialogs
|
|
121
|
+
- [ ] Focus trapped inside modal
|
|
122
|
+
- [ ] Close with Escape key
|
|
123
|
+
- [ ] Focus returns on close
|
|
124
|
+
- [ ] `role="dialog"` and `aria-modal="true"`
|
|
125
|
+
|
|
126
|
+
### Menus & Dropdowns
|
|
127
|
+
- [ ] Arrow key navigation
|
|
128
|
+
- [ ] `role="menu"` and `role="menuitem"`
|
|
129
|
+
- [ ] Current item indicated
|
|
130
|
+
|
|
131
|
+
## Testing Tools
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Automated testing
|
|
135
|
+
npx axe-cli https://example.com
|
|
136
|
+
npx pa11y https://example.com
|
|
137
|
+
|
|
138
|
+
# Browser extensions
|
|
139
|
+
- axe DevTools (Chrome)
|
|
140
|
+
- WAVE (Chrome/Firefox)
|
|
141
|
+
- Accessibility Insights (Chrome)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Manual Testing
|
|
145
|
+
- [ ] Navigate with keyboard only
|
|
146
|
+
- [ ] Test with screen reader (VoiceOver/NVDA)
|
|
147
|
+
- [ ] Zoom to 200%
|
|
148
|
+
- [ ] Use high contrast mode
|
|
149
|
+
- [ ] Test with reduced motion
|
|
150
|
+
|
|
151
|
+
## ARIA Roles Quick Reference
|
|
152
|
+
|
|
153
|
+
| Role | Use For |
|
|
154
|
+
|------|---------|
|
|
155
|
+
| `button` | Clickable elements (prefer `<button>`) |
|
|
156
|
+
| `link` | Navigation (prefer `<a>`) |
|
|
157
|
+
| `dialog` | Modal windows |
|
|
158
|
+
| `alert` | Important messages |
|
|
159
|
+
| `status` | Status updates |
|
|
160
|
+
| `navigation` | Nav sections (prefer `<nav>`) |
|
|
161
|
+
| `main` | Main content (prefer `<main>`) |
|
|
162
|
+
| `region` | Distinct sections with label |
|
|
163
|
+
| `tablist`, `tab`, `tabpanel` | Tab interfaces |
|
|
164
|
+
|
|
165
|
+
## Common Issues
|
|
166
|
+
|
|
167
|
+
| Issue | Fix |
|
|
168
|
+
|-------|-----|
|
|
169
|
+
| Missing alt text | Add descriptive alt or `alt=""` |
|
|
170
|
+
| Low contrast | Increase color contrast |
|
|
171
|
+
| Missing form labels | Add `<label>` elements |
|
|
172
|
+
| Focus not visible | Don't remove outline, style it |
|
|
173
|
+
| Mouse-only interactions | Add keyboard handlers |
|
|
174
|
+
| Auto-playing media | Add controls, respect preferences |
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Performance Checklist
|
|
2
|
+
|
|
3
|
+
> Quick reference for performance optimization.
|
|
4
|
+
|
|
5
|
+
## Core Web Vitals Targets
|
|
6
|
+
|
|
7
|
+
| Metric | Good | Needs Work | Poor |
|
|
8
|
+
|--------|------|------------|------|
|
|
9
|
+
| **LCP** (Largest Contentful Paint) | < 2.5s | 2.5-4s | > 4s |
|
|
10
|
+
| **INP** (Interaction to Next Paint) | < 200ms | 200-500ms | > 500ms |
|
|
11
|
+
| **CLS** (Cumulative Layout Shift) | < 0.1 | 0.1-0.25 | > 0.25 |
|
|
12
|
+
|
|
13
|
+
## Frontend Performance
|
|
14
|
+
|
|
15
|
+
### Critical Render Path
|
|
16
|
+
- [ ] Minimize critical CSS (inline above-fold styles)
|
|
17
|
+
- [ ] Defer non-critical JavaScript
|
|
18
|
+
- [ ] Preload critical resources
|
|
19
|
+
- [ ] Optimize font loading (font-display: swap)
|
|
20
|
+
|
|
21
|
+
### Images
|
|
22
|
+
- [ ] Use modern formats (WebP, AVIF)
|
|
23
|
+
- [ ] Implement lazy loading
|
|
24
|
+
- [ ] Serve responsive sizes (srcset)
|
|
25
|
+
- [ ] Use CDN for static assets
|
|
26
|
+
- [ ] Set explicit width/height (prevent CLS)
|
|
27
|
+
|
|
28
|
+
### JavaScript
|
|
29
|
+
- [ ] Code splitting (dynamic imports)
|
|
30
|
+
- [ ] Tree shaking enabled
|
|
31
|
+
- [ ] Bundle size monitored (< 200KB initial)
|
|
32
|
+
- [ ] No unused dependencies
|
|
33
|
+
|
|
34
|
+
### React Specific
|
|
35
|
+
- [ ] Memoize expensive computations (useMemo)
|
|
36
|
+
- [ ] Prevent unnecessary re-renders (React.memo)
|
|
37
|
+
- [ ] Virtualize long lists (react-window)
|
|
38
|
+
- [ ] Use Suspense for code splitting
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
// ✅ Good: Memoized component
|
|
42
|
+
const ExpensiveList = React.memo(({ items }) => {
|
|
43
|
+
return items.map(item => <Item key={item.id} {...item} />);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ✅ Good: Memoized computation
|
|
47
|
+
const sortedItems = useMemo(
|
|
48
|
+
() => items.sort((a, b) => a.name.localeCompare(b.name)),
|
|
49
|
+
[items]
|
|
50
|
+
);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Backend Performance
|
|
54
|
+
|
|
55
|
+
### Database
|
|
56
|
+
- [ ] Indexes on queried columns
|
|
57
|
+
- [ ] No N+1 queries
|
|
58
|
+
- [ ] Pagination implemented
|
|
59
|
+
- [ ] Connection pooling configured
|
|
60
|
+
- [ ] Query timeouts set
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
// ❌ N+1 Problem
|
|
64
|
+
const users = await db.user.findMany();
|
|
65
|
+
for (const user of users) {
|
|
66
|
+
user.orders = await db.order.findMany({ where: { userId: user.id } });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ✅ Fixed: Include relation
|
|
70
|
+
const users = await db.user.findMany({
|
|
71
|
+
include: { orders: true }
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Caching
|
|
76
|
+
- [ ] Cache frequently accessed data
|
|
77
|
+
- [ ] Appropriate TTLs set
|
|
78
|
+
- [ ] Cache invalidation strategy
|
|
79
|
+
- [ ] CDN for static content
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
// Cache pattern
|
|
83
|
+
async function getUser(id) {
|
|
84
|
+
const cached = await redis.get(`user:${id}`);
|
|
85
|
+
if (cached) return JSON.parse(cached);
|
|
86
|
+
|
|
87
|
+
const user = await db.user.findUnique({ where: { id } });
|
|
88
|
+
await redis.setex(`user:${id}`, 3600, JSON.stringify(user));
|
|
89
|
+
return user;
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### API
|
|
94
|
+
- [ ] Response compression (gzip/brotli)
|
|
95
|
+
- [ ] Pagination for lists
|
|
96
|
+
- [ ] Field selection (select only needed)
|
|
97
|
+
- [ ] Async operations for slow tasks
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
// ✅ Good: Paginated API
|
|
101
|
+
GET /api/users?page=1&limit=20&fields=id,name,email
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Measurement Commands
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Lighthouse (Chrome)
|
|
108
|
+
npx lighthouse https://example.com --output=json
|
|
109
|
+
|
|
110
|
+
# Bundle analysis
|
|
111
|
+
npx webpack-bundle-analyzer stats.json
|
|
112
|
+
|
|
113
|
+
# Check bundle size
|
|
114
|
+
npx bundlephobia <package-name>
|
|
115
|
+
|
|
116
|
+
# Database query analysis
|
|
117
|
+
EXPLAIN ANALYZE SELECT * FROM users WHERE email = '...';
|
|
118
|
+
|
|
119
|
+
# Redis latency
|
|
120
|
+
redis-cli --latency
|
|
121
|
+
|
|
122
|
+
# API response time
|
|
123
|
+
curl -o /dev/null -s -w '%{time_total}\n' https://api.example.com/users
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Performance Budget
|
|
127
|
+
|
|
128
|
+
| Resource | Budget |
|
|
129
|
+
|----------|--------|
|
|
130
|
+
| Initial JS | < 200KB |
|
|
131
|
+
| Initial CSS | < 50KB |
|
|
132
|
+
| Total page weight | < 1MB |
|
|
133
|
+
| Time to Interactive | < 3s |
|
|
134
|
+
| API response (p95) | < 200ms |
|
|
135
|
+
|
|
136
|
+
## Monitoring
|
|
137
|
+
|
|
138
|
+
- [ ] Real User Monitoring (RUM) enabled
|
|
139
|
+
- [ ] Synthetic monitoring configured
|
|
140
|
+
- [ ] Alerting on degradation
|
|
141
|
+
- [ ] Performance tracked in CI
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
// Example: Track Core Web Vitals
|
|
145
|
+
import { onLCP, onINP, onCLS } from 'web-vitals';
|
|
146
|
+
|
|
147
|
+
onLCP(metric => sendToAnalytics('LCP', metric.value));
|
|
148
|
+
onINP(metric => sendToAnalytics('INP', metric.value));
|
|
149
|
+
onCLS(metric => sendToAnalytics('CLS', metric.value));
|
|
150
|
+
```
|