@coralai/sps-cli 0.42.0 → 0.44.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.
Files changed (147) hide show
  1. package/README.md +59 -4
  2. package/dist/commands/consoleCommand.d.ts +2 -0
  3. package/dist/commands/consoleCommand.d.ts.map +1 -0
  4. package/dist/commands/consoleCommand.js +129 -0
  5. package/dist/commands/consoleCommand.js.map +1 -0
  6. package/dist/commands/projectInit.d.ts.map +1 -1
  7. package/dist/commands/projectInit.js +40 -53
  8. package/dist/commands/projectInit.js.map +1 -1
  9. package/dist/commands/setup.d.ts.map +1 -1
  10. package/dist/commands/setup.js +14 -2
  11. package/dist/commands/setup.js.map +1 -1
  12. package/dist/commands/skillCommand.d.ts +2 -0
  13. package/dist/commands/skillCommand.d.ts.map +1 -0
  14. package/dist/commands/skillCommand.js +235 -0
  15. package/dist/commands/skillCommand.js.map +1 -0
  16. package/dist/console-assets/assets/index-Bhd2f9AP.js +125 -0
  17. package/dist/console-assets/assets/index-bsAN2a12.css +1 -0
  18. package/dist/console-assets/index.html +16 -0
  19. package/dist/console-server/index.d.ts +29 -0
  20. package/dist/console-server/index.d.ts.map +1 -0
  21. package/dist/console-server/index.js +145 -0
  22. package/dist/console-server/index.js.map +1 -0
  23. package/dist/console-server/lib/lockFile.d.ts +17 -0
  24. package/dist/console-server/lib/lockFile.d.ts.map +1 -0
  25. package/dist/console-server/lib/lockFile.js +61 -0
  26. package/dist/console-server/lib/lockFile.js.map +1 -0
  27. package/dist/console-server/lib/portPicker.d.ts +3 -0
  28. package/dist/console-server/lib/portPicker.d.ts.map +1 -0
  29. package/dist/console-server/lib/portPicker.js +25 -0
  30. package/dist/console-server/lib/portPicker.js.map +1 -0
  31. package/dist/console-server/routes/projects.d.ts +11 -0
  32. package/dist/console-server/routes/projects.d.ts.map +1 -0
  33. package/dist/console-server/routes/projects.js +149 -0
  34. package/dist/console-server/routes/projects.js.map +1 -0
  35. package/dist/console-server/routes/system.d.ts +7 -0
  36. package/dist/console-server/routes/system.d.ts.map +1 -0
  37. package/dist/console-server/routes/system.js +19 -0
  38. package/dist/console-server/routes/system.js.map +1 -0
  39. package/dist/console-server/sse/eventBus.d.ts +25 -0
  40. package/dist/console-server/sse/eventBus.d.ts.map +1 -0
  41. package/dist/console-server/sse/eventBus.js +32 -0
  42. package/dist/console-server/sse/eventBus.js.map +1 -0
  43. package/dist/console-server/watchers/cardWatcher.d.ts +9 -0
  44. package/dist/console-server/watchers/cardWatcher.d.ts.map +1 -0
  45. package/dist/console-server/watchers/cardWatcher.js +42 -0
  46. package/dist/console-server/watchers/cardWatcher.js.map +1 -0
  47. package/dist/core/skillStore.d.ts +46 -0
  48. package/dist/core/skillStore.d.ts.map +1 -0
  49. package/dist/core/skillStore.js +210 -0
  50. package/dist/core/skillStore.js.map +1 -0
  51. package/dist/core/skillStore.test.d.ts +2 -0
  52. package/dist/core/skillStore.test.d.ts.map +1 -0
  53. package/dist/core/skillStore.test.js +203 -0
  54. package/dist/core/skillStore.test.js.map +1 -0
  55. package/dist/main.js +27 -17
  56. package/dist/main.js.map +1 -1
  57. package/package.json +8 -2
  58. package/skills/architecture-decision-records/SKILL.md +207 -0
  59. package/skills/backend/SKILL.md +62 -0
  60. package/skills/backend/references/api-design.md +168 -0
  61. package/skills/backend/references/caching.md +181 -0
  62. package/skills/backend/references/data-access.md +173 -0
  63. package/skills/backend/references/layering.md +181 -0
  64. package/skills/backend/references/observability.md +190 -0
  65. package/skills/backend/references/resilience.md +201 -0
  66. package/skills/backend/references/security.md +186 -0
  67. package/skills/backend-architect/SKILL.md +119 -0
  68. package/skills/code-reviewer/SKILL.md +143 -0
  69. package/skills/coding-standards/SKILL.md +60 -0
  70. package/skills/coding-standards/references/clean-code.md +258 -0
  71. package/skills/coding-standards/references/code-review.md +192 -0
  72. package/skills/coding-standards/references/commits-and-prs.md +226 -0
  73. package/skills/coding-standards/references/error-strategy.md +193 -0
  74. package/skills/coding-standards/references/naming.md +185 -0
  75. package/skills/coding-standards/references/tdd.md +171 -0
  76. package/skills/database/SKILL.md +53 -0
  77. package/skills/database/references/indexing.md +190 -0
  78. package/skills/database/references/migrations.md +199 -0
  79. package/skills/database/references/nosql.md +185 -0
  80. package/skills/database/references/queries.md +295 -0
  81. package/skills/database/references/scaling.md +203 -0
  82. package/skills/database/references/schema.md +191 -0
  83. package/skills/database-optimizer/SKILL.md +168 -0
  84. package/skills/debugging-workflow/SKILL.md +244 -0
  85. package/skills/devops/SKILL.md +55 -0
  86. package/skills/devops/references/ci-cd.md +204 -0
  87. package/skills/devops/references/containers.md +272 -0
  88. package/skills/devops/references/deploy.md +201 -0
  89. package/skills/devops/references/iac.md +252 -0
  90. package/skills/devops/references/observability.md +228 -0
  91. package/skills/devops/references/secrets.md +178 -0
  92. package/skills/devops-automator/SKILL.md +164 -0
  93. package/skills/frontend/SKILL.md +52 -0
  94. package/skills/frontend/references/accessibility.md +222 -0
  95. package/skills/frontend/references/components.md +206 -0
  96. package/skills/frontend/references/performance.md +219 -0
  97. package/skills/frontend/references/routing.md +209 -0
  98. package/skills/frontend/references/state.md +190 -0
  99. package/skills/frontend/references/testing.md +216 -0
  100. package/skills/frontend-developer/SKILL.md +115 -0
  101. package/skills/git-workflow/SKILL.md +355 -0
  102. package/skills/golang/SKILL.md +49 -0
  103. package/skills/golang/references/concurrency.md +284 -0
  104. package/skills/golang/references/errors.md +241 -0
  105. package/skills/golang/references/idioms.md +285 -0
  106. package/skills/golang/references/testing.md +238 -0
  107. package/skills/java/SKILL.md +50 -0
  108. package/skills/java/references/concurrency.md +194 -0
  109. package/skills/java/references/idioms.md +283 -0
  110. package/skills/java/references/testing.md +228 -0
  111. package/skills/kotlin/SKILL.md +47 -0
  112. package/skills/kotlin/references/coroutines.md +240 -0
  113. package/skills/kotlin/references/idioms.md +268 -0
  114. package/skills/kotlin/references/testing.md +219 -0
  115. package/skills/mobile/SKILL.md +50 -0
  116. package/skills/mobile/references/architecture.md +204 -0
  117. package/skills/mobile/references/navigation.md +158 -0
  118. package/skills/mobile/references/performance.md +152 -0
  119. package/skills/mobile/references/platform.md +166 -0
  120. package/skills/mobile/references/state-and-data.md +174 -0
  121. package/skills/python/SKILL.md +51 -0
  122. package/skills/python/THIRD_PARTY.md +14 -0
  123. package/skills/python/references/async.md +218 -0
  124. package/skills/python/references/error-handling.md +254 -0
  125. package/skills/python/references/idioms.md +279 -0
  126. package/skills/python/references/packaging.md +233 -0
  127. package/skills/python/references/testing.md +269 -0
  128. package/skills/python/references/typing.md +292 -0
  129. package/skills/qa-tester/SKILL.md +186 -0
  130. package/skills/rust/SKILL.md +50 -0
  131. package/skills/rust/references/async.md +224 -0
  132. package/skills/rust/references/errors.md +240 -0
  133. package/skills/rust/references/ownership.md +263 -0
  134. package/skills/rust/references/testing.md +274 -0
  135. package/skills/rust/references/traits.md +250 -0
  136. package/skills/security-engineer/SKILL.md +157 -0
  137. package/skills/swift/SKILL.md +48 -0
  138. package/skills/swift/references/concurrency.md +280 -0
  139. package/skills/swift/references/idioms.md +334 -0
  140. package/skills/swift/references/testing.md +229 -0
  141. package/skills/typescript/SKILL.md +51 -0
  142. package/skills/typescript/references/async.md +241 -0
  143. package/skills/typescript/references/errors.md +208 -0
  144. package/skills/typescript/references/idioms.md +246 -0
  145. package/skills/typescript/references/testing.md +225 -0
  146. package/skills/typescript/references/tooling.md +208 -0
  147. package/skills/typescript/references/types.md +259 -0
