opencode-agent-kit 1.0.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 (361) hide show
  1. package/README.md +796 -0
  2. package/bin/commands/init.mjs +221 -0
  3. package/bin/init.mjs +21 -0
  4. package/package.json +22 -0
  5. package/template/.opencode/agent-docs/backend/README.md +0 -0
  6. package/template/.opencode/agent-docs/backend/node/BACKEND_PATTERNS.md +82 -0
  7. package/template/.opencode/agent-docs/backend/node/BACKEND_QUICK_START.md +49 -0
  8. package/template/.opencode/agent-docs/frontend/next/README.md +0 -0
  9. package/template/.opencode/agent-docs/frontend/nuxt/API_PATTERNS.md +807 -0
  10. package/template/.opencode/agent-docs/frontend/nuxt/CHEATSHEET.md +676 -0
  11. package/template/.opencode/agent-docs/frontend/nuxt/COMPLETION_REPORT.md +613 -0
  12. package/template/.opencode/agent-docs/frontend/nuxt/EXAMPLES.md +956 -0
  13. package/template/.opencode/agent-docs/frontend/nuxt/INDEX.md +596 -0
  14. package/template/.opencode/agent-docs/frontend/nuxt/MCP_GUIDE.md +881 -0
  15. package/template/.opencode/agent-docs/frontend/nuxt/MENTOR_CURRICULUM_30_DAYS.md +256 -0
  16. package/template/.opencode/agent-docs/frontend/nuxt/MENTOR_CURRICULUM_CHECKLIST.md +156 -0
  17. package/template/.opencode/agent-docs/frontend/nuxt/MENTOR_WEEKLY_ASSIGNMENTS.md +191 -0
  18. package/template/.opencode/agent-docs/frontend/nuxt/QUICK_START.md +509 -0
  19. package/template/.opencode/agent-docs/frontend/nuxt/README.md +506 -0
  20. package/template/.opencode/agent-docs/frontend/nuxt/README_AGENTS.md +140 -0
  21. package/template/.opencode/agent-docs/frontend/nuxt/README_DOCS.md +65 -0
  22. package/template/.opencode/agent-docs/frontend/nuxt/SUMMARY.md +474 -0
  23. package/template/.opencode/agent-docs/frontend/nuxt/TEAM_OPERATING_GUIDE.md +54 -0
  24. package/template/.opencode/agent-docs/frontend/nuxt/TESTING_GUIDE.md +904 -0
  25. package/template/.opencode/agent-docs/frontend/nuxt/WORKFLOWS.md +758 -0
  26. package/template/.opencode/agent-docs/frontend/react/API_PATTERNS.md +187 -0
  27. package/template/.opencode/agent-docs/frontend/react/CHEATSHEET.md +87 -0
  28. package/template/.opencode/agent-docs/frontend/react/INDEX.md +45 -0
  29. package/template/.opencode/agent-docs/frontend/react/QUICK_START.md +43 -0
  30. package/template/.opencode/agent-docs/frontend/react/README.md +159 -0
  31. package/template/.opencode/agent-docs/frontend/vue/README.md +0 -0
  32. package/template/.opencode/agent-docs/mobile/android/README.md +45 -0
  33. package/template/.opencode/agent-docs/mobile/flutter/README.md +44 -0
  34. package/template/.opencode/agents/android-developer.md +418 -0
  35. package/template/.opencode/agents/code-igniter-3-fullstack.md +345 -0
  36. package/template/.opencode/agents/code-reviewer.md +517 -0
  37. package/template/.opencode/agents/database-specialist.md +455 -0
  38. package/template/.opencode/agents/devops-specialist.md +562 -0
  39. package/template/.opencode/agents/flutter-developer.md +556 -0
  40. package/template/.opencode/agents/it-leader.md +911 -0
  41. package/template/.opencode/agents/laravel-advanced.md +691 -0
  42. package/template/.opencode/agents/node-backend-developer.md +343 -0
  43. package/template/.opencode/agents/nuxt-frontend-developer-mentor.md +402 -0
  44. package/template/.opencode/agents/nuxt-frontend-developer.md +1573 -0
  45. package/template/.opencode/agents/react-frontend-developer.md +1017 -0
  46. package/template/.opencode/agents/seo-specialist.md +681 -0
  47. package/template/.opencode/agents/ui-ux-designer.md +783 -0
  48. package/template/.opencode/commands/android-build/command.md +25 -0
  49. package/template/.opencode/commands/android-test/command.md +23 -0
  50. package/template/.opencode/commands/build-fix.md +29 -0
  51. package/template/.opencode/commands/checkpoint.md +74 -0
  52. package/template/.opencode/commands/code-review.md +40 -0
  53. package/template/.opencode/commands/e2e.md +363 -0
  54. package/template/.opencode/commands/eval.md +120 -0
  55. package/template/.opencode/commands/evolve.md +193 -0
  56. package/template/.opencode/commands/flutter-build/command.md +25 -0
  57. package/template/.opencode/commands/flutter-test/command.md +24 -0
  58. package/template/.opencode/commands/go-build.md +183 -0
  59. package/template/.opencode/commands/go-review.md +148 -0
  60. package/template/.opencode/commands/go-test.md +268 -0
  61. package/template/.opencode/commands/gpc-release/command.md +30 -0
  62. package/template/.opencode/commands/instinct-export.md +91 -0
  63. package/template/.opencode/commands/instinct-import.md +142 -0
  64. package/template/.opencode/commands/instinct-status.md +86 -0
  65. package/template/.opencode/commands/learn.md +70 -0
  66. package/template/.opencode/commands/multi-backend.md +158 -0
  67. package/template/.opencode/commands/multi-execute.md +310 -0
  68. package/template/.opencode/commands/multi-frontend.md +158 -0
  69. package/template/.opencode/commands/multi-plan.md +261 -0
  70. package/template/.opencode/commands/multi-workflow.md +183 -0
  71. package/template/.opencode/commands/orchestrate.md +172 -0
  72. package/template/.opencode/commands/plan.md +113 -0
  73. package/template/.opencode/commands/pm2.md +271 -0
  74. package/template/.opencode/commands/python-review.md +297 -0
  75. package/template/.opencode/commands/refactor-clean.md +28 -0
  76. package/template/.opencode/commands/sessions.md +305 -0
  77. package/template/.opencode/commands/setup-pm.md +80 -0
  78. package/template/.opencode/commands/skill-create.md +174 -0
  79. package/template/.opencode/commands/tdd.md +326 -0
  80. package/template/.opencode/commands/test-coverage.md +27 -0
  81. package/template/.opencode/commands/update-codemaps.md +17 -0
  82. package/template/.opencode/commands/update-docs.md +31 -0
  83. package/template/.opencode/commands/verify.md +59 -0
  84. package/template/.opencode/config.example.json +309 -0
  85. package/template/.opencode/config.json +341 -0
  86. package/template/.opencode/contexts/dev.md +20 -0
  87. package/template/.opencode/contexts/research.md +26 -0
  88. package/template/.opencode/contexts/review.md +22 -0
  89. package/template/.opencode/hooks/hooks.json +169 -0
  90. package/template/.opencode/instructions/INSTRUCTIONS.md +388 -0
  91. package/template/.opencode/package.json +5 -0
  92. package/template/.opencode/rules/README.md +82 -0
  93. package/template/.opencode/rules/android/gradle.md +62 -0
  94. package/template/.opencode/rules/android/testing.md +27 -0
  95. package/template/.opencode/rules/common/agents.md +49 -0
  96. package/template/.opencode/rules/common/coding-style.md +48 -0
  97. package/template/.opencode/rules/common/git-workflow.md +45 -0
  98. package/template/.opencode/rules/common/hooks.md +30 -0
  99. package/template/.opencode/rules/common/patterns.md +31 -0
  100. package/template/.opencode/rules/common/performance.md +55 -0
  101. package/template/.opencode/rules/common/security.md +29 -0
  102. package/template/.opencode/rules/common/testing.md +29 -0
  103. package/template/.opencode/rules/flutter/state-management.md +57 -0
  104. package/template/.opencode/rules/flutter/testing.md +42 -0
  105. package/template/.opencode/rules/golang/coding-style.md +26 -0
  106. package/template/.opencode/rules/golang/hooks.md +11 -0
  107. package/template/.opencode/rules/golang/patterns.md +39 -0
  108. package/template/.opencode/rules/golang/security.md +28 -0
  109. package/template/.opencode/rules/golang/testing.md +25 -0
  110. package/template/.opencode/rules/mobile/performance.md +36 -0
  111. package/template/.opencode/rules/python/coding-style.md +37 -0
  112. package/template/.opencode/rules/python/hooks.md +14 -0
  113. package/template/.opencode/rules/python/patterns.md +34 -0
  114. package/template/.opencode/rules/python/security.md +25 -0
  115. package/template/.opencode/rules/python/testing.md +33 -0
  116. package/template/.opencode/rules/typescript/coding-style.md +58 -0
  117. package/template/.opencode/rules/typescript/hooks.md +15 -0
  118. package/template/.opencode/rules/typescript/patterns.md +45 -0
  119. package/template/.opencode/rules/typescript/security.md +21 -0
  120. package/template/.opencode/rules/typescript/testing.md +11 -0
  121. package/template/.opencode/skills/api-documentation/SKILL.md +188 -0
  122. package/template/.opencode/skills/backend-patterns/SKILL.md +587 -0
  123. package/template/.opencode/skills/building-components/SKILL.md +37 -0
  124. package/template/.opencode/skills/building-components/references/accessibility.mdx +819 -0
  125. package/template/.opencode/skills/building-components/references/as-child.mdx +324 -0
  126. package/template/.opencode/skills/building-components/references/composition.mdx +239 -0
  127. package/template/.opencode/skills/building-components/references/data-attributes.mdx +413 -0
  128. package/template/.opencode/skills/building-components/references/definitions.mdx +258 -0
  129. package/template/.opencode/skills/building-components/references/design-tokens.mdx +57 -0
  130. package/template/.opencode/skills/building-components/references/docs.mdx +155 -0
  131. package/template/.opencode/skills/building-components/references/marketplaces.mdx +144 -0
  132. package/template/.opencode/skills/building-components/references/npm.mdx +166 -0
  133. package/template/.opencode/skills/building-components/references/polymorphism.mdx +583 -0
  134. package/template/.opencode/skills/building-components/references/principles.mdx +61 -0
  135. package/template/.opencode/skills/building-components/references/registry.mdx +169 -0
  136. package/template/.opencode/skills/building-components/references/state.mdx +99 -0
  137. package/template/.opencode/skills/building-components/references/styling.mdx +286 -0
  138. package/template/.opencode/skills/building-components/references/types.mdx +191 -0
  139. package/template/.opencode/skills/clickhouse-io/SKILL.md +429 -0
  140. package/template/.opencode/skills/coding-standards/SKILL.md +520 -0
  141. package/template/.opencode/skills/configure-ecc/SKILL.md +298 -0
  142. package/template/.opencode/skills/continuous-learning/SKILL.md +110 -0
  143. package/template/.opencode/skills/continuous-learning/config.json +18 -0
  144. package/template/.opencode/skills/continuous-learning/evaluate-session.sh +60 -0
  145. package/template/.opencode/skills/continuous-learning-v2/SKILL.md +284 -0
  146. package/template/.opencode/skills/continuous-learning-v2/agents/observer.md +137 -0
  147. package/template/.opencode/skills/continuous-learning-v2/agents/start-observer.sh +134 -0
  148. package/template/.opencode/skills/continuous-learning-v2/config.json +41 -0
  149. package/template/.opencode/skills/continuous-learning-v2/hooks/observe.sh +153 -0
  150. package/template/.opencode/skills/continuous-learning-v2/scripts/instinct-cli.py +489 -0
  151. package/template/.opencode/skills/continuous-learning-v2/scripts/test_parse_instinct.py +82 -0
  152. package/template/.opencode/skills/dart-add-unit-test/SKILL.md +122 -0
  153. package/template/.opencode/skills/dart-build-cli-app/SKILL.md +185 -0
  154. package/template/.opencode/skills/dart-collect-coverage/SKILL.md +141 -0
  155. package/template/.opencode/skills/dart-fix-runtime-errors/SKILL.md +166 -0
  156. package/template/.opencode/skills/dart-generate-test-mocks/SKILL.md +155 -0
  157. package/template/.opencode/skills/dart-migrate-to-checks-package/SKILL.md +126 -0
  158. package/template/.opencode/skills/dart-resolve-package-conflicts/SKILL.md +116 -0
  159. package/template/.opencode/skills/dart-run-static-analysis/SKILL.md +104 -0
  160. package/template/.opencode/skills/dart-use-pattern-matching/SKILL.md +146 -0
  161. package/template/.opencode/skills/django-patterns/SKILL.md +733 -0
  162. package/template/.opencode/skills/django-security/SKILL.md +592 -0
  163. package/template/.opencode/skills/django-tdd/SKILL.md +728 -0
  164. package/template/.opencode/skills/django-verification/SKILL.md +460 -0
  165. package/template/.opencode/skills/eval-harness/SKILL.md +227 -0
  166. package/template/.opencode/skills/firebase-basics/SKILL.md +103 -0
  167. package/template/.opencode/skills/firebase-basics/references/additional-skills.md +113 -0
  168. package/template/.opencode/skills/firebase-basics/references/cli-usage.md +31 -0
  169. package/template/.opencode/skills/firebase-basics/references/client-library-usage.md +45 -0
  170. package/template/.opencode/skills/firebase-basics/references/core-concepts.md +61 -0
  171. package/template/.opencode/skills/firebase-basics/references/iac-usage.md +40 -0
  172. package/template/.opencode/skills/firebase-basics/references/iam-security.md +74 -0
  173. package/template/.opencode/skills/firebase-basics/references/mcp-usage.md +63 -0
  174. package/template/.opencode/skills/flutter/SKILL.md +292 -0
  175. package/template/.opencode/skills/flutter-add-integration-test/SKILL.md +163 -0
  176. package/template/.opencode/skills/flutter-add-widget-preview/SKILL.md +145 -0
  177. package/template/.opencode/skills/flutter-add-widget-test/SKILL.md +154 -0
  178. package/template/.opencode/skills/flutter-apply-architecture-best-practices/SKILL.md +162 -0
  179. package/template/.opencode/skills/flutter-build-responsive-layout/SKILL.md +139 -0
  180. package/template/.opencode/skills/flutter-fix-layout-issues/SKILL.md +130 -0
  181. package/template/.opencode/skills/flutter-implement-json-serialization/SKILL.md +153 -0
  182. package/template/.opencode/skills/flutter-setup-declarative-routing/SKILL.md +255 -0
  183. package/template/.opencode/skills/flutter-setup-localization/SKILL.md +210 -0
  184. package/template/.opencode/skills/flutter-use-http-package/SKILL.md +174 -0
  185. package/template/.opencode/skills/frontend-design/SKILL.md +89 -0
  186. package/template/.opencode/skills/frontend-patterns/SKILL.md +631 -0
  187. package/template/.opencode/skills/golang-patterns/SKILL.md +673 -0
  188. package/template/.opencode/skills/golang-testing/SKILL.md +719 -0
  189. package/template/.opencode/skills/impeccable/SKILL.md +165 -0
  190. package/template/.opencode/skills/impeccable/agents/impeccable-asset-producer.md +101 -0
  191. package/template/.opencode/skills/impeccable/reference/adapt.md +190 -0
  192. package/template/.opencode/skills/impeccable/reference/animate.md +175 -0
  193. package/template/.opencode/skills/impeccable/reference/audit.md +133 -0
  194. package/template/.opencode/skills/impeccable/reference/bolder.md +113 -0
  195. package/template/.opencode/skills/impeccable/reference/brand.md +118 -0
  196. package/template/.opencode/skills/impeccable/reference/clarify.md +174 -0
  197. package/template/.opencode/skills/impeccable/reference/codex.md +105 -0
  198. package/template/.opencode/skills/impeccable/reference/cognitive-load.md +106 -0
  199. package/template/.opencode/skills/impeccable/reference/color-and-contrast.md +105 -0
  200. package/template/.opencode/skills/impeccable/reference/colorize.md +154 -0
  201. package/template/.opencode/skills/impeccable/reference/craft.md +123 -0
  202. package/template/.opencode/skills/impeccable/reference/critique.md +273 -0
  203. package/template/.opencode/skills/impeccable/reference/delight.md +302 -0
  204. package/template/.opencode/skills/impeccable/reference/distill.md +111 -0
  205. package/template/.opencode/skills/impeccable/reference/document.md +427 -0
  206. package/template/.opencode/skills/impeccable/reference/extract.md +69 -0
  207. package/template/.opencode/skills/impeccable/reference/harden.md +347 -0
  208. package/template/.opencode/skills/impeccable/reference/heuristics-scoring.md +234 -0
  209. package/template/.opencode/skills/impeccable/reference/interaction-design.md +195 -0
  210. package/template/.opencode/skills/impeccable/reference/layout.md +141 -0
  211. package/template/.opencode/skills/impeccable/reference/live.md +622 -0
  212. package/template/.opencode/skills/impeccable/reference/motion-design.md +109 -0
  213. package/template/.opencode/skills/impeccable/reference/onboard.md +234 -0
  214. package/template/.opencode/skills/impeccable/reference/optimize.md +258 -0
  215. package/template/.opencode/skills/impeccable/reference/overdrive.md +130 -0
  216. package/template/.opencode/skills/impeccable/reference/personas.md +179 -0
  217. package/template/.opencode/skills/impeccable/reference/polish.md +242 -0
  218. package/template/.opencode/skills/impeccable/reference/product.md +62 -0
  219. package/template/.opencode/skills/impeccable/reference/quieter.md +99 -0
  220. package/template/.opencode/skills/impeccable/reference/responsive-design.md +114 -0
  221. package/template/.opencode/skills/impeccable/reference/shape.md +165 -0
  222. package/template/.opencode/skills/impeccable/reference/spatial-design.md +100 -0
  223. package/template/.opencode/skills/impeccable/reference/teach.md +156 -0
  224. package/template/.opencode/skills/impeccable/reference/typeset.md +124 -0
  225. package/template/.opencode/skills/impeccable/reference/typography.md +159 -0
  226. package/template/.opencode/skills/impeccable/reference/ux-writing.md +107 -0
  227. package/template/.opencode/skills/impeccable/scripts/cleanup-deprecated.mjs +284 -0
  228. package/template/.opencode/skills/impeccable/scripts/command-metadata.json +94 -0
  229. package/template/.opencode/skills/impeccable/scripts/critique-storage.mjs +242 -0
  230. package/template/.opencode/skills/impeccable/scripts/design-parser.mjs +820 -0
  231. package/template/.opencode/skills/impeccable/scripts/detect-csp.mjs +198 -0
  232. package/template/.opencode/skills/impeccable/scripts/detect.mjs +21 -0
  233. package/template/.opencode/skills/impeccable/scripts/impeccable-paths.mjs +110 -0
  234. package/template/.opencode/skills/impeccable/scripts/is-generated.mjs +69 -0
  235. package/template/.opencode/skills/impeccable/scripts/live-accept.mjs +595 -0
  236. package/template/.opencode/skills/impeccable/scripts/live-browser-session.js +123 -0
  237. package/template/.opencode/skills/impeccable/scripts/live-browser.js +4860 -0
  238. package/template/.opencode/skills/impeccable/scripts/live-complete.mjs +75 -0
  239. package/template/.opencode/skills/impeccable/scripts/live-completion.mjs +18 -0
  240. package/template/.opencode/skills/impeccable/scripts/live-inject.mjs +446 -0
  241. package/template/.opencode/skills/impeccable/scripts/live-poll.mjs +200 -0
  242. package/template/.opencode/skills/impeccable/scripts/live-resume.mjs +48 -0
  243. package/template/.opencode/skills/impeccable/scripts/live-server.mjs +838 -0
  244. package/template/.opencode/skills/impeccable/scripts/live-session-store.mjs +254 -0
  245. package/template/.opencode/skills/impeccable/scripts/live-status.mjs +47 -0
  246. package/template/.opencode/skills/impeccable/scripts/live-wrap.mjs +632 -0
  247. package/template/.opencode/skills/impeccable/scripts/live.mjs +247 -0
  248. package/template/.opencode/skills/impeccable/scripts/load-context.mjs +141 -0
  249. package/template/.opencode/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
  250. package/template/.opencode/skills/impeccable/scripts/pin.mjs +214 -0
  251. package/template/.opencode/skills/iterative-retrieval/SKILL.md +202 -0
  252. package/template/.opencode/skills/java-coding-standards/SKILL.md +138 -0
  253. package/template/.opencode/skills/jetpack-compose/.skillfish.json +10 -0
  254. package/template/.opencode/skills/jetpack-compose/SKILL.md +420 -0
  255. package/template/.opencode/skills/jpa-patterns/SKILL.md +141 -0
  256. package/template/.opencode/skills/nutrient-document-processing/SKILL.md +165 -0
  257. package/template/.opencode/skills/nuxt-ui/SKILL.md +334 -0
  258. package/template/.opencode/skills/nuxt-ui/references/components.md +377 -0
  259. package/template/.opencode/skills/nuxt-ui/references/composables.md +127 -0
  260. package/template/.opencode/skills/nuxt-ui/references/layouts/chat.md +266 -0
  261. package/template/.opencode/skills/nuxt-ui/references/layouts/dashboard.md +220 -0
  262. package/template/.opencode/skills/nuxt-ui/references/layouts/docs.md +141 -0
  263. package/template/.opencode/skills/nuxt-ui/references/layouts/editor.md +168 -0
  264. package/template/.opencode/skills/nuxt-ui/references/layouts/page.md +260 -0
  265. package/template/.opencode/skills/nuxt-ui/references/theming.md +427 -0
  266. package/template/.opencode/skills/postgres-patterns/SKILL.md +146 -0
  267. package/template/.opencode/skills/project-guidelines-example/SKILL.md +345 -0
  268. package/template/.opencode/skills/python-patterns/SKILL.md +749 -0
  269. package/template/.opencode/skills/python-testing/SKILL.md +815 -0
  270. package/template/.opencode/skills/security-review/SKILL.md +494 -0
  271. package/template/.opencode/skills/security-review/cloud-infrastructure-security.md +361 -0
  272. package/template/.opencode/skills/shadcn-ui/README.md +248 -0
  273. package/template/.opencode/skills/shadcn-ui/SKILL.md +326 -0
  274. package/template/.opencode/skills/shadcn-ui/examples/auth-layout.tsx +177 -0
  275. package/template/.opencode/skills/shadcn-ui/examples/data-table.tsx +313 -0
  276. package/template/.opencode/skills/shadcn-ui/examples/form-pattern.tsx +177 -0
  277. package/template/.opencode/skills/shadcn-ui/resources/component-catalog.md +481 -0
  278. package/template/.opencode/skills/shadcn-ui/resources/customization-guide.md +516 -0
  279. package/template/.opencode/skills/shadcn-ui/resources/migration-guide.md +463 -0
  280. package/template/.opencode/skills/shadcn-ui/resources/setup-guide.md +412 -0
  281. package/template/.opencode/skills/shadcn-ui/scripts/verify-setup.sh +134 -0
  282. package/template/.opencode/skills/springboot-patterns/SKILL.md +304 -0
  283. package/template/.opencode/skills/springboot-security/SKILL.md +119 -0
  284. package/template/.opencode/skills/springboot-tdd/SKILL.md +157 -0
  285. package/template/.opencode/skills/springboot-verification/SKILL.md +100 -0
  286. package/template/.opencode/skills/strategic-compact/SKILL.md +63 -0
  287. package/template/.opencode/skills/strategic-compact/suggest-compact.sh +52 -0
  288. package/template/.opencode/skills/tdd-workflow/SKILL.md +409 -0
  289. package/template/.opencode/skills/vercel-composition-patterns/AGENTS.md +946 -0
  290. package/template/.opencode/skills/vercel-composition-patterns/SKILL.md +89 -0
  291. package/template/.opencode/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  292. package/template/.opencode/skills/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
  293. package/template/.opencode/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  294. package/template/.opencode/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
  295. package/template/.opencode/skills/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
  296. package/template/.opencode/skills/vercel-composition-patterns/rules/state-context-interface.md +191 -0
  297. package/template/.opencode/skills/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
  298. package/template/.opencode/skills/vercel-composition-patterns/rules/state-lift-state.md +125 -0
  299. package/template/.opencode/skills/vercel-react-best-practices/AGENTS.md +2934 -0
  300. package/template/.opencode/skills/vercel-react-best-practices/SKILL.md +136 -0
  301. package/template/.opencode/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  302. package/template/.opencode/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
  303. package/template/.opencode/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
  304. package/template/.opencode/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
  305. package/template/.opencode/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
  306. package/template/.opencode/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
  307. package/template/.opencode/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
  308. package/template/.opencode/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
  309. package/template/.opencode/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
  310. package/template/.opencode/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
  311. package/template/.opencode/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
  312. package/template/.opencode/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  313. package/template/.opencode/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
  314. package/template/.opencode/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
  315. package/template/.opencode/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
  316. package/template/.opencode/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
  317. package/template/.opencode/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
  318. package/template/.opencode/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
  319. package/template/.opencode/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
  320. package/template/.opencode/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
  321. package/template/.opencode/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
  322. package/template/.opencode/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
  323. package/template/.opencode/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
  324. package/template/.opencode/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
  325. package/template/.opencode/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
  326. package/template/.opencode/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
  327. package/template/.opencode/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
  328. package/template/.opencode/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
  329. package/template/.opencode/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
  330. package/template/.opencode/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
  331. package/template/.opencode/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  332. package/template/.opencode/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
  333. package/template/.opencode/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
  334. package/template/.opencode/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  335. package/template/.opencode/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  336. package/template/.opencode/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  337. package/template/.opencode/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
  338. package/template/.opencode/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  339. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
  340. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
  341. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  342. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
  343. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
  344. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  345. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  346. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
  347. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  348. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  349. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
  350. package/template/.opencode/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  351. package/template/.opencode/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
  352. package/template/.opencode/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
  353. package/template/.opencode/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
  354. package/template/.opencode/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
  355. package/template/.opencode/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
  356. package/template/.opencode/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
  357. package/template/.opencode/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
  358. package/template/.opencode/skills/verification-loop/SKILL.md +120 -0
  359. package/template/.opencode/skills/web-design-guidelines/SKILL.md +39 -0
  360. package/template/AGENTS.md +32 -0
  361. package/template/opencode.json +354 -0
