@mmerterden/multi-agent-pipeline 20.36.0 → 20.37.1

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 (222) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/docs/facts.json +2 -2
  3. package/docs/features.md +1 -1
  4. package/docs/recovery-guide.md +1 -1
  5. package/install/catalog-history.json +1 -1
  6. package/manifest.json +225 -148
  7. package/package.json +1 -1
  8. package/pipeline/commands/multi-agent/SKILL.md +33 -154
  9. package/pipeline/commands/multi-agent/analysis/SKILL.md +14 -68
  10. package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +5 -8
  11. package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +38 -109
  12. package/pipeline/commands/multi-agent/channels/SKILL.md +38 -167
  13. package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +37 -144
  14. package/pipeline/commands/multi-agent/design-check/SKILL.md +27 -131
  15. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +22 -48
  16. package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +37 -180
  17. package/pipeline/commands/multi-agent/jira/SKILL.md +16 -46
  18. package/pipeline/commands/multi-agent/manual-test/SKILL.md +12 -7
  19. package/pipeline/commands/multi-agent/refactor/SKILL.md +23 -184
  20. package/pipeline/commands/multi-agent/resume/SKILL.md +30 -136
  21. package/pipeline/commands/multi-agent/review/SKILL.md +24 -199
  22. package/pipeline/commands/multi-agent/setup/SKILL.md +25 -181
  23. package/pipeline/commands/multi-agent/stack/SKILL.md +9 -53
  24. package/pipeline/commands/multi-agent/status/SKILL.md +13 -2
  25. package/pipeline/commands/multi-agent/store-ready/SKILL.md +27 -226
  26. package/pipeline/commands/multi-agent/sync/SKILL.md +35 -164
  27. package/pipeline/commands/multi-agent/test-gen/SKILL.md +2 -4
  28. package/pipeline/commands/multi-agent/update/SKILL.md +18 -60
  29. package/pipeline/contract/CHANGELOG.md +9 -0
  30. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  31. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  32. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  33. package/pipeline/contract/fixtures/runs-awaiting-question.json +1 -1
  34. package/pipeline/contract/fixtures/runs-empty.json +1 -1
  35. package/pipeline/contract/fixtures/runs-failed.json +1 -1
  36. package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +1 -1
  37. package/pipeline/contract/fixtures/runs-pr-opened.json +1 -1
  38. package/pipeline/contract/fixtures/runs-running.json +1 -1
  39. package/pipeline/contract/manifest.json +5 -5
  40. package/pipeline/contract/types/index.d.ts +3 -2
  41. package/pipeline/multi-agent-refs/audit-guide.md +20 -0
  42. package/pipeline/multi-agent-refs/autopilot-on/details.md +148 -0
  43. package/pipeline/multi-agent-refs/channels/generator.md +14 -0
  44. package/pipeline/multi-agent-refs/channels/preview.md +13 -0
  45. package/pipeline/multi-agent-refs/channels/reference.md +46 -0
  46. package/pipeline/multi-agent-refs/complaint-analysis/phases.md +159 -0
  47. package/pipeline/multi-agent-refs/design-check/export.md +1 -0
  48. package/pipeline/multi-agent-refs/design-check/init-inventory.md +1 -1
  49. package/pipeline/multi-agent-refs/design-check/scope-and-ledger.md +64 -0
  50. package/pipeline/multi-agent-refs/features/analysis-command-notes.md +53 -0
  51. package/pipeline/multi-agent-refs/features/commit-multi-repo.md +63 -0
  52. package/pipeline/multi-agent-refs/features/dev-multi-repo.md +51 -0
  53. package/pipeline/multi-agent-refs/features/test-gap.md +40 -0
  54. package/pipeline/multi-agent-refs/features/worktree-finalize.md +1 -1
  55. package/pipeline/multi-agent-refs/garbage-collect/phases.md +60 -0
  56. package/pipeline/multi-agent-refs/ios-coding-standard/plan.md +71 -0
  57. package/pipeline/multi-agent-refs/ios-coding-standard/resolve-and-scan.md +135 -0
  58. package/pipeline/multi-agent-refs/jira/picker-detail.md +71 -0
  59. package/pipeline/multi-agent-refs/orchestrator/copilot-pipeline.md +88 -0
  60. package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +96 -1
  61. package/pipeline/multi-agent-refs/orchestrator/phase-3-user-test.md +12 -9
  62. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -4
  63. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +8 -47
  64. package/pipeline/multi-agent-refs/phases/phase-3-review.md +21 -65
  65. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +9 -59
  66. package/pipeline/multi-agent-refs/phases/phase-5-report.md +0 -31
  67. package/pipeline/multi-agent-refs/refactor/sweep.md +190 -0
  68. package/pipeline/multi-agent-refs/resume/details.md +115 -2
  69. package/pipeline/multi-agent-refs/review/inputs.md +33 -1
  70. package/pipeline/multi-agent-refs/review/module-guides.md +9 -1
  71. package/pipeline/multi-agent-refs/review/panel.md +75 -0
  72. package/pipeline/multi-agent-refs/review/valven.md +5 -1
  73. package/pipeline/multi-agent-refs/rules.md +1 -1
  74. package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +4 -0
  75. package/pipeline/multi-agent-refs/setup/token-save-flow.md +13 -0
  76. package/pipeline/multi-agent-refs/stack/background.md +72 -0
  77. package/pipeline/multi-agent-refs/store-ready/gates.md +139 -0
  78. package/pipeline/multi-agent-refs/store-ready/report.md +40 -2
  79. package/pipeline/multi-agent-refs/sync/office.md +10 -0
  80. package/pipeline/multi-agent-refs/sync/overview.md +61 -0
  81. package/pipeline/multi-agent-refs/sync/repo.md +91 -0
  82. package/pipeline/multi-agent-refs/update/details.md +83 -0
  83. package/pipeline/office/index.html +6 -3
  84. package/pipeline/office/toolkit.json +1 -1
  85. package/pipeline/rules/figma-pipeline.md +6 -3
  86. package/pipeline/schemas/prefs.schema.json +1 -1
  87. package/pipeline/schemas/runs-index.schema.json +2 -1
  88. package/pipeline/scripts/_memory-policy.mjs +27 -5
  89. package/pipeline/scripts/checkpoint-fold.sh +59 -0
  90. package/pipeline/scripts/contract-server.mjs +1 -1
  91. package/pipeline/scripts/gate-ledger.mjs +1 -1
  92. package/pipeline/scripts/office-control.mjs +41 -15
  93. package/pipeline/scripts/offload-ref.sh +1 -1
  94. package/pipeline/scripts/rework-rulings.mjs +5 -3
  95. package/pipeline/scripts/runs-index.mjs +34 -7
  96. package/pipeline/scripts/scope-check-gate.mjs +15 -2
  97. package/pipeline/scripts/status-overview.mjs +401 -0
  98. package/pipeline/scripts/user-test-handoff.sh +203 -0
  99. package/pipeline/scripts/validate-planning.mjs +5 -3
  100. package/pipeline/scripts/validate-triage.mjs +2 -1
  101. package/pipeline/scripts/write-state.mjs +13 -4
  102. package/pipeline/skills/.skill-manifest.json +64 -64
  103. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +7 -70
  104. package/pipeline/skills/shared/core/apple-archive-compliance/references/examples.md +67 -0
  105. package/pipeline/skills/shared/core/apple-archive-compliance/references/version-history.md +8 -0
  106. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +10 -72
  107. package/pipeline/skills/shared/core/google-play-compliance/references/examples.md +69 -0
  108. package/pipeline/skills/shared/core/google-play-compliance/references/notes.md +7 -0
  109. package/pipeline/skills/shared/core/google-play-compliance/references/version-history.md +5 -0
  110. package/pipeline/skills/shared/core/multi-agent/SKILL.md +39 -262
  111. package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +35 -73
  112. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +37 -337
  113. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +27 -122
  114. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +8 -340
  115. package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +37 -180
  116. package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +12 -7
  117. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +23 -184
  118. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +14 -82
  119. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +25 -277
  120. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +13 -2
  121. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +73 -217
  122. package/pipeline/skills/shared/external/alarmkit/SKILL.md +11 -162
  123. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-code.md +257 -0
  124. package/pipeline/skills/shared/external/android-architecture/SKILL.md +7 -58
  125. package/pipeline/skills/shared/external/android-architecture/references/code-examples.md +68 -0
  126. package/pipeline/skills/shared/external/app-intents/SKILL.md +3 -45
  127. package/pipeline/skills/shared/external/app-intents/references/code-examples.md +54 -0
  128. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +5 -81
  129. package/pipeline/skills/shared/external/app-store-optimization/references/engagement-and-localization.md +96 -0
  130. package/pipeline/skills/shared/external/app-store-review/SKILL.md +6 -112
  131. package/pipeline/skills/shared/external/app-store-review/references/monetization-and-submission.md +138 -0
  132. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +10 -117
  133. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models-code.md +186 -0
  134. package/pipeline/skills/shared/external/authentication/SKILL.md +3 -50
  135. package/pipeline/skills/shared/external/authentication/references/code-examples.md +59 -0
  136. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +3 -84
  137. package/pipeline/skills/shared/external/callkit-voip/references/code-examples.md +93 -0
  138. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +7 -263
  139. package/pipeline/skills/shared/external/ci-cd-pipelines/references/pipeline-examples.md +281 -0
  140. package/pipeline/skills/shared/external/clean-code/SKILL.md +8 -41
  141. package/pipeline/skills/shared/external/clean-code/references/code-examples.md +56 -0
  142. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +8 -146
  143. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-code.md +214 -0
  144. package/pipeline/skills/shared/external/compose-components/SKILL.md +6 -80
  145. package/pipeline/skills/shared/external/compose-components/references/code-examples.md +89 -0
  146. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +5 -64
  147. package/pipeline/skills/shared/external/compose-navigation/references/code-examples.md +70 -0
  148. package/pipeline/skills/shared/external/core-data/SKILL.md +9 -157
  149. package/pipeline/skills/shared/external/core-data/references/core-data-code.md +221 -0
  150. package/pipeline/skills/shared/external/core-nfc/SKILL.md +3 -89
  151. package/pipeline/skills/shared/external/core-nfc/references/code-examples.md +98 -0
  152. package/pipeline/skills/shared/external/coreml/SKILL.md +17 -133
  153. package/pipeline/skills/shared/external/coreml/references/coreml-code.md +208 -0
  154. package/pipeline/skills/shared/external/css-modern/SKILL.md +8 -131
  155. package/pipeline/skills/shared/external/css-modern/references/code-examples.md +142 -0
  156. package/pipeline/skills/shared/external/database-patterns/SKILL.md +6 -103
  157. package/pipeline/skills/shared/external/database-patterns/references/orm-examples.md +112 -0
  158. package/pipeline/skills/shared/external/html-semantic/SKILL.md +7 -48
  159. package/pipeline/skills/shared/external/html-semantic/references/code-examples.md +54 -0
  160. package/pipeline/skills/shared/external/humanizer/SKILL.md +6 -19
  161. package/pipeline/skills/shared/external/humanizer/references/voice.md +24 -0
  162. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +13 -170
  163. package/pipeline/skills/shared/external/ios-accessibility/references/assistive-tech-review.md +224 -0
  164. package/pipeline/skills/shared/external/ios-localization/SKILL.md +2 -24
  165. package/pipeline/skills/shared/external/ios-localization/references/code-examples.md +30 -0
  166. package/pipeline/skills/shared/external/ios-networking/SKILL.md +3 -58
  167. package/pipeline/skills/shared/external/ios-networking/references/code-examples.md +67 -0
  168. package/pipeline/skills/shared/external/ios-security/SKILL.md +4 -64
  169. package/pipeline/skills/shared/external/ios-security/references/code-examples.md +74 -0
  170. package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -90
  171. package/pipeline/skills/shared/external/live-activities/references/code-examples.md +108 -0
  172. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -23
  173. package/pipeline/skills/shared/external/localization-reuse-map/references/project-settings.md +22 -0
  174. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +5 -95
  175. package/pipeline/skills/shared/external/mapkit-location/references/code-examples.md +117 -0
  176. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +7 -106
  177. package/pipeline/skills/shared/external/nextjs-app-router/references/app-router-examples.md +124 -0
  178. package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -31
  179. package/pipeline/skills/shared/external/permissionkit/references/code-examples.md +37 -0
  180. package/pipeline/skills/shared/external/play-store-review/SKILL.md +6 -74
  181. package/pipeline/skills/shared/external/play-store-review/references/signing-and-policies.md +83 -0
  182. package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -125
  183. package/pipeline/skills/shared/external/push-notifications/references/notification-code.md +176 -0
  184. package/pipeline/skills/shared/external/python-patterns/SKILL.md +7 -158
  185. package/pipeline/skills/shared/external/python-patterns/references/python-examples.md +176 -0
  186. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +7 -157
  187. package/pipeline/skills/shared/external/rest-api-design/references/api-examples.md +175 -0
  188. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +11 -230
  189. package/pipeline/skills/shared/external/swift-architecture/references/pattern-examples.md +295 -0
  190. package/pipeline/skills/shared/external/swift-codable/SKILL.md +17 -149
  191. package/pipeline/skills/shared/external/swift-codable/references/codable-recipes.md +207 -0
  192. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +12 -99
  193. package/pipeline/skills/shared/external/swift-concurrency/references/code-examples.md +100 -0
  194. package/pipeline/skills/shared/external/swift-concurrency/references/swift-6-2-additions.md +18 -0
  195. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +9 -78
  196. package/pipeline/skills/shared/external/swift-formatstyle/references/formatstyle-examples.md +113 -0
  197. package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -56
  198. package/pipeline/skills/shared/external/swift-language/references/code-examples.md +65 -0
  199. package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +1 -0
  200. package/pipeline/skills/shared/external/swiftdata/SKILL.md +6 -122
  201. package/pipeline/skills/shared/external/swiftdata/references/code-examples.md +140 -0
  202. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +12 -139
  203. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/interop-code.md +198 -0
  204. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +9 -81
  205. package/pipeline/skills/shared/external/tailwind-css/references/code-examples.md +91 -0
  206. package/pipeline/skills/shared/external/testing-backend/SKILL.md +7 -207
  207. package/pipeline/skills/shared/external/testing-backend/references/testing-examples.md +225 -0
  208. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +8 -89
  209. package/pipeline/skills/shared/external/typescript-patterns/references/code-examples.md +100 -0
  210. package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -20
  211. package/pipeline/skills/shared/external/vision-framework/references/code-examples.md +26 -0
  212. package/pipeline/skills/shared/external/vue-composition/SKILL.md +7 -169
  213. package/pipeline/skills/shared/external/vue-composition/references/vue-examples.md +187 -0
  214. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +7 -174
  215. package/pipeline/skills/shared/external/web-accessibility/references/a11y-examples.md +192 -0
  216. package/pipeline/skills/shared/external/web-performance/SKILL.md +8 -66
  217. package/pipeline/skills/shared/external/web-performance/references/code-examples.md +77 -0
  218. package/pipeline/skills/shared/external/web-testing/SKILL.md +11 -186
  219. package/pipeline/skills/shared/external/web-testing/references/testing-examples.md +213 -0
  220. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/format-and-output.md +0 -0
  221. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/publish-and-snapshot.md +0 -0
  222. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/sources-and-recipes.md +0 -0
