@salesforce/afv-skills 1.55.0 → 1.57.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 (274) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-generate/SKILL.md +8 -0
  3. package/skills/agentforce-generate/references/actions-reference.md +2 -2
  4. package/skills/agentforce-generate/references/voice-modality-reference.md +51 -15
  5. package/skills/agentforce-generate/references/voice-telephony-cli.md +193 -0
  6. package/skills/agentforce-observe/SKILL.md +13 -9
  7. package/skills/agentforce-observe/references/ahm-alerts.md +193 -14
  8. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +24 -15
  9. package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +11 -0
  10. package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
  11. package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +32 -1
  12. package/skills/automation-sandbox-post-copy-configure/SKILL.md +75 -71
  13. package/skills/automation-sandbox-post-copy-configure/assets/scheduled_apex_template.apex +13 -0
  14. package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json +13 -0
  15. package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +155 -0
  16. package/skills/automation-sandbox-post-copy-configure/scripts/build-scheduled-apex.mjs +62 -0
  17. package/skills/automation-sandbox-post-copy-configure/scripts/soql-escape-job-name.mjs +28 -0
  18. package/skills/commerce-b2b-open-code-components-integrate/SKILL.md +1 -1
  19. package/skills/commerce-b2b-open-code-components-replace/SKILL.md +16 -16
  20. package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +383 -0
  21. package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +324 -0
  22. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +63 -0
  23. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +100 -0
  24. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -0
  25. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +46 -0
  26. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/04-deploy-engine.js +198 -0
  27. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/05-deploy-tpm-accruals.js +231 -0
  28. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +82 -0
  29. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +517 -0
  30. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +416 -0
  31. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -0
  32. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +514 -0
  33. package/skills/consumer-goods-sync-management-configure/SKILL.md +137 -0
  34. package/skills/consumer-goods-sync-management-configure/examples/scenarios.md +294 -0
  35. package/skills/consumer-goods-sync-management-configure/references/act1-setup-sync.md +333 -0
  36. package/skills/consumer-goods-sync-management-configure/references/act2-assign-users.md +324 -0
  37. package/skills/consumer-goods-sync-management-configure/references/act3-plan-verify.md +113 -0
  38. package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md +164 -0
  39. package/skills/consumer-goods-sync-management-configure/references/sync-management-app-install-backbone.md +159 -0
  40. package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md +220 -0
  41. package/skills/consumer-goods-sync-management-configure/references/verify-and-smoke.md +175 -0
  42. package/skills/dx-devops-project-manage/SKILL.md +1 -1
  43. package/skills/dx-org-analyze/SKILL.md +0 -1
  44. package/skills/dx-org-analyze/scripts/collect_org_data.py +2 -2
  45. package/skills/dx-org-analyze/scripts/compute_diff.py +2 -4
  46. package/skills/dx-org-analyze/scripts/introspect_org.py +4 -40
  47. package/skills/dx-org-manage/SKILL.md +6 -50
  48. package/skills/dx-org-manage/examples/README.md +10 -16
  49. package/skills/dx-org-manage/references/scratch-org-create.md +1 -1
  50. package/skills/dx-org-manage/references/scratch-org-operations.md +1 -1
  51. package/skills/dx-org-shape-manage/SKILL.md +4 -3
  52. package/skills/education-cloud-domain-configure/references/gotchas-extended.md +1 -0
  53. package/skills/experience-accessibility-validate/SKILL.md +8 -5
  54. package/skills/experience-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +1 -4
  55. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +0 -4
  56. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +20 -85
  57. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +4 -4
  58. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +0 -4
  59. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +2 -4
  60. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +0 -4
  61. package/skills/experience-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +4 -14
  62. package/skills/experience-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +9 -5
  63. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +1 -4
  64. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +0 -4
  65. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -4
  66. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -4
  67. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +0 -4
  68. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +0 -4
  69. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +0 -4
  70. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +1 -4
  71. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +1 -4
  72. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +1 -4
  73. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +1 -4
  74. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +4 -5
  75. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +1 -4
  76. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +0 -4
  77. package/skills/experience-cms-content-generate/SKILL.md +1 -1
  78. package/skills/experience-cms-content-render/SKILL.md +3 -3
  79. package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +35 -7
  80. package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +13 -4
  81. package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +30 -8
  82. package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +14 -4
  83. package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +11 -1
  84. package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +1 -1
  85. package/skills/experience-cms-content-render/assets/shared/mediaLabels.ts +62 -0
  86. package/skills/experience-cms-content-render/references/init-scaffold.md +13 -6
  87. package/skills/experience-content-media-stock-image-search/SKILL.md +0 -1
  88. package/skills/experience-lwc-accessibility-jest-run/SKILL.md +104 -25
  89. package/skills/experience-lwc-accessibility-jest-run/assets/jest.config.js +4 -0
  90. package/skills/experience-lwc-accessibility-jest-run/assets/sa11y-jest-setup.js +5 -0
  91. package/skills/experience-lwc-design-generate/SKILL.md +1 -1
  92. package/skills/experience-lwc-legacy-migrate/SKILL.md +212 -0
  93. package/skills/experience-lwc-legacy-migrate/assets/lo20-host-page-template.html +192 -0
  94. package/skills/experience-lwc-legacy-migrate/references/aura-to-lwc-completeness-checklist.md +188 -0
  95. package/skills/experience-lwc-legacy-migrate/references/lightning-out-2-system-reference.md +495 -0
  96. package/skills/experience-lwc-legacy-migrate/references/lightning-out-beta-to-2-migration.md +824 -0
  97. package/skills/experience-lwc-legacy-migrate/scripts/convert-lo-names.py +94 -0
  98. package/skills/experience-lwc-legacy-migrate/scripts/validate-lo20-page.py +133 -0
  99. package/skills/experience-ui-bundle-app-coordinate/SKILL.md +5 -5
  100. package/skills/experience-ui-bundle-features-generate/SKILL.md +65 -36
  101. package/skills/experience-ui-bundle-features-generate/references/angular/features.md +74 -0
  102. package/skills/experience-ui-bundle-features-generate/references/react/features.md +59 -0
  103. package/skills/experience-ui-bundle-features-generate/scripts/detect-framework.sh +73 -0
  104. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -2
  105. package/skills/experience-ui-bundle-metadata-generate/SKILL.md +26 -14
  106. package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +37 -0
  107. package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +32 -0
  108. package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +73 -0
  109. package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +30 -8
  110. package/skills/experience-ui-bundle-project-generate/SKILL.md +1 -1
  111. package/skills/field-service-data-capture-migrate/SKILL.md +391 -0
  112. package/skills/field-service-data-capture-migrate/assets/sample-legacy-flow.flow-meta.xml +163 -0
  113. package/skills/field-service-data-capture-migrate/references/architecture-notes.md +40 -0
  114. package/skills/field-service-data-capture-migrate/references/component-mapping.md +278 -0
  115. package/skills/field-service-data-capture-migrate/references/converter-builder-contract.md +183 -0
  116. package/skills/field-service-data-capture-migrate/references/dc-platform-rules.md +130 -0
  117. package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +215 -0
  118. package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +36 -0
  119. package/skills/field-service-data-capture-migrate/references/migration-checklist.md +121 -0
  120. package/skills/field-service-data-capture-migrate/references/transformer-rules.md +132 -0
  121. package/skills/field-service-data-capture-migrate/scripts/analyze_flow.py +277 -0
  122. package/skills/field-service-data-capture-migrate/scripts/convert_to_dc_spec.py +1311 -0
  123. package/skills/field-service-data-capture-migrate/scripts/cud_analysis.py +402 -0
  124. package/skills/field-service-data-capture-migrate/scripts/deploy_flow.sh +230 -0
  125. package/skills/field-service-data-capture-migrate/scripts/discover_subflow_tree.py +230 -0
  126. package/skills/field-service-data-capture-migrate/scripts/flow_input.py +186 -0
  127. package/skills/field-service-data-capture-migrate/scripts/flow_xml_utils.py +39 -0
  128. package/skills/field-service-data-capture-migrate/scripts/fsm_dc_catalog.py +413 -0
  129. package/skills/field-service-data-capture-migrate/scripts/generate_summary.py +199 -0
  130. package/skills/field-service-data-capture-migrate/scripts/json_to_flow_xml.py +146 -0
  131. package/skills/field-service-data-capture-migrate/scripts/manual_review_flags.py +42 -0
  132. package/skills/field-service-data-capture-migrate/scripts/retrieve_flow_rest.sh +155 -0
  133. package/skills/field-service-data-capture-migrate/scripts/subflow_names.py +49 -0
  134. package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +1661 -0
  135. package/skills/field-service-data-capture-migrate/scripts/validate_flow.sh +147 -0
  136. package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +1258 -0
  137. package/skills/integration-connectivity-connected-app-configure/SKILL.md +5 -5
  138. package/skills/integration-connectivity-generate/SKILL.md +10 -10
  139. package/skills/integration-connectivity-generate/scripts/configure-named-credential.sh +2 -2
  140. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +1 -1
  141. package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +1 -1
  142. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +14 -1
  143. package/skills/mobile-apps-create/SKILL.md +1 -1
  144. package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/SKILL.md +7 -7
  145. package/skills/platform-apex-test-generate/SKILL.md +1 -1
  146. package/skills/platform-apex-test-run/SKILL.md +1 -1
  147. package/skills/platform-custom-field-generate/SKILL.md +1 -1
  148. package/skills/platform-custom-lightning-type-generate/SKILL.md +1 -1
  149. package/skills/platform-custom-metadata-type-generate/SKILL.md +1 -1
  150. package/skills/platform-custom-object-generate/SKILL.md +1 -1
  151. package/skills/platform-custom-report-type-generate/SKILL.md +1 -1
  152. package/skills/platform-custom-setting-generate/SKILL.md +1 -1
  153. package/skills/platform-data-and-tooling-api-context-get/SKILL.md +1 -1
  154. package/skills/platform-data-manage/SKILL.md +10 -10
  155. package/skills/platform-flexipage-generate/SKILL.md +1 -1
  156. package/skills/platform-lightning-app-coordinate/SKILL.md +1 -1
  157. package/skills/platform-metadata-api-context-get/SKILL.md +1 -1
  158. package/skills/platform-metadata-retrieve/SKILL.md +1 -1
  159. package/skills/platform-permission-set-generate/SKILL.md +1 -1
  160. package/skills/platform-report-generate/SKILL.md +1 -1
  161. package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +4 -4
  162. package/skills/platform-sandbox-configure/SKILL.md +108 -133
  163. package/skills/platform-sandbox-configure/references/api-response-shapes.md +39 -0
  164. package/skills/platform-sandbox-configure/references/definition-file-approach.md +73 -0
  165. package/skills/platform-sharing-owd-configure/SKILL.md +1 -1
  166. package/skills/platform-sharing-rules-generate/SKILL.md +1 -1
  167. package/skills/platform-soql-query/SKILL.md +1 -1
  168. package/skills/platform-value-set-generate/SKILL.md +1 -1
  169. package/skills/platform-widget-generate/SKILL.md +26 -18
  170. package/skills/platform-widget-generate/examples/formulas.json +191 -0
  171. package/skills/platform-widget-generate/references/widget-formulas.md +123 -0
  172. package/skills/platform-widget-generate/references/widget-meta-directives.md +4 -4
  173. package/skills/sales-call-scoring-configure/SKILL.md +318 -0
  174. package/skills/sales-call-scoring-configure/assets/best-practice-competencies.json +67 -0
  175. package/skills/sales-call-scoring-configure/assets/ootb-competencies.json +58 -0
  176. package/skills/sales-call-scoring-configure/references/admin-communication.md +79 -0
  177. package/skills/sales-call-scoring-configure/references/competency-crud.md +130 -0
  178. package/skills/sales-call-scoring-configure/scripts/create-competency.sh +166 -0
  179. package/skills/sales-call-scoring-configure/scripts/create-custom-competency.sh +166 -0
  180. package/skills/sales-call-scoring-configure/scripts/edit-competency.sh +185 -0
  181. package/skills/sales-call-scoring-configure/scripts/edit-custom-competency.sh +190 -0
  182. package/skills/sales-call-scoring-configure/scripts/enable-call-scoring.sh +436 -0
  183. package/skills/sales-call-scoring-configure/scripts/get-competency.sh +72 -0
  184. package/skills/sales-call-scoring-configure/scripts/get-custom-competency.sh +78 -0
  185. package/skills/sales-call-scoring-configure/scripts/install-best-practice-competencies.sh +223 -0
  186. package/skills/sales-call-scoring-configure/scripts/install-ootb-competencies.sh +237 -0
  187. package/skills/sales-call-scoring-configure/scripts/list-competencies.sh +99 -0
  188. package/skills/sales-call-scoring-configure/scripts/shared/auth.sh +116 -0
  189. package/skills/sales-call-scoring-configure/scripts/shared/cap.sh +86 -0
  190. package/skills/sales-call-scoring-configure/scripts/shared/soap.sh +267 -0
  191. package/skills/sales-call-scoring-configure/scripts/shared/soql.sh +25 -0
  192. package/skills/sales-call-scoring-configure/scripts/toggle-competency.sh +112 -0
  193. package/skills/sales-call-scoring-configure/scripts/toggle-custom-competency.sh +125 -0
  194. package/skills/sales-call-scoring-configure/scripts/verify-ootb-competencies.sh +116 -0
  195. package/skills/service-agentforce-human-escalation-configure/SKILL.md +4 -0
  196. package/skills/service-agentforce-human-escalation-configure/scripts/tests/_bootstrap.py +26 -6
  197. package/skills/service-agentforce-human-escalation-configure/scripts/tests/test_escalation_contracts.py +5 -4
  198. package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1 -0
  199. package/skills/service-de-channel-activate/SKILL.md +6 -5
  200. package/skills/service-de-channel-create/SKILL.md +3 -3
  201. package/skills/service-de-channel-routing-configure/SKILL.md +3 -3
  202. package/skills/service-de-channel-settings-configure/SKILL.md +195 -0
  203. package/skills/service-de-channel-settings-configure/references/channel-settings-facets.md +152 -0
  204. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/gotchas.md +3 -3
  205. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/language-keywords.md +4 -4
  206. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/worked-examples.md +14 -14
  207. package/skills/service-de-headless-channel-configure/SKILL.md +6 -6
  208. package/skills/service-de-headless-channel-configure/references/worked-examples.md +1 -1
  209. package/skills/service-de-waba-integrate/SKILL.md +2 -1
  210. package/skills/service-digital-engagement-deployment-configure/SKILL.md +2 -0
  211. package/skills/service-digital-engagement-deployment-configure/scripts/check-api-version.sh +29 -0
  212. package/skills/service-email-to-case-configure/SKILL.md +1 -1
  213. package/skills/service-helpagent-coordinate/SKILL.md +3 -2
  214. package/skills/service-helpagent-coordinate/references/channel-voice.md +15 -4
  215. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +49 -31
  216. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +71 -6
  217. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +26 -4
  218. package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +35 -10
  219. package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +25 -5
  220. package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -1
  221. package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +5 -5
  222. package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +1 -1
  223. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +2 -2
  224. package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +1 -1
  225. package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +25 -15
  226. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -5
  227. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +4 -2
  228. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +1 -1
  229. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +29 -16
  230. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -5
  231. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +4 -2
  232. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +1 -1
  233. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +29 -16
  234. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +138 -68
  235. package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +31 -15
  236. package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +156 -0
  237. package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +148 -0
  238. package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +162 -0
  239. package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +149 -0
  240. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +103 -89
  241. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
  242. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +21 -39
  243. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +3 -3
  244. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +18 -1
  245. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +63 -37
  246. package/skills/service-omni-attribute-routing-configure/SKILL.md +53 -0
  247. package/skills/service-omni-attribute-routing-configure/references/api-notes.md +7 -0
  248. package/skills/service-omni-attribute-routing-configure/scripts/configure-and-report.sh +224 -0
  249. package/skills/service-omni-attribute-routing-configure/scripts/tests/test_attribute_routing_contracts.py +137 -0
  250. package/skills/service-omni-channel-inventory-analyze/SKILL.md +96 -0
  251. package/skills/service-omni-channel-inventory-analyze/references/api-notes.md +52 -0
  252. package/skills/service-omni-channel-inventory-analyze/scripts/analyze.sh +185 -0
  253. package/skills/service-omni-channel-inventory-analyze/scripts/tests/_bootstrap.py +84 -0
  254. package/skills/service-omni-channel-inventory-analyze/scripts/tests/test_inventory_analyze_contracts.py +141 -0
  255. package/skills/service-omni-channel-limits-analyze/SKILL.md +70 -0
  256. package/skills/service-omni-channel-limits-analyze/references/api-notes.md +22 -0
  257. package/skills/service-omni-channel-limits-analyze/scripts/analyze.sh +159 -0
  258. package/skills/service-omni-channel-limits-analyze/scripts/tests/_bootstrap.py +87 -0
  259. package/skills/service-omni-channel-limits-analyze/scripts/tests/test_limits_analyze_contracts.py +99 -0
  260. package/skills/service-omni-channel-setup-coordinate/SKILL.md +12 -20
  261. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +55 -5
  262. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +38 -0
  263. package/skills/service-omni-command-center-analyze/SKILL.md +2 -2
  264. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
  265. package/skills/dx-org-manage/examples/snapshots/error_output.json +0 -9
  266. package/skills/dx-org-manage/examples/snapshots/success_output.json +0 -15
  267. package/skills/dx-org-manage/references/cli_flags.md +0 -67
  268. package/skills/dx-org-manage/references/creating-snapshot.md +0 -103
  269. package/skills/dx-org-manage/references/snapshot_usage.md +0 -74
  270. package/skills/experience-accessibility-validate/scripts/contrast-ratio.py +0 -153
  271. package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +0 -205
  272. package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +0 -14
  273. /package/skills/experience-ui-bundle-features-generate/references/{conflict-resolution-schema.json → common/conflict-resolution-schema.json} +0 -0
  274. /package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/references/api-response-schema.md +0 -0
