@mmerterden/multi-agent-pipeline 20.6.0 → 20.8.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 (326) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +328 -329
  5. package/package.json +2 -2
  6. package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
  7. package/pipeline/scripts/_notices.mjs +12 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  9. package/pipeline/skills/.skill-manifest.json +106 -106
  10. package/pipeline/skills/shared/README.md +71 -71
  11. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
  12. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  13. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  14. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  15. package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
  16. package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
  17. package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
  18. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  19. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  20. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  21. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  22. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  23. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  24. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  25. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  26. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  27. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  28. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  29. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  30. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  31. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  32. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  33. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  34. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  35. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  37. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  38. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  39. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  40. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  41. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  42. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  43. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  44. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  45. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  46. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  47. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  48. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  49. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  50. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  51. package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
  52. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  53. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  54. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  55. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
  56. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  57. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  58. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  59. package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
  60. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
  61. package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
  62. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  63. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  64. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  65. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  66. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  67. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  68. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  69. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  70. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  71. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  72. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  73. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  74. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  75. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  76. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  77. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  78. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  79. package/pipeline/skills/shared/external/council/SKILL.md +1 -0
  80. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  81. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  82. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  83. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
  84. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
  85. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  86. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  87. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  88. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  89. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  90. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  91. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  92. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  93. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  94. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  95. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  96. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  97. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  98. package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
  99. package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
  100. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
  101. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  102. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  103. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  104. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  105. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  106. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  107. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
  108. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
  109. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  110. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  111. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  112. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  113. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  114. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
  115. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  116. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  117. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  118. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  119. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
  120. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  121. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  122. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  123. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  124. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  125. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  126. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  127. package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
  128. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  129. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  130. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  131. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  132. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  133. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  134. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
  135. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
  136. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
  137. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
  138. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
  139. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
  140. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
  141. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
  142. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
  143. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
  144. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
  145. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
  146. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
  147. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
  148. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
  149. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
  150. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  151. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  152. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  153. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  154. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  155. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  156. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  157. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  158. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  159. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  160. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  161. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  162. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  163. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
  164. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  165. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  166. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  167. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  168. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  169. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  170. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  171. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  172. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  173. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  174. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  175. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  176. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  177. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  178. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  179. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  180. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  181. package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
  182. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  183. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  184. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  185. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  186. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
  187. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
  188. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  189. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  190. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  191. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
  192. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
  193. package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
  194. package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
  195. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  196. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  197. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  198. package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
  199. package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
  200. package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
  201. package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
  202. package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
  203. package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
  204. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
  205. package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
  206. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  207. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  208. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  209. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  210. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  211. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  212. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  213. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  214. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  215. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  216. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  217. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  218. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  219. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  220. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  221. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  222. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  223. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  224. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  225. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  226. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  227. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  228. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  229. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  230. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  231. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  232. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  233. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  234. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  235. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  236. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  237. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  238. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  239. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  240. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  241. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  242. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  243. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  244. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  245. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  246. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  247. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  248. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  249. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  250. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  251. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  252. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  253. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  254. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  255. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  256. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  257. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  258. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  259. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  260. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  261. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  262. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  263. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  264. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  265. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  266. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  267. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  268. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  269. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  270. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  271. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  272. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  273. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  274. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  275. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  276. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  277. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  278. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  279. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  280. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  281. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  282. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  283. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  284. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  285. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  286. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  287. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  288. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  289. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  290. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  291. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  292. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  293. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  294. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  295. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  296. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  297. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  298. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  299. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  300. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  301. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  302. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  303. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  304. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  305. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  306. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  307. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  308. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  309. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  310. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
  311. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
  312. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  313. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  314. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
  315. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  316. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  317. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  318. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
  319. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  320. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  321. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
  322. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
  323. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
  324. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  325. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  326. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,90 +1,96 @@
1
1
  ---
2
2
  name: ios-accessibility
3
- description: "Implements, reviews, or improves accessibility in iOS/macOS apps with SwiftUI, UIKit, and AppKit. Use when adding VoiceOver, Voice Control, Switch Control, or Full Keyboard Access support; when working with accessibility labels, hints, values, traits, accessibilityInputLabels, NSAccessibility, grouping, reading order, accessibility focus restoration with @AccessibilityFocusState, Dynamic Type, @ScaledMetric, custom rotors, accessibility actions, XCTest accessibility checks, App Store Accessibility Nutrition Labels, App Store Connect accessibility answers, a11y compliance audits, or system accessibility preferences."
3
+ description: "VoiceOver, Voice Control, Switch Control and Full Keyboard Access in SwiftUI, UIKit and AppKit on iOS, iPadOS and macOS: labels, hints, values, traits, accessibilityInputLabels, NSAccessibility, grouping and reading order, @AccessibilityFocusState focus restore, Dynamic Type and @ScaledMetric, rotors, custom actions, XCTest accessibility checks, system accessibility preferences, Accessibility Nutrition Labels and App Store Connect answers. Use when implementing, reviewing or auditing accessibility (a11y). Not for keyboard or directional focus mechanics (Tab order, FocusState, UIFocusGuide): use hig-inputs."
4
4
  metadata:
5
- source: "dpearson2699/swift-ios-skills (PolyForm Perimeter 1.0.0)"
5
+ source: multi-agent-pipeline
6
6
  ---
7
7
 