@@ -0,0 +1,164 @@
1
+ ---
2
+ name: devops-automator
3
+ description: Persona skill — automate everything repeatable, measure everything deployable, rehearse every rollback. Overlay on top of `devops`. For the patterns, load `devops`.
4
+ origin: agency-agents-fork + original (https://github.com/msitarzewski/agency-agents, MIT)
5
+ ---
6
+
7
+ # DevOps Automator
8
+
9
+ Think in pipelines, not heroics. Every manual step is a future incident.
10
+
11
+ ## When to load
12
+
13
+ - Setting up / reviewing CI/CD
14
+ - Building a new deploy topology
15
+ - Writing a runbook
16
+ - Triaging a deploy or infra issue
17
+ - Deciding "should this be a manual step or automated?"
18
+
19
+ ## The posture
20
+
21
+ 1. **Automate the boring.** Manual steps are breeding grounds for incidents. If it's done twice, it's a script.
22
+ 2. **Same artifact dev → prod.** Build once; promote. Building per-environment hides bugs.
23
+ 3. **Rollback is a command, not a procedure.** If rollback takes a checklist, the checklist is the bug.
24
+ 4. **Observability is pre-launch.** Dashboards + alerts + runbooks exist before the first deploy.
25
+ 5. **Plan for the 3am self.** Will you remember at 3am, with dogs barking? Write it down.
26
+ 6. **Infrastructure is code.** Clicks in consoles are for exploration, not for prod.
27
+ 7. **Least privilege, always.** CI keys, service accounts, human access — scoped.
28
+
29
+ ## The questions you always ask
30
+
31
+ - **What happens if this step fails partway?** Idempotent? Rerunnable?
32
+ - **What does rollback look like?** One command. Tested.
33
+ - **What signals say "go ahead" to promote?** Metrics + alerts, not vibes.
34
+ - **Who sees this alert, and what do they do?** Runbook.
35
+ - **What's the blast radius of a bad deploy?** Pod / service / cluster / region?
36
+ - **How do we know what's running in prod right now?** SHA / tag should be a keystroke away.
37
+ - **Who has access to prod?** Named humans, not shared creds.
38
+ - **Is this reproducible on a fresh environment?** Disaster recovery test.
39
+
40
+ ## The checklist — shipping a new service
41
+
42
+ ### CI / CD
43
+ - [ ] Lint, typecheck, unit tests, integration tests in pipeline.
44
+ - [ ] Cache deps; pipeline < 10 min on typical change.
45
+ - [ ] Build artifact once; immutable tag.
46
+ - [ ] Gate prod on explicit approval.
47
+ - [ ] Pipeline secrets scoped per env, rotated on a schedule.
48
+ - [ ] Dependency scanning + image scanning.
49
+
50
+ ### Infra
51
+ - [ ] Every resource in IaC (Terraform / Pulumi / CDK / etc.).
52
+ - [ ] State remote + locked + encrypted.
53
+ - [ ] Env-specific variables, not code forks.
54
+ - [ ] Tagging strategy applied everywhere.
55
+ - [ ] Cost allocation visible.
56
+
57
+ ### Deploy
58
+ - [ ] Rolling / canary / blue-green chosen with reason.
59
+ - [ ] Health checks: live vs. ready.
60
+ - [ ] Graceful shutdown on SIGTERM.
61
+ - [ ] Resource limits (CPU, mem, replicas) sized, not defaults.
62
+ - [ ] Feature flags for behaviour-changing releases.
63
+ - [ ] Rollback command documented and tested.
64
+
65
+ ### Secrets
66
+ - [ ] All secrets in a secret manager.
67
+ - [ ] Runtime fetch via workload identity, not long-lived keys.
68
+ - [ ] Rotation schedule defined.
69
+ - [ ] Pre-commit / CI scanning for leaked secrets.
70
+
71
+ ### Observability
72
+ - [ ] Structured logs to stdout.
73
+ - [ ] Four golden signals exposed.
74
+ - [ ] Traces propagated through service boundaries.
75
+ - [ ] Dashboards on first-day launch.
76
+ - [ ] Alerts on symptoms (user impact), not infrastructure noise.
77
+ - [ ] Runbook linked from every alert.
78
+
79
+ ### Backup & DR
80
+ - [ ] Automated backups + tested restore.
81
+ - [ ] Cross-region replication if applicable.
82
+ - [ ] Recovery time objective (RTO) and recovery point objective (RPO) documented.
83
+ - [ ] Disaster recovery drill at least yearly.
84
+
85
+ ## What you push back on
86
+
87
+ - **"Just SSH in and run this"** as a solution. That's a bug you haven't fixed.
88
+ - **Secrets in env vars set by hand.** Use the secret manager.
89
+ - **Deploys without metrics backing "looks fine"** at the end.
90
+ - **Manual gates that could be automated.** Automated + logged is more trustworthy than "Alice always checks".
91
+ - **Latest-tag deploys.** Never rollback-able.
92
+ - **Dashboards that nobody watches.** Prune.
93
+ - **Alerts that fire and "auto-resolve".** Either it's actionable or it's noise.
94
+
95
+ ## Tradeoffs you name
96
+
97
+ - **Deploy speed vs. safety gates.** Small deploys can be fast; big ones need gates.
98
+ - **Monolith vs. microservices ops cost.** Each service is its own pipeline, dashboard, alert set.
99
+ - **Own vs. managed.** Self-hosted is cheaper until you count the oncall load.
100
+ - **Auto-healing vs. alert-first.** Orchestrator restarts hide problems. Strike a balance.
101
+
102
+ ## Standard runbook skeleton
103
+
104
+ ```markdown
105
+ # Runbook: <alert name>
106
+
107
+ ## What this means
108
+ One sentence. Who is affected, what's failing.
109
+
110
+ ## Immediate checks
111
+ 1. Dashboard link.
112
+ 2. Recent deploys link.
113
+ 3. Dependency status page.
114
+
115
+ ## Common causes
116
+ - Cause A → check X.
117
+ - Cause B → check Y.
118
+
119
+ ## Mitigation (in order)
120
+ 1. Fast: roll back if recent deploy.
121
+ 2. Medium: scale up / restart pool.
122
+ 3. Slow: escalate to service owner.
123
+
124
+ ## Escalation
125
+ - Primary: @service-owner
126
+ - Secondary: @infra
127
+ - SLA: respond within 15 min
128
+ ```
129
+
130
+ Every alert has one. Update as you learn.
131
+
132
+ ## What a good postmortem looks like
133
+
134
+ Blameless. Focused on the system.
135
+
136
+ ```
137
+ Summary: One paragraph.
138
+ Impact: Users affected × time × severity.
139
+ Timeline: Minute-by-minute from detection to resolution.
140
+ Root cause: Technical (what broke) AND process (why we didn't catch it).
141
+ Action items: Specific, owned, dated. Tracked.
142
+ Lessons: What surprised us.
143
+ ```
144
+
145
+ Track action items to completion. The same incident should never happen twice for the same reason.
146
+
147
+ ## Forbidden patterns
148
+
149
+ - Prod secrets on a developer laptop
150
+ - Deploy requires "call John"
151
+ - Untagged, unversioned images in prod
152
+ - "It worked in staging" without staging mirroring prod
153
+ - Alerts without runbooks
154
+ - IaC changes that were really cloud-console changes
155
+ - Long-lived API keys / tokens
156
+ - Pipelines that mask real test failures with `|| true`
157
+ - Single point of failure: one person knows how to deploy
158
+ - Deploys without a recorded event (can't correlate with incidents)
159
+
160
+ ## Pair with
161
+
162
+ - [`devops`](../devops/SKILL.md) — the patterns and recipes.
163
+ - [`backend/references/observability.md`](../backend/references/observability.md) — app-level signal definitions.
164
+ - [`coding-standards`](../coding-standards/SKILL.md) — how the pipelines enforce quality.
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: frontend
3
+ description: Frontend end skill — components, state, routing, performance, accessibility, testing. Language-neutral (React / Vue / Svelte / SolidJS patterns described conceptually). Pair with a language skill and `coding-standards`.
4
+ origin: ecc-fork + original (https://github.com/affaan-m/everything-claude-code, MIT)
5
+ ---
6
+
7
+ # Frontend
8
+
9
+ Client-side architecture. **Framework-agnostic by design** — examples use pseudocode or a generic component model. Pair with a language skill (`typescript`) and a framework choice.
10
+
11
+ ## When to load
12
+
13
+ - Building or reviewing UI in the browser (web / PWA / Electron)
14
+ - State management: local, server, global
15
+ - Routing, navigation, deep linking
16
+ - Rendering performance, bundle size
17
+ - Accessibility, i18n, SEO
18
+ - Frontend testing (component, E2E)
19
+
20
+ ## Core principles
21
+
22
+ 1. **State is the hard part. Components are easy.** Get state shape right and most code writes itself.
23
+ 2. **Server state ≠ client state.** Use a dedicated tool (TanStack Query, SWR, Apollo) for server state; don't cram it into your global store.
24
+ 3. **Derive, don't duplicate.** If two pieces of state must stay in sync, at least one is derived.
25
+ 4. **Optimistic UI for responsiveness, reconciliation for correctness.** Show the expected result immediately; update on server response.
26
+ 5. **Measure before optimizing.** Bundle analyzer, Lighthouse, React Profiler / equivalent.
27
+ 6. **Accessibility is not optional.** Keyboard navigation, semantic HTML, color contrast, screen-reader labels.
28
+ 7. **Keep components dumb.** Props in, events out. Move side effects to hooks / composables / stores.
29
+
30
+ ## How to use references
31
+
32
+ | Reference | When to load |
33
+ |---|---|
34
+ | [`references/components.md`](references/components.md) | Component shape, props, composition, slots / children, separation of concerns |
35
+ | [`references/state.md`](references/state.md) | Local vs. shared, server state, derived, stores, forms |
36
+ | [`references/routing.md`](references/routing.md) | Client routing, navigation, deep linking, query params, guards |
37
+ | [`references/performance.md`](references/performance.md) | Rendering cost, code-splitting, virtualization, images, fonts |
38
+ | [`references/accessibility.md`](references/accessibility.md) | Semantic HTML, keyboard, ARIA, focus management, contrast |
39
+ | [`references/testing.md`](references/testing.md) | Component tests, snapshot tests, E2E (Playwright), visual regression |
40
+
41
+ ## Forbidden patterns (auto-reject)
42
+
43
+ - Business logic inside presentational components
44
+ - Global mutable state for server data (keep server data in a cache, not in Redux/Zustand directly)
45
+ - Fetching in a loop of unrelated components (N+1 on the client)
46
+ - Mutating props
47
+ - `any` / untyped props (in typed frontends)
48
+ - Accessibility via `role="button"` on a `<div>` when `<button>` works
49
+ - Inline styles driven by state when a class + CSS toggle would do
50
+ - `dangerouslySetInnerHTML` / `v-html` without a sanitizer
51
+ - `useEffect` / `watchEffect` for derivations — use computed values
52
+ - Animation / loading state toggled by timeouts instead of by actual completion events
@@ -0,0 +1,222 @@
1
+ # Accessibility
2
+
3
+ Semantic HTML, keyboard, ARIA, focus, contrast. Not optional.
4
+
5
+ ## Who benefits
6
+
7
+ - 15–20% of users have a disability (motor, visual, cognitive, temporary).
8
+ - Everyone benefits from good a11y: keyboard shortcuts, screen readers, screen-reader-like voice assistants, low-light contrast, slow networks.
9
+
10
+ Treat it as a correctness requirement, not a "nice to have".
11
+
12
+ ## Semantic HTML first
13
+
14
+ Every control you use should be the right element for the job.
15
+
16
+ | Use | For |
17
+ |---|---|
18
+ | `<button>` | Action |
19
+ | `<a href>` | Navigation |
20
+ | `<form>` | Group inputs, submit behaviour |
21
+ | `<input type="checkbox">` / `<input type="radio">` | Single / exclusive choice |
22
+ | `<select>` / `<option>` | Enumerated choice from list |
23
+ | `<label>` | Associate text with an input |
24
+ | `<fieldset>` / `<legend>` | Group related inputs |
25
+ | `<nav>`, `<main>`, `<aside>`, `<header>`, `<footer>` | Landmarks |
26
+ | `<h1>` … `<h6>` | Document outline |
27
+
28
+ **Rule**: if the right element exists, use it. `<div onClick>` with `role="button"` is twice the work and half the behaviour.
29
+
30
+ ## Keyboard navigation
31
+
32
+ Every interactive control must be operable with keyboard only:
33
+
34
+ - **Tab** / **Shift+Tab** — move focus.
35
+ - **Enter** / **Space** — activate (varies: buttons on both, links on Enter).
36
+ - **Arrows** — move within composite widgets (menus, tablists, listboxes, grids).
37
+ - **Escape** — dismiss modal, popover, typeahead.
38
+
39
+ Test periodically: unplug the mouse.
40
+
41
+ ### Visible focus
42
+
43
+ Don't remove the focus outline.
44
+
45
+ ```css
46
+ /* ❌ */
47
+ *:focus { outline: none; }
48
+
49
+ /* ✅ style it, don't remove it */
50
+ :focus-visible {
51
+ outline: 2px solid var(--color-focus);
52
+ outline-offset: 2px;
53
+ }
54
+ ```
55
+
56
+ `:focus-visible` shows on keyboard focus but not mouse click — best of both.
57
+
58
+ ### Focus management
59
+
60
+ Modal opens → move focus into the modal. Modal closes → return focus to the trigger.
61
+
62
+ ```
63
+ function Modal() {
64
+ useEffect(() => {
65
+ const prev = document.activeElement;
66
+ firstInput.focus();
67
+ return () => prev.focus(); // restore on unmount
68
+ }, []);
69
+ }
70
+ ```
71
+
72
+ Route changes → announce and move focus to the new page's `<h1>` or main landmark (framework routers handle some of this; verify).
73
+
74
+ ## ARIA — the rule
75
+
76
+ First rule of ARIA: **don't use ARIA.** Use semantic HTML. Every ARIA attribute is a hand-maintained contract with assistive tech; native elements get it for free.
77
+
78
+ When you must use ARIA (custom widgets), follow the [ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) patterns — they are the source of truth.
79
+
80
+ Common attributes:
81
+
82
+ | Attribute | Meaning |
83
+ |---|---|
84
+ | `aria-label` | Override accessible name (use sparingly) |
85
+ | `aria-labelledby` | Name by reference to another element |
86
+ | `aria-describedby` | Extra hint / help text |
87
+ | `aria-hidden="true"` | Hide purely decorative element from AT |
88
+ | `aria-live="polite"` / `"assertive"` | Announce changes |
89
+ | `aria-expanded` | Disclosure state on buttons |
90
+ | `aria-controls` | Associates a control with the thing it controls |
91
+ | `aria-current` | The active item in a set (e.g. nav) |
92
+
93
+ Rules:
94
+ - Don't set `aria-hidden` on a focusable element — screen readers skip, but keyboard doesn't.
95
+ - Don't name duplicates: `<button aria-label="Close">Close</button>` — pick one.
96
+
97
+ ## Labels and descriptions
98
+
99
+ Every form control has a label. Either:
100
+
101
+ ```
102
+ <label for="email">Email</label>
103
+ <input id="email" name="email">
104
+
105
+ <!-- or wrap -->
106
+ <label>
107
+ Email
108
+ <input name="email">
109
+ </label>
110
+ ```
111
+
112
+ Placeholder is NOT a label. It disappears on focus and has poor contrast.
113
+
114
+ For help text / errors:
115
+
116
+ ```
117
+ <label for="pw">Password</label>
118
+ <input id="pw" type="password" aria-describedby="pw-help pw-err">
119
+ <span id="pw-help">At least 12 chars.</span>
120
+ <span id="pw-err" role="alert">Too short.</span>
121
+ ```
122
+
123
+ ## Color contrast
124
+
125
+ Minimum WCAG AA:
126
+ - Normal text vs. background: **4.5 : 1**
127
+ - Large text (18 pt+ or 14 pt bold+): **3 : 1**
128
+ - UI components (border, icon vs. background): **3 : 1**
129
+
130
+ Don't rely on color alone. "Red = error" + icon + text is accessible; red alone isn't for the color-blind.
131
+
132
+ Tools: Lighthouse flags most contrast issues; for design systems, contrast-aware token generators (radix colors, Open Props).
133
+
134
+ ## Live regions — announce changes
135
+
136
+ Status updates that aren't caused by the user's last action (notifications, background sync) need an ARIA live region.
137
+
138
+ ```
139
+ <div aria-live="polite" aria-atomic="true">
140
+ {status}
141
+ </div>
142
+
143
+ <!-- For urgent errors -->
144
+ <div role="alert">
145
+ {errorMessage}
146
+ </div>
147
+ ```
148
+
149
+ `polite` queues the announcement; `assertive` / `role="alert"` interrupts. Use `assertive` sparingly.
150
+
151
+ ## Images and alt text
152
+
153
+ - Content image: `<img alt="screen reader text">` describing what it shows.
154
+ - Decorative image: `<img alt="">` (empty alt, NOT no alt).
155
+ - SVG icon with text next to it: `<svg aria-hidden="true">`.
156
+ - Standalone SVG icon that means something: provide an accessible name.
157
+
158
+ ## Motion and animation
159
+
160
+ - Respect `prefers-reduced-motion`:
161
+ ```css
162
+ @media (prefers-reduced-motion: reduce) {
163
+ *, *::before, *::after { animation: none !important; transition: none !important; }
164
+ }
165
+ ```
166
+ - Don't autoplay videos with audio.
167
+ - Avoid content flashing more than 3 times per second (seizure risk).
168
+
169
+ ## Screen reader testing
170
+
171
+ Two minutes in a screen reader beats an hour of reading spec.
172
+
173
+ - macOS: **VoiceOver** (Cmd+F5).
174
+ - Windows: **NVDA** (free) or **JAWS**.
175
+ - iOS: **VoiceOver**. Android: **TalkBack**.
176
+
177
+ Walk through key flows. If you can't complete a task with only voice output, neither can your users.
178
+
179
+ ## Automated testing
180
+
181
+ Tools that catch a lot cheaply:
182
+
183
+ - **axe-core** (or `@axe-core/react`, `vitest-axe`) — unit-test with it.
184
+ - **Lighthouse a11y score** — smoke test in CI.
185
+ - **Playwright / Cypress + axe** — E2E a11y checks.
186
+ - **ESLint `jsx-a11y`** — catches basics at write time.
187
+
188
+ Automated tools find roughly 30% of issues. The rest needs a human.
189
+
190
+ ## Common patterns — the right way
191
+
192
+ | Widget | Key rules |
193
+ |---|---|
194
+ | Modal | Trap focus, restore on close, `aria-modal`, visible backdrop |
195
+ | Menu | Button with `aria-expanded`, arrow-key navigation, Escape to close |
196
+ | Tabs | Arrow keys move focus; Tab exits; `aria-selected` on active |
197
+ | Combobox | WAI-ARIA APG pattern — complex; use a library |
198
+ | Tooltip | `aria-describedby`, hover + focus triggers, escape to dismiss |
199
+ | Toast | `role="status"` or `aria-live="polite"`, don't auto-dismiss critical messages |
200
+ | Accordion | Button with `aria-expanded`, content with `role="region"` |
201
+
202
+ ## i18n hooks
203
+
204
+ Accessibility overlaps with i18n:
205
+ - `lang` attribute on `<html>` and on sections that differ.
206
+ - `dir="rtl"` / `dir="ltr"` for text direction.
207
+ - Locale-aware number / date formatting.
208
+
209
+ ## Anti-patterns
210
+
211
+ | Anti-pattern | Fix |
212
+ |---|---|
213
+ | `<div onClick>` acting as a button | `<button>` |
214
+ | `outline: none` with no replacement | Style `:focus-visible` |
215
+ | Placeholder-as-label | Visible `<label>` |
216
+ | Tab order that skips around visually | Match DOM order to visual order |
217
+ | Modal without focus trap | Library (`focus-trap-react`) or correct hand-rolled |
218
+ | Color-only error state | Add icon + text |
219
+ | Auto-focusing unexpected things | Focus jumps break the user's place |
220
+ | `aria-hidden` on a focusable control | Either hide visually too, or remove aria-hidden |
221
+ | Missing `lang` | Set on `<html>` |
222
+ | "Click here" link text | Descriptive link text: "Read the pricing guide" |
@@ -0,0 +1,206 @@
1
+ # Components
2
+
3
+ Shape, composition, props, children / slots.
4
+
5
+ ## One responsibility per component
6
+
7
+ A component that renders a user profile should not fetch the user. A component that fetches the user shouldn't render it.
8
+
9
+ ```
10
+ <UserProfile data={user} onEdit={handleEdit} /> # presentational
11
+ <UserProfileContainer userId={id} /> # loads + renders
12
+ ```
13
+
14
+ This split (presentational / container, or dumb / smart) keeps each piece testable in isolation. The container deals with data; the presentational piece with pixels.
15
+
16
+ ## Props in, events out
17
+
18
+ Data flows down through props. Changes flow up through callbacks / emitted events.
19
+
20
+ ```
21
+ <Button
22
+ label="Save"
23
+ disabled={!form.valid}
24
+ onClick={() => submit()}
25
+ />
26
+ ```
27
+
28
+ Avoid:
29
+ - Parents reaching into children via refs for non-imperative purposes.
30
+ - Children calling setters on state that belongs to the parent.
31
+ - Two-way binding magic that hides data flow.
32
+
33
+ ## Composition over configuration
34
+
35
+ Accept children / slots, don't try to anticipate every variant with boolean props.
36
+
37
+ ```
38
+ # ❌ exploding configuration
39
+ <Card
40
+ hasHeader
41
+ hasFooter
42
+ headerText="Hi"
43
+ footerAction="Dismiss"
44
+ iconLeft
45
+ iconRight
46
+ />
47
+
48
+ # ✅ composition
49
+ <Card>
50
+ <CardHeader>Hi</CardHeader>
51
+ <CardBody>...</CardBody>
52
+ <CardFooter>
53
+ <Button onClick={dismiss}>Dismiss</Button>
54
+ </CardFooter>
55
+ </Card>
56
+ ```
57
+
58
+ Fewer props, more flexibility. The boundary between "configure a primitive" and "compose primitives" is roughly when boolean props start bumping past 3–4 that interact with each other.
59
+
60
+ ## Slots / children
61
+
62
+ Every modern framework has a way to inject arbitrary content into a component:
63
+
64
+ | Framework | Name |
65
+ |---|---|
66
+ | React | `children` (and render props) |
67
+ | Vue | `<slot>` (named + scoped) |
68
+ | Svelte | `<slot>` + named slots |
69
+ | Solid | `children` |
70
+ | Web Components | `<slot>` |
71
+
72
+ Prefer these over passing JSX / VNodes through props. Slots / children are the idiomatic "here's your content".
73
+
74
+ ## Smart vs. dumb — the rough divide
75
+
76
+ | Smart (container, hook, composable) | Dumb (presentational) |
77
+ |---|---|
78
+ | Fetches data | Renders what it's given |
79
+ | Owns mutable state | Stateless (or local UI state only) |
80
+ | Wires up events to side effects | Emits events, doesn't handle them |
81
+ | Tests: usually via integration | Tests: snapshot / visual |
82
+
83
+ The boundary is not rigid. Don't create a container for a `<Button>`. But for anything with data or non-trivial behaviour, having the data part extractable is a win.
84
+
85
+ ## Prop typing (typed frameworks)
86
+
87
+ ```ts
88
+ type UserCardProps = {
89
+ user: User;
90
+ variant?: 'compact' | 'full';
91
+ onEdit?: (user: User) => void;
92
+ };
93
+
94
+ function UserCard({ user, variant = 'full', onEdit }: UserCardProps) { ... }
95
+ ```
96
+
97
+ Rules:
98
+ - Default values inline at destructure.
99
+ - Optional callbacks are fine; treat them as optional behaviour.
100
+ - Don't type `user: any` — if the shape is unclear, define the type.
101
+
102
+ ## Render props vs. hooks / composables
103
+
104
+ Modern frameworks prefer hooks / composables for reusing logic. Render props / slot props are still useful for reusing *markup+logic* bundles (a `<Downshift>` combobox, a headless UI primitive).
105
+
106
+ ```
107
+ # Hook — reuse logic
108
+ function useDebouncedSearch(value, ms) {
109
+ const [debounced, setDebounced] = useState(value);
110
+ useEffect(() => { const t = setTimeout(() => setDebounced(value), ms); return () => clearTimeout(t); }, [value, ms]);
111
+ return debounced;
112
+ }
113
+
114
+ # Render prop / slot — reuse logic + markup hooks
115
+ <Combobox items={items}>
116
+ {({ input, menu, option }) => (
117
+ <div>
118
+ <input {...input} />
119
+ <ul {...menu}>{items.map(i => <li {...option(i)}>{i.label}</li>)}</ul>
120
+ </div>
121
+ )}
122
+ </Combobox>
123
+ ```
124
+
125
+ ## Controlled vs. uncontrolled inputs
126
+
127
+ | Controlled | State in the parent; parent dictates the value |
128
+ | Uncontrolled | State in the DOM; parent reads via ref on submit |
129
+
130
+ Use controlled for forms with validation, dynamic enablement, real-time feedback. Use uncontrolled for simple forms where only the submit value matters — less re-render noise.
131
+
132
+ ## Forwarded refs — the escape hatch
133
+
134
+ Let a parent attach a ref through a wrapping component to reach an underlying DOM node (focus a text input, scroll into view). Use sparingly — most needs are better solved with props.
135
+
136
+ ## Error boundaries
137
+
138
+ Every app root should have a boundary that catches render errors and shows a graceful fallback. Otherwise one bad prop crashes the whole tree to white.
139
+
140
+ ```
141
+ <ErrorBoundary fallback={<Error500 />}>
142
+ <App />
143
+ </ErrorBoundary>
144
+ ```
145
+
146
+ Don't wrap every component in its own boundary — a handful of strategic ones (app shell, each route, expensive widgets) is plenty.
147
+
148
+ ## Suspense / loading states
149
+
150
+ Where the framework supports it, declarative loading UX beats imperative flags.
151
+
152
+ ```
153
+ # Declarative
154
+ <Suspense fallback={<Skeleton />}>
155
+ <UserCard id={id} />
156
+ </Suspense>
157
+
158
+ # Imperative — works too, more verbose
159
+ {loading ? <Skeleton /> : <UserCard user={user} />}
160
+ ```
161
+
162
+ ## Folder structure — organize by feature
163
+
164
+ ```
165
+ src/
166
+ ├── features/
167
+ │ ├── user/
168
+ │ │ ├── UserCard.tsx
169
+ │ │ ├── UserCard.test.tsx
170
+ │ │ ├── useUser.ts
171
+ │ │ └── api.ts
172
+ │ └── billing/
173
+ │ └── ...
174
+ ├── shared/
175
+ │ ├── components/ # truly shared primitives (Button, Input)
176
+ │ ├── hooks/
177
+ │ └── utils/
178
+ └── app/
179
+ ├── routes.tsx
180
+ └── root.tsx
181
+ ```
182
+
183
+ Organize by feature first, by type (components, hooks, utils) second. Kitchen-sink `components/` folders become dumping grounds.
184
+
185
+ ## Styling — pick one, consistent
186
+
187
+ - **CSS modules / scoped styles** — simple, portable.
188
+ - **Utility-first (Tailwind)** — fast to write, mature ecosystem, needs team buy-in.
189
+ - **CSS-in-JS** — component-local; runtime cost varies.
190
+ - **Design tokens** — common layer regardless of above choice (`--color-primary`, `var(...)`).
191
+
192
+ Mixing strategies across the codebase is the antipattern, not the choice itself.
193
+
194
+ ## Anti-patterns
195
+
196
+ | Anti-pattern | Fix |
197
+ |---|---|
198
+ | Huge props object passed through many layers | Composition / context / slots |
199
+ | `useEffect` / `watchEffect` to "sync two states" | Derive one from the other |
200
+ | Logic in JSX (`{users.filter(...).map(...).length > 0 && ...}`) | Extract to a named variable or helper |
201
+ | Deep prop drilling | Context / store, or restructure component tree |
202
+ | Rendering a component for each row of thousands | Virtualize (see `performance.md`) |
203
+ | Side effects during render | Side effects belong in effects / handlers |
204
+ | Mutating props or items inside `.map` | Make new objects / arrays |
205
+ | Callback prop recreated on every render causing child re-renders | Memoize or lift |
206
+ | Inline objects / arrays in deps arrays | Memoize or use stable refs |