@@ -11,16 +11,10 @@ This reviewer focuses on ensuring enough contrast between text and its backgroun
11
11
  1. Identify all visible text elements (e.g., in tags like `<h1>`, `<div>`, `<p>`, `<label>`, `<button>`, etc.).
12
12
  2. Determine the **color** (foreground) and **background color** (background) for each text element.
13
13
  3. Determine the **font size** and **weight** to classify the text as Standard or Large-Scale.
14
- 4. Compute the contrast ratio with the bundled helper — **do not calculate luminance by hand**:
14
+ 4. Calculate the contrast ratio using the WCAG formula: $(L1 + 0.05) / (L2 + 0.05)$, where L1 is the relative luminance of the lighter color, and L2 is the relative luminance of the darker color.
15
+ 5. Check if the calculated ratio meets the required threshold (4.5:1 or 3:1).
15
16
 
16
- ```bash
17
- python3 scripts/contrast-ratio.py FOREGROUND BACKGROUND [--large]
18
- ```
19
-
20
- Pass the resolved foreground and background colors (`#RGB`, `#RRGGBB`, `rgb()`, or `rgba()`); add `--large` when the text is large-scale (≥24px, or ≥18.66px bold). The script alpha-composites a translucent *foreground* over the background, applies the exact WCAG formula $(L1 + 0.05) / (L2 + 0.05)$, and prints the ratio plus a `PASS`/`FAIL` verdict for both thresholds (exit code `0` = meets the applicable threshold, `1` = below it, `2` = parse error). The **background must be opaque** — a translucent `rgba()` background is rejected (exit code `2`) because its rendered color depends on the unknown layer beneath it; resolve the effective opaque background first and pass that. Use `--json` for a machine-readable object. The script's verdict is authoritative; use it rather than an estimate.
21
- 5. Treat the script's verdict against the required threshold (4.5:1 standard, 3:1 large) as the check result.
22
-
23
- > **Do not estimate luminance by hand.** Mentally approximating relative luminance is unreliable — it has produced false positives and even different ratios for the _same_ color. Run `scripts/contrast-ratio.py` for the exact value. The verified reference below (sRGB gray `#NNNNNN` on `#FFFFFF`) is a quick sanity check for the common grayscale-text-on-white case, not a substitute for the script:
17
+ > **Do not estimate luminance by hand.** Mentally approximating relative luminance is unreliable — it has produced false positives and even different ratios for the _same_ color. Compute the WCAG formula exactly, or for the common grayscale-text-on-white case use this verified reference (sRGB gray `#NNNNNN` on `#FFFFFF`):
24
18
  >
