@su-record/vibe 2.8.24 → 2.8.26
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/commands/config.d.ts +17 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +207 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/index.d.ts +2 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +2 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +2 -0
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +78 -54
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/stats.d.ts +13 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +280 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +33 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/postinstall/constants.d.ts.map +1 -1
- package/dist/cli/postinstall/constants.js +1 -0
- package/dist/cli/postinstall/constants.js.map +1 -1
- package/dist/cli/setup/GlobalInstaller.d.ts.map +1 -1
- package/dist/cli/setup/GlobalInstaller.js +7 -7
- package/dist/cli/setup/GlobalInstaller.js.map +1 -1
- package/dist/cli/setup/ProjectSetup.d.ts.map +1 -1
- package/dist/cli/setup/ProjectSetup.js +18 -12
- package/dist/cli/setup/ProjectSetup.js.map +1 -1
- package/dist/infra/lib/ContextCompressor.d.ts.map +1 -1
- package/dist/infra/lib/ContextCompressor.js +10 -4
- package/dist/infra/lib/ContextCompressor.js.map +1 -1
- package/dist/infra/lib/ProjectCache.d.ts +2 -2
- package/dist/infra/lib/ProjectCache.d.ts.map +1 -1
- package/dist/infra/lib/ProjectCache.js +4 -3
- package/dist/infra/lib/ProjectCache.js.map +1 -1
- package/dist/infra/lib/utils.d.ts +24 -0
- package/dist/infra/lib/utils.d.ts.map +1 -1
- package/dist/infra/lib/utils.js +41 -0
- package/dist/infra/lib/utils.js.map +1 -1
- package/dist/infra/orchestrator/SmartRouter.d.ts +3 -0
- package/dist/infra/orchestrator/SmartRouter.d.ts.map +1 -1
- package/dist/infra/orchestrator/SmartRouter.js +11 -1
- package/dist/infra/orchestrator/SmartRouter.js.map +1 -1
- package/dist/infra/orchestrator/SmartRouter.test.d.ts +5 -0
- package/dist/infra/orchestrator/SmartRouter.test.d.ts.map +1 -0
- package/dist/infra/orchestrator/SmartRouter.test.js +457 -0
- package/dist/infra/orchestrator/SmartRouter.test.js.map +1 -0
- package/dist/tools/convention/analyzeComplexity.d.ts.map +1 -1
- package/dist/tools/convention/analyzeComplexity.js +18 -10
- package/dist/tools/convention/analyzeComplexity.js.map +1 -1
- package/dist/tools/convention/checkCouplingCohesion.d.ts.map +1 -1
- package/dist/tools/convention/checkCouplingCohesion.js +14 -6
- package/dist/tools/convention/checkCouplingCohesion.js.map +1 -1
- package/dist/tools/semantic/analyzeDependencyGraph.d.ts.map +1 -1
- package/dist/tools/semantic/analyzeDependencyGraph.js +1 -1
- package/dist/tools/semantic/analyzeDependencyGraph.js.map +1 -1
- package/dist/tools/semantic/findReferences.d.ts.map +1 -1
- package/dist/tools/semantic/findReferences.js +13 -13
- package/dist/tools/semantic/findReferences.js.map +1 -1
- package/dist/tools/semantic/findSymbol.d.ts.map +1 -1
- package/dist/tools/semantic/findSymbol.js +12 -13
- package/dist/tools/semantic/findSymbol.js.map +1 -1
- package/dist/tools/semantic/lsp.d.ts.map +1 -1
- package/dist/tools/semantic/lsp.js +22 -14
- package/dist/tools/semantic/lsp.js.map +1 -1
- package/hooks/hooks.json +29 -0
- package/hooks/scripts/__tests__/keyword-detector.test.js +199 -0
- package/hooks/scripts/__tests__/pre-tool-guard.test.js +286 -0
- package/hooks/scripts/__tests__/sentinel-guard.test.js +210 -0
- package/hooks/scripts/auto-commit.js +65 -0
- package/hooks/scripts/auto-format.js +64 -0
- package/hooks/scripts/auto-test.js +81 -0
- package/hooks/scripts/code-check.js +139 -0
- package/hooks/scripts/command-log.js +32 -0
- package/hooks/scripts/context-save.js +60 -6
- package/hooks/scripts/hud-status.js +32 -2
- package/hooks/scripts/llm-orchestrate.js +95 -17
- package/hooks/scripts/pr-test-gate.js +52 -0
- package/package.json +1 -1
- package/skills/agents-md/rubrics/what-to-keep.md +49 -0
- package/skills/agents-md/templates/agents-md.md +36 -0
- package/skills/arch-guard/agents/detector.md +48 -0
- package/skills/arch-guard/agents/reporter.md +48 -0
- package/skills/arch-guard/agents/rule-generator.md +49 -0
- package/skills/arch-guard/agents/violation-checker.md +51 -0
- package/skills/arch-guard/frameworks/clean-architecture.md +108 -0
- package/skills/arch-guard/frameworks/solid.md +102 -0
- package/skills/arch-guard/scripts/check-boundaries.js +90 -0
- package/skills/arch-guard/templates/arch-rules.json +47 -0
- package/skills/arch-guard/templates/violation-report.md +53 -0
- package/skills/brand-assets/rubrics/asset-checklist.md +98 -0
- package/skills/brand-assets/templates/brand-guide.md +161 -0
- package/skills/capability-loop/agents/capability-designer.md +61 -0
- package/skills/capability-loop/agents/failure-analyst.md +55 -0
- package/skills/capability-loop/agents/implementer.md +50 -0
- package/skills/capability-loop/agents/tester.md +53 -0
- package/skills/capability-loop/templates/capability-spec.md +118 -0
- package/skills/capability-loop/templates/failure-analysis.md +118 -0
- package/skills/characterization-test/agents/behavior-capturer.md +50 -0
- package/skills/characterization-test/agents/coverage-checker.md +54 -0
- package/skills/characterization-test/agents/reporter.md +50 -0
- package/skills/characterization-test/agents/test-writer.md +49 -0
- package/skills/characterization-test/rubrics/coverage-criteria.md +53 -0
- package/skills/characterization-test/templates/test-template.ts +101 -0
- package/skills/claude-md-guide/rubrics/anti-patterns.md +88 -0
- package/skills/claude-md-guide/templates/claude-md.md +54 -0
- package/skills/commerce-patterns/rubrics/checkout-flow.md +48 -0
- package/skills/commerce-patterns/templates/product-schema.md +85 -0
- package/skills/commit-push-pr/agents/change-analyzer.md +55 -0
- package/skills/commit-push-pr/agents/message-writer.md +50 -0
- package/skills/commit-push-pr/agents/pr-writer.md +58 -0
- package/skills/commit-push-pr/agents/reviewer.md +52 -0
- package/skills/commit-push-pr/rubrics/commit-message.md +73 -0
- package/skills/commit-push-pr/templates/pr-body.md +63 -0
- package/skills/context7-usage/rubrics/when-to-use.md +50 -0
- package/skills/create-prd/agents/edge-case-finder.md +48 -0
- package/skills/create-prd/agents/prioritizer.md +60 -0
- package/skills/create-prd/agents/requirements-writer.md +48 -0
- package/skills/create-prd/agents/researcher.md +55 -0
- package/skills/create-prd/agents/reviewer.md +54 -0
- package/skills/create-prd/frameworks/jobs-to-be-done.md +96 -0
- package/skills/create-prd/frameworks/rice-scoring.md +97 -0
- package/skills/create-prd/orchestrator.md +70 -0
- package/skills/create-prd/rubrics/completeness.md +58 -0
- package/skills/create-prd/templates/prd.md +139 -0
- package/skills/design-audit/agents/a11y-auditor.md +43 -0
- package/skills/design-audit/agents/performance-auditor.md +46 -0
- package/skills/design-audit/agents/responsive-auditor.md +46 -0
- package/skills/design-audit/agents/scorer.md +47 -0
- package/skills/design-audit/agents/slop-detector.md +47 -0
- package/skills/design-audit/frameworks/core-web-vitals.md +107 -0
- package/skills/design-audit/frameworks/wcag-checklist.md +64 -0
- package/skills/design-audit/orchestrator.md +64 -0
- package/skills/design-audit/rubrics/ai-slop-patterns.md +83 -0
- package/skills/design-audit/rubrics/scoring.md +63 -0
- package/skills/design-audit/templates/report.md +88 -0
- package/skills/design-critique/rubrics/ux-heuristics.md +143 -0
- package/skills/design-critique/templates/critique-report.md +86 -0
- package/skills/design-distill/templates/design-system.md +132 -0
- package/skills/design-normalize/rubrics/token-naming.md +117 -0
- package/skills/design-normalize/templates/token-audit.md +89 -0
- package/skills/design-polish/rubrics/polish-checklist.md +68 -0
- package/skills/design-polish/templates/polish-report.md +64 -0
- package/skills/design-teach/rubrics/brand-personality.md +73 -0
- package/skills/design-teach/templates/design-context.json +36 -0
- package/skills/e2e-commerce/templates/test-scenarios.md +170 -0
- package/skills/event-comms/templates/email-invite.md +99 -0
- package/skills/event-comms/templates/sns-post.md +133 -0
- package/skills/event-ops/rubrics/contingency.md +85 -0
- package/skills/event-ops/templates/d-day-checklist.md +65 -0
- package/skills/event-planning/rubrics/timeline.md +70 -0
- package/skills/event-planning/templates/event-plan.md +91 -0
- package/skills/exec-plan/agents/decomposer.md +47 -0
- package/skills/exec-plan/agents/dependency-mapper.md +44 -0
- package/skills/exec-plan/agents/estimator.md +43 -0
- package/skills/exec-plan/agents/validator.md +55 -0
- package/skills/exec-plan/orchestrator.md +70 -0
- package/skills/exec-plan/rubrics/complexity-scoring.md +75 -0
- package/skills/exec-plan/templates/plan.md +147 -0
- package/skills/git-worktree/rubrics/when-to-use.md +55 -0
- package/skills/handoff/agents/context-summarizer.md +51 -0
- package/skills/handoff/agents/document-writer.md +63 -0
- package/skills/handoff/agents/state-collector.md +53 -0
- package/skills/handoff/agents/verifier.md +48 -0
- package/skills/handoff/rubrics/completeness.md +62 -0
- package/skills/handoff/templates/handoff.md +107 -0
- package/skills/parallel-research/agents/best-practices.md +43 -0
- package/skills/parallel-research/agents/codebase-patterns.md +46 -0
- package/skills/parallel-research/agents/framework-docs.md +45 -0
- package/skills/parallel-research/agents/security-advisory.md +46 -0
- package/skills/parallel-research/agents/synthesizer.md +52 -0
- package/skills/parallel-research/experts/best-practices.md +50 -0
- package/skills/parallel-research/experts/codebase-patterns.md +70 -0
- package/skills/parallel-research/experts/framework-docs.md +65 -0
- package/skills/parallel-research/experts/security-advisory.md +69 -0
- package/skills/parallel-research/orchestrator.md +65 -0
- package/skills/parallel-research/templates/synthesis.md +101 -0
- package/skills/prioritization-frameworks/rubrics/frameworks.md +79 -0
- package/skills/prioritization-frameworks/templates/scoring-matrix.md +69 -0
- package/skills/priority-todos/rubrics/prioritization.md +70 -0
- package/skills/priority-todos/templates/todo-board.md +59 -0
- package/skills/seo-checklist/frameworks/structured-data.md +153 -0
- package/skills/seo-checklist/rubrics/content-seo.md +42 -0
- package/skills/seo-checklist/rubrics/technical-seo.md +48 -0
- package/skills/techdebt/agents/analyzer.md +50 -0
- package/skills/techdebt/agents/fixer.md +41 -0
- package/skills/techdebt/agents/reviewer.md +47 -0
- package/skills/techdebt/agents/scanner.md +44 -0
- package/skills/techdebt/orchestrator.md +70 -0
- package/skills/techdebt/rubrics/severity.md +51 -0
- package/skills/techdebt/scripts/scan.js +90 -0
- package/skills/techdebt/templates/report.md +86 -0
- package/skills/tool-fallback/rubrics/fallback-chain.md +58 -0
- package/skills/typescript-advanced-types/rubrics/type-patterns.md +109 -0
- package/skills/ui-ux-pro-max/rubrics/interaction-states.md +83 -0
- package/skills/ui-ux-pro-max/rubrics/responsive-breakpoints.md +99 -0
- package/skills/user-personas/rubrics/research-methods.md +56 -0
- package/skills/user-personas/templates/persona.md +89 -0
- package/skills/vercel-react-best-practices/rubrics/performance.md +82 -0
- package/skills/vercel-react-best-practices/rubrics/server-components.md +86 -0
- package/skills/vibe-docs/SKILL.md +171 -0
- package/skills/vibe-docs/templates/architecture.md +80 -0
- package/skills/vibe-docs/templates/readme.md +84 -0
- package/skills/vibe-docs/templates/release-notes.md +74 -0
- package/skills/vibe-figma/SKILL.md +122 -206
- package/skills/vibe-figma/rubrics/extraction-checklist.md +51 -0
- package/skills/vibe-figma/templates/figma-handoff.md +96 -0
- package/skills/vibe-figma-analyze/rubrics/analysis-dimensions.md +53 -0
- package/skills/vibe-figma-codegen/rubrics/code-quality.md +54 -0
- package/skills/vibe-figma-consolidate/templates/consolidation-report.md +95 -0
- package/skills/vibe-figma-convert/SKILL.md +13 -175
- package/skills/vibe-figma-convert/rubrics/conversion-rules.md +83 -0
- package/skills/vibe-figma-convert/templates/component.md +152 -0
- package/skills/vibe-figma-extract/rubrics/image-rules.md +67 -0
- package/skills/vibe-figma-frame/rubrics/frame-selection.md +55 -0
- package/skills/vibe-figma-pipeline/rubrics/pipeline-stages.md +96 -0
- package/skills/vibe-figma-rules/rubrics/naming-conventions.md +70 -0
- package/skills/vibe-figma-style/rubrics/style-mapping.md +100 -0
- package/skills/video-production/rubrics/quality-checklist.md +58 -0
- package/skills/video-production/templates/production-plan.md +104 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Technical Debt Report
|
|
2
|
+
|
|
3
|
+
**Project**: {{PROJECT_NAME}}
|
|
4
|
+
**Date**: {{DATE}}
|
|
5
|
+
**Scanned Directory**: {{SCAN_DIR}}
|
|
6
|
+
**Files Scanned**: {{FILE_COUNT}}
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
| Priority | Count | Status |
|
|
13
|
+
|----------|-------|--------|
|
|
14
|
+
| P1 — Blocks Merge | {{P1_COUNT}} | {{P1_STATUS}} |
|
|
15
|
+
| P2 — Fix Before PR | {{P2_COUNT}} | {{P2_STATUS}} |
|
|
16
|
+
| P3 — Backlog | {{P3_COUNT}} | {{P3_STATUS}} |
|
|
17
|
+
| **Total** | **{{TOTAL_COUNT}}** | |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## P1 — Blocks Merge ({{P1_COUNT}} items)
|
|
22
|
+
|
|
23
|
+
{{P1_ITEMS}}
|
|
24
|
+
|
|
25
|
+
<!-- Example entry:
|
|
26
|
+
- `src/services/auth.ts:42` — `any` type usage: parameter `payload` typed as `any`
|
|
27
|
+
-->
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## P2 — Fix Before PR ({{P2_COUNT}} items)
|
|
32
|
+
|
|
33
|
+
### Console Statements ({{CONSOLE_COUNT}})
|
|
34
|
+
|
|
35
|
+
{{CONSOLE_ITEMS}}
|
|
36
|
+
|
|
37
|
+
### Unused Imports ({{UNUSED_IMPORT_COUNT}})
|
|
38
|
+
|
|
39
|
+
{{UNUSED_IMPORT_ITEMS}}
|
|
40
|
+
|
|
41
|
+
### Long Functions ({{LONG_FUNCTION_COUNT}})
|
|
42
|
+
|
|
43
|
+
{{LONG_FUNCTION_ITEMS}}
|
|
44
|
+
|
|
45
|
+
### Deep Nesting ({{DEEP_NESTING_COUNT}})
|
|
46
|
+
|
|
47
|
+
{{DEEP_NESTING_ITEMS}}
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## P3 — Backlog ({{P3_COUNT}} items)
|
|
52
|
+
|
|
53
|
+
### Magic Numbers ({{MAGIC_NUMBER_COUNT}})
|
|
54
|
+
|
|
55
|
+
{{MAGIC_NUMBER_ITEMS}}
|
|
56
|
+
|
|
57
|
+
### Commented-Out Code ({{COMMENTED_CODE_COUNT}})
|
|
58
|
+
|
|
59
|
+
{{COMMENTED_CODE_ITEMS}}
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Auto-fixable Items
|
|
64
|
+
|
|
65
|
+
The following can be removed safely without review:
|
|
66
|
+
|
|
67
|
+
{{AUTOFIXABLE_LIST}}
|
|
68
|
+
|
|
69
|
+
> Run with user confirmation before applying auto-fixes.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Requires Manual Review
|
|
74
|
+
|
|
75
|
+
The following require human judgment:
|
|
76
|
+
|
|
77
|
+
{{MANUAL_REVIEW_LIST}}
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Next Steps
|
|
82
|
+
|
|
83
|
+
1. Fix all P1 items immediately
|
|
84
|
+
2. Address P2 items before opening PR
|
|
85
|
+
3. Create tracked tickets for P3 items
|
|
86
|
+
4. Re-run scan after fixes: `node skills/techdebt/scripts/scan.js {{SCAN_DIR}}`
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Tool Fallback Priority Chain
|
|
2
|
+
|
|
3
|
+
## Web / Documentation Search
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
1. Web Search (Brave/Tavily)
|
|
7
|
+
2. context7 plugin ← library/framework docs
|
|
8
|
+
3. Claude built-in knowledge ← last resort, may be stale
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Skip to step 2 immediately** for library/API questions — context7 is faster and more accurate than web search for docs.
|
|
12
|
+
|
|
13
|
+
## Code/File Discovery
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
1. Glob (pattern match by name)
|
|
17
|
+
2. Grep (content search)
|
|
18
|
+
3. git log --all -- <path> ← file was deleted or renamed
|
|
19
|
+
4. Ask user for path ← exhausted all options
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## LLM Routing
|
|
23
|
+
|
|
24
|
+
| Task Type | Primary | Secondary | Fallback |
|
|
25
|
+
|-----------|---------|-----------|----------|
|
|
26
|
+
| Architecture, debugging | GPT | Gemini | Claude direct |
|
|
27
|
+
| UI/UX, code analysis | Gemini | GPT | Claude direct |
|
|
28
|
+
| Code generation, general | Claude | — | — |
|
|
29
|
+
|
|
30
|
+
**On 429 / rate limit**: skip to secondary immediately — no retry on rate-limited primary.
|
|
31
|
+
|
|
32
|
+
## External API Calls
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
1. Try primary endpoint
|
|
36
|
+
2. On 429: skip to alternative, mark circuit OPEN
|
|
37
|
+
3. On 5xx: retry once with backoff (2s), then skip
|
|
38
|
+
4. On timeout: retry once with smaller payload, then skip
|
|
39
|
+
5. On 401/403: re-auth, do NOT count as circuit failure
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Circuit States
|
|
43
|
+
|
|
44
|
+
| State | Trigger | Recovery |
|
|
45
|
+
|-------|---------|----------|
|
|
46
|
+
| CLOSED | Normal | — |
|
|
47
|
+
| OPEN | 3 failures | Auto after 30s cooldown |
|
|
48
|
+
| HALF-OPEN | After cooldown | 1 test request; success → CLOSED |
|
|
49
|
+
|
|
50
|
+
## Never-Stop Rule
|
|
51
|
+
|
|
52
|
+
Every chain must have a terminal fallback that produces **some** output. Acceptable terminal fallbacks:
|
|
53
|
+
|
|
54
|
+
- Partial result with `TODO` annotation
|
|
55
|
+
- Claude's own knowledge with explicit uncertainty note
|
|
56
|
+
- Ask the user for the missing piece (with specific question)
|
|
57
|
+
|
|
58
|
+
**Never** return empty-handed without exhausting the chain first.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Advanced TypeScript Type Patterns
|
|
2
|
+
|
|
3
|
+
## Discriminated Unions
|
|
4
|
+
|
|
5
|
+
Use a literal `kind`/`type` field to let TypeScript narrow exhaustively.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
type Result<T> =
|
|
9
|
+
| { kind: 'ok'; value: T }
|
|
10
|
+
| { kind: 'err'; error: string };
|
|
11
|
+
|
|
12
|
+
function handle<T>(r: Result<T>): T {
|
|
13
|
+
if (r.kind === 'ok') return r.value; // narrowed: r.value exists
|
|
14
|
+
throw new Error(r.error); // narrowed: r.error exists
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Exhaustive check pattern** — compile-time guard against missing branches:
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
function assertNever(x: never): never {
|
|
22
|
+
throw new Error(`Unhandled case: ${JSON.stringify(x)}`);
|
|
23
|
+
}
|
|
24
|
+
// In switch default: case: return assertNever(action);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Template Literal Types
|
|
28
|
+
|
|
29
|
+
Compose string types at the type level.
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
type EventName = 'click' | 'focus' | 'blur';
|
|
33
|
+
type Handler = `on${Capitalize<EventName>}`;
|
|
34
|
+
// → 'onClick' | 'onFocus' | 'onBlur'
|
|
35
|
+
|
|
36
|
+
type RouteParam<T extends string> =
|
|
37
|
+
T extends `${infer _Start}:${infer Param}/${infer Rest}`
|
|
38
|
+
? Param | RouteParam<Rest>
|
|
39
|
+
: T extends `${infer _Start}:${infer Param}`
|
|
40
|
+
? Param
|
|
41
|
+
: never;
|
|
42
|
+
// RouteParam<'/user/:id/post/:postId'> → 'id' | 'postId'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Gotcha**: Template literals distribute over unions — one type per combination.
|
|
46
|
+
|
|
47
|
+
## Conditional Types
|
|
48
|
+
|
|
49
|
+
Branch the type system on type relationships.
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
type NonNullable<T> = T extends null | undefined ? never : T;
|
|
53
|
+
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Distributive gotcha** — to prevent distribution, wrap in a tuple:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
type IsArray<T> = T extends unknown[] ? true : false;
|
|
60
|
+
// IsArray<string | number[]> → boolean (distributed!)
|
|
61
|
+
|
|
62
|
+
type IsArrayExact<T> = [T] extends [unknown[]] ? true : false;
|
|
63
|
+
// IsArrayExact<string | number[]> → false (no distribution)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Mapped Types
|
|
67
|
+
|
|
68
|
+
Transform every property in a type.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
type Readonly<T> = { readonly [K in keyof T]: T[K] };
|
|
72
|
+
type Optional<T> = { [K in keyof T]?: T[K] };
|
|
73
|
+
|
|
74
|
+
// Key remapping with 'as'
|
|
75
|
+
type Getters<T> = {
|
|
76
|
+
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
|
|
77
|
+
};
|
|
78
|
+
// Getters<{ name: string }> → { getName: () => string }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Branded / Opaque Types
|
|
82
|
+
|
|
83
|
+
Prevent mixing semantically different strings/numbers.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
type UserId = string & { readonly _brand: 'UserId' };
|
|
87
|
+
type PostId = string & { readonly _brand: 'PostId' };
|
|
88
|
+
|
|
89
|
+
function toUserId(id: string): UserId {
|
|
90
|
+
return id as UserId; // only one cast, at the boundary
|
|
91
|
+
}
|
|
92
|
+
// toUserId('abc') satisfies UserId ✅
|
|
93
|
+
// 'raw-string' satisfies UserId ✗ (compile error)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## `satisfies` vs `as const`
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const config = {
|
|
100
|
+
port: 3000,
|
|
101
|
+
host: 'localhost',
|
|
102
|
+
} satisfies Record<string, string | number>;
|
|
103
|
+
// satisfies: validates shape AND keeps literal types (port: 3000, not number)
|
|
104
|
+
|
|
105
|
+
const colors = ['red', 'green', 'blue'] as const;
|
|
106
|
+
// as const: freezes to readonly tuple of literals
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Use `satisfies` when you need both **validation** and **literal type preservation**.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Required Interaction States Checklist
|
|
2
|
+
|
|
3
|
+
Every interactive element must implement all applicable states. Missing states are P1–P2 findings.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## State Definitions
|
|
8
|
+
|
|
9
|
+
| State | Trigger | Visual Requirement | ARIA Requirement |
|
|
10
|
+
|-------|---------|-------------------|-----------------|
|
|
11
|
+
| **Default** | No user interaction | Base appearance defined with sufficient contrast | — |
|
|
12
|
+
| **Hover** | Mouse enters element | Visible color, shadow, or opacity change | — |
|
|
13
|
+
| **Focus** | Keyboard navigation / tab | Clearly visible focus ring (≥ 2px, non-color-only) | — |
|
|
14
|
+
| **Active / Pressed** | Mouse down or tap | Distinct from hover (darker, scale-down, or depressed look) | `aria-pressed` on toggles |
|
|
15
|
+
| **Disabled** | Element not actionable | Reduced opacity (≤ 50%) + `cursor: not-allowed` | `disabled` attr or `aria-disabled="true"` |
|
|
16
|
+
| **Loading** | Async operation in progress | Spinner, skeleton, or progress bar replaces or overlays content | `aria-busy="true"` |
|
|
17
|
+
| **Error** | Validation fails or operation fails | Error color on border/background + error message visible | `aria-invalid="true"` + `aria-describedby` pointing to error |
|
|
18
|
+
| **Success** | Operation completes | Confirmation feedback (color change, checkmark, toast) | `aria-live` announcement |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Element-State Matrix
|
|
23
|
+
|
|
24
|
+
Use this matrix to verify each element type has all required states implemented.
|
|
25
|
+
|
|
26
|
+
| Element | Default | Hover | Focus | Active | Disabled | Loading | Error | Success |
|
|
27
|
+
|---------|---------|-------|-------|--------|----------|---------|-------|---------|
|
|
28
|
+
| Button (primary) | Required | Required | Required | Required | Required | Required | — | — |
|
|
29
|
+
| Button (destructive) | Required | Required | Required | Required | Required | Required | — | — |
|
|
30
|
+
| Link | Required | Required | Required | Required | — | — | — | — |
|
|
31
|
+
| Text input | Required | Optional | Required | — | Required | — | Required | Optional |
|
|
32
|
+
| Select / dropdown | Required | Required | Required | — | Required | — | Required | — |
|
|
33
|
+
| Checkbox | Required | Required | Required | Required | Required | — | — | — |
|
|
34
|
+
| Radio button | Required | Required | Required | Required | Required | — | — | — |
|
|
35
|
+
| Toggle / switch | Required | Required | Required | Required | Required | — | — | — |
|
|
36
|
+
| Form submit button | Required | Required | Required | Required | Required | Required | — | Required |
|
|
37
|
+
| Card (interactive) | Required | Required | Required | — | — | Required | — | — |
|
|
38
|
+
| Tab | Required | Required | Required | Required | Required | — | — | — |
|
|
39
|
+
| Menu item | Required | Required | Required | Required | Required | — | — | — |
|
|
40
|
+
| Icon button | Required | Required | Required | Required | Required | Required | — | — |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Implementation Checklist
|
|
45
|
+
|
|
46
|
+
### Hover
|
|
47
|
+
- [ ] Color change uses a dedicated hover token (not inline opacity hack)
|
|
48
|
+
- [ ] Transition applied (`transition-colors duration-150` or equivalent)
|
|
49
|
+
- [ ] Layout does not shift on hover (no margin/padding changes)
|
|
50
|
+
|
|
51
|
+
### Focus
|
|
52
|
+
- [ ] Focus ring visible at 2:1 contrast against adjacent color
|
|
53
|
+
- [ ] `outline: none` never used without a custom focus-visible replacement
|
|
54
|
+
- [ ] `:focus-visible` used instead of `:focus` to avoid visible ring on mouse click
|
|
55
|
+
|
|
56
|
+
### Active / Pressed
|
|
57
|
+
- [ ] Distinct visual from hover state
|
|
58
|
+
- [ ] `scale(0.97–0.98)` or darker color on press — not just removing hover
|
|
59
|
+
- [ ] Touch devices: active state triggers on `touchstart`
|
|
60
|
+
|
|
61
|
+
### Disabled
|
|
62
|
+
- [ ] `opacity: 0.4–0.5` on disabled elements
|
|
63
|
+
- [ ] `pointer-events: none` prevents interaction
|
|
64
|
+
- [ ] `cursor: not-allowed` set
|
|
65
|
+
- [ ] `disabled` attribute or `aria-disabled="true"` set on element
|
|
66
|
+
- [ ] Disabled state does not rely on color alone
|
|
67
|
+
|
|
68
|
+
### Loading
|
|
69
|
+
- [ ] Original button/element width preserved during loading (no layout jump)
|
|
70
|
+
- [ ] Spinner or skeleton shown within 100ms of action trigger
|
|
71
|
+
- [ ] Interaction re-enabled after completion or error
|
|
72
|
+
- [ ] `aria-busy="true"` set on loading container
|
|
73
|
+
|
|
74
|
+
### Error
|
|
75
|
+
- [ ] Error message visible near the element (not just a global toast)
|
|
76
|
+
- [ ] Error styling uses `--color-error` token
|
|
77
|
+
- [ ] `aria-invalid="true"` set on invalid input
|
|
78
|
+
- [ ] Error message linked via `aria-describedby`
|
|
79
|
+
|
|
80
|
+
### Success
|
|
81
|
+
- [ ] Confirmation feedback shown within 300ms of completion
|
|
82
|
+
- [ ] Success message auto-dismisses or provides clear dismiss action
|
|
83
|
+
- [ ] `aria-live="polite"` region announces success to screen readers
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Standard Breakpoint Definitions
|
|
2
|
+
|
|
3
|
+
Reference for responsive design implementation across all stacks.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Core Breakpoints
|
|
8
|
+
|
|
9
|
+
| Name | Min Width | Target Devices | Priority |
|
|
10
|
+
|------|-----------|---------------|----------|
|
|
11
|
+
| `xs` | 375px | Small phones (iPhone SE, Galaxy A) | Test target |
|
|
12
|
+
| `sm` | 640px | Large phones (landscape), small tablets | Layout change |
|
|
13
|
+
| `md` | 768px | Tablets (portrait), large phones | Major breakpoint |
|
|
14
|
+
| `lg` | 1024px | Tablets (landscape), small laptops | Major breakpoint |
|
|
15
|
+
| `xl` | 1280px | Standard desktop | Primary design target |
|
|
16
|
+
| `2xl` | 1536px | Large monitors | Wide-layout target |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Framework Mappings
|
|
21
|
+
|
|
22
|
+
| Breakpoint | Tailwind class | CSS media query | Common use |
|
|
23
|
+
|------------|---------------|-----------------|------------|
|
|
24
|
+
| Mobile default | (no prefix) | base styles | Mobile-first base |
|
|
25
|
+
| sm | `sm:` | `@media (min-width: 640px)` | Large phone adjustments |
|
|
26
|
+
| md | `md:` | `@media (min-width: 768px)` | Tablet layout |
|
|
27
|
+
| lg | `lg:` | `@media (min-width: 1024px)` | Desktop layout |
|
|
28
|
+
| xl | `xl:` | `@media (min-width: 1280px)` | Wide desktop |
|
|
29
|
+
| 2xl | `2xl:` | `@media (min-width: 1536px)` | Ultra-wide |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Layout Behavior by Breakpoint
|
|
34
|
+
|
|
35
|
+
### Navigation
|
|
36
|
+
| Breakpoint | Pattern |
|
|
37
|
+
|------------|---------|
|
|
38
|
+
| < md | Hamburger menu / bottom tab bar |
|
|
39
|
+
| md–lg | Collapsed sidebar or top nav with fewer items |
|
|
40
|
+
| ≥ lg | Full sidebar or top nav with all items visible |
|
|
41
|
+
|
|
42
|
+
### Content Grid
|
|
43
|
+
| Breakpoint | Columns |
|
|
44
|
+
|------------|---------|
|
|
45
|
+
| xs–sm | 1 column |
|
|
46
|
+
| md | 2 columns |
|
|
47
|
+
| lg | 3 columns |
|
|
48
|
+
| xl+ | 4 columns (data-heavy) or 3 columns (content) |
|
|
49
|
+
|
|
50
|
+
### Typography Scale Adjustments
|
|
51
|
+
| Element | Mobile | Desktop |
|
|
52
|
+
|---------|--------|---------|
|
|
53
|
+
| Hero heading | `text-3xl` (30px) | `text-5xl` (48px) |
|
|
54
|
+
| Section heading | `text-xl` (20px) | `text-3xl` (30px) |
|
|
55
|
+
| Body | `text-base` (16px) | `text-base` (16px) — never reduce below 16px |
|
|
56
|
+
| Caption | `text-sm` (14px) | `text-sm` (14px) |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Touch Target Rules
|
|
61
|
+
|
|
62
|
+
| Device | Minimum touch target | Recommended |
|
|
63
|
+
|--------|---------------------|-------------|
|
|
64
|
+
| Mobile (< md) | 44×44px | 48×48px |
|
|
65
|
+
| Tablet (md–lg) | 44×44px | 44×44px |
|
|
66
|
+
| Desktop (≥ lg) | 32×32px (mouse) | 36×36px |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Testing Checklist
|
|
71
|
+
|
|
72
|
+
| Breakpoint | Must Verify |
|
|
73
|
+
|------------|-------------|
|
|
74
|
+
| 375px | No horizontal scroll; text readable; nav accessible; forms usable |
|
|
75
|
+
| 768px | Layout transitions correctly; no content overlap |
|
|
76
|
+
| 1024px | Desktop features visible; sidebar or nav fully expanded |
|
|
77
|
+
| 1440px | Max-width container centered; content not stretched too wide |
|
|
78
|
+
|
|
79
|
+
### Max-width containers
|
|
80
|
+
|
|
81
|
+
| Use case | Recommended max-width |
|
|
82
|
+
|----------|-----------------------|
|
|
83
|
+
| Marketing / landing pages | `max-w-5xl` (1024px) or `max-w-6xl` (1152px) |
|
|
84
|
+
| Application layouts | `max-w-7xl` (1280px) |
|
|
85
|
+
| Reading / article content | `max-w-2xl` (672px) or `max-w-3xl` (768px) |
|
|
86
|
+
| Full-bleed dashboards | None (use grid with sidebars) |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Container Query Usage
|
|
91
|
+
|
|
92
|
+
Use `@container` when a component's layout depends on its parent width, not the viewport.
|
|
93
|
+
|
|
94
|
+
| Scenario | Use viewport breakpoint | Use container query |
|
|
95
|
+
|----------|------------------------|---------------------|
|
|
96
|
+
| Page-level layout | Yes | No |
|
|
97
|
+
| Component in sidebar vs main | No | Yes |
|
|
98
|
+
| Card grid columns | No | Yes |
|
|
99
|
+
| Navbar collapsing | Yes | No |
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Persona Research Methodology
|
|
2
|
+
|
|
3
|
+
## Research Method Selection
|
|
4
|
+
|
|
5
|
+
| Situation | Recommended Method | Sample Size |
|
|
6
|
+
|-----------|-------------------|-------------|
|
|
7
|
+
| New product, no users yet | Exploratory interviews | 8–12 participants |
|
|
8
|
+
| Existing product, need depth | In-depth interviews | 5–8 per segment |
|
|
9
|
+
| Validating existing personas | Survey | 50+ respondents |
|
|
10
|
+
| Behavioral patterns | Analytics + session recordings | All available data |
|
|
11
|
+
| Competitive landscape | Competitor review + job forums | Qualitative |
|
|
12
|
+
|
|
13
|
+
## Interview Design Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Questions focus on past behavior, not hypothetical future behavior
|
|
16
|
+
- [ ] "Tell me about a time when..." used instead of "Would you...?"
|
|
17
|
+
- [ ] No leading questions (avoid "Don't you find X frustrating?")
|
|
18
|
+
- [ ] Follow-up probes prepared: "Why?", "What happened next?", "How did that make you feel?"
|
|
19
|
+
- [ ] Job-to-be-done lens: ask what they're trying to accomplish, not what features they want
|
|
20
|
+
- [ ] Session recorded (with consent) for later synthesis
|
|
21
|
+
- [ ] Duration: 45–60 minutes per interview
|
|
22
|
+
|
|
23
|
+
## Data Collection Quality Gates
|
|
24
|
+
|
|
25
|
+
- [ ] Minimum 5 interviews per distinct segment before drawing conclusions
|
|
26
|
+
- [ ] Direct quotes captured verbatim (not paraphrased during interview)
|
|
27
|
+
- [ ] Demographic bias checked — sample matches target market
|
|
28
|
+
- [ ] B2B: multiple roles interviewed (buyer, user, influencer)
|
|
29
|
+
- [ ] Negative cases included — interview users who churned or rejected the product
|
|
30
|
+
|
|
31
|
+
## Synthesis Process
|
|
32
|
+
|
|
33
|
+
1. **Affinity mapping**: Group raw observations by theme (not by participant)
|
|
34
|
+
2. **Pattern extraction**: Identify recurring pain points and jobs across 3+ participants
|
|
35
|
+
3. **Segmentation**: Cluster participants by shared motivations (not demographics)
|
|
36
|
+
4. **Persona drafting**: One persona per distinct motivation cluster
|
|
37
|
+
5. **Validation**: Cross-reference each persona claim against source data
|
|
38
|
+
|
|
39
|
+
## Persona Validity Checklist
|
|
40
|
+
|
|
41
|
+
- [ ] Each persona grounded in ≥3 real participants from research
|
|
42
|
+
- [ ] Personas are mutually distinct — overlapping personas should be merged
|
|
43
|
+
- [ ] Unexpected insights included — if all insights are obvious, dig deeper
|
|
44
|
+
- [ ] Demographics are secondary — job-to-be-done drives persona definition
|
|
45
|
+
- [ ] Quotes used are verbatim from research (not invented)
|
|
46
|
+
- [ ] Data gaps explicitly flagged — don't fill gaps with assumptions
|
|
47
|
+
|
|
48
|
+
## Common Research Mistakes
|
|
49
|
+
|
|
50
|
+
| Mistake | Consequence | Fix |
|
|
51
|
+
|---------|-------------|-----|
|
|
52
|
+
| Asking "what features do you want?" | Users design solutions, not needs | Ask about current behavior and pain |
|
|
53
|
+
| Only interviewing enthusiasts | Survivorship bias | Include churned users and non-adopters |
|
|
54
|
+
| Conflating segment with persona | Personas become stereotypes | Base personas on motivations, not age/gender |
|
|
55
|
+
| Too many personas (>5) | Team ignores them | Merge overlapping clusters, max 3–4 |
|
|
56
|
+
| No data gaps acknowledged | False confidence | Flag what you don't yet know |
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# User Persona: {{PERSONA_NAME}}
|
|
2
|
+
|
|
3
|
+
> Product: {{PRODUCT_NAME}} | Research basis: {{DATA_SOURCE}} | Created: {{DATE}}
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Identity
|
|
8
|
+
|
|
9
|
+
| Field | Value |
|
|
10
|
+
|-------|-------|
|
|
11
|
+
| Name (fictional) | {{PERSONA_NAME}} |
|
|
12
|
+
| Age range | {{AGE_RANGE}} |
|
|
13
|
+
| Role / Title | {{ROLE}} |
|
|
14
|
+
| Company size | {{COMPANY_SIZE}} (if B2B) |
|
|
15
|
+
| Location | {{LOCATION}} |
|
|
16
|
+
| Tech comfort | {{LOW / MEDIUM / HIGH}} |
|
|
17
|
+
|
|
18
|
+
**In their own words**: "{{REPRESENTATIVE_QUOTE_FROM_RESEARCH}}"
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Primary Job-to-be-Done
|
|
23
|
+
|
|
24
|
+
> {{THE CORE OUTCOME THIS PERSONA IS TRYING TO ACHIEVE}}
|
|
25
|
+
|
|
26
|
+
**Context**: {{WHEN AND HOW OFTEN DOES THIS JOB ARISE?}}
|
|
27
|
+
**Definition of done**: {{HOW DOES THIS PERSONA KNOW THEY SUCCEEDED?}}
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Top 3 Pain Points
|
|
32
|
+
|
|
33
|
+
| # | Pain | Impact | Severity |
|
|
34
|
+
|---|------|--------|----------|
|
|
35
|
+
| 1 | {{PAIN_1}} | {{CONSEQUENCE}} | High / Med / Low |
|
|
36
|
+
| 2 | {{PAIN_2}} | {{CONSEQUENCE}} | High / Med / Low |
|
|
37
|
+
| 3 | {{PAIN_3}} | {{CONSEQUENCE}} | High / Med / Low |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Top 3 Desired Gains
|
|
42
|
+
|
|
43
|
+
| # | Gain | How They Measure It |
|
|
44
|
+
|---|------|---------------------|
|
|
45
|
+
| 1 | {{GAIN_1}} | {{METRIC_OR_FEELING}} |
|
|
46
|
+
| 2 | {{GAIN_2}} | {{METRIC_OR_FEELING}} |
|
|
47
|
+
| 3 | {{GAIN_3}} | {{METRIC_OR_FEELING}} |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Unexpected Insight
|
|
52
|
+
|
|
53
|
+
> {{A COUNTERINTUITIVE BEHAVIORAL PATTERN OR MOTIVATION FROM RESEARCH DATA}}
|
|
54
|
+
|
|
55
|
+
**Why it matters for product decisions**: {{IMPLICATION}}
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Current Alternatives
|
|
60
|
+
|
|
61
|
+
How does this persona solve the problem today?
|
|
62
|
+
|
|
63
|
+
| Alternative | Why they use it | What frustrates them |
|
|
64
|
+
|-------------|----------------|----------------------|
|
|
65
|
+
| {{ALT_1}} | {{REASON}} | {{FRUSTRATION}} |
|
|
66
|
+
| {{ALT_2}} | {{REASON}} | {{FRUSTRATION}} |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Product Fit Assessment
|
|
71
|
+
|
|
72
|
+
**How {{PRODUCT_NAME}} addresses their needs**:
|
|
73
|
+
- {{FIT_POINT_1}}
|
|
74
|
+
- {{FIT_POINT_2}}
|
|
75
|
+
|
|
76
|
+
**Potential friction points**:
|
|
77
|
+
- {{FRICTION_1}}
|
|
78
|
+
- {{FRICTION_2}}
|
|
79
|
+
|
|
80
|
+
**Unmet needs (future opportunities)**:
|
|
81
|
+
- {{UNMET_NEED_1}}
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Data Sources
|
|
86
|
+
|
|
87
|
+
- {{SOURCE_1}} (e.g., "10 user interviews, March 2026")
|
|
88
|
+
- {{SOURCE_2}} (e.g., "NPS survey, n=142")
|
|
89
|
+
- **Data gaps**: {{WHAT WE DON'T KNOW YET}}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# React Performance Patterns
|
|
2
|
+
|
|
3
|
+
## memo — Skip Re-render
|
|
4
|
+
|
|
5
|
+
Wrap components that receive stable props but re-render from parent updates.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
const ItemRow = memo(function ItemRow({ item }: { item: Item }) {
|
|
9
|
+
return <tr>{item.name}</tr>;
|
|
10
|
+
});
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**When to use**: Pure display components in long lists, or children of frequently-updating parents.
|
|
14
|
+
**When NOT to use**: Components that almost always re-render anyway (memo check costs CPU too).
|
|
15
|
+
|
|
16
|
+
## useMemo — Cache Expensive Computation
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// Expensive filter/sort that depends on list + query
|
|
20
|
+
const filtered = useMemo(
|
|
21
|
+
() => items.filter(i => i.name.includes(query)).sort(byDate),
|
|
22
|
+
[items, query]
|
|
23
|
+
);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Rule**: Only memoize when the computation is genuinely expensive (> 1ms). Measuring before adding is best practice.
|
|
27
|
+
|
|
28
|
+
**Gotcha**: Object/array deps break memoization — use primitive values:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// Bad: new object reference every render breaks memo
|
|
32
|
+
useMemo(() => compute(config), [config]);
|
|
33
|
+
|
|
34
|
+
// Good: primitive values stay stable
|
|
35
|
+
useMemo(() => compute(config), [config.id, config.mode]);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## useCallback — Stable Function References
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// Without useCallback: new function ref → effect re-runs every render
|
|
42
|
+
const handleSubmit = useCallback(
|
|
43
|
+
(e: FormEvent) => { submitForm(formData); },
|
|
44
|
+
[formData]
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Use when**: passing callbacks to memo-wrapped children, or as useEffect dependencies.
|
|
49
|
+
**Skip when**: the component receiving it is not memoized (no benefit).
|
|
50
|
+
|
|
51
|
+
## Suspense — Stream Slow Components
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
// Without Suspense: entire page waits for slowComponent
|
|
55
|
+
// With Suspense: page streams, slot renders when ready
|
|
56
|
+
<Suspense fallback={<Skeleton />}>
|
|
57
|
+
<SlowDataComponent /> {/* server component with slow fetch */}
|
|
58
|
+
</Suspense>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Gotcha**: Suspense boundaries must be placed at the **component that fetches**, not a parent.
|
|
62
|
+
|
|
63
|
+
## useTransition — Keep UI Responsive
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const [isPending, startTransition] = useTransition();
|
|
67
|
+
|
|
68
|
+
// Mark state update as non-urgent — keeps input responsive
|
|
69
|
+
startTransition(() => setSearchQuery(inputValue));
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Use for filtering, sorting, navigation — updates that can briefly lag without hurting UX.
|
|
73
|
+
|
|
74
|
+
## Quick Reference
|
|
75
|
+
|
|
76
|
+
| Pattern | Use Case | Skip When |
|
|
77
|
+
|---------|----------|-----------|
|
|
78
|
+
| `memo` | Pure component in updating tree | Almost always re-renders anyway |
|
|
79
|
+
| `useMemo` | Expensive calculation | Fast computation (< 1ms) |
|
|
80
|
+
| `useCallback` | Stable dep for memo/effect | Recipient is not memoized |
|
|
81
|
+
| `Suspense` | Slow async data in subtree | Synchronous rendering |
|
|
82
|
+
| `useTransition` | Non-urgent state updates | Critical/immediate feedback |
|