@@ -57,186 +57,15 @@ keyboard operability, screen reader support, and sufficient color contrast.
57
57
 
58
58
  ## ARIA Roles, States, and Properties
59
59
 
60
- ```html
61
- <!-- Dialog / Modal -->
62
- <div
63
- role="dialog"
64
- aria-modal="true"
65
- aria-labelledby="dialog-title"
66
- aria-describedby="dialog-desc"
67
- >
68
- <h2 id="dialog-title">Confirm Delete</h2>
69
- <p id="dialog-desc">This action cannot be undone.</p>
70
- <button>Cancel</button>
71
- <button>Delete</button>
72
- </div>
73
-
74
- <!-- Tabs -->
75
- <div role="tablist" aria-label="Product info">
76
- <button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1">
77
- Description
78
- </button>
79
- <button role="tab" id="tab-2" aria-selected="false" aria-controls="panel-2" tabindex="-1">
80
- Reviews
81
- </button>
82
- </div>
83
- <div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
84
- <p>Product description...</p>
85
- </div>
86
- <div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
87
- <p>Product reviews...</p>
88
- </div>
89
-
90
- <!-- Live region for dynamic content -->
91
- <div aria-live="polite" aria-atomic="true">
92
- <!-- Screen reader announces changes here -->
93
- <p>3 items in your cart</p>
94
- </div>
95
-
96
- <!-- Status messages -->
97
- <div role="status" aria-live="polite">
98
- Form submitted successfully.
99
- </div>
100
- <div role="alert" aria-live="assertive">
101
- Error: Please correct the highlighted fields.
102
- </div>
103
-
104
- <!-- Loading state -->
105
- <div aria-busy="true" aria-live="polite">
106
- Loading results...
107
- </div>
108
-
109
- <!-- Expandable content -->
110
- <button aria-expanded="false" aria-controls="details-section">
111
- Show Details
112
- </button>
113
- <div id="details-section" hidden>
114
- <p>Additional information...</p>
115
- </div>
116
- ```
60
+ Code for this section: [Web accessibility examples](references/a11y-examples.md).
117
61
 