25
19
  > | Gray | Ratio | Standard (4.5:1) | Large (3:1) |
26
20
  > | --------- | ----- | ---------------- | --------------- |
@@ -104,12 +98,8 @@ Rules to follow:
104
98
  - Flag color combinations that result in a contrast ratio **below 4.5:1 for Standard Text**.
105
99
  - Flag color combinations that result in a contrast ratio **below 3:1 for Large-Scale Text**.
106
100
  - **Ignore** text clearly marked as decorative, part of a logo, or permanently disabled/inactive components.
107
- - Do not assume or estimate the contrast ratio — compute it with `scripts/contrast-ratio.py` (which also handles alpha compositing), falling back to the grayscale-on-white reference table above only as a sanity check. When the resolved colors themselves are uncertain (gradients, images behind text, unknown background), do not flag.
101
+ - Do not assume or estimate the contrast ratio — compute it exactly, or use the grayscale-on-white reference table above. When the exact ratio is uncertain (gradients, alpha compositing, images behind text, unknown background), do not flag.
108
102
  - For each issue found, provide a separate, detailed report.
109
103
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
110
104
  - Assume that any imported functionality works as expected and was already analyzed.
111
105
  - Components do not supplement or provide global functionalities.
112
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
113
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
114
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
115
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
@@ -6,7 +6,14 @@ All functionality of the content is operable through a keyboard interface withou
6
6
 
