agentic-team-templates 0.16.0 → 0.18.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/README.md +30 -24
- package/package.json +1 -1
- package/src/index.js +221 -123
- package/src/index.test.js +138 -66
- package/templates/ux-designer/.cursor/rules/accessibility.md +214 -0
- package/templates/ux-designer/.cursor/rules/emotional-design.md +217 -0
- package/templates/ux-designer/.cursor/rules/handoff.md +251 -0
- package/templates/ux-designer/.cursor/rules/information-architecture.md +193 -0
- package/templates/ux-designer/.cursor/rules/interaction-design.md +221 -0
- package/templates/ux-designer/.cursor/rules/overview.md +110 -0
- package/templates/ux-designer/.cursor/rules/research.md +181 -0
- package/templates/ux-designer/.cursor/rules/visual-design.md +191 -0
- package/templates/ux-designer/CLAUDE.md +124 -0
- /package/templates/blockchain/{.cursorrules → .cursor/rules}/defi-patterns.md +0 -0
- /package/templates/blockchain/{.cursorrules → .cursor/rules}/gas-optimization.md +0 -0
- /package/templates/blockchain/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/blockchain/{.cursorrules → .cursor/rules}/security.md +0 -0
- /package/templates/blockchain/{.cursorrules → .cursor/rules}/smart-contracts.md +0 -0
- /package/templates/blockchain/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/blockchain/{.cursorrules → .cursor/rules}/web3-integration.md +0 -0
- /package/templates/cli-tools/{.cursorrules → .cursor/rules}/architecture.md +0 -0
- /package/templates/cli-tools/{.cursorrules → .cursor/rules}/arguments.md +0 -0
- /package/templates/cli-tools/{.cursorrules → .cursor/rules}/distribution.md +0 -0
- /package/templates/cli-tools/{.cursorrules → .cursor/rules}/error-handling.md +0 -0
- /package/templates/cli-tools/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/cli-tools/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/cli-tools/{.cursorrules → .cursor/rules}/user-experience.md +0 -0
- /package/templates/cpp-expert/{.cursorrules → .cursor/rules}/concurrency.md +0 -0
- /package/templates/cpp-expert/{.cursorrules → .cursor/rules}/error-handling.md +0 -0
- /package/templates/cpp-expert/{.cursorrules → .cursor/rules}/memory-and-ownership.md +0 -0
- /package/templates/cpp-expert/{.cursorrules → .cursor/rules}/modern-cpp.md +0 -0
- /package/templates/cpp-expert/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/cpp-expert/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/cpp-expert/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/cpp-expert/{.cursorrules → .cursor/rules}/tooling.md +0 -0
- /package/templates/csharp-expert/{.cursorrules → .cursor/rules}/aspnet-core.md +0 -0
- /package/templates/csharp-expert/{.cursorrules → .cursor/rules}/async-patterns.md +0 -0
- /package/templates/csharp-expert/{.cursorrules → .cursor/rules}/dependency-injection.md +0 -0
- /package/templates/csharp-expert/{.cursorrules → .cursor/rules}/error-handling.md +0 -0
- /package/templates/csharp-expert/{.cursorrules → .cursor/rules}/language-features.md +0 -0
- /package/templates/csharp-expert/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/csharp-expert/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/csharp-expert/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/csharp-expert/{.cursorrules → .cursor/rules}/tooling.md +0 -0
- /package/templates/data-engineering/{.cursorrules → .cursor/rules}/data-modeling.md +0 -0
- /package/templates/data-engineering/{.cursorrules → .cursor/rules}/data-quality.md +0 -0
- /package/templates/data-engineering/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/data-engineering/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/data-engineering/{.cursorrules → .cursor/rules}/pipeline-design.md +0 -0
- /package/templates/data-engineering/{.cursorrules → .cursor/rules}/security.md +0 -0
- /package/templates/data-engineering/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/capacity-planning.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/change-management.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/chaos-engineering.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/disaster-recovery.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/incident-management.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/observability.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/postmortems.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/runbooks.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/slo-sli.md +0 -0
- /package/templates/devops-sre/{.cursorrules → .cursor/rules}/toil-reduction.md +0 -0
- /package/templates/documentation/{.cursorrules → .cursor/rules}/adr.md +0 -0
- /package/templates/documentation/{.cursorrules → .cursor/rules}/api-documentation.md +0 -0
- /package/templates/documentation/{.cursorrules → .cursor/rules}/code-comments.md +0 -0
- /package/templates/documentation/{.cursorrules → .cursor/rules}/maintenance.md +0 -0
- /package/templates/documentation/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/documentation/{.cursorrules → .cursor/rules}/readme-standards.md +0 -0
- /package/templates/educator/{.cursorrules → .cursor/rules}/accessibility.md +0 -0
- /package/templates/educator/{.cursorrules → .cursor/rules}/assessment.md +0 -0
- /package/templates/educator/{.cursorrules → .cursor/rules}/curriculum.md +0 -0
- /package/templates/educator/{.cursorrules → .cursor/rules}/engagement.md +0 -0
- /package/templates/educator/{.cursorrules → .cursor/rules}/instructional-design.md +0 -0
- /package/templates/educator/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/educator/{.cursorrules → .cursor/rules}/retention.md +0 -0
- /package/templates/fullstack/{.cursorrules → .cursor/rules}/api-contracts.md +0 -0
- /package/templates/fullstack/{.cursorrules → .cursor/rules}/architecture.md +0 -0
- /package/templates/fullstack/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/fullstack/{.cursorrules → .cursor/rules}/shared-types.md +0 -0
- /package/templates/fullstack/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/golang-expert/{.cursorrules → .cursor/rules}/concurrency.md +0 -0
- /package/templates/golang-expert/{.cursorrules → .cursor/rules}/error-handling.md +0 -0
- /package/templates/golang-expert/{.cursorrules → .cursor/rules}/interfaces-and-types.md +0 -0
- /package/templates/golang-expert/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/golang-expert/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/golang-expert/{.cursorrules → .cursor/rules}/production-patterns.md +0 -0
- /package/templates/golang-expert/{.cursorrules → .cursor/rules}/stdlib-and-tooling.md +0 -0
- /package/templates/golang-expert/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/java-expert/{.cursorrules → .cursor/rules}/concurrency.md +0 -0
- /package/templates/java-expert/{.cursorrules → .cursor/rules}/error-handling.md +0 -0
- /package/templates/java-expert/{.cursorrules → .cursor/rules}/modern-java.md +0 -0
- /package/templates/java-expert/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/java-expert/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/java-expert/{.cursorrules → .cursor/rules}/persistence.md +0 -0
- /package/templates/java-expert/{.cursorrules → .cursor/rules}/spring-boot.md +0 -0
- /package/templates/java-expert/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/java-expert/{.cursorrules → .cursor/rules}/tooling.md +0 -0
- /package/templates/javascript-expert/{.cursorrules → .cursor/rules}/language-deep-dive.md +0 -0
- /package/templates/javascript-expert/{.cursorrules → .cursor/rules}/node-patterns.md +0 -0
- /package/templates/javascript-expert/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/javascript-expert/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/javascript-expert/{.cursorrules → .cursor/rules}/react-patterns.md +0 -0
- /package/templates/javascript-expert/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/javascript-expert/{.cursorrules → .cursor/rules}/tooling.md +0 -0
- /package/templates/javascript-expert/{.cursorrules → .cursor/rules}/typescript-deep-dive.md +0 -0
- /package/templates/kotlin-expert/{.cursorrules → .cursor/rules}/coroutines.md +0 -0
- /package/templates/kotlin-expert/{.cursorrules → .cursor/rules}/error-handling.md +0 -0
- /package/templates/kotlin-expert/{.cursorrules → .cursor/rules}/frameworks.md +0 -0
- /package/templates/kotlin-expert/{.cursorrules → .cursor/rules}/language-features.md +0 -0
- /package/templates/kotlin-expert/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/kotlin-expert/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/kotlin-expert/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/kotlin-expert/{.cursorrules → .cursor/rules}/tooling.md +0 -0
- /package/templates/ml-ai/{.cursorrules → .cursor/rules}/data-engineering.md +0 -0
- /package/templates/ml-ai/{.cursorrules → .cursor/rules}/deployment.md +0 -0
- /package/templates/ml-ai/{.cursorrules → .cursor/rules}/model-development.md +0 -0
- /package/templates/ml-ai/{.cursorrules → .cursor/rules}/monitoring.md +0 -0
- /package/templates/ml-ai/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/ml-ai/{.cursorrules → .cursor/rules}/security.md +0 -0
- /package/templates/ml-ai/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/mobile/{.cursorrules → .cursor/rules}/navigation.md +0 -0
- /package/templates/mobile/{.cursorrules → .cursor/rules}/offline-first.md +0 -0
- /package/templates/mobile/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/mobile/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/mobile/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/platform-engineering/{.cursorrules → .cursor/rules}/ci-cd.md +0 -0
- /package/templates/platform-engineering/{.cursorrules → .cursor/rules}/developer-experience.md +0 -0
- /package/templates/platform-engineering/{.cursorrules → .cursor/rules}/infrastructure-as-code.md +0 -0
- /package/templates/platform-engineering/{.cursorrules → .cursor/rules}/kubernetes.md +0 -0
- /package/templates/platform-engineering/{.cursorrules → .cursor/rules}/observability.md +0 -0
- /package/templates/platform-engineering/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/platform-engineering/{.cursorrules → .cursor/rules}/security.md +0 -0
- /package/templates/platform-engineering/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/product-manager/{.cursorrules → .cursor/rules}/communication.md +0 -0
- /package/templates/product-manager/{.cursorrules → .cursor/rules}/discovery.md +0 -0
- /package/templates/product-manager/{.cursorrules → .cursor/rules}/metrics.md +0 -0
- /package/templates/product-manager/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/product-manager/{.cursorrules → .cursor/rules}/prioritization.md +0 -0
- /package/templates/product-manager/{.cursorrules → .cursor/rules}/requirements.md +0 -0
- /package/templates/python-expert/{.cursorrules → .cursor/rules}/async-python.md +0 -0
- /package/templates/python-expert/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/python-expert/{.cursorrules → .cursor/rules}/patterns-and-idioms.md +0 -0
- /package/templates/python-expert/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/python-expert/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/python-expert/{.cursorrules → .cursor/rules}/tooling.md +0 -0
- /package/templates/python-expert/{.cursorrules → .cursor/rules}/type-system.md +0 -0
- /package/templates/python-expert/{.cursorrules → .cursor/rules}/web-and-apis.md +0 -0
- /package/templates/qa-engineering/{.cursorrules → .cursor/rules}/automation.md +0 -0
- /package/templates/qa-engineering/{.cursorrules → .cursor/rules}/metrics.md +0 -0
- /package/templates/qa-engineering/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/qa-engineering/{.cursorrules → .cursor/rules}/quality-gates.md +0 -0
- /package/templates/qa-engineering/{.cursorrules → .cursor/rules}/test-design.md +0 -0
- /package/templates/qa-engineering/{.cursorrules → .cursor/rules}/test-strategy.md +0 -0
- /package/templates/rust-expert/{.cursorrules → .cursor/rules}/concurrency.md +0 -0
- /package/templates/rust-expert/{.cursorrules → .cursor/rules}/ecosystem-and-tooling.md +0 -0
- /package/templates/rust-expert/{.cursorrules → .cursor/rules}/error-handling.md +0 -0
- /package/templates/rust-expert/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/rust-expert/{.cursorrules → .cursor/rules}/ownership-and-borrowing.md +0 -0
- /package/templates/rust-expert/{.cursorrules → .cursor/rules}/performance-and-unsafe.md +0 -0
- /package/templates/rust-expert/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/rust-expert/{.cursorrules → .cursor/rules}/traits-and-generics.md +0 -0
- /package/templates/swift-expert/{.cursorrules → .cursor/rules}/concurrency.md +0 -0
- /package/templates/swift-expert/{.cursorrules → .cursor/rules}/error-handling.md +0 -0
- /package/templates/swift-expert/{.cursorrules → .cursor/rules}/language-features.md +0 -0
- /package/templates/swift-expert/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/swift-expert/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/swift-expert/{.cursorrules → .cursor/rules}/swiftui.md +0 -0
- /package/templates/swift-expert/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/swift-expert/{.cursorrules → .cursor/rules}/tooling.md +0 -0
- /package/templates/testing/{.cursorrules → .cursor/rules}/advanced-techniques.md +0 -0
- /package/templates/testing/{.cursorrules → .cursor/rules}/ci-cd-integration.md +0 -0
- /package/templates/testing/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/testing/{.cursorrules → .cursor/rules}/performance-testing.md +0 -0
- /package/templates/testing/{.cursorrules → .cursor/rules}/quality-metrics.md +0 -0
- /package/templates/testing/{.cursorrules → .cursor/rules}/reliability.md +0 -0
- /package/templates/testing/{.cursorrules → .cursor/rules}/tdd-methodology.md +0 -0
- /package/templates/testing/{.cursorrules → .cursor/rules}/test-data.md +0 -0
- /package/templates/testing/{.cursorrules → .cursor/rules}/test-design.md +0 -0
- /package/templates/testing/{.cursorrules → .cursor/rules}/test-types.md +0 -0
- /package/templates/utility-agent/{.cursorrules → .cursor/rules}/action-control.md +0 -0
- /package/templates/utility-agent/{.cursorrules → .cursor/rules}/context-management.md +0 -0
- /package/templates/utility-agent/{.cursorrules → .cursor/rules}/hallucination-prevention.md +0 -0
- /package/templates/utility-agent/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/utility-agent/{.cursorrules → .cursor/rules}/token-optimization.md +0 -0
- /package/templates/web-backend/{.cursorrules → .cursor/rules}/api-design.md +0 -0
- /package/templates/web-backend/{.cursorrules → .cursor/rules}/authentication.md +0 -0
- /package/templates/web-backend/{.cursorrules → .cursor/rules}/database-patterns.md +0 -0
- /package/templates/web-backend/{.cursorrules → .cursor/rules}/error-handling.md +0 -0
- /package/templates/web-backend/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/web-backend/{.cursorrules → .cursor/rules}/security.md +0 -0
- /package/templates/web-backend/{.cursorrules → .cursor/rules}/testing.md +0 -0
- /package/templates/web-frontend/{.cursorrules → .cursor/rules}/accessibility.md +0 -0
- /package/templates/web-frontend/{.cursorrules → .cursor/rules}/component-patterns.md +0 -0
- /package/templates/web-frontend/{.cursorrules → .cursor/rules}/overview.md +0 -0
- /package/templates/web-frontend/{.cursorrules → .cursor/rules}/performance.md +0 -0
- /package/templates/web-frontend/{.cursorrules → .cursor/rules}/state-management.md +0 -0
- /package/templates/web-frontend/{.cursorrules → .cursor/rules}/styling.md +0 -0
- /package/templates/web-frontend/{.cursorrules → .cursor/rules}/testing.md +0 -0
package/src/index.test.js
CHANGED
|
@@ -7,6 +7,8 @@ import { run, _internals } from './index.js';
|
|
|
7
7
|
const {
|
|
8
8
|
PACKAGE_NAME,
|
|
9
9
|
CURRENT_VERSION,
|
|
10
|
+
CURSOR_RULES_DIR,
|
|
11
|
+
LEGACY_CURSORRULES_DIR,
|
|
10
12
|
TEMPLATES,
|
|
11
13
|
TEMPLATE_ALIASES,
|
|
12
14
|
SHARED_RULES,
|
|
@@ -98,6 +100,7 @@ describe('Constants', () => {
|
|
|
98
100
|
'swift-expert',
|
|
99
101
|
'testing',
|
|
100
102
|
'utility-agent',
|
|
103
|
+
'ux-designer',
|
|
101
104
|
'web-backend',
|
|
102
105
|
'web-frontend',
|
|
103
106
|
];
|
|
@@ -714,104 +717,139 @@ describe('Install/Remove/Reset Operations', () => {
|
|
|
714
717
|
});
|
|
715
718
|
|
|
716
719
|
describe('install', () => {
|
|
717
|
-
it('should create .
|
|
718
|
-
install(tempDir, ['web-frontend'], false, false, ['cursor']);
|
|
719
|
-
|
|
720
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
720
|
+
it('should create .cursor/rules directory', async () => {
|
|
721
|
+
await install(tempDir, ['web-frontend'], false, false, ['cursor']);
|
|
722
|
+
|
|
723
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules'))).toBe(true);
|
|
721
724
|
});
|
|
722
725
|
|
|
723
|
-
it('should install shared rules', () => {
|
|
724
|
-
install(tempDir, ['web-frontend'], false, false, ['cursor']);
|
|
725
|
-
|
|
726
|
+
it('should install shared rules', async () => {
|
|
727
|
+
await install(tempDir, ['web-frontend'], false, false, ['cursor']);
|
|
728
|
+
|
|
726
729
|
for (const rule of SHARED_RULES) {
|
|
727
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
730
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', rule))).toBe(true);
|
|
728
731
|
}
|
|
729
732
|
});
|
|
730
733
|
|
|
731
|
-
it('should install template-specific rules with prefix', () => {
|
|
732
|
-
install(tempDir, ['web-frontend'], false, false, ['cursor']);
|
|
733
|
-
|
|
734
|
+
it('should install template-specific rules with prefix', async () => {
|
|
735
|
+
await install(tempDir, ['web-frontend'], false, false, ['cursor']);
|
|
736
|
+
|
|
734
737
|
for (const rule of TEMPLATES['web-frontend'].rules) {
|
|
735
738
|
const prefixedName = `web-frontend-${rule}`;
|
|
736
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
739
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', prefixedName))).toBe(true);
|
|
737
740
|
}
|
|
738
741
|
});
|
|
739
742
|
|
|
740
|
-
it('should create CLAUDE.md for claude IDE', () => {
|
|
741
|
-
install(tempDir, ['web-frontend'], false, false, ['claude']);
|
|
742
|
-
|
|
743
|
+
it('should create CLAUDE.md for claude IDE', async () => {
|
|
744
|
+
await install(tempDir, ['web-frontend'], false, false, ['claude']);
|
|
745
|
+
|
|
743
746
|
expect(fs.existsSync(path.join(tempDir, 'CLAUDE.md'))).toBe(true);
|
|
744
747
|
const content = fs.readFileSync(path.join(tempDir, 'CLAUDE.md'), 'utf8');
|
|
745
748
|
expect(content).toContain('# CLAUDE.md - Development Guide');
|
|
746
749
|
});
|
|
747
750
|
|
|
748
|
-
it('should create copilot-instructions.md for codex IDE', () => {
|
|
749
|
-
install(tempDir, ['web-frontend'], false, false, ['codex']);
|
|
750
|
-
|
|
751
|
+
it('should create copilot-instructions.md for codex IDE', async () => {
|
|
752
|
+
await install(tempDir, ['web-frontend'], false, false, ['codex']);
|
|
753
|
+
|
|
751
754
|
expect(fs.existsSync(path.join(tempDir, '.github', 'copilot-instructions.md'))).toBe(true);
|
|
752
755
|
});
|
|
753
756
|
|
|
754
|
-
it('should install for all IDEs by default', () => {
|
|
755
|
-
install(tempDir, ['web-frontend'], false, false, DEFAULT_IDES);
|
|
756
|
-
|
|
757
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
757
|
+
it('should install for all IDEs by default', async () => {
|
|
758
|
+
await install(tempDir, ['web-frontend'], false, false, DEFAULT_IDES);
|
|
759
|
+
|
|
760
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules'))).toBe(true);
|
|
758
761
|
expect(fs.existsSync(path.join(tempDir, 'CLAUDE.md'))).toBe(true);
|
|
759
762
|
expect(fs.existsSync(path.join(tempDir, '.github', 'copilot-instructions.md'))).toBe(true);
|
|
760
763
|
});
|
|
761
764
|
|
|
762
|
-
it('should not write files in dry-run mode', () => {
|
|
763
|
-
install(tempDir, ['web-frontend'], true, false, ['cursor']);
|
|
764
|
-
|
|
765
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
765
|
+
it('should not write files in dry-run mode', async () => {
|
|
766
|
+
await install(tempDir, ['web-frontend'], true, false, ['cursor']);
|
|
767
|
+
|
|
768
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules'))).toBe(false);
|
|
766
769
|
});
|
|
767
770
|
|
|
768
|
-
it('should install multiple templates', () => {
|
|
769
|
-
install(tempDir, ['web-frontend', 'web-backend'], false, false, ['cursor']);
|
|
770
|
-
|
|
771
|
+
it('should install multiple templates', async () => {
|
|
772
|
+
await install(tempDir, ['web-frontend', 'web-backend'], false, false, ['cursor']);
|
|
773
|
+
|
|
771
774
|
// Check web-frontend rules
|
|
772
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
775
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', 'web-frontend-overview.md'))).toBe(true);
|
|
773
776
|
// Check web-backend rules
|
|
774
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
777
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', 'web-backend-overview.md'))).toBe(true);
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it('should detect legacy .cursorrules/ and create notice when cleanup declined', async () => {
|
|
781
|
+
// Create a legacy .cursorrules/ directory
|
|
782
|
+
const legacyDir = path.join(tempDir, LEGACY_CURSORRULES_DIR);
|
|
783
|
+
fs.mkdirSync(legacyDir, { recursive: true });
|
|
784
|
+
fs.writeFileSync(path.join(legacyDir, 'old-rule.md'), '# Old rule');
|
|
785
|
+
|
|
786
|
+
// Install with skipConfirm=false but mock stdin to decline
|
|
787
|
+
// Since we can't easily mock stdin, use the fact that confirm defaults to 'N'
|
|
788
|
+
// We'll test the --yes path which auto-cleans, and also the notice path
|
|
789
|
+
// For the notice path, install without skipConfirm - the confirm() will fail in test env
|
|
790
|
+
// Actually, let's just test the --yes (skipConfirm) paths
|
|
791
|
+
|
|
792
|
+
// Test: skipConfirm=true should remove legacy dir
|
|
793
|
+
await install(tempDir, ['web-frontend'], false, false, ['cursor'], true);
|
|
794
|
+
|
|
795
|
+
expect(fs.existsSync(legacyDir)).toBe(false);
|
|
796
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', 'web-frontend-overview.md'))).toBe(true);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
it('should show legacy warning in dry-run mode without prompting', async () => {
|
|
800
|
+
// Create a legacy .cursorrules/ directory
|
|
801
|
+
const legacyDir = path.join(tempDir, LEGACY_CURSORRULES_DIR);
|
|
802
|
+
fs.mkdirSync(legacyDir, { recursive: true });
|
|
803
|
+
fs.writeFileSync(path.join(legacyDir, 'old-rule.md'), '# Old rule');
|
|
804
|
+
|
|
805
|
+
await install(tempDir, ['web-frontend'], true, false, ['cursor']);
|
|
806
|
+
|
|
807
|
+
// Legacy dir should still exist (dry-run doesn't modify)
|
|
808
|
+
expect(fs.existsSync(legacyDir)).toBe(true);
|
|
809
|
+
// Warning should have been printed
|
|
810
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
811
|
+
expect.stringContaining('Deprecated')
|
|
812
|
+
);
|
|
775
813
|
});
|
|
776
814
|
});
|
|
777
815
|
|
|
778
816
|
describe('remove', () => {
|
|
779
|
-
beforeEach(() => {
|
|
817
|
+
beforeEach(async () => {
|
|
780
818
|
// First install a template
|
|
781
|
-
install(tempDir, ['web-frontend'], false, false, ['cursor']);
|
|
819
|
+
await install(tempDir, ['web-frontend'], false, false, ['cursor']);
|
|
782
820
|
});
|
|
783
821
|
|
|
784
822
|
it('should remove template-specific files', async () => {
|
|
785
823
|
await remove(tempDir, ['web-frontend'], false, false, true, ['cursor']);
|
|
786
|
-
|
|
824
|
+
|
|
787
825
|
for (const rule of TEMPLATES['web-frontend'].rules) {
|
|
788
826
|
const prefixedName = `web-frontend-${rule}`;
|
|
789
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
827
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', prefixedName))).toBe(false);
|
|
790
828
|
}
|
|
791
829
|
});
|
|
792
830
|
|
|
793
831
|
it('should keep shared rules when removing template', async () => {
|
|
794
832
|
await remove(tempDir, ['web-frontend'], false, false, true, ['cursor']);
|
|
795
|
-
|
|
833
|
+
|
|
796
834
|
for (const rule of SHARED_RULES) {
|
|
797
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
835
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', rule))).toBe(true);
|
|
798
836
|
}
|
|
799
837
|
});
|
|
800
838
|
|
|
801
839
|
it('should not remove files in dry-run mode', async () => {
|
|
802
840
|
await remove(tempDir, ['web-frontend'], true, false, true, ['cursor']);
|
|
803
|
-
|
|
841
|
+
|
|
804
842
|
// Files should still exist
|
|
805
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
843
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', 'web-frontend-overview.md'))).toBe(true);
|
|
806
844
|
});
|
|
807
845
|
|
|
808
846
|
it('should skip modified files without force', async () => {
|
|
809
847
|
// Modify a file
|
|
810
|
-
const filePath = path.join(tempDir, '.
|
|
848
|
+
const filePath = path.join(tempDir, '.cursor', 'rules', 'web-frontend-overview.md');
|
|
811
849
|
fs.writeFileSync(filePath, '# Modified content');
|
|
812
|
-
|
|
850
|
+
|
|
813
851
|
await remove(tempDir, ['web-frontend'], false, false, true, ['cursor']);
|
|
814
|
-
|
|
852
|
+
|
|
815
853
|
// Modified file should still exist
|
|
816
854
|
expect(fs.existsSync(filePath)).toBe(true);
|
|
817
855
|
expect(fs.readFileSync(filePath, 'utf8')).toBe('# Modified content');
|
|
@@ -819,73 +857,107 @@ describe('Install/Remove/Reset Operations', () => {
|
|
|
819
857
|
|
|
820
858
|
it('should remove modified files with force', async () => {
|
|
821
859
|
// Modify a file
|
|
822
|
-
const filePath = path.join(tempDir, '.
|
|
860
|
+
const filePath = path.join(tempDir, '.cursor', 'rules', 'web-frontend-overview.md');
|
|
823
861
|
fs.writeFileSync(filePath, '# Modified content');
|
|
824
|
-
|
|
862
|
+
|
|
825
863
|
await remove(tempDir, ['web-frontend'], false, true, true, ['cursor']);
|
|
826
|
-
|
|
864
|
+
|
|
827
865
|
// Modified file should be removed
|
|
828
866
|
expect(fs.existsSync(filePath)).toBe(false);
|
|
829
867
|
});
|
|
868
|
+
|
|
869
|
+
it('should also remove files from legacy .cursorrules/ directory', async () => {
|
|
870
|
+
// Manually create files in legacy location
|
|
871
|
+
const legacyDir = path.join(tempDir, LEGACY_CURSORRULES_DIR);
|
|
872
|
+
fs.mkdirSync(legacyDir, { recursive: true });
|
|
873
|
+
for (const rule of TEMPLATES['web-frontend'].rules) {
|
|
874
|
+
fs.writeFileSync(path.join(legacyDir, `web-frontend-${rule}`), '# legacy content');
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
await remove(tempDir, ['web-frontend'], false, true, true, ['cursor']);
|
|
878
|
+
|
|
879
|
+
// Both new and legacy files should be removed
|
|
880
|
+
for (const rule of TEMPLATES['web-frontend'].rules) {
|
|
881
|
+
const prefixedName = `web-frontend-${rule}`;
|
|
882
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', prefixedName))).toBe(false);
|
|
883
|
+
expect(fs.existsSync(path.join(legacyDir, prefixedName))).toBe(false);
|
|
884
|
+
}
|
|
885
|
+
});
|
|
830
886
|
});
|
|
831
887
|
|
|
832
888
|
describe('reset', () => {
|
|
833
|
-
beforeEach(() => {
|
|
889
|
+
beforeEach(async () => {
|
|
834
890
|
// Install templates
|
|
835
|
-
install(tempDir, ['web-frontend', 'web-backend'], false, false, DEFAULT_IDES);
|
|
891
|
+
await install(tempDir, ['web-frontend', 'web-backend'], false, false, DEFAULT_IDES);
|
|
836
892
|
});
|
|
837
893
|
|
|
838
|
-
it('should remove all template files from .
|
|
894
|
+
it('should remove all template files from .cursor/rules', async () => {
|
|
839
895
|
await reset(tempDir, false, false, true, ['cursor']);
|
|
840
|
-
|
|
896
|
+
|
|
841
897
|
// Template files should be removed
|
|
842
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
843
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
898
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', 'web-frontend-overview.md'))).toBe(false);
|
|
899
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', 'web-backend-overview.md'))).toBe(false);
|
|
844
900
|
});
|
|
845
901
|
|
|
846
902
|
it('should remove shared rules', async () => {
|
|
847
903
|
await reset(tempDir, false, false, true, ['cursor']);
|
|
848
|
-
|
|
904
|
+
|
|
849
905
|
for (const rule of SHARED_RULES) {
|
|
850
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
906
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', rule))).toBe(false);
|
|
851
907
|
}
|
|
852
908
|
});
|
|
853
909
|
|
|
854
910
|
it('should remove CLAUDE.md', async () => {
|
|
855
911
|
await reset(tempDir, false, false, true, ['claude']);
|
|
856
|
-
|
|
912
|
+
|
|
857
913
|
expect(fs.existsSync(path.join(tempDir, 'CLAUDE.md'))).toBe(false);
|
|
858
914
|
});
|
|
859
915
|
|
|
860
916
|
it('should remove copilot-instructions.md', async () => {
|
|
861
917
|
await reset(tempDir, false, false, true, ['codex']);
|
|
862
|
-
|
|
918
|
+
|
|
863
919
|
expect(fs.existsSync(path.join(tempDir, '.github', 'copilot-instructions.md'))).toBe(false);
|
|
864
920
|
});
|
|
865
921
|
|
|
866
922
|
it('should not remove files in dry-run mode', async () => {
|
|
867
923
|
await reset(tempDir, true, false, true, DEFAULT_IDES);
|
|
868
|
-
|
|
924
|
+
|
|
869
925
|
// All files should still exist
|
|
870
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
926
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules'))).toBe(true);
|
|
871
927
|
expect(fs.existsSync(path.join(tempDir, 'CLAUDE.md'))).toBe(true);
|
|
872
928
|
});
|
|
873
929
|
|
|
874
|
-
it('should remove empty .
|
|
930
|
+
it('should remove empty .cursor/rules directory', async () => {
|
|
875
931
|
await reset(tempDir, false, false, true, ['cursor']);
|
|
876
|
-
|
|
877
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
932
|
+
|
|
933
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules'))).toBe(false);
|
|
878
934
|
});
|
|
879
935
|
|
|
880
|
-
it('should keep .
|
|
936
|
+
it('should keep .cursor/rules if non-template files remain', async () => {
|
|
881
937
|
// Add a custom file
|
|
882
|
-
fs.writeFileSync(path.join(tempDir, '.
|
|
883
|
-
|
|
938
|
+
fs.writeFileSync(path.join(tempDir, '.cursor', 'rules', 'my-custom-rules.md'), '# Custom');
|
|
939
|
+
|
|
884
940
|
await reset(tempDir, false, false, true, ['cursor']);
|
|
885
|
-
|
|
941
|
+
|
|
886
942
|
// Directory should still exist with custom file
|
|
887
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
888
|
-
expect(fs.existsSync(path.join(tempDir, '.
|
|
943
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules'))).toBe(true);
|
|
944
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', 'my-custom-rules.md'))).toBe(true);
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
it('should also clean up legacy .cursorrules/ directory', async () => {
|
|
948
|
+
// Manually create legacy directory with template files
|
|
949
|
+
const legacyDir = path.join(tempDir, LEGACY_CURSORRULES_DIR);
|
|
950
|
+
fs.mkdirSync(legacyDir, { recursive: true });
|
|
951
|
+
for (const rule of SHARED_RULES) {
|
|
952
|
+
fs.writeFileSync(path.join(legacyDir, rule), '# legacy shared');
|
|
953
|
+
}
|
|
954
|
+
fs.writeFileSync(path.join(legacyDir, 'web-frontend-overview.md'), '# legacy template');
|
|
955
|
+
|
|
956
|
+
await reset(tempDir, false, true, true, ['cursor']);
|
|
957
|
+
|
|
958
|
+
// Both directories should be cleaned up
|
|
959
|
+
expect(fs.existsSync(path.join(tempDir, '.cursor', 'rules', 'web-frontend-overview.md'))).toBe(false);
|
|
960
|
+
expect(fs.existsSync(path.join(legacyDir, 'web-frontend-overview.md'))).toBe(false);
|
|
889
961
|
});
|
|
890
962
|
});
|
|
891
963
|
});
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Accessibility
|
|
2
|
+
|
|
3
|
+
Designing inclusive experiences that work for all users regardless of ability, device, or context.
|
|
4
|
+
|
|
5
|
+
## Core Principle
|
|
6
|
+
|
|
7
|
+
**Accessibility is not a feature — it is a requirement.** An inaccessible product is a broken product. Design for the most constrained user and everyone benefits.
|
|
8
|
+
|
|
9
|
+
## WCAG 2.2 AA — POUR Framework
|
|
10
|
+
|
|
11
|
+
The Web Content Accessibility Guidelines organize requirements into four principles:
|
|
12
|
+
|
|
13
|
+
### Perceivable
|
|
14
|
+
|
|
15
|
+
Users must be able to perceive all information and UI components.
|
|
16
|
+
|
|
17
|
+
```markdown
|
|
18
|
+
Requirements:
|
|
19
|
+
- Text alternatives for non-text content (alt text, aria-labels)
|
|
20
|
+
- Captions and transcripts for audio/video content
|
|
21
|
+
- Content adaptable to different presentations (screen reader, zoom, reflow)
|
|
22
|
+
- Sufficient color contrast (4.5:1 for text, 3:1 for large text and UI components)
|
|
23
|
+
- No information conveyed by color alone (add icons, text, patterns)
|
|
24
|
+
- Text resizable up to 200% without loss of content or function
|
|
25
|
+
- Content reflows at 320px width without horizontal scrolling
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Operable
|
|
29
|
+
|
|
30
|
+
Users must be able to operate all UI components and navigation.
|
|
31
|
+
|
|
32
|
+
```markdown
|
|
33
|
+
Requirements:
|
|
34
|
+
- All functionality available via keyboard
|
|
35
|
+
- No keyboard traps (users can always Tab/Escape out)
|
|
36
|
+
- Skip navigation link as first focusable element
|
|
37
|
+
- Visible focus indicators on all interactive elements
|
|
38
|
+
- No time limits (or provide extend/disable options)
|
|
39
|
+
- No content that flashes more than 3 times per second
|
|
40
|
+
- Page titles describe topic or purpose
|
|
41
|
+
- Focus order follows logical reading sequence
|
|
42
|
+
- Link purpose clear from link text alone (no "click here")
|
|
43
|
+
- Multiple ways to find pages (navigation, search, sitemap)
|
|
44
|
+
- Touch targets minimum 24x24px (44x44px recommended)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Understandable
|
|
48
|
+
|
|
49
|
+
Users must be able to understand the information and UI operation.
|
|
50
|
+
|
|
51
|
+
```markdown
|
|
52
|
+
Requirements:
|
|
53
|
+
- Language of page declared in HTML lang attribute
|
|
54
|
+
- Consistent navigation across pages
|
|
55
|
+
- Consistent identification of UI components
|
|
56
|
+
- Error identification with clear description
|
|
57
|
+
- Labels or instructions for user input
|
|
58
|
+
- Error prevention for legal/financial/data submissions (review before submit)
|
|
59
|
+
- Context does not change unexpectedly on focus or input
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Robust
|
|
63
|
+
|
|
64
|
+
Content must work with current and future assistive technologies.
|
|
65
|
+
|
|
66
|
+
```markdown
|
|
67
|
+
Requirements:
|
|
68
|
+
- Valid HTML with proper semantic structure
|
|
69
|
+
- Name, role, value programmatically determinable for all UI components
|
|
70
|
+
- Status messages communicated via ARIA live regions without focus change
|
|
71
|
+
- Compatible with screen readers, voice control, switch devices, and magnification
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Keyboard Navigation
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
Requirements:
|
|
78
|
+
- Tab: Move forward through focusable elements
|
|
79
|
+
- Shift+Tab: Move backward
|
|
80
|
+
- Enter/Space: Activate buttons and links
|
|
81
|
+
- Arrow keys: Navigate within components (tabs, menus, radio groups)
|
|
82
|
+
- Escape: Close modals, dropdowns, popovers
|
|
83
|
+
- Home/End: Jump to first/last item in a list or slider
|
|
84
|
+
|
|
85
|
+
Focus management:
|
|
86
|
+
- Focus moves into modal when opened, returns to trigger when closed
|
|
87
|
+
- Focus never gets lost or stuck
|
|
88
|
+
- Focus indicators visible in all themes (light and dark)
|
|
89
|
+
- Custom focus styles: minimum 2px solid outline with offset, 3:1 contrast ratio
|
|
90
|
+
|
|
91
|
+
Tab order:
|
|
92
|
+
- Follows visual layout (left-to-right, top-to-bottom in LTR languages)
|
|
93
|
+
- Skip hidden/inactive elements
|
|
94
|
+
- tabindex="0" for custom interactive elements
|
|
95
|
+
- tabindex="-1" for programmatic focus (not in tab order)
|
|
96
|
+
- Never use tabindex > 0 (breaks natural order)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Screen Readers
|
|
100
|
+
|
|
101
|
+
```markdown
|
|
102
|
+
Semantic HTML:
|
|
103
|
+
- Use <nav>, <main>, <header>, <footer>, <aside>, <section>, <article>
|
|
104
|
+
- Headings form a logical outline (h1 → h2 → h3, no skipping levels)
|
|
105
|
+
- Lists use <ul>/<ol>/<dl>, not styled divs
|
|
106
|
+
- Tables use <th>, <caption>, and scope attributes
|
|
107
|
+
- Forms use <label> associated with inputs via for/id
|
|
108
|
+
|
|
109
|
+
ARIA (Accessible Rich Internet Applications):
|
|
110
|
+
- Use native HTML elements first; ARIA is a supplement, not a replacement
|
|
111
|
+
- aria-label: Name an element when visible text is insufficient
|
|
112
|
+
- aria-describedby: Associate additional descriptions
|
|
113
|
+
- aria-live="polite": Announce dynamic content changes (toasts, status updates)
|
|
114
|
+
- aria-live="assertive": Interrupt for urgent messages (errors)
|
|
115
|
+
- aria-expanded: Communicate open/closed state of collapsibles
|
|
116
|
+
- aria-hidden="true": Hide decorative content from assistive tech
|
|
117
|
+
- role attributes: Only when native HTML semantics are insufficient
|
|
118
|
+
|
|
119
|
+
Testing:
|
|
120
|
+
- Test with VoiceOver (macOS/iOS), NVDA or JAWS (Windows), TalkBack (Android)
|
|
121
|
+
- Navigate the entire flow using only the screen reader
|
|
122
|
+
- Verify all actions can be completed without visual reference
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## ARIA Authoring Practices Guide (APG)
|
|
126
|
+
|
|
127
|
+
Follow the WAI-ARIA APG patterns for complex components:
|
|
128
|
+
|
|
129
|
+
```markdown
|
|
130
|
+
Common patterns:
|
|
131
|
+
- Accordion: aria-expanded, aria-controls, Enter/Space to toggle
|
|
132
|
+
- Dialog (Modal): aria-modal, focus trap, Escape to close
|
|
133
|
+
- Tabs: role="tablist"/"tab"/"tabpanel", Arrow keys to switch
|
|
134
|
+
- Combobox: role="combobox", aria-autocomplete, list association
|
|
135
|
+
- Menu: role="menu"/"menuitem", Arrow keys, Enter to select
|
|
136
|
+
- Tooltip: role="tooltip", aria-describedby, Escape to dismiss
|
|
137
|
+
- Disclosure: aria-expanded, controls relationship
|
|
138
|
+
|
|
139
|
+
Rules:
|
|
140
|
+
- Follow the APG keyboard interaction patterns exactly
|
|
141
|
+
- Test with assistive technology, not just keyboard
|
|
142
|
+
- Complex widgets need comprehensive ARIA markup
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Inclusive Design (Microsoft)
|
|
146
|
+
|
|
147
|
+
Design for the full range of human diversity.
|
|
148
|
+
|
|
149
|
+
```markdown
|
|
150
|
+
Disability spectrum:
|
|
151
|
+
Permanent → Temporary → Situational
|
|
152
|
+
One arm → Arm injury → Carrying a baby
|
|
153
|
+
Blind → Eye surgery → Driving
|
|
154
|
+
Deaf → Ear infection → Loud environment
|
|
155
|
+
Non-verbal → Laryngitis → Non-native speaker
|
|
156
|
+
|
|
157
|
+
Principles:
|
|
158
|
+
1. Recognize exclusion (who can't use this?)
|
|
159
|
+
2. Learn from diversity (edge cases reveal design flaws)
|
|
160
|
+
3. Solve for one, extend to many (curb cuts help wheelchairs, strollers, bikes)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Cognitive Accessibility
|
|
164
|
+
|
|
165
|
+
```markdown
|
|
166
|
+
Guidelines:
|
|
167
|
+
- Use plain language (aim for 8th-grade reading level)
|
|
168
|
+
- Break content into short, scannable chunks
|
|
169
|
+
- Use consistent and predictable patterns
|
|
170
|
+
- Provide clear error messages with recovery instructions
|
|
171
|
+
- Avoid time pressure (allow extended time or remove limits)
|
|
172
|
+
- Support undo for destructive actions
|
|
173
|
+
- Minimize required memory (show context, don't require recall)
|
|
174
|
+
- Use familiar icons and established UI patterns
|
|
175
|
+
- Progress indicators for multi-step processes
|
|
176
|
+
- No unexpected changes in context
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Testing Checklist
|
|
180
|
+
|
|
181
|
+
```markdown
|
|
182
|
+
Automated (catch ~30% of issues):
|
|
183
|
+
- axe-core or Lighthouse accessibility audit
|
|
184
|
+
- Color contrast checker
|
|
185
|
+
- HTML validation
|
|
186
|
+
|
|
187
|
+
Manual (catch ~70% of issues):
|
|
188
|
+
- Keyboard-only navigation (can you complete every task?)
|
|
189
|
+
- Screen reader walkthrough (does every element announce correctly?)
|
|
190
|
+
- Zoom to 200% (does content reflow without horizontal scroll?)
|
|
191
|
+
- Color blindness simulation (is meaning preserved?)
|
|
192
|
+
- Reduced motion test (does prefers-reduced-motion work?)
|
|
193
|
+
- Touch target size verification (44x44px minimum)
|
|
194
|
+
- Focus order verification (logical and predictable?)
|
|
195
|
+
- Form error experience (clear, specific, recoverable?)
|
|
196
|
+
|
|
197
|
+
User testing:
|
|
198
|
+
- Include users with disabilities in research participants
|
|
199
|
+
- Test with the assistive technologies your users actually use
|
|
200
|
+
- Observe real behavior, don't rely on automated reports alone
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Anti-Patterns
|
|
204
|
+
|
|
205
|
+
```markdown
|
|
206
|
+
- Accessibility overlay widgets: Third-party "fix-it" overlays don't work and create new problems
|
|
207
|
+
- Missing alt text: Images without alternatives are invisible to screen readers
|
|
208
|
+
- Placeholder-only labels: Placeholders disappear on focus, leaving users without context
|
|
209
|
+
- Focus suppression: outline: none without a replacement focus style
|
|
210
|
+
- Mouse-only interactions: Drag-and-drop, hover reveals, or swipe without keyboard alternatives
|
|
211
|
+
- CAPTCHAs without alternatives: Audio CAPTCHA or bypass for authenticated users
|
|
212
|
+
- Auto-playing media: Audio or video that plays without user initiation
|
|
213
|
+
- "Accessibility is done": It's never done — it requires ongoing testing and maintenance
|
|
214
|
+
```
|