118
62
  ## Form Accessibility
119
63
 
120
- ```html
121
- <form aria-label="Contact form">
122
- <!-- Visible label (preferred) -->
123
- <div>
124
- <label for="name">Full Name <span aria-hidden="true">*</span></label>
125
- <input
126
- type="text"
127
- id="name"
128
- name="name"
129
- required
130
- aria-required="true"
131
- autocomplete="name"
132
- />
133
- </div>
134
-
135
- <!-- Error state -->
136
- <div>
137
- <label for="email">Email <span aria-hidden="true">*</span></label>
138
- <input
139
- type="email"
140
- id="email"
141
- name="email"
142
- required
143
- aria-required="true"
144
- aria-invalid="true"
145
- aria-describedby="email-error email-hint"
146
- autocomplete="email"
147
- />
148
- <p id="email-hint" class="hint">We'll never share your email.</p>
149
- <p id="email-error" class="error" role="alert">
150
- Please enter a valid email address.
151
- </p>
152
- </div>
153
-
154
- <!-- Fieldset for related inputs -->
155
- <fieldset>
156
- <legend>Preferred contact method</legend>
157
- <label>
158
- <input type="radio" name="contact" value="email" /> Email
159
- </label>
160
- <label>
161
- <input type="radio" name="contact" value="phone" /> Phone
162
- </label>
163
- </fieldset>
164
-
165
- <button type="submit">Send Message</button>
166
- </form>
167
- ```
64
+ Code for this section: [Web accessibility examples](references/a11y-examples.md).
168
65
 