7
7
  Analyze the given file using the following framework:
8
8
 
9
- Review the provided HTML and JS files for accessibility violations per WCAG 2.2 SC 2.1.1, 'Keyboard'. For all elements on the page, ensure that the element exposes all interactivity that is achievable via mouse to keyboard in order to determine if they comply with SC 2.1.1. If they don't comply with the success criterion, then flag them a violation.
9
+ Review the provided HTML and JS files for accessibility violations per WCAG 2.2 SC 2.1.1, 'Keyboard'.
10
+
11
+ **Scope — this criterion applies to _interactive_ elements only; native controls pass.** A violation requires an element that is operable by mouse/pointer (a click/pointer/mouse handler, or a custom control role) but is **not** equivalently operable by keyboard. Before flagging anything, apply these two gates — an element that clears them **passes** and must not be flagged:
12
+
13
+ - **Native interactive HTML is keyboard-operable for free.** `<a href>`, `<button>`, `<input>` (including `type="checkbox"`, `type="radio"`, `type="submit"`, etc.), `<select>`, `<textarea>`, and `<summary>` receive focus and handle keyboard activation by default. Do **not** flag a native control for a missing `tabindex`, missing keyboard handler, or keyboard inoperability unless its native behavior has been explicitly overridden or suppressed.
14
+ - **Non-interactive elements are out of scope.** A `<div>`, `<span>`, `<li>`, `<p>`, etc. that has **no** click/pointer/key handler and no interactive role is not a control — it does not need `tabindex` and must not be flagged. Only a non-semantic element that _carries a click/pointer handler_ (making it a de-facto control) needs `tabindex="0"` plus a keyboard handler.
15
+
16
+ For an element that is in scope (an interactive control), ensure it exposes via keyboard all interactivity achievable via mouse; if it does not, flag it as a violation. The exception is when the underlying functionality requires path-based movement.
10
17
 
11
18
  Interactive controls must be accessible when using the keyboard alone, and when using a keyboard along with assistive technology, such as a screen reader, without requiring specific timings for keystrokes. The exception to this is when the underlying functionality requires path-based movement.