8
- # iOS/macOS Accessibility - SwiftUI, UIKit, and AppKit
9
-
10
- Every user-facing view must be usable with VoiceOver, Switch Control, Voice Control, Full Keyboard Access, and other assistive technologies. This skill covers SwiftUI, UIKit, and AppKit patterns required to build accessible iOS, iPadOS, and macOS apps.
11
-
12
- ## Contents
13
-
14
- - [Core Principles](#core-principles)
15
- - [How VoiceOver Reads Elements](#how-voiceover-reads-elements)
16
- - [SwiftUI Accessibility Modifiers](#swiftui-accessibility-modifiers)
17
- - [Focus Management](#focus-management)
18
- - [Dynamic Type](#dynamic-type)
19
- - [Custom Rotors](#custom-rotors)
20
- - [System Accessibility Preferences](#system-accessibility-preferences)
21
- - [Decorative Content](#decorative-content)
22
- - [Voice Control](#voice-control)
23
- - [Switch Control](#switch-control)
24
- - [Full Keyboard Access](#full-keyboard-access)
25
- - [Assistive Access (iOS 18+)](#assistive-access-ios-18)
26
- - [UIKit Accessibility Patterns](#uikit-accessibility-patterns)
27
- - [AppKit Accessibility Patterns](#appkit-accessibility-patterns)
28
- - [Accessibility Custom Content](#accessibility-custom-content)
29
- - [App Store Accessibility Nutrition Labels](#app-store-accessibility-nutrition-labels)
30
- - [Testing Accessibility](#testing-accessibility)
31
- - [Common Mistakes](#common-mistakes)
32
- - [Review Checklist](#review-checklist)
33
- - [References](#references)
34
-
35
- ---
36
-
37
- ## Core Principles
38
-
39
- 1. Every interactive element MUST have an accessible label. If no visible text exists, add `.accessibilityLabel`.
40
- 2. Every custom control MUST have correct traits via `.accessibilityAddTraits` (never direct assignment). For binary custom controls such as favorite/star buttons, prefer a real `Toggle`; otherwise expose toggle behavior with `.accessibilityAddTraits(.isToggle)` and a current state value without putting the control type in the label.
41
- 3. Custom adjustable controls such as quantity steppers MUST expose adjustable behavior with `.accessibilityAdjustableAction`; UIKit custom adjustable controls also need the `.adjustable` trait.
42
- 4. Decorative images MUST be hidden from assistive technologies.
43
- 5. Sheet and dialog dismissals MUST return VoiceOver focus to the trigger element.
44
- 6. All tap targets MUST be at least 44x44 points.
45
- 7. Dynamic Type MUST be supported everywhere (system fonts, `@ScaledMetric`, adaptive layouts).
46
- 8. No information conveyed by color alone -- always provide text or icon alternatives.
47
- 9. System accessibility preferences MUST be respected: Reduce Motion, Reduce Transparency, Bold Text, Increase Contrast.
48
-
49
- ## How VoiceOver Reads Elements
50
-
51
- VoiceOver reads element properties in a fixed, non-configurable order:
52
-
53
- **Label -> Value -> Trait -> Hint**
54
-
55
- Design your labels, values, and hints with this reading order in mind.
56
-
57
- ## SwiftUI Accessibility Modifiers
58
-
59
- See [references/a11y-patterns.md](references/a11y-patterns.md) for detailed SwiftUI modifier examples (labels, hints, traits, grouping, custom controls, adjustable actions, and custom actions).
60
-
61
- ## Focus Management
62
-
63
- Focus management is where most apps fail. When a sheet, alert, or popover is dismissed, VoiceOver focus MUST return to the element that triggered it.
64
-
65
- This section is about accessibility focus for assistive technologies. For keyboard focus, directional focus, `focusSection()`, scene-focused values, and `UIFocusGuide`, use the `focus-engine` skill.
66
-
67
- When triaging broad focus bugs, still call out accessibility traversal separately: accessibility element order and grouping in the view hierarchy directly affect VoiceOver swipe order, Switch Control scan order, Voice Control overlay targeting, and Full Keyboard Access reachability review. Route keyboard-focus implementation to `focus-engine`, but keep this traversal impact in `ios-accessibility`.
8
+ # iOS Accessibility
9
+
10
+ Whatever appears on screen must also work for people who navigate with
11
+ VoiceOver, speak commands through Voice Control, scan with Switch Control, drive
12
+ a hardware keyboard through Full Keyboard Access, or rely on any other
13
+ assistive technology. Platforms: iOS, iPadOS and macOS. Frameworks: SwiftUI,
14
+ UIKit and AppKit.
15
+
16
+ References:
17
+
18
+ - [Code patterns](references/a11y-patterns.md): worked examples of the
19
+ modifiers in both UI frameworks, grouping, custom and adjustable controls, custom actions, rotors,
20
+ Dynamic Type, AppKit, Voice Control, Switch Control, Full Keyboard Access,
21
+ XCTest checks.
22
+ - [Nutrition labels](references/nutrition-labels.md): the current App Store
23
+ label categories and what passes or fails each one.
24
+ - [Media](references/media-accessibility.md): captions, SDH, audio
25
+ descriptions and `AVMediaCharacteristic`.
26
+
27
+ ## Ground rules
28
+
29
+ 1. Anything interactive has an accessible label. No visible text means you add
30
+ `.accessibilityLabel`.
31
+ 2. Custom controls gain traits with `.accessibilityAddTraits`. Never replace
32
+ the trait set wholesale.
33
+ 3. A two-state custom control (star, pin, favourite) should be a real
34
+ `Toggle`. If it cannot be, add `.accessibilityAddTraits(.isToggle)`
35
+ (iOS 17+) and report on/off as the value; the label never says what kind of
36
+ control it is.
37
+ 4. Custom adjustable controls, such as a quantity stepper, implement
38
+ `.accessibilityAdjustableAction`. In UIKit they also carry the `.adjustable`
39
+ trait.
40
+ 5. Purely decorative images are hidden from assistive technologies.
41
+ 6. When a sheet or dialog closes, VoiceOver focus goes back to whatever
42
+ opened it.
43
+ 7. Nothing tappable is smaller than 44x44 points.
44
+ 8. Dynamic Type works everywhere: system text styles, `@ScaledMetric`, layouts
45
+ that adapt.
46
+ 9. Color is never the only signal; pair it with text or an icon.
47
+ 10. Honour Increase Contrast, Bold Text, Reduce Transparency and Reduce Motion.
48
+
49
+ ## What VoiceOver says, and in which order
50
+
51
+ VoiceOver speaks an element's parts in a fixed order you cannot change:
52
+ **label, value, trait, hint**. Write each piece knowing what comes before and
53
+ after it, so the sentence as a whole makes sense ("Volume, 60 percent,
54
+ adjustable, swipe up or down to adjust").
55
+
56
+ Modifier examples for labels, hints, traits, grouping, custom and adjustable
57
+ controls and custom actions: [code patterns](references/a11y-patterns.md).
58
+
59
+ ## Accessibility focus
60
+
61
+ Focus is where apps most often break for assistive technology users. After a
62
+ sheet, alert or popover goes away, VoiceOver focus must land back on the
63
+ control that opened it.
64
+
65
+ Scope: this section is about the assistive-technology cursor. `UIFocusGuide`,
66
+ `focusSection()`, scene-focused values, and keyboard or directional focus in
67
+ general are implementation topics for the `hig-inputs` skill. Even when
68
+ triaging a broad focus bug, report accessibility traversal as its own finding:
69
+ the order and grouping of elements decide the path a VoiceOver swipe follows,
70
+ the sequence a switch scan visits, where Voice Control places its overlays and
71
+ whether Full Keyboard Access can get somewhere. The keyboard wiring goes to `hig-inputs`; the traversal impact
72
+ finding stays here.
68
73
 
69
74
  ### `@AccessibilityFocusState` (iOS 15+)
70
75
 
71
- `@AccessibilityFocusState` is a property wrapper that reads and writes the current accessibility focus. It works with `Bool` for single-target focus or an optional `Hashable` enum for multi-target focus.
76
+ A property wrapper you can read to learn where accessibility focus is and
77
+ write to move it. One target: bind a `Bool`. Several: bind an optional enum
78
+ that conforms to `Hashable`.
72
79
 
73
80
  ```swift
74
- struct ContentView: View {
75
- @State private var showSheet = false
76
- @AccessibilityFocusState private var focusOnTrigger: Bool
81
+ struct ReceiptRow: View {
82
+ @AccessibilityFocusState private var detailButtonFocused: Bool
83
+ @State private var showsDetail = false
77
84
 
78
85
  var body: some View {
79
- Button("Open Settings") { showSheet = true }
80
- .accessibilityFocused($focusOnTrigger)
81
- .sheet(isPresented: $showSheet) {
82
- SettingsSheet()
86
+ Button("Receipt details") { showsDetail = true }
87
+ .accessibilityFocused($detailButtonFocused)
88
+ .sheet(isPresented: $showsDetail) {
89
+ ReceiptDetail()
83
90
  .onDisappear {
84
- // Slight delay allows the transition to complete before moving focus
85
91
  Task { @MainActor in
86
- try? await Task.sleep(for: .milliseconds(100))
87
- focusOnTrigger = true
92
+ try? await Task.sleep(for: .seconds(0.1)) // about 100 ms
93
+ detailButtonFocused = true
88
94
  }
89
95
  }
90
96
  }
@@ -92,368 +98,390 @@ struct ContentView: View {
92
98
  }
93
99
  ```
94
100
 
95
- ### Multi-Target Focus with Enum
101
+ The short pause lets the dismissal animation finish before focus moves;
102
+ moving it mid-transition tends to be ignored. `Task.sleep(for:)` needs iOS 16;
103
+ on iOS 15 use `Task.sleep(nanoseconds: 100_000_000)`.
104
+
105
+ ### Several targets
96
106
 
97
107
  ```swift
98
- enum A11yFocus: Hashable {
99
- case nameField
100
- case emailField
101
- case submitButton
102
- }
108
+ enum SignUpField: Hashable { case handle, password, create }
103
109
 
104
- struct FormView: View {
105
- @AccessibilityFocusState private var focus: A11yFocus?
110
+ struct SignUpForm: View {
111
+ @State private var handle = ""
112
+ @AccessibilityFocusState private var a11yFocus: SignUpField?
106
113
 
107
114
  var body: some View {
108
115
  Form {
109
- TextField("Name", text: $name)
110
- .accessibilityFocused($focus, equals: .nameField)
111
- TextField("Email", text: $email)
112
- .accessibilityFocused($focus, equals: .emailField)
113
- Button("Submit") { validate() }
114
- .accessibilityFocused($focus, equals: .submitButton)
116
+ TextField("Handle", text: $handle)
117
+ .accessibilityFocused($a11yFocus, equals: .handle)
118
+ // ...same for the password field and the Create account button
115
119
  }
116
120
  }
117
121
 
118
- func validate() {
119
- if name.isEmpty {
120
- focus = .nameField // Move VoiceOver to the invalid field
121
- }
122
- }
122
+ private func submit() { if handle.isEmpty { a11yFocus = .handle } }
123
123
  }
124
124
  ```
125
125
 
126
- ### Custom Modals
126
+ When validation fails, assign the case of the offending field and VoiceOver
127
+ jumps straight to it.
128
+
129
+ ### Custom modals
127
130
 
128
- Custom overlay views need the `.isModal` trait to trap VoiceOver focus and an escape action for dismissal:
131
+ A hand-built overlay needs two things: the `.isModal` trait so VoiceOver
132
+ cannot wander outside it, and an escape action so the standard dismiss gesture
133
+ works.
129
134
 
130
135
  ```swift
131
- CustomDialog()
136
+ CouponPopover(onClose: close)
132
137
  .accessibilityAddTraits(.isModal)
133
- .accessibilityAction(.escape) { dismiss() }
138
+ .accessibilityAction(.escape) { close() }
134
139
  ```
135
140
 
136
- Test dismissal as part of the modal contract: users must be able to dismiss the overlay with the relevant assistive-technology escape gesture or keyboard escape path, and focus should return to the trigger or next logical target.
141
+ Closing is part of the modal's contract: test that the VoiceOver escape
142
+ gesture (two-finger scrub) or the keyboard Escape key dismisses it. Afterwards
143
+ focus belongs on the trigger or on the next sensible element.
137
144
 
138
- ### Accessibility Notifications (UIKit)
139
-
140
- When you need to announce changes or move focus imperatively in UIKit contexts:
145
+ ### UIKit notifications
141
146
 
142
147
  ```swift
143
- // Announce a status change (e.g., "Item deleted", "Upload complete")
144
- UIAccessibility.post(notification: .announcement, argument: "Upload complete")
145
-
146
- // Partial screen update -- move focus to a specific element
147
- UIAccessibility.post(notification: .layoutChanged, argument: targetView)
148
-
149
- // Full screen transition -- move focus to the new screen
150
- UIAccessibility.post(notification: .screenChanged, argument: newScreenView)
148
+ UIAccessibility.post(notification: .announcement, argument: "Draft saved")
149
+ UIAccessibility.post(notification: .layoutChanged, argument: errorBanner)
150
+ UIAccessibility.post(notification: .screenChanged, argument: summaryHeader)
151
151
  ```
152
152
 
153
- ## Dynamic Type
154
-
155
- Scale text with system text styles. Scale non-text dimensions too: icon sizes, spacing, control heights, and custom hit-region dimensions should use `@ScaledMetric(relativeTo:)` where they need to track text size.
153
+ - `.announcement`: speak a status change (an export finished, a row was
154
+ removed).
155
+ - `.layoutChanged`: part of the screen changed; focus moves to the argument.
156
+ - `.screenChanged`: a whole new screen; focus moves to the argument.
156
157
 
157
- See [references/a11y-patterns.md](references/a11y-patterns.md) for Dynamic Type and adaptive layout examples, including `@ScaledMetric` and minimum tap target patterns.
158
+ ## Dynamic Type
158
159
 
159
- ## Custom Rotors
160
+ Text scales when you use system text styles. Sizes that should grow with the
161
+ text (icons, spacing, control heights, custom hit regions) use
162
+ `@ScaledMetric(relativeTo:)`. Examples, including minimum tap targets:
163
+ [code patterns](references/a11y-patterns.md).
160
164
 
161
- Rotors let VoiceOver users quickly navigate to specific content types. Add custom rotors for content-heavy screens. See [references/a11y-patterns.md](references/a11y-patterns.md) for complete rotor examples.
165
+ ## Custom rotors
162
166
 
163
- ## System Accessibility Preferences
167
+ A rotor lets a VoiceOver user hop straight between items of one kind, such as
168
+ unread threads or headings. Add them to screens dense with content; see
169
+ [code patterns](references/a11y-patterns.md).
164
170
 
165
- Always respect these environment values:
171
+ ## System preferences
166
172
 
167
- ```swift
168
- @Environment(\.accessibilityReduceMotion) var reduceMotion
169
- @Environment(\.accessibilityReduceTransparency) var reduceTransparency
170
- @Environment(\.colorSchemeContrast) var contrast // .standard or .increased
171
- @Environment(\.legibilityWeight) var legibilityWeight // .regular or .bold
172
- ```
173
+ | Environment value | Type | Setting |
174
+ |---|---|---|
175
+ | `\.accessibilityReduceMotion` | `Bool` | Reduce Motion |
176
+ | `\.accessibilityReduceTransparency` | `Bool` | Reduce Transparency |
177
+ | `\.colorSchemeContrast` | `.standard` / `.increased` | Increase Contrast |
178
+ | `\.legibilityWeight` | `.regular` / `.bold` | Bold Text |
173
179
 
174
180
  ### Reduce Motion
175
181
 
176
- Replace movement-based animations with crossfades or no animation:
182
+ With Reduce Motion on, replace movement with a crossfade or with no animation.
177
183
 
178
184
  ```swift
179
- withAnimation(reduceMotion ? nil : .spring()) {
180
- showContent.toggle()
185
+ struct FilterDrawer: View {
186
+ @Environment(\.accessibilityReduceMotion) private var reduceMotion
187
+ @State private var isExpanded = false
188
+
189
+ var body: some View {
190
+ VStack {
191
+ Button("Filters") {
192
+ withAnimation(reduceMotion ? nil : .spring()) { isExpanded.toggle() }
193
+ }
194
+ if isExpanded {
195
+ PanelView()
196
+ .transition(reduceMotion ? .opacity : .move(edge: .bottom))
197
+ }
198
+ }
199
+ }
181
200
  }
182
- content.transition(reduceMotion ? .opacity : .slide)
183
201
  ```
184
202
 
185
- Review every moving transition, including row deletion, quantity changes, sheet or checkout presentation, and modal dismissal. Under Reduce Motion, replace slide, bounce, parallax, spring, and large spatial transitions with opacity changes, instant state changes, or no animation.
203
+ Go through every moving transition, not just the obvious one: row removal,
204
+ quantity changes, presenting a sheet or checkout, dismissing a modal. Slides,
205
+ bounces, parallax, springs and big spatial moves become fades, an immediate
206
+ jump to the end state, or no animation at all.
186
207
 
187
- ### Reduce Transparency, Increase Contrast, Bold Text
208
+ ### Transparency, contrast, bold text
188
209
 
189
210
  ```swift
190
- // Solid backgrounds when transparency is reduced
191
- .background(reduceTransparency ? Color(.systemBackground) : Color(.systemBackground).opacity(0.85))
211
+ @Environment(\.accessibilityReduceTransparency) private var reduceTransparency
212
+ @Environment(\.colorSchemeContrast) private var contrast
213
+ @Environment(\.legibilityWeight) private var legibilityWeight
192
214
 
193
- // Stronger colors when contrast is increased
194
- .foregroundStyle(contrast == .increased ? .primary : .secondary)
215
+ private let base = Color(uiColor: .systemBackground)
195
216
 
196
- // Bold weight when system bold text is enabled
197
- .fontWeight(legibilityWeight == .bold ? .bold : .regular)
217
+ var body: some View {
218
+ Text(caption)
219
+ .foregroundStyle(contrast == .increased ? .primary : .secondary)
220
+ .fontWeight(legibilityWeight == .bold ? .bold : .regular)
221
+ .background(reduceTransparency ? base : base.opacity(0.8))
222
+ }
198
223
  ```
199
224
 
200
- ## Decorative Content
225
+ ## Decorative versus meaningful images
201
226
 
202
227
  ```swift
203
- // Decorative images: hidden from VoiceOver
204
- Image(decorative: "background-pattern")
205
- Image("visual-divider").accessibilityHidden(true)
206
-
207
- // Icon next to text: Label handles this automatically
208
- Label("Settings", systemImage: "gear")
209
-
210
- // Icon-only buttons: MUST have an accessibility label
211
- Button(action: { }) {
212
- Image(systemName: "gear")
228
+ Image(decorative: "confetti") // never announced
229
+ Image("wave-divider").accessibilityHidden(true) // hide an existing image
230
+ Label("Downloads", systemImage: "arrow.down") // icon folded into the text
231
+ Button { openFilters() } label: {
232
+ Image(systemName: "slider.horizontal.3")
213
233
  }
214
- .accessibilityLabel("Settings")
234
+ .accessibilityLabel("Filters") // icon-only button needs a name
215
235
  ```
216
236
 
217
- Treat an image as decorative only when it adds no information beyond adjacent accessible text. If it communicates a product variant, state, chart point, user-generated content, or another distinguishing detail, provide a meaningful description instead of hiding it.
237
+ An image counts as decorative only when nearby accessible text already says
238
+ everything it says. If it shows a product variant, a state, a chart point,
239
+ something a user posted, or any other distinguishing detail, describe it
240
+ instead of hiding it.
218
241
 
219
242
  ## Voice Control
220
243
 
221
- Voice Control relies on accessibility labels to generate spoken tap targets. If a label is missing or unspeakable, Voice Control cannot target the element.
222
-
223
- - Every interactive element MUST have a speakable accessibility label (no emoji-only, no symbol-only).
224
- - Labels must be unique within the visible screen - duplicate labels force users to disambiguate with overlay numbers.
225
- - Treat `accessibilityInputLabels` as pre-freeze accessibility work for long, awkward, localized, acronym-heavy, or commonly shortened spoken labels; do not defer it as polish. Voice Control and Full Keyboard Access use these. List alternatives in descending order of importance.
226
- - Apply `accessibilityInputLabels` broadly to any visible target whose primary label is hard to say, including repeated row actions, quantity controls, account/settings links, media controls, and localized labels with acronyms or product names.
227
- - Test with Voice Control enabled: say "Show Names" and "Show Numbers" to verify all interactive elements are targetable.
228
- - For Voice Control reviews, verify both overlays: "Show Names" confirms speakable labels, and "Show Numbers" confirms every visible interactive target can still be reached when names are missing, duplicated, or awkward.
229
-
230
- See [references/a11y-patterns.md](references/a11y-patterns.md) for `accessibilityInputLabels` examples and speakable label guidelines.
244
+ Spoken commands find their target through its accessibility label: the label
245
+ is the name a person says to tap the element. An element with no label, or a label nobody can say, cannot be
246
+ targeted by voice.
247
+
248
+ - Every interactive element needs a label a person can say aloud. No labels
249
+ made only of emoji or symbols.
250
+ - Labels must differ among the elements on screen at the same time;
251
+ duplicates push users onto overlay numbers.
252
+ - Add `accessibilityInputLabels` before feature freeze, not as late polish,
253
+ wherever a label is long, clumsy, localized, full of acronyms, or usually
254
+ shortened when spoken. Both Full Keyboard Access and Voice Control consume
255
+ them. List the entries most important first.
256
+ - Good candidates: media controls, settings and account links, per-row
257
+ actions that repeat down a list, quantity steppers, localized labels that
258
+ contain product names or acronyms.
259
+ - Turn Voice Control on and say "Show Names", then "Show Numbers". The names
260
+ overlay proves labels can be spoken; the numbers overlay proves every
261
+ visible target stays reachable even if its name is missing, duplicated or
262
+ awkward. A review
263
+ checks both overlays.
264
+
265
+ Examples and speakable-label guidance: [code patterns](references/a11y-patterns.md).
231
266
 
232
267
  ## Switch Control
233
268
 
234
- Switch Control scans accessibility elements sequentially in reading order. Proper grouping and custom actions are critical for usability.
235
-
236
- - Group related content with `.accessibilityElement(children: .combine)` to reduce scan stops.
237
- - Every scan target should be meaningful and actionable. Decorative elements hidden from VoiceOver are also hidden from Switch Control.
238
- - Switch Control users cannot perform swipe-to-delete, long-press, or multi-finger gestures. Expose these interactions as `.accessibilityAction(named:)` custom actions instead - Switch Control presents them as a menu.
239
- - Custom controls with non-standard hit areas should ensure `accessibilityFrame` accurately reflects the tappable region (for point scanning mode).
269
+ Switch Control steps through accessibility elements one at a time in reading
270
+ order, so grouping and custom actions matter more here than anywhere else.
240
271
 
241
- See [references/a11y-patterns.md](references/a11y-patterns.md) for custom action and grouping examples.
272
+ - `.accessibilityElement(children: .combine)` merges related pieces and
273
+ removes scan stops. Each remaining stop should mean something and do
274
+ something.
275
+ - Anything hidden from VoiceOver as decorative is skipped by Switch Control
276
+ too.
277
+ - Multi-finger gestures, long presses and swipe-to-delete are out of reach
278
+ for switch users. Offer those operations as `.accessibilityAction(named:)`; Switch
279
+ Control lists them in a menu.
280
+ - In point scanning mode, a custom control whose tappable area is unusual
281
+ must report an `accessibilityFrame` that matches that area.
242
282
 
243
283
  ## Full Keyboard Access
244
284
 
245
- Full Keyboard Access (iOS/iPadOS 13.4+) lets users navigate and operate an app with a hardware keyboard.
246
-
247
- This skill covers the accessibility review surface: whether all controls are reachable, clearly labeled, visibly focused, and operable without touch. If the bug is Tab traversal, skipped custom cards, `.focusable()`, `@FocusState`, `focusSection()`, directional movement, scene-focused values, tvOS focus behavior, or `UIFocusGuide`, route implementation to the `focus-engine` skill first. Keep only the accessibility finding here.
248
-
249
- - Every interactive element can be reached and activated with the keyboard.
250
- - Traversal order is logical and does not trap focus.
251
- - Focus indicators remain visible at all contrast and text-size settings.
252
- - Gesture-only behavior has a keyboard-operable alternative.
253
- - App shortcuts do not override system-defined shortcuts such as Cmd+C, Cmd+V, or Cmd+Tab.
254
-
255
- See [references/a11y-patterns.md](references/a11y-patterns.md) for Full Keyboard Access audit checks.
256
-
257
- ## Traversal Order
258
-
259
- Explicitly assess how accessibility element order and grouping affect traversal outcomes: VoiceOver swipe order, Switch Control scan order, Voice Control overlay targeting, and Full Keyboard Access reachability review can all break when grouping/order differs from visual or task order. Missing labels, duplicate labels, excessive row children, hidden custom controls, or grouping that does not match the visual/task order can make traversal confusing across all of them. Keep implementation mechanics for keyboard or directional routing in `focus-engine`; keep the accessibility impact and ordering audit here.
285
+ Available since iOS and iPadOS 13.4, Full Keyboard Access lets people run the
286
+ app from a hardware keyboard. This skill owns the review: can everything be
287
+ reached, is it clearly labelled, is focus visible, does it work without touch.
288
+ `UIFocusGuide`, tvOS focus, scene-focused values, directional movement,
289
+ `focusSection()`, `@FocusState`, `.focusable()`, custom cards that Tab jumps
290
+ over, and Tab traversal bugs in general go to `hig-inputs` first; only the accessibility finding
291
+ stays here.
292
+
293
+ Audit:
294
+
295
+ - the keyboard alone can reach and trigger every interactive element;
296
+ - the traversal order makes sense and never traps focus;
297
+ - focus indicators stay visible at every contrast and text-size setting;
298
+ - anything that only works through a gesture has a keyboard route;
299
+ - the app's own shortcuts leave system ones (Cmd+C, Cmd+V, Cmd+Tab) alone.
300
+
301
+ ## Traversal order
302
+
303
+ Always judge the effect of ordering and grouping on four things at once: the
304
+ path of VoiceOver swipes, the steps of a switch scan, where Voice Control
305
+ overlays land, and what the keyboard can reach. All four go wrong together
306
+ when grouping or order disagrees with the visual layout or the task order. Usual causes:
307
+ missing labels, duplicate labels, rows exposing too many children, custom
308
+ controls hidden from accessibility, grouping that does not match what the eye
309
+ sees. The keyboard and directional routing mechanics are `hig-inputs` work;
310
+ the ordering audit and its accessibility impact are reported here.
260
311
 
261
312
  ## Assistive Access (iOS 18+)
262
313
 
263
- Assistive Access provides a simplified interface for users with cognitive disabilities. Apps should support this mode:
314
+ This pared-down system mode serves people with cognitive disabilities, and
315
+ apps should behave well inside it.
264
316
 
265
317
  ```swift
266
- // Check if Assistive Access is active (iOS 18+)
267
- @Environment(\.accessibilityAssistiveAccessEnabled) var isAssistiveAccessEnabled
318
+ struct LibraryScreen: View {
319
+ @Environment(\.accessibilityAssistiveAccessEnabled) private var assistiveAccess
268
320
 
269
- var body: some View {
270
- if isAssistiveAccessEnabled {
271
- SimplifiedContentView()
272
- } else {
273
- FullContentView()
321
+ var body: some View {
322
+ if assistiveAccess { SimpleLibraryView() } else { FullLibraryView() }
274
323
  }
275
324
  }
276
325
  ```
277
326
 
278
- Key guidelines:
279
- - Reduce visual complexity: fewer controls, larger tap targets, simpler navigation
280
- - Use clear, literal language for labels and instructions
281
- - Minimize the number of choices presented at once
282
- - Test with Assistive Access enabled in Settings > Accessibility > Assistive Access
327
+ - Less on screen: fewer controls, bigger targets, flatter navigation.
328
+ - Literal, plain wording in labels and instructions.
329
+ - Only a few choices at a time.
330
+ - Try it under Settings > Accessibility > Assistive Access.
283
331
 
284
- ## UIKit Accessibility Patterns
332
+ From iOS 26 an app can also declare `UISupportsAssistiveAccess` in its
333
+ Info.plist and provide a dedicated `AssistiveAccess` scene.
285
334
 
286
- When working with UIKit views:
335
+ ## UIKit
287
336
 
288
- - Set `isAccessibilityElement = true` on meaningful custom views.
289
- - Set `accessibilityLabel` on all interactive elements without visible text.
290
- - Use `.insert()` and `.remove()` for trait modification (not direct assignment).
291
- - Set `accessibilityViewIsModal = true` on custom overlay views to trap focus.
292
- - Post `.announcement` for transient status messages.
293
- - Post `.layoutChanged` with a target view for partial screen updates.
294
- - Post `.screenChanged` for full screen transitions.
337
+ - `isAccessibilityElement = true` on custom views that carry meaning.
338
+ - `accessibilityLabel` on every interactive element without visible text.
339
+ - Change traits with `.insert()` and `.remove()` instead of assigning:
340
+ `badgeView.accessibilityTraits.insert(.button)`,
341
+ `badgeView.accessibilityTraits.remove(.staticText)`.
342
+ - `accessibilityViewIsModal = true` on custom overlays to keep focus inside.
343
+ - Post `.announcement` for passing status, `.layoutChanged` with a target for
344
+ partial updates, `.screenChanged` for full transitions.
295
345
 
296
- ```swift
297
- // UIKit trait modification
298
- customButton.accessibilityTraits.insert(.button)
299
- customButton.accessibilityTraits.remove(.staticText)
346
+ ## AppKit
300
347
 
301
- // Modal overlay
302
- overlayView.accessibilityViewIsModal = true
303
- ```
348
+ AppKit accessibility rests on `NSAccessibilityProtocol` plus role-specific
349
+ protocols. Standard controls already publish most of what assistive apps need,
350
+ including notifications, so prefer them and customise only where the defaults
351
+ fall short.
304
352
 
305
- ## AppKit Accessibility Patterns
353
+ - A custom `NSView` adopts the right role behaviour and reports its role,
354
+ label, value and supported actions accurately.
355
+ - Items with no view behind them use `NSAccessibilityElement`.
356
+ - Post `NSAccessibility` notifications when a change must reach assistive
357
+ apps.
306
358
 
307
- AppKit accessibility uses `NSAccessibilityProtocol` and related role-specific protocols to describe accessible elements. Standard AppKit controls already provide much of this behavior; customize labels, values, roles, and actions only when the defaults are insufficient.
359
+ The full custom-view example (a toggle-like `NSView` returning role, label,
360
+ "On"/"Off" value and a press action, posting `.valueChanged` when its state
361
+ changes) and `NSAccessibilityElement` usage are in
362
+ [code patterns](references/a11y-patterns.md#appkit).
308
363
 
309
- - Prefer standard AppKit controls first - they already expose accessibility metadata and notifications.
310
- - For custom `NSView` subclasses, adopt the appropriate role-specific accessibility behavior and return the correct role, label, value, and actions.
311
- - Use `NSAccessibilityElement` for accessible items that are not backed by their own `NSView`.
312
- - Post `NSAccessibility` notifications when state changes need to be announced to assistive apps.
364
+ ## Custom content
313
365
 
314
- ```swift
315
- final class FavoriteToggleView: NSView {
316
- var isFavorite = false {
317
- didSet {
318
- NSAccessibility.post(element: self, notification: .valueChanged)
319
- }
320
- }
321
-
322
- override func isAccessibilityElement() -> Bool { true }
323
- override func accessibilityRole() -> NSAccessibility.Role? { .button }
324
- override func accessibilityLabel() -> String? { "Favorite" }
325
- override func accessibilityValue() -> Any? { isFavorite ? "On" : "Off" }
326
-
327
- override func accessibilityPerformPress() -> Bool {
328
- isFavorite.toggle()
329
- return true
330
- }
331
- }
332
- ```
333
-
334
- See [references/a11y-patterns.md](references/a11y-patterns.md) for AppKit examples including `NSAccessibilityElement` and announcement notifications.
335
-
336
- ## Accessibility Custom Content
337
-
338
- See [references/a11y-patterns.md](references/a11y-patterns.md) for UIKit and AppKit accessibility patterns and custom content examples.
366
+ `.accessibilityCustomContent` attaches extra labelled facts to an element.
367
+ By default VoiceOver offers them only on request (More Content rotor); with
368
+ `importance: .high` they are read along with the element.
339
369
 
340
370
  ```swift
341
- ProductRow(product: product)
342
- .accessibilityCustomContent("Price", product.formattedPrice)
343
- .accessibilityCustomContent("Rating", "\(product.rating) out of 5")
344
- .accessibilityCustomContent(
345
- "Availability",
346
- product.inStock ? "In stock" : "Out of stock",
347
- importance: .high // .high reads automatically with the element
348
- )
371
+ HotelCard(hotel: hotel)
372
+ .accessibilityCustomContent("Nightly rate", Text(hotel.rateText), importance: .high)
373
+ .accessibilityCustomContent("Guest score", Text(hotel.scoreText))
349
374
  ```
350
375
 
351
- ## App Store Accessibility Nutrition Labels
352
-
353
- For App Store accessibility nutrition labels, product-page claims, or App Store Connect accessibility answers, read [references/nutrition-labels.md](references/nutrition-labels.md).
354
-
355
- Before recommending a claim, require evidence that users can complete all common tasks with that feature on the relevant device type. Use a structured common-task by accessibility-feature matrix, include media transcripts when captions for audio-only content are relevant, and explicitly warn that App Store accessibility answers must stay accurate and must not be treated as marketing claims.
356
-
357
- ## Testing Accessibility
358
-
359
- ### Manual Testing
376
+ More UIKit, AppKit and custom-content examples:
377
+ [code patterns](references/a11y-patterns.md).
360
378
 
361
- - **Accessibility Inspector** (Xcode > Open Developer Tool): Audit views for missing labels, traits, and contrast issues. Run audits against the Simulator or connected device.
362
- - **VoiceOver testing**: Enable in Settings > Accessibility > VoiceOver. Navigate every screen with swipe gestures.
363
- - **Voice Control testing**: Enable in Settings > Accessibility > Voice Control. Say both "Show Names" and "Show Numbers"; names verify speakable labels, while numbers verify every visible interactive target is reachable even when names are duplicated, missing, or awkward.
364
- - **Full Keyboard Access testing**: Enable in Settings > Accessibility > Keyboards > Full Keyboard Access. Tab through every screen and verify all interactive elements receive focus.
365
- - **Switch Control testing**: Enable in Settings > Accessibility > Switch Control. Verify scan order is logical and custom actions appear for gesture-based interactions.
366
- - **Dynamic Type**: Test with all text sizes in Settings > Accessibility > Display & Text Size > Larger Text.
379
+ ## App Store Accessibility Nutrition Labels
367
380
 
368
- ### Automated Testing with XCTest
381
+ Questions about the labels, accessibility claims on the product page, or the
382
+ accessibility answers in App Store Connect: open the
383
+ [nutrition label reference](references/nutrition-labels.md) first. A claim is
384
+ only recommended after you:
369
385
 
370
- Use `XCUIElement` accessibility attributes to write UI tests that verify accessibility properties:
386
+ - ask for evidence that users can finish every common task with that feature
387
+ on the device type in question;
388
+ - lay the evidence out as a matrix of common tasks against features;
389
+ - include transcripts where audio-only content needs a text equivalent;
390
+ - say plainly that these answers must stay accurate and are not marketing.
371
391
 
372
- ```swift
373
- func testProductRowAccessibility() throws {
374
- let app = XCUIApplication()
375
- app.launch()
392
+ ## Testing
376
393
 
377
- let productCell = app.cells["product-organic-apples"]
378
- XCTAssertTrue(productCell.exists)
379
- XCTAssertTrue(productCell.isEnabled)
394
+ ### By hand
380
395
 
381
- // Verify the label is set and meaningful
382
- XCTAssertFalse(productCell.label.isEmpty)
396
+ | Tool | What to do | Where |
397
+ |---|---|---|
398
+ | Accessibility Inspector | Run an audit against the Simulator or a device; it reports contrast issues, absent labels and wrong traits | Xcode > Open Developer Tool |
399
+ | VoiceOver | Swipe from the first element to the last on each screen | Settings > Accessibility > VoiceOver |
400
+ | Voice Control | Say "Show Names", then "Show Numbers" | Settings > Accessibility > Voice Control |
401
+ | Full Keyboard Access | Press Tab across each screen and confirm each control receives focus | Settings > Accessibility > Keyboards > Full Keyboard Access |
402
+ | Switch Control | Watch the scan order; gesture features need custom actions | Settings > Accessibility > Switch Control |
403
+ | Dynamic Type | Step through the whole size range | Settings > Accessibility > Display & Text Size > Larger Text |
383
404
 
384
- // Verify a specific element has the expected label
385
- let favoriteButton = productCell.buttons["Favorite"]
386
- XCTAssertTrue(favoriteButton.exists)
387
- XCTAssertTrue(favoriteButton.isEnabled)
388
- }
389
- ```
390
-
391
- Key `XCUIElementAttributes` properties for accessibility verification: `label`, `identifier`, `value`, `isEnabled`, `hasFocus`, `isSelected`, `placeholderValue`, `title`.
405
+ ### With XCTest
392
406
 
393
- Test dismissal focus restoration:
407
+ UI tests can assert accessibility through `XCUIElement` attributes. The useful
408
+ ones in `XCUIElementAttributes` are `identifier`, `label`, `title`, `value`,
409
+ `placeholderValue`, `isSelected`, `isEnabled` and `hasFocus`.
394
410
 
395
411
  ```swift
396
- func testSheetDismissReturnsFocus() throws {
412
+ @MainActor
413
+ func testFocusReturnsAfterSheet() {
397
414
  let app = XCUIApplication()
398
415
  app.launch()
399
416
 
400
- let triggerButton = app.buttons["Open Settings"]
401
- triggerButton.tap()
402
-
403
- // Dismiss the sheet
404
- let doneButton = app.buttons["Done"]
405
- doneButton.tap()
406
-
407
- // Verify focus returns to trigger (in accessibility-focused testing)
408
- XCTAssertTrue(triggerButton.hasFocus)
417
+ let opener = app.buttons["Seat map"]
418
+ opener.tap()
419
+ app.buttons["Done"].tap()
420
+ XCTAssertTrue(opener.hasFocus)
409
421
  }
410
422
  ```
411
423
 
412
- ## Common Mistakes
413
-
414
- 1. **Direct trait assignment**: UIKit trait mutation or incorrect SwiftUI trait APIs can overwrite existing behavior. In SwiftUI, use `.accessibilityAddTraits(.isButton)`.
415
- 2. **Missing focus restoration**: Dismissing sheets without returning VoiceOver focus to the trigger element.
416
- 3. **Ungrouped list rows**: Multiple text elements per row create excessive swipe stops. Use `.accessibilityElement(children: .combine)`.
417
- 4. **Redundant trait in labels**: `.accessibilityLabel("Settings button")` reads as "Settings button, button." Omit the type.
418
- 5. **Missing labels on icon-only buttons**: Every `Image`-only button MUST have `.accessibilityLabel`.
419
- 6. **Ignoring Reduce Motion**: Always check `accessibilityReduceMotion` before movement animations.
420
- 7. **Fixed font sizes**: `.font(.system(size: 16))` ignores Dynamic Type. Use `.font(.body)` or similar text styles.
421
- 8. **Small tap targets**: Icons without `frame(minWidth: 44, minHeight: 44)` and `.contentShape()`.
422
- 9. **Color as sole indicator**: Red/green for error/success without text or icon alternatives.
423
- 10. **Missing `.isModal` on overlays**: Custom modals without `.accessibilityAddTraits(.isModal)` let VoiceOver escape.
424
-
425
- ## Review Checklist
426
-
427
- For every user-facing view, verify:
428
-
429
- - [ ] Every interactive element has an accessible label
430
- - [ ] Custom controls use correct traits via `.accessibilityAddTraits`
431
- - [ ] Adjustable custom controls expose adjustable behavior with `.accessibilityAdjustableAction` or UIKit `.adjustable`
432
- - [ ] Decorative images are hidden (`Image(decorative:)` or `.accessibilityHidden(true)`)
433
- - [ ] List rows group content with `.accessibilityElement(children: .combine)`
434
- - [ ] Sheets and dialogs return focus to the trigger on dismiss
435
- - [ ] Custom overlays have `.isModal` trait and escape action
436
- - [ ] All tap targets are at least 44x44 points
437
- - [ ] Dynamic Type supported (`@ScaledMetric`, system fonts, adaptive layouts)
438
- - [ ] Reduce Motion respected (no movement animations when enabled)
439
- - [ ] Row, checkout, sheet, and modal animations have Reduce Motion alternatives
440
- - [ ] Reduce Transparency respected (solid backgrounds when enabled)
441
- - [ ] Increase Contrast respected (stronger foreground colors)
442
- - [ ] No information conveyed by color alone
443
- - [ ] Custom actions provided for swipe-to-reveal and context menu features
444
- - [ ] Icon-only buttons have labels
445
- - [ ] Heading traits set on section headers
446
- - [ ] Custom accessibility types and notification payloads are `Sendable` when passed across concurrency boundaries
447
- - [ ] Labels are speakable and unique for Voice Control (no emoji-only or duplicate labels on screen)
448
- - [ ] Voice Control testing covers both "Show Names" and "Show Numbers"
449
- - [ ] `accessibilityInputLabels` provided for elements with long or awkward primary labels
450
- - [ ] Gesture-based interactions (swipe-to-delete, long-press) have accessibility custom action equivalents for Switch Control
451
- - [ ] Full Keyboard Access reaches and activates every control without focus traps
452
- - [ ] Element order and grouping are checked for traversal impact across VoiceOver, Switch Control, Voice Control overlays, and Full Keyboard Access review
453
- - [ ] System keyboard shortcuts are not overridden
454
-
455
- ## References
456
-
457
- - [references/a11y-patterns.md](references/a11y-patterns.md) - SwiftUI and UIKit modifier examples, grouping, custom actions, rotors, Dynamic Type
458
- - [references/nutrition-labels.md](references/nutrition-labels.md) - App Store Accessibility Nutrition Labels: current categories with pass/fail criteria
459
- - [references/media-accessibility.md](references/media-accessibility.md) - Captions, audio descriptions, AVMediaCharacteristic, SDH
424
+ A row test (querying `app.cells["id"]`, asserting `exists`, `isEnabled`, a
425
+ non-empty `label` and a nested `buttons[...]` query) is in
426
+ [code patterns](references/a11y-patterns.md#automated-checks-with-xctest).
427
+
428
+ `hasFocus` reports keyboard focus (Full Keyboard Access, hardware keyboard),
429
+ not the VoiceOver cursor, which XCTest cannot observe. Confirm VoiceOver focus
430
+ restoration by hand as well.
431
+
432
+ ## Mistakes seen most often
433
+
434
+ - Assigning traits (or using the wrong trait API) wipes existing behaviour;
435
+ use `.accessibilityAddTraits(.isButton)`.
436
+ - A sheet closes and VoiceOver focus is not put back on its trigger.
437
+ - Ungrouped list rows, one swipe stop per subview; combine them.
438
+ - The control type inside the label: `.accessibilityLabel("Filter button")`
439
+ is heard as "Filter button, button".
440
+ - `Image`-only buttons without `.accessibilityLabel`.
441
+ - Movement animations that never check `accessibilityReduceMotion`.
442
+ - Fixed sizes like `.font(.system(size: 15))` instead of `.font(.body)`.
443
+ - Small targets; fix with `.frame(minWidth: 44, minHeight: 44)` and
444
+ `.contentShape(Rectangle())`.
445
+ - Red versus green as the only error or success signal.
446
+ - A custom modal without `.accessibilityAddTraits(.isModal)`, so VoiceOver
447
+ wanders out of it.
448
+
449
+ ## Review checklist
450
+
451
+ - [ ] Every interactive element has a label.
452
+ - [ ] Custom controls get traits through `.accessibilityAddTraits`.
453
+ - [ ] Adjustable custom controls use `.accessibilityAdjustableAction`, or the
454
+ UIKit `.adjustable` trait.
455
+ - [ ] Decorative images are hidden (`Image(decorative:)` or
456
+ `.accessibilityHidden(true)`).
457
+ - [ ] Sheets and dialogs give focus back to their trigger.
458
+ - [ ] Each list row is a single combined element
459
+ (`.accessibilityElement(children: .combine)`).
460
+ - [ ] Custom overlays carry `.isModal` and an escape action.
461
+ - [ ] Every tap target is at least 44x44 pt.
462
+ - [ ] Dynamic Type works (`@ScaledMetric`, system fonts, adaptive layout).
463
+ - [ ] Reduce Motion removes movement animations.
464
+ - [ ] Each animated flow (sheet, modal, checkout, row changes) has a
465
+ reduced-motion version.
466
+ - [ ] Reduce Transparency gets solid backgrounds.
467
+ - [ ] Increase Contrast gets stronger foreground colors.
468
+ - [ ] Nothing is communicated by color alone.
469
+ - [ ] Context menus and swipe-revealed actions are also offered as custom
470
+ actions.
471
+ - [ ] Icon-only buttons are labelled.
472
+ - [ ] Section headers have the header trait.
473
+ - [ ] Any custom accessibility type or notification payload that crosses an
474
+ isolation boundary conforms to `Sendable`.
475
+ - [ ] Voice Control labels are speakable and unique (no emoji-only labels, no
476
+ duplicates on screen).
477
+ - [ ] Voice Control was exercised with the "Show Numbers" overlay as well as
478
+ "Show Names".
479
+ - [ ] Long or awkward labels have `accessibilityInputLabels`.
480
+ - [ ] Long-press and swipe-to-delete can also be done through custom actions,
481
+ for switch users.
482
+ - [ ] With Full Keyboard Access every control can be reached and triggered,
483
+ and focus never gets stuck.
484
+ - [ ] Ordering and grouping were reviewed for their effect on all four
485
+ traversal paths (VoiceOver, switch scanning, Voice Control overlays,
486
+ keyboard).
487
+ - [ ] The app leaves system keyboard shortcuts alone.