@wazir-dev/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,1136 @@
|
|
|
1
|
+
# Accessibility Anti-Patterns -- Design Domain
|
|
2
|
+
|
|
3
|
+
> 95.9% of the top one million homepages fail WCAG 2.2 Level A/AA conformance, averaging 51 accessibility errors per page. Over 4,000 ADA web accessibility lawsuits were filed in 2024 alone, with a 37% increase in the first half of 2025. These anti-patterns are not theoretical -- they are the direct cause of litigation, user exclusion, and reputational damage.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Anti-Pattern Index
|
|
8
|
+
|
|
9
|
+
| # | Anti-Pattern | Severity | WCAG SC | Prevalence |
|
|
10
|
+
|----|-------------------------------------------|------------|------------------|------------|
|
|
11
|
+
| 1 | Missing Alt Text on Images | Critical | 1.1.1 | 55.5% |
|
|
12
|
+
| 2 | Div/Span Soup Instead of Semantic HTML | Critical | 1.3.1, 4.1.2 | Very High |
|
|
13
|
+
| 3 | Keyboard Traps | Critical | 2.1.2 | 12% |
|
|
14
|
+
| 4 | Missing Focus Indicators | High | 2.4.7, 2.4.13 | Very High |
|
|
15
|
+
| 5 | Color-Only Information | High | 1.4.1 | High |
|
|
16
|
+
| 6 | ARIA Misuse | Critical | 4.1.2 | Very High |
|
|
17
|
+
| 7 | Auto-Playing Media Without Controls | High | 1.4.2 | Moderate |
|
|
18
|
+
| 8 | Time Limits Without Extension | High | 2.2.1 | Moderate |
|
|
19
|
+
| 9 | Missing Skip Navigation Links | Moderate | 2.4.1 | 83% |
|
|
20
|
+
| 10 | Inaccessible Forms | Critical | 1.3.1, 3.3.2 | 45% |
|
|
21
|
+
| 11 | Custom Controls Without Keyboard Support | Critical | 2.1.1 | High |
|
|
22
|
+
| 12 | Low Contrast Text | High | 1.4.3 | 79.1% |
|
|
23
|
+
| 13 | Non-Resizable / Tiny Text | High | 1.4.4 | Moderate |
|
|
24
|
+
| 14 | Mouse-Only Interactions | Critical | 2.1.1, 2.5.1 | High |
|
|
25
|
+
| 15 | PDF-Only Content Without HTML Alternative | Moderate | 1.1.1, 4.1.2 | High |
|
|
26
|
+
| 16 | CAPTCHAs Without Accessible Alternatives | Critical | 1.1.1, 3.3.8 | High |
|
|
27
|
+
| 17 | Dynamic Content Without Live Regions | High | 4.1.3 | High |
|
|
28
|
+
| 18 | Inaccessible Modals and Dialogs | Critical | 2.1.2, 4.1.2 | Very High |
|
|
29
|
+
| 19 | Tables Without Proper Headers | Moderate | 1.3.1 | High |
|
|
30
|
+
| 20 | Focus Order Mismatching Visual Order | High | 2.4.3, 1.3.2 | High |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 1. Missing Alt Text on Images
|
|
35
|
+
|
|
36
|
+
**Also known as:** Decorative-by-default, silent images, empty information.
|
|
37
|
+
|
|
38
|
+
**Description:** Images are rendered without `alt` attributes or with meaningless values like `alt="image"`, `alt="photo"`, or `alt="IMG_20240301.jpg"`. Screen readers either skip the image entirely or announce the filename, leaving blind and low-vision users without critical information.
|
|
39
|
+
|
|
40
|
+
**Why it happens:**
|
|
41
|
+
- Developers treat alt text as optional or low-priority
|
|
42
|
+
- CMS systems allow image uploads without requiring alt text
|
|
43
|
+
- Stock photo integrations auto-populate filenames as alt values
|
|
44
|
+
- Teams lack content authoring guidelines for image descriptions
|
|
45
|
+
|
|
46
|
+
**Real-world impact:**
|
|
47
|
+
- NFB v. Target Corp. (2006-2008): Target.com lacked alt text on images, preventing blind users from navigating or purchasing products. Settlement: $6 million in class damages plus mandatory remediation.
|
|
48
|
+
- Conner v. Parkwood Entertainment (2019): Beyonce.com sued for missing alt text on graphics, inaccessible drop-down menus, and lack of keyboard access.
|
|
49
|
+
- WebAIM Million 2025: 55.5% of homepages have images missing alt text, averaging 11 images per page without alternatives.
|
|
50
|
+
|
|
51
|
+
**What to do instead:**
|
|
52
|
+
```html
|
|
53
|
+
<!-- WRONG: Missing alt -->
|
|
54
|
+
<img src="sale-banner.jpg">
|
|
55
|
+
|
|
56
|
+
<!-- WRONG: Meaningless alt -->
|
|
57
|
+
<img src="sale-banner.jpg" alt="image">
|
|
58
|
+
<img src="sale-banner.jpg" alt="banner">
|
|
59
|
+
|
|
60
|
+
<!-- RIGHT: Descriptive alt -->
|
|
61
|
+
<img src="sale-banner.jpg" alt="Summer sale: 40% off all outdoor furniture through July 31">
|
|
62
|
+
|
|
63
|
+
<!-- RIGHT: Decorative image explicitly marked -->
|
|
64
|
+
<img src="decorative-border.png" alt="" role="presentation">
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Detection:**
|
|
68
|
+
- Automated: axe-core rule `image-alt`, Lighthouse audit, htmlhint
|
|
69
|
+
- Manual: Screen reader walkthrough (NVDA: press `G` to jump between images)
|
|
70
|
+
- CI gate: eslint-plugin-jsx-a11y `alt-text` rule
|
|
71
|
+
|
|
72
|
+
**WCAG:** 1.1.1 Non-text Content (Level A)
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 2. Div/Span Soup Instead of Semantic HTML
|
|
77
|
+
|
|
78
|
+
**Also known as:** Div-itis, unsemantic markup, reinventing native elements.
|
|
79
|
+
|
|
80
|
+
**Description:** Developers build interactive controls entirely from `<div>` and `<span>` elements, manually adding click handlers, rather than using native HTML elements like `<button>`, `<a>`, `<nav>`, `<main>`, `<header>`, or `<select>`. The result is elements that look correct visually but are invisible or broken for assistive technology.
|
|
81
|
+
|
|
82
|
+
**Why it happens:**
|
|
83
|
+
- CSS styling of native elements is perceived as difficult
|
|
84
|
+
- Component libraries abstract away HTML, producing `<div>` wrappers
|
|
85
|
+
- Developers lack understanding of implicit ARIA roles and native keyboard behavior
|
|
86
|
+
- "It works for me" testing only with a mouse in a visual browser
|
|
87
|
+
|
|
88
|
+
**Real-world impact:**
|
|
89
|
+
- Screen readers cannot identify interactive elements -- a `<div onclick="...">` is announced as "text" not "button"
|
|
90
|
+
- Keyboard users cannot Tab to `<div>` elements (they are not focusable by default)
|
|
91
|
+
- WebAIM 2025: Pages using excessive ARIA to compensate for non-semantic HTML averaged 57 errors, more than double pages without ARIA (implying semantic HTML)
|
|
92
|
+
- 67.5% of screen reader users navigate by headings; missing `<h1>`-`<h6>` hierarchy destroys their primary navigation strategy
|
|
93
|
+
|
|
94
|
+
**What to do instead:**
|
|
95
|
+
```html
|
|
96
|
+
<!-- WRONG: Fake button -->
|
|
97
|
+
<div class="btn" onclick="submit()">Submit</div>
|
|
98
|
+
|
|
99
|
+
<!-- RIGHT: Native button -->
|
|
100
|
+
<button type="submit">Submit</button>
|
|
101
|
+
|
|
102
|
+
<!-- WRONG: Fake navigation -->
|
|
103
|
+
<div class="nav">
|
|
104
|
+
<div class="nav-item" onclick="goto('/home')">Home</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- RIGHT: Semantic navigation -->
|
|
108
|
+
<nav aria-label="Main">
|
|
109
|
+
<ul>
|
|
110
|
+
<li><a href="/home">Home</a></li>
|
|
111
|
+
</ul>
|
|
112
|
+
</nav>
|
|
113
|
+
|
|
114
|
+
<!-- RIGHT: Landmark regions -->
|
|
115
|
+
<header>...</header>
|
|
116
|
+
<main>...</main>
|
|
117
|
+
<footer>...</footer>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Detection:**
|
|
121
|
+
- Automated: axe-core rules `button-name`, `landmark-*`, eslint `click-events-have-key-events`
|
|
122
|
+
- Manual: Tab through page -- can you reach and activate every control by keyboard alone?
|
|
123
|
+
- Audit: Run Accessibility Tree inspector in Chrome DevTools and compare against visual layout
|
|
124
|
+
|
|
125
|
+
**WCAG:** 1.3.1 Info and Relationships (Level A), 4.1.2 Name, Role, Value (Level A)
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 3. Keyboard Traps
|
|
130
|
+
|
|
131
|
+
**Also known as:** Focus jail, inescapable widgets, Tab black holes.
|
|
132
|
+
|
|
133
|
+
**Description:** Keyboard focus enters a component -- such as a modal, embedded video player, rich text editor, or third-party widget -- and the user cannot navigate away using standard keyboard commands (Tab, Shift+Tab, Escape). The user is effectively trapped.
|
|
134
|
+
|
|
135
|
+
**Why it happens:**
|
|
136
|
+
- Third-party embeds (video players, maps, iframes, CAPTCHA widgets) do not implement keyboard exit
|
|
137
|
+
- JavaScript `onBlur` / `onChange` handlers force focus back to the same element on validation failure
|
|
138
|
+
- Modal dialogs trap focus intentionally but fail to release it when closed
|
|
139
|
+
- Rich text editors and code editors capture all key events including Tab and Escape
|
|
140
|
+
|
|
141
|
+
**Real-world impact:**
|
|
142
|
+
- WCAG SC 2.1.2 is Level A -- the most fundamental requirement. Failure means the site cannot be considered accessible at any level.
|
|
143
|
+
- WebAIM analysis found keyboard traps on 12% of websites
|
|
144
|
+
- For keyboard-only users (motor disabilities, RSI, blindness), a trap means total inability to complete the task -- they must close and reopen the browser
|
|
145
|
+
|
|
146
|
+
**What to do instead:**
|
|
147
|
+
```javascript
|
|
148
|
+
// WRONG: Focus forced back on blur
|
|
149
|
+
input.addEventListener('blur', () => {
|
|
150
|
+
if (!input.value) input.focus(); // TRAP!
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// RIGHT: Show error but allow navigation
|
|
154
|
+
input.addEventListener('blur', () => {
|
|
155
|
+
if (!input.value) showError(input, 'Required field');
|
|
156
|
+
// User can still Tab away
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// RIGHT: Modal focus trap with Escape release
|
|
160
|
+
dialog.addEventListener('keydown', (e) => {
|
|
161
|
+
if (e.key === 'Escape') {
|
|
162
|
+
closeDialog();
|
|
163
|
+
triggerButton.focus(); // Return focus to trigger
|
|
164
|
+
}
|
|
165
|
+
// Trap Tab within modal (but Escape always exits)
|
|
166
|
+
if (e.key === 'Tab') trapFocusWithinDialog(e);
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Detection:**
|
|
171
|
+
- Manual: Tab through every interactive element; can you always Tab/Shift+Tab/Escape away?
|
|
172
|
+
- Automated: Lighthouse "User focus is not accidentally trapped in a region"
|
|
173
|
+
- Test: Navigate into embedded iframes, video players, and third-party widgets
|
|
174
|
+
|
|
175
|
+
**WCAG:** 2.1.2 No Keyboard Trap (Level A)
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## 4. Missing Focus Indicators
|
|
180
|
+
|
|
181
|
+
**Also known as:** Invisible focus, outline:none, ghost navigation.
|
|
182
|
+
|
|
183
|
+
**Description:** The visible focus indicator (typically a browser-default outline) is removed via CSS (`outline: none` or `outline: 0`) without providing a custom replacement. Keyboard users cannot see which element is currently focused.
|
|
184
|
+
|
|
185
|
+
**Why it happens:**
|
|
186
|
+
- Designers find the browser default focus ring "ugly" and remove it globally
|
|
187
|
+
- CSS resets (normalize.css, reset.css) zero out outlines
|
|
188
|
+
- Teams add `:focus { outline: none }` to satisfy visual QA without understanding the accessibility consequence
|
|
189
|
+
- Focus styles are tested only with a mouse (where they are irrelevant)
|
|
190
|
+
|
|
191
|
+
**Real-world impact:**
|
|
192
|
+
- WCAG 2.2 added SC 2.4.13 Focus Appearance, requiring minimum area and contrast for focus indicators
|
|
193
|
+
- Keyboard-only users (estimated 7-10% of web users) cannot navigate pages where focus is invisible
|
|
194
|
+
- Low-contrast focus rings cause the same disorientation as missing ones
|
|
195
|
+
|
|
196
|
+
**What to do instead:**
|
|
197
|
+
```css
|
|
198
|
+
/* WRONG: Removing focus without replacement */
|
|
199
|
+
*:focus {
|
|
200
|
+
outline: none;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* RIGHT: Custom visible focus indicator */
|
|
204
|
+
*:focus-visible {
|
|
205
|
+
outline: 3px solid #1a73e8;
|
|
206
|
+
outline-offset: 2px;
|
|
207
|
+
border-radius: 2px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* RIGHT: High-contrast focus for dark backgrounds */
|
|
211
|
+
.dark-theme *:focus-visible {
|
|
212
|
+
outline: 3px solid #ffffff;
|
|
213
|
+
box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.5);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* RIGHT: Remove only for mouse, keep for keyboard */
|
|
217
|
+
*:focus:not(:focus-visible) {
|
|
218
|
+
outline: none;
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Detection:**
|
|
223
|
+
- Automated: axe-core rule `focus-visible` (partial), CSS grep for `outline: none` or `outline: 0`
|
|
224
|
+
- Manual: Unplug mouse, navigate entire page with Tab key -- can you always see where you are?
|
|
225
|
+
- WCAG 2.2 SC 2.4.13 requires focus indicator with minimum 2px perimeter and 3:1 contrast
|
|
226
|
+
|
|
227
|
+
**WCAG:** 2.4.7 Focus Visible (Level AA), 2.4.13 Focus Appearance (Level AA, WCAG 2.2)
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 5. Color-Only Information
|
|
232
|
+
|
|
233
|
+
**Also known as:** Red means error, green means go, color-coded status.
|
|
234
|
+
|
|
235
|
+
**Description:** Information is conveyed solely through color -- red text for errors, green for success, color-coded charts without patterns, required fields indicated only by a red asterisk with no label. Users who are colorblind, use monochrome displays, or have low vision cannot distinguish the states.
|
|
236
|
+
|
|
237
|
+
**Why it happens:**
|
|
238
|
+
- Designers assume universal color perception
|
|
239
|
+
- "Red = error" feels culturally obvious
|
|
240
|
+
- Data visualization libraries default to color-only legends
|
|
241
|
+
- Form validation shows only red/green borders with no text
|
|
242
|
+
|
|
243
|
+
**Real-world impact:**
|
|
244
|
+
- 8% of males and 0.5% of females have some form of color vision deficiency (roughly 300 million people worldwide)
|
|
245
|
+
- Red-green colorblindness (deuteranopia/protanopia) makes error/success states indistinguishable
|
|
246
|
+
- Color-coded dashboards become unreadable for affected users
|
|
247
|
+
|
|
248
|
+
**What to do instead:**
|
|
249
|
+
```html
|
|
250
|
+
<!-- WRONG: Color-only error -->
|
|
251
|
+
<input style="border-color: red;" />
|
|
252
|
+
|
|
253
|
+
<!-- RIGHT: Color + icon + text -->
|
|
254
|
+
<div class="field-error">
|
|
255
|
+
<input aria-invalid="true" aria-describedby="email-error" />
|
|
256
|
+
<span id="email-error" role="alert">
|
|
257
|
+
<svg aria-hidden="true"><!-- error icon --></svg>
|
|
258
|
+
Please enter a valid email address.
|
|
259
|
+
</span>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<!-- WRONG: Color-only chart legend -->
|
|
263
|
+
<div style="color: green;">Revenue</div>
|
|
264
|
+
<div style="color: blue;">Costs</div>
|
|
265
|
+
|
|
266
|
+
<!-- RIGHT: Color + pattern + label -->
|
|
267
|
+
<!-- Use distinct line patterns (solid, dashed, dotted) in addition to colors -->
|
|
268
|
+
<!-- Include direct labels on chart lines, not just a separate legend -->
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Detection:**
|
|
272
|
+
- Manual: View page in grayscale (DevTools > Rendering > Emulate vision deficiencies)
|
|
273
|
+
- Automated: axe-core `color-contrast` (partial -- does not catch semantic color-only issues)
|
|
274
|
+
- Review: Search codebase for validation that sets only `border-color` or `color` without companion text
|
|
275
|
+
|
|
276
|
+
**WCAG:** 1.4.1 Use of Color (Level A)
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 6. ARIA Misuse
|
|
281
|
+
|
|
282
|
+
**Also known as:** Bad ARIA, cargo-cult accessibility, role soup, "No ARIA is better than bad ARIA."
|
|
283
|
+
|
|
284
|
+
**Description:** ARIA attributes are applied incorrectly -- wrong roles, missing required children/parent roles, conflicting attributes, `aria-role` instead of `role`, or ARIA used where native HTML semantics suffice. Misused ARIA actively harms assistive technology users by announcing incorrect information.
|
|
285
|
+
|
|
286
|
+
**Why it happens:**
|
|
287
|
+
- Developers copy-paste ARIA from Stack Overflow without understanding the specification
|
|
288
|
+
- ARIA is added as an afterthought to patch non-semantic HTML rather than fixing the HTML
|
|
289
|
+
- Role hierarchy requirements (e.g., `tab` must be inside `tablist`) are not understood
|
|
290
|
+
- Testing is done visually, never with a screen reader
|
|
291
|
+
|
|
292
|
+
**Real-world impact:**
|
|
293
|
+
- WebAIM Million 2025: Pages with ARIA averaged 57 errors -- more than double pages without ARIA. Over 105 million ARIA attributes detected across 1 million pages (106 per page), an 18.5% increase year-over-year.
|
|
294
|
+
- The accessibility community maxim: "No ARIA is better than bad ARIA" -- incorrect roles cause screen readers to announce wrong element types, breaking user mental models.
|
|
295
|
+
- Using `role="menu"` on a navigation bar causes screen readers to expect arrow-key navigation like an OS menu, confusing users when standard Tab behavior is needed.
|
|
296
|
+
|
|
297
|
+
**What to do instead:**
|
|
298
|
+
```html
|
|
299
|
+
<!-- WRONG: ARIA on a native element that already has the role -->
|
|
300
|
+
<button role="button">Submit</button>
|
|
301
|
+
|
|
302
|
+
<!-- RIGHT: Native element, no ARIA needed -->
|
|
303
|
+
<button>Submit</button>
|
|
304
|
+
|
|
305
|
+
<!-- WRONG: Broken role hierarchy -->
|
|
306
|
+
<div role="tab">Settings</div> <!-- Missing tablist parent -->
|
|
307
|
+
|
|
308
|
+
<!-- RIGHT: Correct hierarchy -->
|
|
309
|
+
<div role="tablist" aria-label="Settings">
|
|
310
|
+
<button role="tab" aria-selected="true" aria-controls="panel-1">General</button>
|
|
311
|
+
<button role="tab" aria-selected="false" aria-controls="panel-2">Privacy</button>
|
|
312
|
+
</div>
|
|
313
|
+
<div role="tabpanel" id="panel-1">...</div>
|
|
314
|
+
|
|
315
|
+
<!-- WRONG: Invalid attribute name -->
|
|
316
|
+
<div aria-role="alert">Error!</div>
|
|
317
|
+
|
|
318
|
+
<!-- RIGHT: Correct attribute -->
|
|
319
|
+
<div role="alert">Error!</div>
|
|
320
|
+
|
|
321
|
+
<!-- WRONG: Redundant ARIA overriding native semantics -->
|
|
322
|
+
<a href="/home" role="button">Home</a>
|
|
323
|
+
|
|
324
|
+
<!-- RIGHT: Use the correct element -->
|
|
325
|
+
<a href="/home">Home</a>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**Detection:**
|
|
329
|
+
- Automated: axe-core rules `aria-*`, Lighthouse ARIA audits, `eslint-plugin-jsx-a11y`
|
|
330
|
+
- Manual: Screen reader testing (NVDA + Firefox, VoiceOver + Safari, JAWS + Chrome)
|
|
331
|
+
- Linting: `aria-query` and `axe-core` rule sets catch invalid roles, missing required attributes
|
|
332
|
+
|
|
333
|
+
**WCAG:** 4.1.2 Name, Role, Value (Level A)
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 7. Auto-Playing Media Without Controls
|
|
338
|
+
|
|
339
|
+
**Also known as:** Surprise audio, autoplay assault, sensory ambush.
|
|
340
|
+
|
|
341
|
+
**Description:** Audio or video content plays automatically when a page loads, without visible pause/stop controls, volume adjustment, or the ability to mute. This disorients screen reader users (whose audio output is overridden), triggers vestibular disorders, and overwhelms users with cognitive disabilities.
|
|
342
|
+
|
|
343
|
+
**Why it happens:**
|
|
344
|
+
- Marketing teams want "engaging" hero videos on landing pages
|
|
345
|
+
- Developers add `autoplay` attribute without considering assistive technology
|
|
346
|
+
- Background ambient audio is deemed "subtle enough" to not need controls
|
|
347
|
+
- Third-party ad embeds autoplay without site control
|
|
348
|
+
|
|
349
|
+
**Real-world impact:**
|
|
350
|
+
- Screen reader users cannot hear their assistive technology output over auto-playing audio
|
|
351
|
+
- Users with PTSD, anxiety disorders, or sensory processing conditions are startled or overwhelmed
|
|
352
|
+
- WCAG SC 1.4.2 requires any audio playing for more than 3 seconds to have pause/stop/mute controls
|
|
353
|
+
|
|
354
|
+
**What to do instead:**
|
|
355
|
+
```html
|
|
356
|
+
<!-- WRONG: Autoplay with sound, no controls -->
|
|
357
|
+
<video autoplay src="promo.mp4"></video>
|
|
358
|
+
|
|
359
|
+
<!-- RIGHT: Autoplay muted with controls visible -->
|
|
360
|
+
<video autoplay muted loop playsinline controls>
|
|
361
|
+
<source src="promo.mp4" type="video/mp4">
|
|
362
|
+
<track kind="captions" src="promo-captions.vtt" srclang="en" label="English">
|
|
363
|
+
</video>
|
|
364
|
+
|
|
365
|
+
<!-- RIGHT: No autoplay, user initiates -->
|
|
366
|
+
<video controls preload="metadata">
|
|
367
|
+
<source src="promo.mp4" type="video/mp4">
|
|
368
|
+
<track kind="captions" src="promo-captions.vtt" srclang="en" label="English">
|
|
369
|
+
</video>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**Detection:**
|
|
373
|
+
- Automated: Search codebase for `autoplay` attribute without `muted`
|
|
374
|
+
- Manual: Load page -- does anything play without user action?
|
|
375
|
+
- Audit: Check that all media players have visible, keyboard-accessible controls
|
|
376
|
+
|
|
377
|
+
**WCAG:** 1.4.2 Audio Control (Level A)
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## 8. Time Limits Without Extension
|
|
382
|
+
|
|
383
|
+
**Also known as:** Session timeout ambush, countdown wall, timed-out mid-task.
|
|
384
|
+
|
|
385
|
+
**Description:** Users are given a fixed time to complete a task (form submission, checkout, exam) with no warning, no extension option, and no ability to save progress. When time expires, work is lost silently.
|
|
386
|
+
|
|
387
|
+
**Why it happens:**
|
|
388
|
+
- Security teams impose aggressive session timeouts (e.g., 2 minutes) without accessibility review
|
|
389
|
+
- Shopping cart / booking systems use countdown timers without extension
|
|
390
|
+
- Developers do not consider users who need more time (cognitive disabilities, screen reader users, motor impairments)
|
|
391
|
+
|
|
392
|
+
**Real-world impact:**
|
|
393
|
+
- Screen reader users take 3-10x longer to complete forms than sighted mouse users
|
|
394
|
+
- Users with cognitive disabilities need more time to process and respond
|
|
395
|
+
- Motor-impaired users using switch devices or eye tracking are significantly slower
|
|
396
|
+
- WCAG requires that for each time limit, users must be able to turn it off, adjust it, or extend it
|
|
397
|
+
|
|
398
|
+
**What to do instead:**
|
|
399
|
+
```javascript
|
|
400
|
+
// WRONG: Silent timeout
|
|
401
|
+
setTimeout(() => { window.location = '/session-expired'; }, 120000);
|
|
402
|
+
|
|
403
|
+
// RIGHT: Warning with extension option
|
|
404
|
+
function warnBeforeTimeout() {
|
|
405
|
+
const warning = document.getElementById('timeout-warning');
|
|
406
|
+
warning.removeAttribute('hidden');
|
|
407
|
+
warning.focus();
|
|
408
|
+
// "Your session will expire in 2 minutes.
|
|
409
|
+
// Press 'Continue' to extend by 20 minutes."
|
|
410
|
+
}
|
|
411
|
+
// Warn at least 20 seconds before expiry (WCAG SC 2.2.1)
|
|
412
|
+
setTimeout(warnBeforeTimeout, timeoutMs - 20000);
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**Detection:**
|
|
416
|
+
- Manual: Complete all timed workflows at slow speed; does anything expire without warning?
|
|
417
|
+
- Code review: Search for `setTimeout`, `setInterval` paired with redirect or form clearing
|
|
418
|
+
- Audit: Check session timeout configuration and whether extension is available
|
|
419
|
+
|
|
420
|
+
**WCAG:** 2.2.1 Timing Adjustable (Level A)
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## 9. Missing Skip Navigation Links
|
|
425
|
+
|
|
426
|
+
**Also known as:** Header gauntlet, navigation treadmill, 50-tabs-to-content.
|
|
427
|
+
|
|
428
|
+
**Description:** Pages lack a "Skip to main content" link, forcing keyboard and screen reader users to Tab through the entire header, navigation bar, and sidebar on every page load before reaching the primary content.
|
|
429
|
+
|
|
430
|
+
**Why it happens:**
|
|
431
|
+
- Developers and designers are unaware skip links exist as a pattern
|
|
432
|
+
- Skip links are visually hidden by default and forgotten during QA
|
|
433
|
+
- SPAs re-render headers on route changes without providing skip links
|
|
434
|
+
- Broken implementations: skip link target lacks `tabindex="-1"`, so focus does not actually move
|
|
435
|
+
|
|
436
|
+
**Real-world impact:**
|
|
437
|
+
- WebAIM 2025: Only 17% of the top one million homepages have skip links. Of those, one in six are broken.
|
|
438
|
+
- A site with 30 navigation links forces 30+ Tab presses before reaching content on every page
|
|
439
|
+
- Conner v. Parkwood Entertainment (2019): "Lack of navigation links" was among the cited violations
|
|
440
|
+
|
|
441
|
+
**What to do instead:**
|
|
442
|
+
```html
|
|
443
|
+
<!-- RIGHT: Skip link as first focusable element -->
|
|
444
|
+
<body>
|
|
445
|
+
<a href="#main-content" class="skip-link">Skip to main content</a>
|
|
446
|
+
<header><!-- navigation --></header>
|
|
447
|
+
<main id="main-content" tabindex="-1">
|
|
448
|
+
<!-- page content -->
|
|
449
|
+
</main>
|
|
450
|
+
</body>
|
|
451
|
+
|
|
452
|
+
<style>
|
|
453
|
+
/* Visually hidden until focused */
|
|
454
|
+
.skip-link {
|
|
455
|
+
position: absolute;
|
|
456
|
+
left: -9999px;
|
|
457
|
+
top: auto;
|
|
458
|
+
width: 1px;
|
|
459
|
+
height: 1px;
|
|
460
|
+
overflow: hidden;
|
|
461
|
+
}
|
|
462
|
+
.skip-link:focus {
|
|
463
|
+
position: fixed;
|
|
464
|
+
top: 10px;
|
|
465
|
+
left: 10px;
|
|
466
|
+
width: auto;
|
|
467
|
+
height: auto;
|
|
468
|
+
padding: 8px 16px;
|
|
469
|
+
background: #000;
|
|
470
|
+
color: #fff;
|
|
471
|
+
z-index: 10000;
|
|
472
|
+
font-size: 1rem;
|
|
473
|
+
}
|
|
474
|
+
</style>
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**Detection:**
|
|
478
|
+
- Manual: Press Tab on page load -- does a "Skip" link appear?
|
|
479
|
+
- Automated: axe-core rule `skip-link`, Lighthouse bypass audit
|
|
480
|
+
- Verify: After activating skip link, does focus actually land on the main content?
|
|
481
|
+
|
|
482
|
+
**WCAG:** 2.4.1 Bypass Blocks (Level A)
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## 10. Inaccessible Forms
|
|
487
|
+
|
|
488
|
+
**Also known as:** Label-less inputs, placeholder-as-label, orphan errors.
|
|
489
|
+
|
|
490
|
+
**Description:** Form fields lack programmatically associated labels, rely on placeholder text as the only label, or display error messages that are not connected to their respective fields. Screen readers cannot tell users what a field is for or what went wrong.
|
|
491
|
+
|
|
492
|
+
**Why it happens:**
|
|
493
|
+
- Designers prefer "clean" UIs with placeholder-only inputs (no visible labels)
|
|
494
|
+
- Developers use `<label>` visually but forget the `for` attribute to create programmatic association
|
|
495
|
+
- Error messages are generic ("Please fix errors above") rather than field-specific
|
|
496
|
+
- CMS/form builders generate inaccessible markup by default
|
|
497
|
+
|
|
498
|
+
**Real-world impact:**
|
|
499
|
+
- WebAIM Million 2025: Missing form labels appear on 45% of homepages -- the third most common error
|
|
500
|
+
- Placeholder text disappears on input, leaving users with cognitive disabilities unable to remember what to enter
|
|
501
|
+
- Generic errors force screen reader users to search the entire form for problems
|
|
502
|
+
- Courts have consistently ruled inaccessible forms violate the ADA (WCAG 3.3.1, 3.3.2)
|
|
503
|
+
|
|
504
|
+
**What to do instead:**
|
|
505
|
+
```html
|
|
506
|
+
<!-- WRONG: Placeholder as label -->
|
|
507
|
+
<input type="email" placeholder="Email address">
|
|
508
|
+
|
|
509
|
+
<!-- RIGHT: Visible label with association -->
|
|
510
|
+
<label for="email">Email address</label>
|
|
511
|
+
<input type="email" id="email" name="email"
|
|
512
|
+
aria-describedby="email-hint email-error"
|
|
513
|
+
aria-invalid="false">
|
|
514
|
+
<span id="email-hint" class="hint">We will never share your email.</span>
|
|
515
|
+
|
|
516
|
+
<!-- RIGHT: Error associated with field -->
|
|
517
|
+
<label for="password">Password</label>
|
|
518
|
+
<input type="password" id="password" aria-invalid="true"
|
|
519
|
+
aria-describedby="password-error">
|
|
520
|
+
<span id="password-error" role="alert">
|
|
521
|
+
Password must be at least 8 characters.
|
|
522
|
+
</span>
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
**Detection:**
|
|
526
|
+
- Automated: axe-core rules `label`, `form-field-multiple-labels`, eslint `label-has-associated-control`
|
|
527
|
+
- Manual: Click on each label text -- does it focus the corresponding input?
|
|
528
|
+
- Screen reader: Navigate form in JAWS/NVDA forms mode -- are all fields announced with labels?
|
|
529
|
+
|
|
530
|
+
**WCAG:** 1.3.1 Info and Relationships (Level A), 3.3.2 Labels or Instructions (Level A), 3.3.1 Error Identification (Level A)
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## 11. Custom Controls Without Keyboard Support
|
|
535
|
+
|
|
536
|
+
**Also known as:** Mouse-only widgets, Tab-immune components, click-only interactives.
|
|
537
|
+
|
|
538
|
+
**Description:** Custom UI components (carousels, accordions, tree views, drag-and-drop interfaces, star ratings, sliders) are built without implementing keyboard event handlers. They respond only to mouse clicks and hover events.
|
|
539
|
+
|
|
540
|
+
**Why it happens:**
|
|
541
|
+
- Developers implement `onClick` but not `onKeyDown`
|
|
542
|
+
- Custom components do not use native interactive elements as their base
|
|
543
|
+
- "Accessible later" is deferred indefinitely
|
|
544
|
+
- Testing never includes keyboard-only users
|
|
545
|
+
|
|
546
|
+
**Real-world impact:**
|
|
547
|
+
- Robles v. Domino's Pizza (2016-2022): Blind user Guillermo Robles could not order pizza via keyboard and screen reader. The Supreme Court declined to hear Domino's appeal, establishing that the ADA applies to websites. After six years of litigation, Domino's settled.
|
|
548
|
+
- Any control that is mouse-only excludes keyboard users, screen reader users, switch device users, and voice control users
|
|
549
|
+
- WCAG SC 2.1.1 is Level A: all functionality must be operable via keyboard
|
|
550
|
+
|
|
551
|
+
**What to do instead:**
|
|
552
|
+
```javascript
|
|
553
|
+
// WRONG: Click-only accordion
|
|
554
|
+
div.addEventListener('click', togglePanel);
|
|
555
|
+
|
|
556
|
+
// RIGHT: Keyboard-accessible accordion
|
|
557
|
+
button.addEventListener('click', togglePanel);
|
|
558
|
+
button.addEventListener('keydown', (e) => {
|
|
559
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
560
|
+
e.preventDefault();
|
|
561
|
+
togglePanel();
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
// Better yet: use <button> which handles Enter/Space natively
|
|
565
|
+
// <button aria-expanded="false" aria-controls="panel-1">Section 1</button>
|
|
566
|
+
|
|
567
|
+
// WRONG: Drag-only reordering
|
|
568
|
+
item.addEventListener('dragstart', onDrag);
|
|
569
|
+
|
|
570
|
+
// RIGHT: Drag AND keyboard alternative
|
|
571
|
+
// Provide up/down buttons or use aria-grabbed + arrow keys
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
**Detection:**
|
|
575
|
+
- Manual: Disconnect mouse, complete every workflow using only keyboard
|
|
576
|
+
- Automated: eslint `click-events-have-key-events`, `interactive-supports-focus`
|
|
577
|
+
- Review: Search for `addEventListener('click'` without corresponding keyboard handlers
|
|
578
|
+
|
|
579
|
+
**WCAG:** 2.1.1 Keyboard (Level A)
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
## 12. Low Contrast Text
|
|
584
|
+
|
|
585
|
+
**Also known as:** Gray-on-white, whisper text, aesthetic-over-readable.
|
|
586
|
+
|
|
587
|
+
**Description:** Text color does not meet the minimum contrast ratio against its background: 4.5:1 for normal text or 3:1 for large text (18pt+ or 14pt+ bold). Users with low vision, cataracts, or those in bright ambient light cannot read the content.
|
|
588
|
+
|
|
589
|
+
**Why it happens:**
|
|
590
|
+
- Designers prioritize "soft" or "modern" aesthetics (light gray on white)
|
|
591
|
+
- Brand color palettes are chosen without contrast checking
|
|
592
|
+
- Placeholder text, disabled states, and footer text are left at very low contrast
|
|
593
|
+
- Dark mode implementations invert poorly, creating low-contrast combinations
|
|
594
|
+
|
|
595
|
+
**Real-world impact:**
|
|
596
|
+
- WebAIM Million 2025: Low contrast text found on 79.1% of homepages -- the single most common accessibility error for the fifth consecutive year. Average of 29.6 low-contrast instances per page.
|
|
597
|
+
- 253 million people worldwide have vision impairment (WHO). Low contrast text is the most widespread barrier they face online.
|
|
598
|
+
- Low contrast issues are cited in virtually every ADA web accessibility lawsuit
|
|
599
|
+
|
|
600
|
+
**What to do instead:**
|
|
601
|
+
```css
|
|
602
|
+
/* WRONG: 2.5:1 contrast ratio */
|
|
603
|
+
body { color: #999999; background: #ffffff; }
|
|
604
|
+
|
|
605
|
+
/* RIGHT: 7.4:1 contrast ratio */
|
|
606
|
+
body { color: #333333; background: #ffffff; }
|
|
607
|
+
|
|
608
|
+
/* WRONG: Placeholder too faint */
|
|
609
|
+
::placeholder { color: #cccccc; }
|
|
610
|
+
|
|
611
|
+
/* RIGHT: Placeholder meeting 4.5:1 */
|
|
612
|
+
::placeholder { color: #767676; }
|
|
613
|
+
|
|
614
|
+
/* Tools: Use WebAIM Contrast Checker, Chrome DevTools
|
|
615
|
+
color picker, or Polypane built-in contrast overlay */
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
**Detection:**
|
|
619
|
+
- Automated: axe-core `color-contrast`, Lighthouse contrast audit, Polypane
|
|
620
|
+
- Manual: Chrome DevTools > Elements > color picker shows contrast ratio
|
|
621
|
+
- Design: Check all color pairs in the design system against WCAG thresholds before coding
|
|
622
|
+
|
|
623
|
+
**WCAG:** 1.4.3 Contrast Minimum (Level AA), 1.4.6 Contrast Enhanced (Level AAA)
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## 13. Non-Resizable / Tiny Text
|
|
628
|
+
|
|
629
|
+
**Also known as:** Fixed font size, pixel-locked typography, zoom-breaking layout.
|
|
630
|
+
|
|
631
|
+
**Description:** Text is set in fixed pixel sizes (`font-size: 12px`) or the page layout breaks when text is resized to 200%. Content overflows, gets clipped by `overflow: hidden`, or overlaps other elements. Some sites use `maximum-scale=1` in the viewport meta tag, preventing pinch-to-zoom on mobile.
|
|
632
|
+
|
|
633
|
+
**Why it happens:**
|
|
634
|
+
- Pixel-perfect designs use fixed `px` units throughout
|
|
635
|
+
- `overflow: hidden` is used to "clean up" layout without testing at larger text sizes
|
|
636
|
+
- Viewport meta includes `user-scalable=no` or `maximum-scale=1`
|
|
637
|
+
- Component heights are fixed, causing text to overflow containers when resized
|
|
638
|
+
|
|
639
|
+
**What to do instead:**
|
|
640
|
+
```css
|
|
641
|
+
/* WRONG: Fixed pixel sizes */
|
|
642
|
+
body { font-size: 12px; }
|
|
643
|
+
.card { height: 200px; overflow: hidden; }
|
|
644
|
+
|
|
645
|
+
/* RIGHT: Relative units */
|
|
646
|
+
body { font-size: 1rem; } /* 16px default, scales with user preference */
|
|
647
|
+
.card { min-height: 12rem; } /* Grows with text size */
|
|
648
|
+
|
|
649
|
+
/* WRONG: Preventing zoom */
|
|
650
|
+
<meta name="viewport" content="width=device-width, maximum-scale=1, user-scalable=no">
|
|
651
|
+
|
|
652
|
+
/* RIGHT: Allowing zoom */
|
|
653
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
**Detection:**
|
|
657
|
+
- Manual: Set browser zoom to 200% -- does all content remain visible and functional?
|
|
658
|
+
- Automated: axe-core `meta-viewport`, grep for `user-scalable=no` or `maximum-scale=1`
|
|
659
|
+
- Design review: Ensure all containers use `min-height` not `height` with `overflow: hidden`
|
|
660
|
+
|
|
661
|
+
**WCAG:** 1.4.4 Resize Text (Level AA)
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
665
|
+
## 14. Mouse-Only Interactions
|
|
666
|
+
|
|
667
|
+
**Also known as:** Hover-dependent UI, click-to-reveal, tooltip-only information.
|
|
668
|
+
|
|
669
|
+
**Description:** Critical functionality or content is available only through mouse-specific events: hover-triggered menus, tooltips with essential information that appear only on `mouseover`, right-click context menus, drag-and-drop as the only reordering mechanism. Keyboard, touch, and assistive technology users are excluded.
|
|
670
|
+
|
|
671
|
+
**Why it happens:**
|
|
672
|
+
- CSS `:hover` is used without corresponding `:focus` styles
|
|
673
|
+
- Dropdown menus appear on hover with no keyboard or touch equivalent
|
|
674
|
+
- Drag-and-drop is the sole method for reordering (no button alternative)
|
|
675
|
+
- Tooltips with critical info lack `focus` triggers and `aria-describedby` association
|
|
676
|
+
|
|
677
|
+
**Real-world impact:**
|
|
678
|
+
- Touch device users (mobile, tablet) have no hover capability
|
|
679
|
+
- Voice control users (Dragon NaturallySpeaking) cannot trigger hover events
|
|
680
|
+
- Conner v. Parkwood Entertainment (2019): "Denial of keyboard access" and "inaccessible drop-down menus" among cited violations
|
|
681
|
+
|
|
682
|
+
**What to do instead:**
|
|
683
|
+
```css
|
|
684
|
+
/* WRONG: Hover-only dropdown */
|
|
685
|
+
.menu-item:hover > .submenu { display: block; }
|
|
686
|
+
|
|
687
|
+
/* RIGHT: Hover AND focus */
|
|
688
|
+
.menu-item:hover > .submenu,
|
|
689
|
+
.menu-item:focus-within > .submenu {
|
|
690
|
+
display: block;
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
```html
|
|
694
|
+
<!-- WRONG: Tooltip only on hover, with critical info -->
|
|
695
|
+
<span title="Required: must be 8+ characters">Password</span>
|
|
696
|
+
|
|
697
|
+
<!-- RIGHT: Always-visible hint + accessible tooltip -->
|
|
698
|
+
<label for="pw">Password</label>
|
|
699
|
+
<input id="pw" aria-describedby="pw-hint">
|
|
700
|
+
<span id="pw-hint">Must be at least 8 characters.</span>
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
**Detection:**
|
|
704
|
+
- Manual: Complete all workflows without a mouse; are any features missing?
|
|
705
|
+
- Automated: CSS grep for `:hover` without corresponding `:focus` or `:focus-within`
|
|
706
|
+
- Testing: Use iOS VoiceOver or Android TalkBack on mobile -- can you reach all content?
|
|
707
|
+
|
|
708
|
+
**WCAG:** 2.1.1 Keyboard (Level A), 2.5.1 Pointer Gestures (Level A), 1.4.13 Content on Hover or Focus (Level AA)
|
|
709
|
+
|
|
710
|
+
---
|
|
711
|
+
|
|
712
|
+
## 15. PDF-Only Content Without HTML Alternative
|
|
713
|
+
|
|
714
|
+
**Also known as:** PDF wall, document-format gatekeeping, scan-and-post.
|
|
715
|
+
|
|
716
|
+
**Description:** Important content (policies, forms, reports, menus, course materials) is published exclusively as PDF, often scanned images of paper documents. These PDFs lack tagged structure, reading order, alt text, and form field labels, making them partially or fully inaccessible to screen readers.
|
|
717
|
+
|
|
718
|
+
**Why it happens:**
|
|
719
|
+
- Organizations scan paper documents and post them as image-PDFs
|
|
720
|
+
- PDF is seen as the "official" format for legal/compliance documents
|
|
721
|
+
- PDF accessibility tagging (heading structure, reading order, form fields) requires specialized tools and knowledge
|
|
722
|
+
- "Just upload the PDF" is faster than creating an accessible HTML page
|
|
723
|
+
|
|
724
|
+
**Real-world impact:**
|
|
725
|
+
- Government agencies have been sued repeatedly over inaccessible PDF-only content (Section 508)
|
|
726
|
+
- Scanned-image PDFs are entirely invisible to screen readers -- they contain zero text to read
|
|
727
|
+
- Even tagged PDFs frequently have incorrect reading order, missing alt text, and broken form fields
|
|
728
|
+
|
|
729
|
+
**What to do instead:**
|
|
730
|
+
- Provide an HTML version of all PDF content as the primary format
|
|
731
|
+
- If PDF is required, ensure it is tagged (Adobe Acrobat Pro > Accessibility Checker)
|
|
732
|
+
- Never publish scanned-image PDFs without OCR and manual verification
|
|
733
|
+
- Include a text-based summary or equivalent HTML page alongside any PDF
|
|
734
|
+
|
|
735
|
+
**Detection:**
|
|
736
|
+
- Manual: Open PDFs in a screen reader -- can the full content be read in logical order?
|
|
737
|
+
- Automated: Adobe Acrobat Accessibility Checker, PAC 2024 (PDF Accessibility Checker)
|
|
738
|
+
- Policy: Require HTML alternative for every PDF published on the site
|
|
739
|
+
|
|
740
|
+
**WCAG:** 1.1.1 Non-text Content (Level A), 1.3.1 Info and Relationships (Level A), 4.1.2 Name, Role, Value (Level A)
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
## 16. CAPTCHAs Without Accessible Alternatives
|
|
745
|
+
|
|
746
|
+
**Also known as:** Bot gate that blocks humans, prove-you-can-see challenge, ability test masquerading as security.
|
|
747
|
+
|
|
748
|
+
**Description:** Visual CAPTCHAs (distorted text, image selection grids) are presented without accessible alternatives. Audio CAPTCHAs, when provided, are heavily distorted and often fail. Users with visual, auditory, cognitive, or motor disabilities are locked out of registration, login, or form submission.
|
|
749
|
+
|
|
750
|
+
**Why it happens:**
|
|
751
|
+
- CAPTCHA vendors prioritize bot detection over accessibility
|
|
752
|
+
- Developers integrate CAPTCHA without evaluating the accessible fallback
|
|
753
|
+
- Audio CAPTCHAs are added as a "checkbox" compliance measure but are unusable
|
|
754
|
+
- reCAPTCHA v2 image challenges are impossible for blind users without sighted assistance
|
|
755
|
+
|
|
756
|
+
**Real-world impact:**
|
|
757
|
+
- WebAIM survey (2023-2024): Screen reader users ranked CAPTCHA as the single most problematic element on the web -- worse than missing alt text, ambiguous links, or lack of keyboard access.
|
|
758
|
+
- W3C published "Inaccessibility of CAPTCHA" (TR/turingtest) acknowledging no fully accessible CAPTCHA exists
|
|
759
|
+
- FTC v. accessiBe (2025): $1 million settlement after FTC found accessiBe's overlay widget failed to make CAPTCHAs accessible despite marketing claims
|
|
760
|
+
|
|
761
|
+
**What to do instead:**
|
|
762
|
+
- Use invisible CAPTCHA / risk-based analysis (reCAPTCHA v3, Cloudflare Turnstile) that requires no user interaction
|
|
763
|
+
- Implement honeypot fields (hidden fields that bots fill out but humans do not)
|
|
764
|
+
- Use time-based analysis (bots submit forms faster than humans)
|
|
765
|
+
- If visual CAPTCHA is required, provide a genuinely usable audio alternative AND a contact/support bypass
|
|
766
|
+
- WCAG 2.2 SC 3.3.8 (Accessible Authentication) prohibits cognitive function tests for login
|
|
767
|
+
|
|
768
|
+
**Detection:**
|
|
769
|
+
- Manual: Attempt to complete all CAPTCHA-protected flows using a screen reader
|
|
770
|
+
- Audit: Test audio CAPTCHA alternatives -- can a real user with hearing understand them?
|
|
771
|
+
- Review: Check if any pathway exists for users who cannot complete the CAPTCHA
|
|
772
|
+
|
|
773
|
+
**WCAG:** 1.1.1 Non-text Content (Level A), 3.3.8 Accessible Authentication (Level AA, WCAG 2.2)
|
|
774
|
+
|
|
775
|
+
---
|
|
776
|
+
|
|
777
|
+
## 17. Dynamic Content Without Live Regions
|
|
778
|
+
|
|
779
|
+
**Also known as:** Silent updates, ghost notifications, invisible status changes.
|
|
780
|
+
|
|
781
|
+
**Description:** Content on the page updates dynamically (AJAX responses, form validation messages, chat messages, notifications, progress indicators, shopping cart counts) without using ARIA live regions. Screen reader users are unaware that anything has changed.
|
|
782
|
+
|
|
783
|
+
**Why it happens:**
|
|
784
|
+
- Developers update the DOM visually but do not consider screen reader announcements
|
|
785
|
+
- SPA frameworks (React, Angular, Vue) update components without native page reload announcements
|
|
786
|
+
- Teams are unaware of `aria-live`, `role="alert"`, `role="status"`, or `role="log"`
|
|
787
|
+
- Live regions are added dynamically after page load, which some screen readers do not detect
|
|
788
|
+
|
|
789
|
+
**What to do instead:**
|
|
790
|
+
```html
|
|
791
|
+
<!-- RIGHT: Status message with polite live region -->
|
|
792
|
+
<!-- IMPORTANT: The live region container must exist in the DOM at page load -->
|
|
793
|
+
<div aria-live="polite" id="status-message"></div>
|
|
794
|
+
|
|
795
|
+
<script>
|
|
796
|
+
// Inject message into the existing container
|
|
797
|
+
document.getElementById('status-message').textContent =
|
|
798
|
+
'Your item has been added to the cart.';
|
|
799
|
+
</script>
|
|
800
|
+
|
|
801
|
+
<!-- RIGHT: Urgent error alert -->
|
|
802
|
+
<div role="alert">
|
|
803
|
+
Payment failed. Please check your card details.
|
|
804
|
+
</div>
|
|
805
|
+
|
|
806
|
+
<!-- RIGHT: Progress indicator -->
|
|
807
|
+
<div role="progressbar" aria-valuenow="65" aria-valuemin="0"
|
|
808
|
+
aria-valuemax="100" aria-label="Upload progress">
|
|
809
|
+
65%
|
|
810
|
+
</div>
|
|
811
|
+
|
|
812
|
+
<!-- WRONG: Dynamically injected live region (may not be detected) -->
|
|
813
|
+
<script>
|
|
814
|
+
const div = document.createElement('div');
|
|
815
|
+
div.setAttribute('aria-live', 'polite');
|
|
816
|
+
div.textContent = 'Updated!';
|
|
817
|
+
document.body.appendChild(div); // Screen reader may ignore this
|
|
818
|
+
</script>
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
**Detection:**
|
|
822
|
+
- Manual: Use NVDA/JAWS while triggering dynamic updates -- are changes announced?
|
|
823
|
+
- Code review: Search for DOM manipulation that updates text without an `aria-live` container
|
|
824
|
+
- SPA audit: Verify route changes announce the new page title via a live region
|
|
825
|
+
|
|
826
|
+
**WCAG:** 4.1.3 Status Messages (Level AA)
|
|
827
|
+
|
|
828
|
+
---
|
|
829
|
+
|
|
830
|
+
## 18. Inaccessible Modals and Dialogs
|
|
831
|
+
|
|
832
|
+
**Also known as:** Ghost dialogs, focus-leaking modals, background-interactive overlays.
|
|
833
|
+
|
|
834
|
+
**Description:** Modal dialogs fail one or more of the following: focus does not move into the dialog on open; focus is not trapped within the dialog while open; background content remains interactive via keyboard; pressing Escape does not close the dialog; focus does not return to the trigger element on close; the dialog lacks `role="dialog"` and `aria-modal="true"`.
|
|
835
|
+
|
|
836
|
+
**Why it happens:**
|
|
837
|
+
- Developers use `<div>` with `display:block` instead of `<dialog>` or proper ARIA markup
|
|
838
|
+
- Focus management requires explicit JavaScript (not built into CSS show/hide)
|
|
839
|
+
- Third-party modal libraries have incomplete accessibility support
|
|
840
|
+
- Background scroll is prevented visually but not from keyboard/screen reader interaction
|
|
841
|
+
|
|
842
|
+
**Real-world impact:**
|
|
843
|
+
- Modals are one of the most common UI patterns and one of the most frequently broken for accessibility
|
|
844
|
+
- Screen readers may not announce that a dialog has appeared at all if `role="dialog"` is missing
|
|
845
|
+
- VoiceOver + Safari has known issues with `aria-modal="true"` making static dialog content inaccessible
|
|
846
|
+
- Keyboard users who can interact with background content while a modal is open lose their place entirely
|
|
847
|
+
|
|
848
|
+
**What to do instead:**
|
|
849
|
+
```html
|
|
850
|
+
<!-- RIGHT: Native dialog element (modern browsers) -->
|
|
851
|
+
<dialog id="confirm-dialog" aria-labelledby="dialog-title">
|
|
852
|
+
<h2 id="dialog-title">Confirm deletion</h2>
|
|
853
|
+
<p>Are you sure you want to delete this item?</p>
|
|
854
|
+
<button id="cancel-btn">Cancel</button>
|
|
855
|
+
<button id="confirm-btn">Delete</button>
|
|
856
|
+
</dialog>
|
|
857
|
+
|
|
858
|
+
<script>
|
|
859
|
+
const dialog = document.getElementById('confirm-dialog');
|
|
860
|
+
const triggerBtn = document.getElementById('delete-trigger');
|
|
861
|
+
|
|
862
|
+
triggerBtn.addEventListener('click', () => {
|
|
863
|
+
dialog.showModal(); // Native: traps focus, adds backdrop, handles Escape
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
dialog.addEventListener('close', () => {
|
|
867
|
+
triggerBtn.focus(); // Return focus to trigger
|
|
868
|
+
});
|
|
869
|
+
</script>
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
**Detection:**
|
|
873
|
+
- Manual: Open modal with keyboard, Tab through all elements -- does focus stay inside?
|
|
874
|
+
- Test: Press Escape -- does the modal close? Does focus return to the trigger?
|
|
875
|
+
- Screen reader: Does the dialog announce its title and role on open?
|
|
876
|
+
- Check: Can you interact with background content while the modal is open?
|
|
877
|
+
|
|
878
|
+
**WCAG:** 2.1.2 No Keyboard Trap (Level A), 4.1.2 Name, Role, Value (Level A), 2.4.3 Focus Order (Level A)
|
|
879
|
+
|
|
880
|
+
---
|
|
881
|
+
|
|
882
|
+
## 19. Tables Without Proper Headers
|
|
883
|
+
|
|
884
|
+
**Also known as:** Layout tables, header-free data, unlabeled grids.
|
|
885
|
+
|
|
886
|
+
**Description:** Data tables lack `<th>` elements, `scope` attributes, `<caption>`, or use `<table>` for visual layout instead of data. Screen readers cannot associate data cells with their headers, making the table content incomprehensible.
|
|
887
|
+
|
|
888
|
+
**Why it happens:**
|
|
889
|
+
- Tables are used for layout (a practice from the 1990s that persists in email templates and legacy codebases)
|
|
890
|
+
- Developers use `<td>` for all cells including headers, styling them bold via CSS
|
|
891
|
+
- Complex tables with merged cells (`colspan`, `rowspan`) lack `headers` attribute associations
|
|
892
|
+
- Data grids rendered by JavaScript frameworks output flat `<div>` structures instead of proper `<table>` markup
|
|
893
|
+
|
|
894
|
+
**What to do instead:**
|
|
895
|
+
```html
|
|
896
|
+
<!-- WRONG: No headers, no caption -->
|
|
897
|
+
<table>
|
|
898
|
+
<tr><td>Name</td><td>Role</td><td>Start Date</td></tr>
|
|
899
|
+
<tr><td>Alice</td><td>Engineer</td><td>2024-01-15</td></tr>
|
|
900
|
+
</table>
|
|
901
|
+
|
|
902
|
+
<!-- RIGHT: Proper headers and caption -->
|
|
903
|
+
<table>
|
|
904
|
+
<caption>Engineering team roster</caption>
|
|
905
|
+
<thead>
|
|
906
|
+
<tr>
|
|
907
|
+
<th scope="col">Name</th>
|
|
908
|
+
<th scope="col">Role</th>
|
|
909
|
+
<th scope="col">Start Date</th>
|
|
910
|
+
</tr>
|
|
911
|
+
</thead>
|
|
912
|
+
<tbody>
|
|
913
|
+
<tr>
|
|
914
|
+
<th scope="row">Alice</th>
|
|
915
|
+
<td>Engineer</td>
|
|
916
|
+
<td>2024-01-15</td>
|
|
917
|
+
</tr>
|
|
918
|
+
</tbody>
|
|
919
|
+
</table>
|
|
920
|
+
|
|
921
|
+
<!-- RIGHT: Complex table with headers attribute -->
|
|
922
|
+
<table>
|
|
923
|
+
<caption>Quarterly revenue by region</caption>
|
|
924
|
+
<thead>
|
|
925
|
+
<tr>
|
|
926
|
+
<td></td>
|
|
927
|
+
<th id="q1" scope="col">Q1</th>
|
|
928
|
+
<th id="q2" scope="col">Q2</th>
|
|
929
|
+
</tr>
|
|
930
|
+
</thead>
|
|
931
|
+
<tbody>
|
|
932
|
+
<tr>
|
|
933
|
+
<th id="na" scope="row">North America</th>
|
|
934
|
+
<td headers="q1 na">$1.2M</td>
|
|
935
|
+
<td headers="q2 na">$1.5M</td>
|
|
936
|
+
</tr>
|
|
937
|
+
</tbody>
|
|
938
|
+
</table>
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
**Detection:**
|
|
942
|
+
- Automated: axe-core rules `td-headers-attr`, `th-has-data-cells`, `table-fake-caption`
|
|
943
|
+
- Manual: Screen reader table navigation (JAWS: `Ctrl+Alt+Arrow` keys) -- are headers announced?
|
|
944
|
+
- Review: Search for `<table>` without `<th>` or `<caption>` elements
|
|
945
|
+
|
|
946
|
+
**WCAG:** 1.3.1 Info and Relationships (Level A)
|
|
947
|
+
|
|
948
|
+
---
|
|
949
|
+
|
|
950
|
+
## 20. Focus Order Mismatching Visual Order
|
|
951
|
+
|
|
952
|
+
**Also known as:** Tab chaos, positive tabindex abuse, CSS-reordered-but-DOM-unchanged.
|
|
953
|
+
|
|
954
|
+
**Description:** The keyboard Tab order does not match the visual layout of the page. This occurs when CSS (`order`, `flex-direction: row-reverse`, `position: absolute`, `float`) rearranges elements visually while the DOM order remains unchanged, or when positive `tabindex` values (1, 2, 3...) override the natural Tab sequence.
|
|
955
|
+
|
|
956
|
+
**Why it happens:**
|
|
957
|
+
- CSS Flexbox/Grid `order` property rearranges visual layout without changing DOM order
|
|
958
|
+
- Developers assign `tabindex="1"`, `tabindex="5"`, etc. to force a Tab sequence
|
|
959
|
+
- Absolutely positioned elements appear in one place visually but are in a different DOM location
|
|
960
|
+
- Mobile-first CSS moves elements to different visual positions on desktop breakpoints
|
|
961
|
+
|
|
962
|
+
**Real-world impact:**
|
|
963
|
+
- Keyboard users Tab to elements in an unexpected sequence, losing their place
|
|
964
|
+
- Screen reader users hear content in DOM order, which may contradict the visual story
|
|
965
|
+
- Using positive tabindex values across a page creates an unpredictable, unmaintainable Tab order
|
|
966
|
+
|
|
967
|
+
**What to do instead:**
|
|
968
|
+
```html
|
|
969
|
+
<!-- WRONG: Positive tabindex -->
|
|
970
|
+
<input tabindex="3" placeholder="Last name">
|
|
971
|
+
<input tabindex="1" placeholder="First name">
|
|
972
|
+
<input tabindex="2" placeholder="Email">
|
|
973
|
+
|
|
974
|
+
<!-- RIGHT: Match DOM order to visual order -->
|
|
975
|
+
<input placeholder="First name">
|
|
976
|
+
<input placeholder="Email">
|
|
977
|
+
<input placeholder="Last name">
|
|
978
|
+
|
|
979
|
+
<!-- WRONG: CSS order mismatches DOM -->
|
|
980
|
+
<div style="display:flex;">
|
|
981
|
+
<div style="order:2;">Second visually, first in DOM</div>
|
|
982
|
+
<div style="order:1;">First visually, second in DOM</div>
|
|
983
|
+
</div>
|
|
984
|
+
|
|
985
|
+
<!-- RIGHT: DOM order matches intended visual order -->
|
|
986
|
+
<div style="display:flex;">
|
|
987
|
+
<div>First visually, first in DOM</div>
|
|
988
|
+
<div>Second visually, second in DOM</div>
|
|
989
|
+
</div>
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
**Detection:**
|
|
993
|
+
- Manual: Tab through page and compare focus sequence against visual reading order
|
|
994
|
+
- Automated: axe-core `tabindex` rule (flags positive tabindex values)
|
|
995
|
+
- CSS review: Search for `order:`, `flex-direction: row-reverse`, `flex-direction: column-reverse`
|
|
996
|
+
- Test at multiple breakpoints: Mobile and desktop may have different visual orders
|
|
997
|
+
|
|
998
|
+
**WCAG:** 2.4.3 Focus Order (Level A), 1.3.2 Meaningful Sequence (Level A)
|
|
999
|
+
|
|
1000
|
+
---
|
|
1001
|
+
|
|
1002
|
+
## Root Cause Analysis
|
|
1003
|
+
|
|
1004
|
+
The 20 anti-patterns above share a small number of systemic root causes:
|
|
1005
|
+
|
|
1006
|
+
### 1. Lack of Assistive Technology Testing
|
|
1007
|
+
Teams test only with mouse and visual browser. If no one on the team uses a screen reader, keyboard-only navigation, or voice control, accessibility failures are invisible during development and QA.
|
|
1008
|
+
|
|
1009
|
+
### 2. "Accessibility Later" Mentality
|
|
1010
|
+
Accessibility is treated as a post-launch polish step rather than a core requirement. By the time it is addressed, the architecture (non-semantic HTML, custom controls, inaccessible third-party dependencies) makes remediation expensive.
|
|
1011
|
+
|
|
1012
|
+
### 3. Aesthetic Over Function
|
|
1013
|
+
Design decisions prioritize visual minimalism (low contrast, hidden focus indicators, placeholder-only inputs, hover-only interactions) over readability and operability for all users.
|
|
1014
|
+
|
|
1015
|
+
### 4. Native HTML Ignorance
|
|
1016
|
+
Developers reach for `<div>` + JavaScript + ARIA instead of using native HTML elements (`<button>`, `<dialog>`, `<nav>`, `<label>`, `<table>`) that provide keyboard support, screen reader semantics, and focus management for free.
|
|
1017
|
+
|
|
1018
|
+
### 5. Third-Party Dependency Trust
|
|
1019
|
+
Teams embed third-party widgets (video players, maps, CAPTCHAs, chat widgets, analytics overlays) without verifying their accessibility. The FTC v. accessiBe settlement ($1 million, 2025) proved that accessibility overlay widgets can create more barriers than they remove.
|
|
1020
|
+
|
|
1021
|
+
### 6. No Automated Enforcement
|
|
1022
|
+
Without accessibility linting in CI/CD (axe-core, eslint-plugin-jsx-a11y, Lighthouse CI), regressions are introduced silently with every deployment.
|
|
1023
|
+
|
|
1024
|
+
---
|
|
1025
|
+
|
|
1026
|
+
## Self-Check Questions
|
|
1027
|
+
|
|
1028
|
+
Before shipping any feature, the team should be able to answer "yes" to every question below:
|
|
1029
|
+
|
|
1030
|
+
1. **Keyboard:** Can every interactive element be reached and operated with Tab, Shift+Tab, Enter, Space, Escape, and Arrow keys alone?
|
|
1031
|
+
2. **Focus:** Is a visible focus indicator present on every focusable element, meeting WCAG 2.4.13 minimum area and contrast?
|
|
1032
|
+
3. **Screen reader:** Have you tested with at least one screen reader (NVDA + Firefox, VoiceOver + Safari, or JAWS + Chrome)? Are all elements announced with correct names, roles, and states?
|
|
1033
|
+
4. **Alt text:** Does every non-decorative image have a descriptive `alt` attribute? Are decorative images marked with `alt=""`?
|
|
1034
|
+
5. **Labels:** Is every form field programmatically associated with a visible `<label>`? Are error messages associated via `aria-describedby`?
|
|
1035
|
+
6. **Contrast:** Do all text/background color pairs meet 4.5:1 (normal text) or 3:1 (large text) contrast ratios?
|
|
1036
|
+
7. **Zoom:** Does the page remain functional and readable at 200% browser zoom?
|
|
1037
|
+
8. **Color independence:** Is every piece of information conveyed by color also conveyed by text, icon, or pattern?
|
|
1038
|
+
9. **Semantic HTML:** Are you using native elements (`<button>`, `<a>`, `<nav>`, `<main>`, `<dialog>`, `<table>`) instead of ARIA-patched `<div>` elements?
|
|
1039
|
+
10. **Dynamic content:** Are AJAX updates, notifications, and status changes announced via ARIA live regions?
|
|
1040
|
+
11. **Time limits:** Can users extend, adjust, or disable any time-limited feature?
|
|
1041
|
+
12. **Skip link:** Does a "Skip to main content" link appear as the first focusable element?
|
|
1042
|
+
13. **Modals:** Do dialogs trap focus, release on Escape, and return focus to the trigger on close?
|
|
1043
|
+
14. **Third-party widgets:** Have all embedded components (video players, maps, CAPTCHAs) been tested for keyboard and screen reader accessibility?
|
|
1044
|
+
|
|
1045
|
+
---
|
|
1046
|
+
|
|
1047
|
+
## Code Smell Quick Reference
|
|
1048
|
+
|
|
1049
|
+
| Smell | Likely Anti-Pattern | Fix |
|
|
1050
|
+
|-------|---------------------|-----|
|
|
1051
|
+
| `<div onclick="...">` | #2 Div soup, #11 No keyboard | Use `<button>` |
|
|
1052
|
+
| `outline: none` without replacement | #4 Missing focus indicator | Use `:focus-visible` with custom outline |
|
|
1053
|
+
| `tabindex="5"` (positive values) | #20 Focus order mismatch | Use `tabindex="0"` or restructure DOM |
|
|
1054
|
+
| `<img>` without `alt` | #1 Missing alt text | Add descriptive `alt` or `alt=""` for decorative |
|
|
1055
|
+
| `<input>` without `<label for="">` | #10 Inaccessible forms | Add associated `<label>` element |
|
|
1056
|
+
| `placeholder` as only label | #10 Inaccessible forms | Add visible `<label>`, keep placeholder as hint |
|
|
1057
|
+
| `color: #999` on `#fff` background | #12 Low contrast | Check contrast ratio, use 4.5:1 minimum |
|
|
1058
|
+
| `user-scalable=no` in viewport | #13 Non-resizable text | Remove, allow user zoom |
|
|
1059
|
+
| `:hover` without `:focus` | #14 Mouse-only interaction | Add `:focus` and `:focus-within` |
|
|
1060
|
+
| `role="menu"` on site nav | #6 ARIA misuse | Remove role or use `<nav>` with `<ul>` |
|
|
1061
|
+
| `aria-role="..."` | #6 ARIA misuse (wrong syntax) | Use `role="..."` |
|
|
1062
|
+
| `autoplay` without `muted` | #7 Auto-playing media | Add `muted` and visible controls |
|
|
1063
|
+
| `setTimeout` + redirect | #8 Time limits | Add warning and extension mechanism |
|
|
1064
|
+
| `<table>` without `<th>` | #19 Headerless tables | Add `<th scope="col/row">` and `<caption>` |
|
|
1065
|
+
| `<div class="modal">` | #18 Inaccessible modal | Use `<dialog>` or add `role="dialog"` + focus management |
|
|
1066
|
+
| DOM update without `aria-live` | #17 Silent dynamic content | Use `aria-live="polite"` or `role="alert"` |
|
|
1067
|
+
| No skip link in `<body>` | #9 Missing skip navigation | Add `<a href="#main">` as first child |
|
|
1068
|
+
| `border-color: red` as only error signal | #5 Color-only information | Add text message + icon + `aria-invalid` |
|
|
1069
|
+
| reCAPTCHA v2 without alternative | #16 Inaccessible CAPTCHA | Use reCAPTCHA v3 or Turnstile |
|
|
1070
|
+
| Scanned PDF without OCR | #15 PDF-only content | Provide HTML alternative or tagged PDF |
|
|
1071
|
+
|
|
1072
|
+
---
|
|
1073
|
+
|
|
1074
|
+
## Key Legal Cases and Regulatory Actions
|
|
1075
|
+
|
|
1076
|
+
| Case / Action | Year | Outcome | Key Lesson |
|
|
1077
|
+
|---|---|---|---|
|
|
1078
|
+
| **NFB v. Target Corp.** | 2006-2008 | $6 million settlement; Target required to make website accessible | First precedent: commercial websites must comply with ADA |
|
|
1079
|
+
| **Robles v. Domino's Pizza** | 2016-2022 | Supreme Court declined Domino's appeal; settled after 6 years | ADA applies to websites; screen reader access is required |
|
|
1080
|
+
| **Conner v. Parkwood Entertainment** (Beyonce.com) | 2019 | Class action for missing alt text, no keyboard access, broken navigation | Celebrity and entertainment sites are not exempt |
|
|
1081
|
+
| **Gil v. Winn-Dixie** | 2016-2022 | Initially won at trial; reversed on appeal; vacated as moot | Legal landscape is evolving but risk remains |
|
|
1082
|
+
| **FTC v. accessiBe** | 2025 | $1 million FTC settlement | Accessibility overlay widgets are not a compliance solution |
|
|
1083
|
+
| **4,000+ ADA lawsuits** | 2024 | 69% targeted eCommerce; 22.6% targeted sites with overlay widgets | Volume is accelerating; no industry is safe |
|
|
1084
|
+
| **2,014 ADA lawsuits (H1)** | 2025 | 37% increase over 2024; fines up to $115,231 first offense | Enforcement is intensifying year over year |
|
|
1085
|
+
|
|
1086
|
+
---
|
|
1087
|
+
|
|
1088
|
+
## Testing Toolkit
|
|
1089
|
+
|
|
1090
|
+
**Automated (catches ~30% of WCAG issues):**
|
|
1091
|
+
- axe-core / axe DevTools (browser extension)
|
|
1092
|
+
- Lighthouse accessibility audit (Chrome DevTools)
|
|
1093
|
+
- eslint-plugin-jsx-a11y (React/JSX linting)
|
|
1094
|
+
- Pa11y CI (CI/CD integration)
|
|
1095
|
+
- WAVE (WebAIM browser extension)
|
|
1096
|
+
|
|
1097
|
+
**Screen Readers (essential for the other ~70%):**
|
|
1098
|
+
- NVDA + Firefox (Windows, free)
|
|
1099
|
+
- JAWS + Chrome (Windows, commercial)
|
|
1100
|
+
- VoiceOver + Safari (macOS/iOS, built-in)
|
|
1101
|
+
- TalkBack + Chrome (Android, built-in)
|
|
1102
|
+
|
|
1103
|
+
**Contrast and Color:**
|
|
1104
|
+
- WebAIM Contrast Checker
|
|
1105
|
+
- Chrome DevTools color picker (shows ratio inline)
|
|
1106
|
+
- Stark (Figma/Sketch plugin)
|
|
1107
|
+
|
|
1108
|
+
**Keyboard Testing:**
|
|
1109
|
+
- Unplug mouse; navigate entire site with Tab, Shift+Tab, Enter, Space, Escape, Arrow keys
|
|
1110
|
+
- Check focus visibility at every step
|
|
1111
|
+
|
|
1112
|
+
---
|
|
1113
|
+
|
|
1114
|
+
## Sources
|
|
1115
|
+
|
|
1116
|
+
- [WebAIM Million 2025 Report](https://webaim.org/projects/million/)
|
|
1117
|
+
- [WebAIM: To ARIA! The Cause of, and Solution to, All Our Accessibility Problems](https://webaim.org/blog/aria-cause-solution/)
|
|
1118
|
+
- [Deque: WAI-ARIA Top 6 Mistakes to Avoid](https://www.deque.com/blog/wai-aria-top-6-mistakes-to-avoid/)
|
|
1119
|
+
- [W3C: Understanding WCAG 2.1 SC 2.1.2 No Keyboard Trap](https://www.w3.org/WAI/WCAG21/Understanding/no-keyboard-trap.html)
|
|
1120
|
+
- [W3C: Inaccessibility of CAPTCHA](https://www.w3.org/TR/turingtest/)
|
|
1121
|
+
- [BOIA: Missing Input Labels](https://www.boia.org/blog/missing-input-labels-how-to-fix-a-common-accessibility-issue)
|
|
1122
|
+
- [BOIA: Low Contrast Text Most Common Issue](https://www.boia.org/blog/low-contrast-text-remains-the-most-common-accessibility-issue-in-2023)
|
|
1123
|
+
- [BOIA: Skip Navigation Links](https://www.boia.org/blog/skip-navigation-links-avoiding-common-accessibility-mistakes)
|
|
1124
|
+
- [NFB v. Target Corp. Settlement](https://nfb.org/images/nfb/publications/bm/bm08/bm0809/bm080915.htm)
|
|
1125
|
+
- [Robles v. Domino's Pizza Settlement](https://www.boia.org/blog/the-robles-v.-dominos-settlement-and-why-it-matters)
|
|
1126
|
+
- [Conner v. Parkwood Entertainment (Beyonce.com)](https://www.billboard.com/music/music-news/beyonce-parkwood-entertainment-sued-website-accessibility-8492195/)
|
|
1127
|
+
- [Gil v. Winn-Dixie (11th Circuit)](https://adasoutheast.org/legal/court/gil-v-winn-dixie-2021/)
|
|
1128
|
+
- [FTC v. accessiBe ($1M Settlement)](https://www.ftc.gov/news-events/news/press-releases/2025/01/ftc-order-requires-online-marketer-pay-1-million-deceptive-claims-its-ai-product-could-make-websites)
|
|
1129
|
+
- [Accessibility.Works: ADA Lawsuit Trends 2024](https://www.accessibility.works/blog/ada-lawsuit-trends-statistics-2024-summary/)
|
|
1130
|
+
- [AudioEye: Website Accessibility in 2025](https://www.audioeye.com/post/website-accessibility-in-2025/)
|
|
1131
|
+
- [TPGi: The Current State of Modal Dialog Accessibility](https://www.tpgi.com/the-current-state-of-modal-dialog-accessibility/)
|
|
1132
|
+
- [Smashing Magazine: Accessibility Problem with CAPTCHA](https://www.smashingmagazine.com/2025/11/accessibility-problem-authentication-methods-captcha/)
|
|
1133
|
+
- [A11Y Collective: Keyboard Trap](https://www.a11y-collective.com/blog/keyboard-trap/)
|
|
1134
|
+
- [MDN: ARIA Live Regions](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Guides/Live_regions)
|
|
1135
|
+
- [WebAIM: Contrast and Color Accessibility](https://webaim.org/articles/contrast/)
|
|
1136
|
+
- [FT Product & Technology: An Outbreak of Accessibility Anti-Patterns](https://medium.com/ft-product-technology/an-outbreak-of-accessibility-anti-patterns-e73577242ee8)
|