12
19
 
@@ -41,7 +48,4 @@ Rules to follow:
41
48
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
42
49
  - Assume that any imported functionality works as expected and was already analyzed.
43
50
  - Components do not supplement or provide global functionalities.
44
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
45
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
46
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
47
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
51
+ - Prefer a real `<button>` for actions. If an anchor must act as a control, use the canonical SLDS `href="#"` plus `event.preventDefault()` pattern and supply the complete button-like semantics and keyboard behavior. Do not recommend `href="javascript:void(0)"` as a first-line fix, and do not flag that value alone as a keyboard violation.
@@ -33,7 +33,4 @@ Rules to follow:
33
33
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
34
34
  - Assume that any imported functionality works as expected and was already analyzed.
35
35
  - Components do not supplement or provide global functionalities.
36
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
37
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
38
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
39
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
36
+ - Judge whether link text describes its destination, not whether the destination opens in a new window. `target="_blank"` and click handlers that call `window.open(...)` are not SC 2.4.4 failures. If new-tab behavior is mentioned, describe indicating it as an optional enhancement rather than a warning or violation.
@@ -44,7 +44,3 @@ Rules to follow:
44
44
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
45
45
  - Assume that any imported functionality works as expected and was already analyzed.
46
46
  - Components do not supplement or provide global functionalities.
47
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
48
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
49
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
50
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
@@ -48,7 +48,3 @@ Rules to follow:
48
48
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
49
49
  - Assume that any imported functionality works as expected and was already analyzed.
50
50
  - Components do not supplement or provide global functionalities.
51
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
52
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
53
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
54
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
@@ -43,7 +43,3 @@ Rules to follow:
43
43
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
44
44
  - Assume that any imported functionality works as expected and was already analyzed.
45
45
  - Components do not supplement or provide global functionalities.
46
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
47
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
48
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
49
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
@@ -71,7 +71,3 @@ Rules to follow:
71
71
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
72
72
  - Assume that any imported functionality works as expected and was already analyzed.
73
73
  - Components do not supplement or provide global functionalities.
74
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
75
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
76
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
77
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
@@ -39,7 +39,3 @@ _Rules to follow_:
39
39
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
40
40
  - Assume that any imported functionality works as expected and was already analyzed.
41
41
  - Components do not supplement or provide global functionalities.
42
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
43
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
44
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
45
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
@@ -49,7 +49,3 @@ _Rules to follow_:
49
49
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
50
50
  - Assume that any imported functionality works as expected and was already analyzed.
51
51
  - Components do not supplement or provide global functionalities.
52
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
53
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
54
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
55
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
@@ -45,7 +45,4 @@ _Rules to follow_ :
45
45
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
46
46
  - Assume that any imported functionality works as expected and was already analyzed.
47
47
  - Components do not supplement or provide global functionalities.
48
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
49
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
50
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
51
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
48
+ - Opening a link or button target in a new browser tab or window (`target="_blank"` or a click handler that calls `window.open(...)`) is not an SC 3.2.2 violation. This criterion concerns unexpected context changes caused by changing a form control's value or state, not the expected result of deliberately activating a link or button. Treat indicating new-tab behavior as optional advisory guidance, never as a Level A or AA failure.
@@ -78,7 +78,4 @@ Rules to follow:
78
78
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
79
79
  - Assume that any imported functionality works as expected and was already analyzed.
80
80
  - Components do not supplement or provide global functionalities.
81
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
82
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
83
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
84
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
81
+ - Associate error text with the actual focusable control. An `aria-describedby` or `aria-errormessage` on a non-focusable wrapper does not reach a composite editor's internal control unless the wrapper forwards it. A disabled control cannot receive focus, so attaching the only error explanation to it also cannot make that explanation available on focus. Use an adjacent visible message, a live region where appropriate, or the control's supported error-association API.
@@ -44,7 +44,4 @@ Rules to follow:
44
44
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
45
45
  - Assume that any imported functionality works as expected and was already analyzed.
46
46
  - Components do not supplement or provide global functionalities.
47
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
48
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
49
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
50
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
47
+ - Attach instructions to the actual focusable control. An `aria-describedby` on a disabled control cannot expose the instructions on focus, and an association on a non-focusable wrapper does not reach a composite editor's internal control unless the wrapper forwards it. Use adjacent visible instructions or the control's supported association API instead.
@@ -58,7 +58,4 @@ Rules to follow:
58
58
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
59
59
  - Assume that any imported functionality works as expected and was already analyzed.
60
60
  - Components do not supplement or provide global functionalities.
61
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
62
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
63
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
64
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
61
+ - Associate an error suggestion with the actual focusable control. An `aria-describedby` or `aria-errormessage` on a non-focusable wrapper does not reach a composite editor's internal control unless the wrapper forwards it. A disabled control cannot expose described text on focus. Use adjacent visible guidance, a live region where appropriate, or the control's supported error-association API.
@@ -73,16 +73,13 @@ Following are some preferred sufficient techniques that can help satisfy name-on
73
73
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
74
74
  - Assume that any imported functionality works as expected and was already analyzed.
75
75
  - Components do not supplement or provide global functionalities.
76
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
77
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
78
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
79
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
80
76
 
81
77
  **ABSOLUTE AND STRICT EXCLUSION RULES FOR SC 4.1.2 'NAME' ANALYSIS (AND WHEN NOT TO FLAG):**
82
78
 
83
79
  - **PRIMARY RULE:** A user interface component **PASSES** the 'Name' criterion if it has _any_ programmatically determinable accessible name, whether visible or invisible, provided through valid HTML or ARIA.