169
66
  ## Keyboard Navigation
170
67
 
171
- ```typescript
172
- // Focus trap for modals
173
- function trapFocus(element: HTMLElement) {
174
- const focusableSelectors = [
175
- 'a[href]', 'button:not([disabled])', 'input:not([disabled])',
176
- 'select:not([disabled])', 'textarea:not([disabled])',
177
- '[tabindex]:not([tabindex="-1"])',
178
- ];
179
- const focusableElements = element.querySelectorAll<HTMLElement>(
180
- focusableSelectors.join(',')
181
- );
182
- const firstFocusable = focusableElements[0];
183
- const lastFocusable = focusableElements[focusableElements.length - 1];
184
-
185
- element.addEventListener('keydown', (e) => {
186
- if (e.key !== 'Tab') return;
187
-
188
- if (e.shiftKey) {
189
- if (document.activeElement === firstFocusable) {
190
- e.preventDefault();
191
- lastFocusable.focus();
192
- }
193
- } else {
194
- if (document.activeElement === lastFocusable) {
195
- e.preventDefault();
196
- firstFocusable.focus();
197
- }
198
- }
199
- });
200
-
201
- firstFocusable?.focus();
202
- }
203
-
204
- // React: accessible keyboard handler
205
- function handleKeyDown(event: React.KeyboardEvent, action: () => void) {
206
- if (event.key === 'Enter' || event.key === ' ') {
207
- event.preventDefault();
208
- action();
209
- }
210
- }
211
-
212
- // Roving tabindex for arrow key navigation (toolbar, menu, listbox)
213
- function useRovingTabindex(items: HTMLElement[]) {
214
- let currentIndex = 0;
215
-
216
- items.forEach((item, index) => {
217
- item.tabIndex = index === 0 ? 0 : -1;
218
- item.addEventListener('keydown', (e) => {
219
- let newIndex = currentIndex;
220
- if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
221
- newIndex = (currentIndex + 1) % items.length;
222
- } else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
223
- newIndex = (currentIndex - 1 + items.length) % items.length;
224
- } else if (e.key === 'Home') {
225
- newIndex = 0;
226
- } else if (e.key === 'End') {
227
- newIndex = items.length - 1;
228
- } else {
229
- return;
230
- }
231
- e.preventDefault();
232
- items[currentIndex].tabIndex = -1;
233
- items[newIndex].tabIndex = 0;
234
- items[newIndex].focus();
235
- currentIndex = newIndex;
236
- });
237
- });
238
- }
239
- ```
68
+ Code for this section: [Web accessibility examples](references/a11y-examples.md).
240
69
 