@@ -0,0 +1,819 @@
1
+ ---
2
+ title: Accessibility
3
+ description: Building components that are usable by everyone, including users with disabilities who rely on assistive technologies.
4
+ type: guide
5
+ summary: Semantic HTML, keyboard navigation, ARIA patterns, focus management, color contrast, and common accessibility pitfalls.
6
+ prerequisites:
7
+ - /definitions
8
+ - /composition
9
+ related:
10
+ - /data-attributes
11
+ - /types
12
+ ---
13
+
14
+ Accessibility (a11y) is not an optional feature—it's a fundamental requirement for modern web components. Every component must be usable by everyone, including people with visual, motor, auditory, or cognitive disabilities.
15
+
16
+ This guide is a non-exhaustive list of accessibility principles and patterns that you should follow when building components. It's not a comprehensive guide, but it should give you a sense of the types of issues you should be aware of.
17
+
18
+ If you use a linter with strong accessibility rules like [Ultracite](https://www.ultracite.ai), these types of issues will likely be caught automatically, but it's still important to understand the principles.
19
+
20
+ ## Core Principles
21
+
22
+ ### 1. Semantic HTML First
23
+
24
+ Always start with the most appropriate HTML element. Semantic HTML provides built-in accessibility features that custom implementations often miss.
25
+
26
+ ```tsx
27
+ // ❌ Don't reinvent the wheel
28
+ <div onClick={handleClick} className="button">
29
+ Click me
30
+ </div>
31
+
32
+ // ✅ Use semantic elements
33
+ <button onClick={handleClick}>
34
+ Click me
35
+ </button>
36
+ ```
37
+
38
+ Semantic elements come with proper role announcements, keyboard interaction, focus management, and form participation.
39
+
40
+ ### 2. Keyboard Navigation
41
+
42
+ Every interactive element must be keyboard accessible. Users should be able to navigate, activate, and interact with all functionality using only a keyboard.
43
+
44
+ ```tsx
45
+ // ✅ Complete keyboard support
46
+ function Menu() {
47
+ const handleKeyDown = (e: React.KeyboardEvent) => {
48
+ switch (e.key) {
49
+ case "ArrowDown":
50
+ focusNextItem();
51
+ break;
52
+ case "ArrowUp":
53
+ focusPreviousItem();
54
+ break;
55
+ case "Home":
56
+ focusFirstItem();
57
+ break;
58
+ case "End":
59
+ focusLastItem();
60
+ break;
61
+ case "Escape":
62
+ closeMenu();
63
+ break;
64
+ }
65
+ };
66
+
67
+ return (
68
+ <div role="menu" onKeyDown={handleKeyDown}>
69
+ {/* menu items */}
70
+ </div>
71
+ );
72
+ }
73
+ ```
74
+
75
+ ### 3. Screen Reader Support
76
+
77
+ Ensure all content and interactions are announced properly to screen readers using ARIA attributes when necessary.
78
+
79
+ ```tsx
80
+ // ✅ Proper ARIA labeling
81
+ <nav aria-label="Main navigation">
82
+ <ul>
83
+ <li><a href="/" aria-current="page">Home</a></li>
84
+ <li><a href="/about">About</a></li>
85
+ </ul>
86
+ </nav>
87
+
88
+ // ✅ Dynamic content announcements
89
+ <div aria-live="polite" aria-atomic="true">
90
+ {isLoading && <span>Loading results...</span>}
91
+ {results && <span>{results.length} results found</span>}
92
+ </div>
93
+ ```
94
+
95
+ ### 4. Visual Accessibility
96
+
97
+ Support users with visual impairments through proper contrast, focus indicators, and responsive text sizing.
98
+
99
+ ```css
100
+ /* ✅ Visible focus indicators */
101
+ button:focus-visible {
102
+ outline: 2px solid var(--color-focus);
103
+ outline-offset: 2px;
104
+ }
105
+
106
+ /* ✅ Sufficient color contrast (4.5:1 for normal text, 3:1 for large text) */
107
+ .text {
108
+ color: #333; /* Against white: 12.6:1 ratio */
109
+ background: white;
110
+ }
111
+
112
+ /* ✅ Responsive text sizing */
113
+ .text {
114
+ font-size: 1rem; /* Respects user preferences */
115
+ }
116
+ ```
117
+
118
+ ## ARIA Patterns
119
+
120
+ ### Understanding ARIA
121
+
122
+ ARIA (Accessible Rich Internet Applications) provides semantic information about elements to assistive technologies. Use ARIA to enhance, not replace, semantic HTML.
123
+
124
+ It has a few rules that you should follow:
125
+
126
+ 1. Don't use ARIA if you can use semantic HTML
127
+ 2. Don't change native semantics unless necessary
128
+ 3. All interactive elements must be keyboard accessible
129
+ 4. Don't hide focusable elements from assistive technologies
130
+ 5. All interactive elements must have accessible names
131
+
132
+ ### Common ARIA Attributes
133
+
134
+ #### Roles
135
+
136
+ Define what an element is:
137
+
138
+ ```tsx
139
+ // Widget roles
140
+ <div role="button" tabIndex={0} onClick={handleClick}>
141
+ Custom Button
142
+ </div>
143
+
144
+ // Landmark roles
145
+ <div role="navigation" aria-label="Breadcrumb">
146
+ {/* breadcrumb items */}
147
+ </div>
148
+
149
+ // Live region roles
150
+ <div role="alert">
151
+ Error: Invalid email address
152
+ </div>
153
+ ```
154
+
155
+ #### States
156
+
157
+ Describe the current state of an element:
158
+
159
+ ```tsx
160
+ // Checked state
161
+ <div
162
+ role="checkbox"
163
+ aria-checked={isChecked}
164
+ tabIndex={0}
165
+ >
166
+ Accept terms
167
+ </div>
168
+
169
+ // Expanded state
170
+ <button
171
+ aria-expanded={isOpen}
172
+ aria-controls="panel-1"
173
+ >
174
+ Toggle Panel
175
+ </button>
176
+ <div id="panel-1" hidden={!isOpen}>
177
+ Panel content
178
+ </div>
179
+
180
+ // Selected state
181
+ <li
182
+ role="option"
183
+ aria-selected={isSelected}
184
+ >
185
+ Option 1
186
+ </li>
187
+ ```
188
+
189
+ #### Properties
190
+
191
+ Provide additional information:
192
+
193
+ ```tsx
194
+ // Labels and descriptions
195
+ <input
196
+ aria-label="Search"
197
+ aria-describedby="search-help"
198
+ type="search"
199
+ />
200
+ <span id="search-help">Press Enter to search</span>
201
+
202
+ // Relationships
203
+ <button aria-controls="modal-1">Open Modal</button>
204
+ <div id="modal-1" role="dialog">{/* modal content */}</div>
205
+
206
+ // Required and invalid
207
+ <input
208
+ aria-required="true"
209
+ aria-invalid={hasError}
210
+ aria-errormessage="email-error"
211
+ />
212
+ <span id="email-error">Please enter a valid email</span>
213
+ ```
214
+
215
+ ## Component Patterns
216
+
217
+ ### Modal/Dialog
218
+
219
+ Modals require careful focus management and keyboard trapping:
220
+
221
+ ```tsx
222
+ function Modal({ isOpen, onClose, children }) {
223
+ const modalRef = useRef<HTMLDivElement>(null);
224
+ const previousFocus = useRef<HTMLElement | null>(null);
225
+
226
+ useEffect(() => {
227
+ if (isOpen) {
228
+ // Store current focus
229
+ previousFocus.current = document.activeElement as HTMLElement;
230
+
231
+ // Focus first focusable element in modal
232
+ const firstFocusable = modalRef.current?.querySelector<HTMLElement>(
233
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
234
+ );
235
+ firstFocusable?.focus();
236
+
237
+ // Prevent body scroll
238
+ document.body.style.overflow = "hidden";
239
+ } else {
240
+ // Restore focus
241
+ previousFocus.current?.focus();
242
+ document.body.style.overflow = "";
243
+ }
244
+
245
+ return () => {
246
+ document.body.style.overflow = "";
247
+ };
248
+ }, [isOpen]);
249
+
250
+ const handleKeyDown = (e: React.KeyboardEvent) => {
251
+ if (e.key === "Escape") {
252
+ onClose();
253
+ }
254
+
255
+ if (e.key === "Tab") {
256
+ // Trap focus within modal
257
+ const focusables = modalRef.current?.querySelectorAll<HTMLElement>(
258
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
259
+ );
260
+
261
+ if (focusables && focusables.length > 0) {
262
+ const firstFocusable = focusables[0];
263
+ const lastFocusable = focusables[focusables.length - 1];
264
+
265
+ if (e.shiftKey && document.activeElement === firstFocusable) {
266
+ e.preventDefault();
267
+ lastFocusable.focus();
268
+ } else if (!e.shiftKey && document.activeElement === lastFocusable) {
269
+ e.preventDefault();
270
+ firstFocusable.focus();
271
+ }
272
+ }
273
+ }
274
+ };
275
+
276
+ if (!isOpen) return null;
277
+
278
+ return (
279
+ <div
280
+ role="dialog"
281
+ aria-modal="true"
282
+ aria-labelledby="modal-title"
283
+ ref={modalRef}
284
+ onKeyDown={handleKeyDown}
285
+ className="modal"
286
+ >
287
+ <button
288
+ onClick={onClose}
289
+ aria-label="Close dialog"
290
+ className="close-button"
291
+ >
292
+ ×
293
+ </button>
294
+ {children}
295
+ </div>
296
+ );
297
+ }
298
+ ```
299
+
300
+ ### Dropdown Menu
301
+
302
+ Dropdowns need proper ARIA attributes and keyboard navigation:
303
+
304
+ ```tsx
305
+ function DropdownMenu({ items }) {
306
+ const [isOpen, setIsOpen] = useState(false);
307
+ const [selectedIndex, setSelectedIndex] = useState(-1);
308
+ const menuRef = useRef<HTMLUListElement>(null);
309
+
310
+ const handleKeyDown = (e: React.KeyboardEvent) => {
311
+ switch (e.key) {
312
+ case "ArrowDown":
313
+ e.preventDefault();
314
+ if (!isOpen) {
315
+ setIsOpen(true);
316
+ setSelectedIndex(0);
317
+ } else {
318
+ setSelectedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0));
319
+ }
320
+ break;
321
+
322
+ case "ArrowUp":
323
+ e.preventDefault();
324
+ if (isOpen) {
325
+ setSelectedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1));
326
+ }
327
+ break;
328
+
329
+ case "Enter":
330
+ case " ":
331
+ e.preventDefault();
332
+ if (!isOpen) {
333
+ setIsOpen(true);
334
+ } else if (selectedIndex >= 0) {
335
+ items[selectedIndex].onClick();
336
+ setIsOpen(false);
337
+ }
338
+ break;
339
+
340
+ case "Escape":
341
+ setIsOpen(false);
342
+ setSelectedIndex(-1);
343
+ break;
344
+ }
345
+ };
346
+
347
+ return (
348
+ <div className="dropdown">
349
+ <button
350
+ aria-haspopup="true"
351
+ aria-expanded={isOpen}
352
+ aria-controls="dropdown-menu"
353
+ onKeyDown={handleKeyDown}
354
+ onClick={() => setIsOpen(!isOpen)}
355
+ >
356
+ Menu
357
+ </button>
358
+
359
+ {isOpen && (
360
+ <ul
361
+ id="dropdown-menu"
362
+ role="menu"
363
+ ref={menuRef}
364
+ onKeyDown={handleKeyDown}
365
+ >
366
+ {items.map((item, index) => (
367
+ <li
368
+ key={item.id}
369
+ role="menuitem"
370
+ tabIndex={-1}
371
+ aria-selected={index === selectedIndex}
372
+ onClick={() => {
373
+ item.onClick();
374
+ setIsOpen(false);
375
+ }}
376
+ >
377
+ {item.label}
378
+ </li>
379
+ ))}
380
+ </ul>
381
+ )}
382
+ </div>
383
+ );
384
+ }
385
+ ```
386
+
387
+ ### Tabs
388
+
389
+ Tab interfaces require specific ARIA patterns and keyboard navigation:
390
+
391
+ ```tsx
392
+ function Tabs({ tabs, defaultTab = 0 }) {
393
+ const [activeTab, setActiveTab] = useState(defaultTab);
394
+
395
+ const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
396
+ let newIndex = index;
397
+
398
+ switch (e.key) {
399
+ case "ArrowLeft":
400
+ newIndex = index > 0 ? index - 1 : tabs.length - 1;
401
+ break;
402
+ case "ArrowRight":
403
+ newIndex = index < tabs.length - 1 ? index + 1 : 0;
404
+ break;
405
+ case "Home":
406
+ newIndex = 0;
407
+ break;
408
+ case "End":
409
+ newIndex = tabs.length - 1;
410
+ break;
411
+ default:
412
+ return;
413
+ }
414
+
415
+ e.preventDefault();
416
+ setActiveTab(newIndex);
417
+
418
+ // Focus the newly selected tab
419
+ const tabElement = document.getElementById(`tab-${newIndex}`);
420
+ tabElement?.focus();
421
+ };
422
+
423
+ return (
424
+ <div className="tabs">
425
+ <div role="tablist" aria-label="Tabs">
426
+ {tabs.map((tab, index) => (
427
+ <button
428
+ key={tab.id}
429
+ id={`tab-${index}`}
430
+ role="tab"
431
+ aria-selected={activeTab === index}
432
+ aria-controls={`panel-${index}`}
433
+ tabIndex={activeTab === index ? 0 : -1}
434
+ onClick={() => setActiveTab(index)}
435
+ onKeyDown={(e) => handleKeyDown(e, index)}
436
+ >
437
+ {tab.label}
438
+ </button>
439
+ ))}
440
+ </div>
441
+
442
+ {tabs.map((tab, index) => (
443
+ <div
444
+ key={tab.id}
445
+ id={`panel-${index}`}
446
+ role="tabpanel"
447
+ aria-labelledby={`tab-${index}`}
448
+ hidden={activeTab !== index}
449
+ tabIndex={0}
450
+ >
451
+ {tab.content}
452
+ </div>
453
+ ))}
454
+ </div>
455
+ );
456
+ }
457
+ ```
458
+
459
+ ### Forms
460
+
461
+ Forms need clear labels, error messages, and validation feedback:
462
+
463
+ ```tsx
464
+ function AccessibleForm() {
465
+ const [errors, setErrors] = useState({});
466
+
467
+ return (
468
+ <form aria-label="Contact form">
469
+ <div className="form-group">
470
+ <label htmlFor="email">
471
+ Email Address
472
+ <span aria-label="required">*</span>
473
+ </label>
474
+ <input
475
+ id="email"
476
+ type="email"
477
+ aria-required="true"
478
+ aria-invalid={!!errors.email}
479
+ aria-describedby={errors.email ? "email-error" : "email-help"}
480
+ />
481
+ <span id="email-help" className="help-text">
482
+ We'll never share your email
483
+ </span>
484
+ {errors.email && (
485
+ <span id="email-error" role="alert" className="error">
486
+ {errors.email}
487
+ </span>
488
+ )}
489
+ </div>
490
+
491
+ <fieldset>
492
+ <legend>Notification Preferences</legend>
493
+ <div>
494
+ <input
495
+ id="notify-email"
496
+ type="checkbox"
497
+ name="notifications"
498
+ value="email"
499
+ />
500
+ <label htmlFor="notify-email">Email notifications</label>
501
+ </div>
502
+ <div>
503
+ <input
504
+ id="notify-sms"
505
+ type="checkbox"
506
+ name="notifications"
507
+ value="sms"
508
+ />
509
+ <label htmlFor="notify-sms">SMS notifications</label>
510
+ </div>
511
+ </fieldset>
512
+
513
+ <button type="submit">Submit</button>
514
+ </form>
515
+ );
516
+ }
517
+ ```
518
+
519
+ ## Focus Management
520
+
521
+ ### Focus Visible
522
+
523
+ Show focus indicators only for keyboard navigation:
524
+
525
+ ```css
526
+ /* Remove default outline */
527
+ *:focus {
528
+ outline: none;
529
+ }
530
+
531
+ /* Show outline only for keyboard focus */
532
+ *:focus-visible {
533
+ outline: 2px solid var(--color-focus);
534
+ outline-offset: 2px;
535
+ }
536
+
537
+ /* Custom focus styles for specific components */
538
+ .button:focus-visible {
539
+ box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
540
+ }
541
+ ```
542
+
543
+ ### Focus Trapping
544
+
545
+ Keep focus within a specific region:
546
+
547
+ ```tsx
548
+ function useFocusTrap(ref: React.RefObject<HTMLElement>, isActive: boolean) {
549
+ useEffect(() => {
550
+ if (!isActive || !ref.current) return;
551
+
552
+ const element = ref.current;
553
+ const focusableSelector =
554
+ 'a[href], button, textarea, input[type="text"], input[type="radio"], input[type="checkbox"], select, [tabindex]:not([tabindex="-1"])';
555
+
556
+ const focusableElements =
557
+ element.querySelectorAll<HTMLElement>(focusableSelector);
558
+ const firstFocusable = focusableElements[0];
559
+ const lastFocusable = focusableElements[focusableElements.length - 1];
560
+
561
+ const handleTabKey = (e: KeyboardEvent) => {
562
+ if (e.key !== "Tab") return;
563
+
564
+ if (e.shiftKey) {
565
+ if (document.activeElement === firstFocusable) {
566
+ e.preventDefault();
567
+ lastFocusable?.focus();
568
+ }
569
+ } else {
570
+ if (document.activeElement === lastFocusable) {
571
+ e.preventDefault();
572
+ firstFocusable?.focus();
573
+ }
574
+ }
575
+ };
576
+
577
+ element.addEventListener("keydown", handleTabKey);
578
+ firstFocusable?.focus();
579
+
580
+ return () => {
581
+ element.removeEventListener("keydown", handleTabKey);
582
+ };
583
+ }, [ref, isActive]);
584
+ }
585
+ ```
586
+
587
+ ### Focus Restoration
588
+
589
+ Return focus to the appropriate element after interactions:
590
+
591
+ ```tsx
592
+ function useRestoreFocus() {
593
+ const previousFocus = useRef<HTMLElement | null>(null);
594
+
595
+ const saveFocus = () => {
596
+ previousFocus.current = document.activeElement as HTMLElement;
597
+ };
598
+
599
+ const restoreFocus = () => {
600
+ previousFocus.current?.focus();
601
+ };
602
+
603
+ return { saveFocus, restoreFocus };
604
+ }
605
+ ```
606
+
607
+ ## Live Regions
608
+
609
+ Announce dynamic content changes to screen readers:
610
+
611
+ ### Status Messages
612
+
613
+ ```tsx
614
+ // Polite announcement (waits for screen reader to finish)
615
+ <div role="status" aria-live="polite">
616
+ {savedMessage && "Settings saved successfully"}
617
+ </div>
618
+
619
+ // Assertive announcement (interrupts screen reader)
620
+ <div role="alert" aria-live="assertive">
621
+ {errorMessage && `Error: ${errorMessage}`}
622
+ </div>
623
+
624
+ // Loading states
625
+ <div aria-live="polite" aria-busy={isLoading}>
626
+ {isLoading ? "Loading..." : `${items.length} items loaded`}
627
+ </div>
628
+ ```
629
+
630
+ ### Progress Indicators
631
+
632
+ ```tsx
633
+ function ProgressBar({ value, max = 100 }) {
634
+ return (
635
+ <div
636
+ role="progressbar"
637
+ aria-valuenow={value}
638
+ aria-valuemin={0}
639
+ aria-valuemax={max}
640
+ aria-label="Upload progress"
641
+ >
642
+ <div
643
+ className="progress-fill"
644
+ style={{ width: `${(value / max) * 100}%` }}
645
+ />
646
+ <span className="sr-only">
647
+ {Math.round((value / max) * 100)}% complete
648
+ </span>
649
+ </div>
650
+ );
651
+ }
652
+ ```
653
+
654
+ ## Color and Contrast
655
+
656
+ ### Contrast Requirements
657
+
658
+ Follow WCAG guidelines for color contrast:
659
+
660
+ ```css
661
+ /* Normal text (< 18pt or < 14pt bold) */
662
+ .text {
663
+ color: #595959; /* 7:1 ratio against white */
664
+ background: white;
665
+ }
666
+
667
+ /* Large text (≥ 18pt or ≥ 14pt bold) */
668
+ .heading {
669
+ color: #767676; /* 4.5:1 ratio against white */
670
+ font-size: 1.5rem;
671
+ font-weight: bold;
672
+ }
673
+
674
+ /* Non-text elements (icons, borders) */
675
+ .icon {
676
+ color: #949494; /* 3:1 ratio against white */
677
+ }
678
+ ```
679
+
680
+ ### Color Independence
681
+
682
+ Never convey information through color alone:
683
+
684
+ ```tsx
685
+ // ❌ Color only
686
+ <span className="text-red-500">Error</span>
687
+
688
+ // ✅ Color with text/icon
689
+ <span className="text-red-500">
690
+ <ErrorIcon aria-hidden="true" />
691
+ <span>Error: Invalid input</span>
692
+ </span>
693
+
694
+ // ✅ Multiple indicators
695
+ <input
696
+ className={hasError ? 'border-red-500' : 'border-gray-300'}
697
+ aria-invalid={hasError}
698
+ aria-describedby={hasError ? 'error-message' : undefined}
699
+ />
700
+ {hasError && (
701
+ <span id="error-message" className="text-red-500">
702
+ <ErrorIcon aria-hidden="true" /> This field is required
703
+ </span>
704
+ )}
705
+ ```
706
+
707
+ ## Mobile Accessibility
708
+
709
+ ### Touch Targets
710
+
711
+ Ensure touch targets are large enough:
712
+
713
+ ```css
714
+ /* Minimum 44x44px for iOS, 48x48dp for Android */
715
+ .button {
716
+ min-height: 44px;
717
+ min-width: 44px;
718
+ padding: 12px 16px;
719
+ }
720
+
721
+ /* Add invisible touch area for small icons */
722
+ .icon-button {
723
+ position: relative;
724
+ padding: 8px;
725
+ }
726
+
727
+ .icon-button::before {
728
+ content: "";
729
+ position: absolute;
730
+ top: -8px;
731
+ right: -8px;
732
+ bottom: -8px;
733
+ left: -8px;
734
+ }
735
+ ```
736
+
737
+ ### Viewport and Zoom
738
+
739
+ Allow users to zoom:
740
+
741
+ ```html
742
+ <!-- ✅ Allows zooming -->
743
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
744
+
745
+ <!-- ❌ Prevents zooming -->
746
+ <meta
747
+ name="viewport"
748
+ content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
749
+ />
750
+ ```
751
+
752
+ ## Common Pitfalls
753
+
754
+ ### Placeholder Text as Labels
755
+
756
+ Don't use placeholders as the only label:
757
+
758
+ ```tsx
759
+ // ❌ Placeholder disappears when typing
760
+ <input placeholder="Email address" />
761
+
762
+ // ✅ Persistent label
763
+ <label>
764
+ Email address
765
+ <input type="email" />
766
+ </label>
767
+
768
+ // ✅ Floating label pattern
769
+ <div className="form-field">
770
+ <input id="email" placeholder=" " />
771
+ <label htmlFor="email">Email address</label>
772
+ </div>
773
+ ```
774
+
775
+ ### Empty Buttons
776
+
777
+ Always provide accessible text for icon buttons:
778
+
779
+ ```tsx
780
+ // ❌ No accessible name
781
+ <button onClick={handleDelete}>
782
+ <TrashIcon />
783
+ </button>
784
+
785
+ // ✅ Screen reader text
786
+ <button onClick={handleDelete} aria-label="Delete item">
787
+ <TrashIcon aria-hidden="true" />
788
+ </button>
789
+
790
+ // ✅ Visually hidden text
791
+ <button onClick={handleDelete}>
792
+ <TrashIcon aria-hidden="true" />
793
+ <span className="sr-only">Delete item</span>
794
+ </button>
795
+ ```
796
+
797
+ ### Disabled Form Elements
798
+
799
+ Disabled elements aren't focusable, which can confuse users:
800
+
801
+ ```tsx
802
+ // ❌ User can't understand why button is disabled
803
+ <button disabled={!isValid}>
804
+ Submit
805
+ </button>
806
+
807
+ // ✅ Use aria-disabled and explain
808
+ <button
809
+ aria-disabled={!isValid}
810
+ aria-describedby="submit-help"
811
+ onClick={isValid ? handleSubmit : undefined}
812
+ className={!isValid ? 'opacity-50 cursor-not-allowed' : ''}
813
+ >
814
+ Submit
815
+ </button>
816
+ <span id="submit-help">
817
+ {!isValid && 'Please fill in all required fields'}
818
+ </span>
819
+ ```