80
+ - **DETERMINE HOW THE ACCESSIBLE NAME IS SUPPLIED IN THE PROVIDED MARKUP BEFORE CONCLUDING A NAME IS MISSING.** Before flagging an element for a missing name, determine its role and applicable accessible-name sources. Check for text content only when the role or host-language semantics support naming from content; otherwise look for a native labeling mechanism, `aria-label`, `aria-labelledby`, or a valid `<label>` association. Proximity alone is not a naming relationship: an adjacent `<label>`, heading, or text node names a control only when the markup explicitly associates them. A named `<fieldset>`, `<section>`, `role="group"`, or `role="radiogroup"` provides group context but does **not** supply an accessible name to each descendant control; every descendant input, button, or other control still needs its own valid name. Evaluate the group container separately: a `<legend>` names its containing `<fieldset>`, while `aria-label` or `aria-labelledby` names the group element on which it appears. Also do not flag an icon or `<svg>` marked `aria-hidden="true"` or `role="presentation"` for a missing name: it is intentionally removed from the accessibility tree, while the interactive parent must provide its own name.
84
81
  - **DO NOT FLAG:** If an element already has a programmatically determinable name via:
85
- - Its text content (e.g., button text, heading text, link text).
82
+ - Its text content when its role or host-language semantics support naming from content (e.g., button text, heading text, link text).
86
83
  - An associated `<label>` element.
87
84
  - An `aria-label` attribute.
88
85
  - An `aria-labelledby` attribute (and the referenced ID exists within the provided code).
@@ -103,3 +100,5 @@ Following are some preferred sufficient techniques that can help satisfy name-on
103
100
  - **For `spinbutton` and similar roles (e.g., `slider`, `progressbar`)**: Ensure `aria-valuemin`, `aria-valuemax`, and `aria-valuenow` use **numeric** values. Flag as an error if non-numeric values are used.
104
101
  - **Focus strictly on identifying where a user interface component's _accessible name_ cannot be programmatically determined, or where an explicit ARIA naming attribute (if used) is invalid or misleading (e.g., empty `aria-label`, `aria-labelledby` referencing non-existent ID).**
105
102
  - **DO NOT flag Salesforce Lightning Base Components (e.g., `lightning-card`, `lightning-record-edit-form`, `lightning-input-field`, `lightning-button`). Assume these components **automatically pass** the 'Name' criterion because Salesforce ensures their accessible names are handled implicitly and correctly.** Only flag if a custom override _explicitly breaks_ their inherent name determination, which is highly unlikely in standard usage.
103
+ - **ARIA SPELLING IS ELEMENT-SPECIFIC:** Plain HTML elements use `aria-labelledby` and `aria-describedby`; the hyphen-split `aria-labelled-by` or `aria-described-by` spelling is invalid there. On an LWC component tag, however, the hyphen-split spelling can be the kebab-case binding for an `@api ariaLabelledBy` or `ariaDescribedBy` property. Do not conclude that the component lacks a name from the spelling alone.
104
+ - **ASSOCIATE THE ACTUAL CONTROL:** An accessible name attached to a non-focusable wrapper around a composite editor does not name the editor's internal control unless the wrapper forwards it. Recommend extending the control to accept or forward the naming relationship, or using its supported labeling prop.
@@ -93,7 +93,4 @@ Rules to follow:
93
93
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
94
94
  - Assume that any imported functionality works as expected and was already analyzed.
95
95
  - Components do not supplement or provide global functionalities.
96
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
97
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
98
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
99
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
96
+ - Prefer a real `<button>` for actions. Do not recommend `href="javascript:void(0)"` as a role fix: that value alone does not give an anchor button semantics. If converting the anchor is impossible, any anchor-based remediation must provide the complete role and keyboard behavior needed for an accessible button. Do not flag an existing `javascript:void(0)` value by itself as a role violation.
@@ -91,7 +91,3 @@ _Rules to follow_:
91
91
  - Keep issues concise, avoid duplicated issues or unnecessary or non-applicable problems.
92
92
  - Assume that any imported functionality works as expected and was already analyzed.
93
93
  - Components do not supplement or provide global functionalities.
94
- - Lightning base components (`lightning-input`, `lightning-combobox`, `lightning-textarea`, `lightning-icon`, etc.) provide their own accessible labeling, ARIA, and error handling in shadow DOM via their dedicated attributes (`label`, `alternative-text`, field-level validation). When such a component is given a `label` attribute it is already programmatically labeled — even with `variant="label-hidden"`, which hides the label visually but keeps it for assistive technology. Do **not** flag a sibling/wrapper `<label>` or `<span>` for a missing `for`/`id` when the actual control is a self-labeling Lightning component, and do not treat a visible heading as if it were the control's only label. This also covers navigation and icon base components (`lightning-vertical-navigation-item`, `lightning-vertical-navigation-item-icon`, `lightning-button-icon`, etc.): when a `label` (or `alternative-text`) attribute is supplied, the base component renders it as the visible link/control text **and** exposes it as the accessible name, and any `icon-name` is decorative — do not emit a 4.1.2 "verify the label is associated with the icon" warning, even a hedged one. The presence of the `label` attribute is the association.
95
- - Resolve template bindings before judging them. A template expression like `{someValue}` refers to a property or getter on the component's controller (`.js`/`.ts`) — including `@api` properties and `get someValue()` getters — not to a module-level imported constant (which LWC templates cannot bind to). Check the controller for the binding's definition before concluding a value is undefined, empty, or missing. When reviewing a diff, verify which side is the corrected code; do not assume the change introduced the problem.
96
- - Prefer a real `<button>` for actions; for a link, `href="#"` with `event.preventDefault()` (the canonical SLDS pattern). Don't recommend `href="javascript:void(0)"` as a first-line remediation — it's a no-op-anchor anti-pattern. It is acceptable only as a last resort when moving `<a>` → `<button>` is genuinely impossible, and even then only when paired with the full set of attributes that make the anchor behave as an accessible button (`role="button"`, keyboard activation, etc.) — `void(0)` on its own is an incomplete fix. Don't flag existing `javascript:void(0)` as a violation.
97
- - ARIA attribute spelling depends on the element type. On a **plain HTML element** (`div`, `span`, `button`, `input`, `a`, `img`, `iframe`, …) the only valid spelling is the W3C form `aria-labelledby` / `aria-describedby`; the hyphen-split `aria-labelled-by` / `aria-described-by` there **is** a real bug — keep flagging it. But on an **LWC component tag** (`lightning-*` or a custom `ns-name` element), the hyphen-split form is the kebab-case binding of the component's `@api ariaLabelledBy` / `ariaDescribedBy` property (each capital letter maps to `-` + lowercase), and LWC reflects it to the spec-correct `aria-labelledby` in the rendered DOM. Do **not** flag `aria-labelled-by` on a component tag as a misspelling, and do not claim it "breaks the accessible name computation" — you cannot determine that from the attribute name alone, because both `aria-labelled-by` (via `@api` property kebab-casing) and `aria-labelledby` (via LWC's ARIA reflection) can wire to the same property depending on the component. Treat a hyphen-split ARIA attribute on a component tag as a valid binding, not a violation.
@@ -6,8 +6,8 @@ metadata:
6
6
  domains: ["Experience"]