241
70
  ## Color Contrast and Visual Design
242
71
 
@@ -374,3 +203,7 @@ test('homepage has no a11y violations', async ({ page }) => {
374
203
  | Tab order is wrong | Unstructured DOM or tabindex misuse | Restructure DOM to match visual order; remove tabindex > 0 |
375
204
  | Modal doesn't trap focus | No focus management | Implement focus trap and restore focus on close |
376
205
  | Live region not announced | Missing `aria-live` or wrong timing | Add `aria-live="polite"` and inject content after region exists in DOM |
206
+
207
+ ## References
208
+
209
+ - [Web accessibility examples](references/a11y-examples.md): open when building ARIA widgets, accessible forms, focus traps or roving tab index.
@@ -0,0 +1,192 @@
1
+ # Web accessibility examples
2
+
3
+ <!-- toc -->
4
+ - [ARIA Roles, States, and Properties](#aria-roles-states-and-properties)
5
+ - [Form Accessibility](#form-accessibility)
6
+ - [Keyboard Navigation](#keyboard-navigation)
7
+ <!-- /toc -->
8
+
9
+ Markup and script for ARIA widgets, accessible forms and keyboard navigation. SKILL.md keeps the principles, contrast rules, testing and troubleshooting.
10
+
11
+ ## ARIA Roles, States, and Properties
12
+
13
+ ```html
14
+ <!-- Dialog / Modal -->
15
+ <div
16
+ role="dialog"
17
+ aria-modal="true"
18
+ aria-labelledby="dialog-title"
19
+ aria-describedby="dialog-desc"
20
+ >
21
+ <h2 id="dialog-title">Confirm Delete</h2>
22
+ <p id="dialog-desc">This action cannot be undone.</p>
23
+ <button>Cancel</button>
24
+ <button>Delete</button>
25
+ </div>
26
+
27
+ <!-- Tabs -->
28
+ <div role="tablist" aria-label="Product info">
29
+ <button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1">
30
+ Description
31
+ </button>
32
+ <button role="tab" id="tab-2" aria-selected="false" aria-controls="panel-2" tabindex="-1">
33
+ Reviews
34
+ </button>
35
+ </div>
36
+ <div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
37
+ <p>Product description...</p>
38
+ </div>
39
+ <div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
40
+ <p>Product reviews...</p>
41
+ </div>
42
+
43
+ <!-- Live region for dynamic content -->
44
+ <div aria-live="polite" aria-atomic="true">
45
+ <!-- Screen reader announces changes here -->
46
+ <p>3 items in your cart</p>
47
+ </div>
48
+
49
+ <!-- Status messages -->
50
+ <div role="status" aria-live="polite">
51
+ Form submitted successfully.
52
+ </div>
53
+ <div role="alert" aria-live="assertive">
54
+ Error: Please correct the highlighted fields.
55
+ </div>
56
+
57
+ <!-- Loading state -->
58
+ <div aria-busy="true" aria-live="polite">
59
+ Loading results...
60
+ </div>
61
+
62
+ <!-- Expandable content -->
63
+ <button aria-expanded="false" aria-controls="details-section">
64
+ Show Details
65
+ </button>
66
+ <div id="details-section" hidden>
67
+ <p>Additional information...</p>
68
+ </div>
69
+ ```
70
+
71
+ ## Form Accessibility
72
+
73
+ ```html
74
+ <form aria-label="Contact form">
75
+ <!-- Visible label (preferred) -->
76
+ <div>
77
+ <label for="name">Full Name <span aria-hidden="true">*</span></label>
78
+ <input
79
+ type="text"
80
+ id="name"
81
+ name="name"
82
+ required
83
+ aria-required="true"
84
+ autocomplete="name"
85
+ />
86
+ </div>
87
+
88
+ <!-- Error state -->
89
+ <div>
90
+ <label for="email">Email <span aria-hidden="true">*</span></label>
91
+ <input
92
+ type="email"
93
+ id="email"
94
+ name="email"
95
+ required
96
+ aria-required="true"
97
+ aria-invalid="true"
98
+ aria-describedby="email-error email-hint"
99
+ autocomplete="email"
100
+ />
101
+ <p id="email-hint" class="hint">We'll never share your email.</p>
102
+ <p id="email-error" class="error" role="alert">
103
+ Please enter a valid email address.
104
+ </p>
105
+ </div>
106
+
107
+ <!-- Fieldset for related inputs -->
108
+ <fieldset>
109
+ <legend>Preferred contact method</legend>
110
+ <label>
111
+ <input type="radio" name="contact" value="email" /> Email
112
+ </label>
113
+ <label>
114
+ <input type="radio" name="contact" value="phone" /> Phone
115
+ </label>
116
+ </fieldset>
117
+
118
+ <button type="submit">Send Message</button>
119
+ </form>
120
+ ```
121
+
122
+ ## Keyboard Navigation
123
+
124
+ ```typescript
125
+ // Focus trap for modals
126
+ function trapFocus(element: HTMLElement) {
127
+ const focusableSelectors = [
128
+ 'a[href]', 'button:not([disabled])', 'input:not([disabled])',
129
+ 'select:not([disabled])', 'textarea:not([disabled])',
130
+ '[tabindex]:not([tabindex="-1"])',
131
+ ];
132
+ const focusableElements = element.querySelectorAll<HTMLElement>(
133
+ focusableSelectors.join(',')
134
+ );
135
+ const firstFocusable = focusableElements[0];
136
+ const lastFocusable = focusableElements[focusableElements.length - 1];
137
+
138
+ element.addEventListener('keydown', (e) => {
139
+ if (e.key !== 'Tab') return;
140
+
141
+ if (e.shiftKey) {
142
+ if (document.activeElement === firstFocusable) {
143
+ e.preventDefault();
144
+ lastFocusable.focus();
145
+ }
146
+ } else {
147
+ if (document.activeElement === lastFocusable) {
148
+ e.preventDefault();
149
+ firstFocusable.focus();
150
+ }
151
+ }
152
+ });
153
+
154
+ firstFocusable?.focus();
155
+ }
156
+
157
+ // React: accessible keyboard handler
158
+ function handleKeyDown(event: React.KeyboardEvent, action: () => void) {
159
+ if (event.key === 'Enter' || event.key === ' ') {
160
+ event.preventDefault();
161
+ action();
162
+ }
163
+ }
164
+
165
+ // Roving tabindex for arrow key navigation (toolbar, menu, listbox)
166
+ function useRovingTabindex(items: HTMLElement[]) {
167
+ let currentIndex = 0;
168
+
169
+ items.forEach((item, index) => {
170
+ item.tabIndex = index === 0 ? 0 : -1;
171
+ item.addEventListener('keydown', (e) => {
172
+ let newIndex = currentIndex;
173
+ if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
174
+ newIndex = (currentIndex + 1) % items.length;
175
+ } else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
176
+ newIndex = (currentIndex - 1 + items.length) % items.length;
177
+ } else if (e.key === 'Home') {
178
+ newIndex = 0;
179
+ } else if (e.key === 'End') {
180
+ newIndex = items.length - 1;
181
+ } else {
182
+ return;
183
+ }
184
+ e.preventDefault();
185
+ items[currentIndex].tabIndex = -1;
186
+ items[newIndex].tabIndex = 0;
187
+ items[newIndex].focus();
188
+ currentIndex = newIndex;
189
+ });
190
+ });
191
+ }
192
+ ```
@@ -159,42 +159,9 @@ module.exports = withBundleAnalyzer({
159
159
 
160
160
  ## Caching Strategies
161
161
 
162
- ```typescript
163
- // Service Worker caching with Workbox
164
- import { precacheAndRoute } from 'workbox-precaching';
165
- import { registerRoute } from 'workbox-routing';
166
- import { StaleWhileRevalidate, CacheFirst, NetworkFirst } from 'workbox-strategies';
167
- import { ExpirationPlugin } from 'workbox-expiration';
168
-
169
- // Precache static assets
170
- precacheAndRoute(self.__WB_MANIFEST);
171
-
172
- // Cache-first for images (long-lived)
173
- registerRoute(
174
- ({ request }) => request.destination === 'image',
175
- new CacheFirst({
176
- cacheName: 'images',
177
- plugins: [
178
- new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 }),
179
- ],
180
- })
181
- );
162
+ With a service worker (Workbox): precache static assets, cache-first for long-lived images, stale-while-revalidate for CSS and JS, network-first for API calls.
182
163
 
183
- // Stale-while-revalidate for CSS/JS
184
- registerRoute(
185
- ({ request }) => request.destination === 'style' || request.destination === 'script',
186
- new StaleWhileRevalidate({ cacheName: 'static-resources' })
187
- );
188
-
189
- // Network-first for API calls
190
- registerRoute(
191
- ({ url }) => url.pathname.startsWith('/api/'),
192
- new NetworkFirst({
193
- cacheName: 'api-cache',
194
- networkTimeoutSeconds: 3,
195
- })
196
- );
197
- ```
164
+ Code: [Caching Strategies](references/code-examples.md#caching-strategies).
198
165
 
199
166
  ### HTTP Caching Headers
200
167
 
@@ -254,38 +221,9 @@ export async function GET() {
254
221
 
255
222
  ## Reducing INP (Interaction to Next Paint)
256
223
 
257
- ```typescript
258
- // Break up long tasks with scheduler.yield() or setTimeout
259
- async function processLargeList(items: Item[]) {
260
- const CHUNK_SIZE = 50;
261
- for (let i = 0; i < items.length; i += CHUNK_SIZE) {
262
- const chunk = items.slice(i, i + CHUNK_SIZE);
263
- processChunk(chunk);
264
-
265
- // Yield to the browser between chunks
266
- if ('scheduler' in globalThis && 'yield' in scheduler) {
267
- await scheduler.yield();
268
- } else {
269
- await new Promise((resolve) => setTimeout(resolve, 0));
270
- }
271
- }
272
- }
273
-
274
- // Debounce expensive input handlers
275
- import { useDebouncedCallback } from 'use-debounce';
276
-
277
- function SearchInput() {
278
- const handleSearch = useDebouncedCallback((term: string) => {
279
- performSearch(term); // Expensive operation
280
- }, 300);
281
-
282
- return <input onChange={(e) => handleSearch(e.target.value)} />;
283
- }
224
+ Break long tasks into chunks and yield to the browser between them, debounce expensive input handlers, and use CSS containment on complex layouts.
284
225
 
285
- // Use CSS containment for complex layouts
286
- // .card { contain: content; }
287
- // .sidebar { contain: layout style; }
288
- ```
226
+ Code: [Reducing INP (Interaction to Next Paint)](references/code-examples.md#reducing-inp-interaction-to-next-paint).
289
227
 
290
228
  ## Lighthouse Audit Checklist
291
229
 
@@ -346,3 +284,7 @@ npx lhci autorun --config=lighthouserc.json
346
284
  | Large bundle | Importing entire libraries | Use tree-shakeable imports, analyze with bundle visualizer |
347
285
  | Slow TTFB | No server caching, slow DB | Add CDN, implement server-side caching, optimize queries |
348
286
  | Flash of unstyled text | Web font loading | Use `font-display: swap`, preload font files |
287
+
288
+ ## References
289
+
290
+ - [Code examples](references/code-examples.md): open for the full code behind Caching Strategies, Reducing INP (Interaction to Next Paint).
@@ -0,0 +1,77 @@
1
+ # Web Performance Code Examples
2
+
3
+ Full code samples for the SKILL.md sections that link here. The rules and the decision guidance stay in SKILL.md; each heading below matches the SKILL.md section the code belongs to.
4
+
5
+ ## Caching Strategies
6
+
7
+ ```typescript
8
+ // Service Worker caching with Workbox
9
+ import { precacheAndRoute } from 'workbox-precaching';
10
+ import { registerRoute } from 'workbox-routing';
11
+ import { StaleWhileRevalidate, CacheFirst, NetworkFirst } from 'workbox-strategies';
12
+ import { ExpirationPlugin } from 'workbox-expiration';
13
+
14
+ // Precache static assets
15
+ precacheAndRoute(self.__WB_MANIFEST);
16
+
17
+ // Cache-first for images (long-lived)
18
+ registerRoute(
19
+ ({ request }) => request.destination === 'image',
20
+ new CacheFirst({
21
+ cacheName: 'images',
22
+ plugins: [
23
+ new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 }),
24
+ ],
25
+ })
26
+ );
27
+
28
+ // Stale-while-revalidate for CSS/JS
29
+ registerRoute(
30
+ ({ request }) => request.destination === 'style' || request.destination === 'script',
31
+ new StaleWhileRevalidate({ cacheName: 'static-resources' })
32
+ );
33
+
34
+ // Network-first for API calls
35
+ registerRoute(
36
+ ({ url }) => url.pathname.startsWith('/api/'),
37
+ new NetworkFirst({
38
+ cacheName: 'api-cache',
39
+ networkTimeoutSeconds: 3,
40
+ })
41
+ );
42
+ ```
43
+
44
+ ## Reducing INP (Interaction to Next Paint)
45
+
46
+ ```typescript
47
+ // Break up long tasks with scheduler.yield() or setTimeout
48
+ async function processLargeList(items: Item[]) {
49
+ const CHUNK_SIZE = 50;
50
+ for (let i = 0; i < items.length; i += CHUNK_SIZE) {
51
+ const chunk = items.slice(i, i + CHUNK_SIZE);
52
+ processChunk(chunk);
53
+
54
+ // Yield to the browser between chunks
55
+ if ('scheduler' in globalThis && 'yield' in scheduler) {
56
+ await scheduler.yield();
57
+ } else {
58
+ await new Promise((resolve) => setTimeout(resolve, 0));
59
+ }
60
+ }
61
+ }
62
+
63
+ // Debounce expensive input handlers
64
+ import { useDebouncedCallback } from 'use-debounce';
65
+
66
+ function SearchInput() {
67
+ const handleSearch = useDebouncedCallback((term: string) => {
68
+ performSearch(term); // Expensive operation
69
+ }, 300);
70
+
71
+ return <input onChange={(e) => handleSearch(e.target.value)} />;
72
+ }
73
+
74
+ // Use CSS containment for complex layouts
75
+ // .card { contain: content; }
76
+ // .sidebar { contain: layout style; }
77
+ ```