7
7
  minApiVersion: "64.0"
8
8
  relatedSkills:
9
- - "experience-cms-content-type-generate"
10
9
  - "experience-cms-content-render"
10
+ - "experience-cms-content-type-generate"
11
11
  cliTools:
12
12
  - tool: ["sf"]
13
13
  semver: ">=2.0.0"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: experience-cms-content-render
3
- description: "Renders, embeds, or displays existing Salesforce CMS content in a React or Angular uiBundle app: installs the CMS toolkit package, registers a typed reference, generates a framework-matched renderer from the live schema, and wires the render path (delivery API, contentKey, RichText, image resolution). Use it — and prefer it over general UI-bundle skills like experience-ui-bundle-frontend-generate, which own only the page/layout shell — for the CMS-rendering part of a prompt, even when editing files under src/. Triggers on 'render the CMS news article on the home page', 'embed the CMS blog post', 'display the featured product in the hero', 'show the blog post on a detail page', 'render the CMS video on the home page', 'show the CMS image in the hero'; if a prompt mixes page/layout work with CMS rendering, activate for the CMS part. Do not trigger when only searching for a CMS item, or when defining a new CMS content type/schema (use experience-cms-content-type-generate)."
3
+ description: "Renders, embeds, or displays existing Salesforce CMS content in a React or Angular uiBundle app: installs the CMS toolkit, registers a typed reference, generates a framework-matched renderer from the live schema, and wires the render path (delivery API, contentKey, RichText, image resolution). Prefer over general UI-bundle skills like experience-ui-bundle-frontend-generate (page/layout shell only) for the CMS-rendering part, even under src/. Triggers on prompts like 'render the CMS article on the home page', 'embed this CMS blog post here', 'show the CMS video in the hero', 'display the CMS image in the banner', or 'add this CMS content block to the detail page'; for mixed prompts, activate for the CMS part only. Do not trigger for searching a CMS item or defining a new CMS content type/schema (use experience-cms-content-type-generate)."
4
4
  metadata:
5
5
  version: "2.1"
6
6
  domains: ["Experience"]
@@ -168,6 +168,6 @@ Dynamic feed discovery ("latest 10 news"); cross-ref audit tooling; media/image-
168
168
 
169
169
  ## Files & references
170
170
 
171
- **Templates** — `assets/shared/`: `cmsCore.types.ts`, `externalRefs.ts`, `cmsContentType.ts` (foundation + per-type body). `assets/react/`: `useCmsItem.ts`, `heuristicRenderer.tsx` (renderer + `RenderField` dispatcher), `TypeRenderer.tsx`, `TypeList.tsx`, `DetailPage.tsx`, `MediaRenderer.tsx`. `assets/angular/`: `cms-item.service.ts`, `cms-content.component.ts`, `TypeRenderer.component.ts`, `TypeList.component.ts`, `DetailPage.component.ts`, `MediaRenderer.component.ts`.
171
+ **Templates** — `assets/shared/`: `cmsCore.types.ts`, `externalRefs.ts`, `cmsContentType.ts` (foundation + per-type body), `mediaLabels.ts`. `assets/react/`: `useCmsItem.ts`, `heuristicRenderer.tsx` (renderer + `RenderField` dispatcher), `TypeRenderer.tsx`, `TypeList.tsx`, `DetailPage.tsx`, `MediaRenderer.tsx`. `assets/angular/`: `cms-item.service.ts`, `cms-content.component.ts`, `TypeRenderer.component.ts`, `TypeList.component.ts`, `DetailPage.component.ts`, `MediaRenderer.component.ts`.
172
172
 
173
- **Docs** (`references/`): `package-api.md` (toolkit API + response contract — ground truth), `interaction-model.md` (question wording + terminal stops), `init-scaffold.md` (runtime tree, copy set, foundation sets), `schema-sync.md` (retrieval + regen), `verify.md` (fix policy + report), `codegen-guardrails.md` (full Rule 1–5), `embed-recipes.md` (URL regex + placement), `heuristic-render-rules.md`, `detail-pages.md`, `styling-scopes.md`, `bulk-loading.md`, `failure-modes.md`.
173
+ **Docs** (`references/`): `package-api.md` (toolkit API + contract — ground truth), `interaction-model.md` (wording + terminal stops), `init-scaffold.md` (runtime tree, copy set, foundation sets), `schema-sync.md` (retrieval + regen), `verify.md` (fix policy + report), `codegen-guardrails.md` (full Rule 1–5), `embed-recipes.md` (URL regex + placement), `heuristic-render-rules.md`, `detail-pages.md`, `styling-scopes.md`, `bulk-loading.md`, `failure-modes.md`.
@@ -30,6 +30,7 @@ import {
30
30
  type CmsMediaBody,
31
31
  type CmsMediaType,
32
32
  } from '../shared/cmsCore.types';
33
+ import { deriveDownloadLabel, effectiveAudioLabel } from '../shared/mediaLabels';
33
34
 
34
35
  @Component({
35
36
  selector: 'cms-media',
@@ -48,10 +49,21 @@ import {
48
49
  @if (src) { <img [class]="className" [src]="src" [alt]="alt" loading="lazy" /> }
49
50
  }
50
51
  @case ('audio') {
51
- @if (src) { <audio [class]="className" controls [src]="src" [attr.aria-label]="alt || null"></audio> }
52
+ <!-- [attr.aria-label]="alt || null" would REMOVE the attribute when alt is
53
+ empty (Angular attribute-binding semantics) — audioLabel always has a
54
+ value, so the control never loses its accessible name. -->
55
+ @if (src) { <audio [class]="className" controls [src]="src" [attr.aria-label]="audioLabel"></audio> }
52
56
  }
53
57
  @case ('video') {
54
- @if (src) { <video [class]="className" controls [src]="src" [attr.aria-label]="alt || null"></video> }
58
+ <!-- Captions render only when the ref supplies `captionsSrc` (SKILL.md §B4)
59
+ — there is no fetched-body field to derive real caption text from. -->
60
+ @if (src) {
61
+ <video [class]="className" controls [src]="src" [attr.aria-label]="alt || null">
62
+ @if (captionsSrc) {
63
+ <track kind="captions" [src]="captionsSrc" [label]="captionsLabel" default />
64
+ }
65
+ </video>
66
+ }
55
67
  }
56
68
  @case ('document') {
57
69
  <!-- Download link only — no inline preview (SKILL.md §B3). Named with the
@@ -75,13 +87,25 @@ export class MediaRendererComponent implements OnChanges {
75
87
  /** Resolved src + alt for the template (recomputed after each load). */
76
88
  src: string | undefined;
77
89
  alt = '';
78
- /** Accessible name for the document download link (altText → title → "Download"). */
79
- docLabel = 'Download';
90
+ /** Accessible name for the document download link (altText → title → derived
91
+ * filename → "Download file"; see `deriveDownloadLabel`). */
92
+ docLabel = 'Download file';
93
+ /** Caller-supplied captions-track URL/label for `<video>` (foreign ref only —
94
+ * see `CmsExternalRef.captionsSrc` in cmsCore.types.ts). Absent on the
95
+ * contentKey path, since the fetched media body carries no captions field. */
96
+ captionsSrc: string | undefined;
97
+ captionsLabel = 'Captions';
80
98
 
81
99
  private loadedRefName?: string;
82
100
 
83
101
  constructor(private readonly cms: CmsItemService) {}
84
102
 
103
+ /** Accessible name for `<audio>` when `altText` is empty — never falls back to
104
+ * `null`/removed, unlike `[attr.aria-label]="alt || null"`. */
105
+ get audioLabel(): string {
106
+ return effectiveAudioLabel(this.alt, this.src);
107
+ }
108
+
85
109
  ngOnChanges(): void {
86
110
  const ref = this.ref;
87
111
  // Only (re)load when the ref identity changes (mirrors TypeRendererComponent).
@@ -92,14 +116,18 @@ export class MediaRendererComponent implements OnChanges {
92
116
  this.error = undefined;
93
117
  this.src = undefined;
94
118
  this.alt = '';
95
- this.docLabel = 'Download';
119
+ this.docLabel = 'Download file';
120
+ this.captionsSrc = undefined;
121
+ this.captionsLabel = 'Captions';
96
122
 
97
123
  // Foreign media ref: the unauthenticatedUrl is the asset src directly — no load
98
124
  // (Rule 1). alt/title ride on the ref (search hand-off or Ref Registration ask).
99
125
  if ('url' in ref && typeof ref.url === 'string') {
100
126
  this.src = ref.url;
101
127
  this.alt = ref.altText ?? '';
102
- this.docLabel = ref.altText || ref.title || 'Download';
128
+ this.docLabel = deriveDownloadLabel(ref.url, ref.altText, ref.title);
129
+ this.captionsSrc = ref.captionsSrc;
130
+ this.captionsLabel = ref.captionsLabel || 'Captions';
103
131
  this.loading = false;
104
132
  return;
105
133
  }
@@ -123,7 +151,7 @@ export class MediaRendererComponent implements OnChanges {
123
151
  private resolveSrc(cmsType: CmsMediaType, body: CmsMediaBody): void {
124
152
  const media = body['sfdc_cms:media'];
125
153
  this.alt = body.altText ?? '';
126
- this.docLabel = body.altText || body.title || 'Download';
154
+ this.docLabel = deriveDownloadLabel(media?.url, body.altText, body.title);
127
155
  if (cmsType === 'image') {
128
156
  this.src = resolveCmsImageUrl(media);
129
157
  return;
@@ -18,6 +18,15 @@ import {
18
18
  import type { AnyCmsRef, CmsRef } from '../shared/cmsCore.types';
19
19
  import { CMS_CHANNEL_ID_FALLBACK } from '../shared/externalRefs';
20
20
 
21
+ /** Runtime guard before the cache's `as TBody` cast: a CMS body is always an
22
+ * object/array, so this screens out a stale primitive from a `ref.name` reused
23
+ * under a different, incompatible `TBody` — it cannot verify the object's shape
24
+ * actually matches `TBody` (no schema is available here), so callers must still
25
+ * keep `ref.name` unique per content shape. */
26
+ function isCacheableBody(value: unknown): boolean {
27
+ return value !== null && typeof value === 'object';
28
+ }
29
+
21
30
  /** Resolved state for one ref read — the component maps this onto signals. */
22
31
  export interface CmsItemResult<TBody> {
23
32
  body: TBody | undefined;
@@ -38,9 +47,9 @@ export class CmsItemService {
38
47
  async load<TBody = unknown>(ref: AnyCmsRef): Promise<CmsItemResult<TBody>> {
39
48
  const name = ref.name;
40
49
 
41
- const cached = this.cache.get(name) as TBody | undefined;
42
- if (cached !== undefined) {
43
- return { body: cached, error: undefined };
50
+ const rawCached = this.cache.get(name);
51
+ if (isCacheableBody(rawCached)) {
52
+ return { body: rawCached as TBody, error: undefined };
44
53
  }
45
54
 
46
55
  let promise = this.inflight.get(name) as Promise<TBody> | undefined;
@@ -89,7 +98,7 @@ export class CmsItemService {
89
98
  ) {
90
99
  return err;
91
100
  }
92
- return err instanceof Error ? err : new Error('Unknown CMS fetch error');
101
+ return err instanceof Error ? err : new Error('Unknown CMS fetch error', { cause: err });
93
102
  }
94
103
 
95
104
  /** Test-only escape hatch — reset the cache between test runs. */