@nextsparkjs/ai-workflow 0.1.0-beta.100
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/LICENSE +21 -0
- package/README.md +115 -0
- package/claude/_docs/workflows-optimizations.md +359 -0
- package/claude/agents/api-tester.md +634 -0
- package/claude/agents/architecture-supervisor.md +1351 -0
- package/claude/agents/backend-developer.md +997 -0
- package/claude/agents/backend-validator.md +417 -0
- package/claude/agents/bdd-docs-writer.md +737 -0
- package/claude/agents/block-developer.md +677 -0
- package/claude/agents/code-reviewer.md +1432 -0
- package/claude/agents/db-developer.md +721 -0
- package/claude/agents/db-validator.md +407 -0
- package/claude/agents/demo-video-generator.md +493 -0
- package/claude/agents/documentation-writer.md +1268 -0
- package/claude/agents/frontend-developer.md +1234 -0
- package/claude/agents/frontend-validator.md +777 -0
- package/claude/agents/functional-validator.md +630 -0
- package/claude/agents/mock-analyst.md +387 -0
- package/claude/agents/product-manager.md +963 -0
- package/claude/agents/qa-automation.md +1762 -0
- package/claude/agents/release-manager.md +634 -0
- package/claude/agents/selectors-translator.md +262 -0
- package/claude/agents/unit-test-writer.md +785 -0
- package/claude/agents/visual-comparator.md +329 -0
- package/claude/agents/workflow-maintainer.md +352 -0
- package/claude/commands/do/README.md +88 -0
- package/claude/commands/do/create-api.md +64 -0
- package/claude/commands/do/create-entity.md +66 -0
- package/claude/commands/do/create-migration.md +64 -0
- package/claude/commands/do/create-plugin.md +56 -0
- package/claude/commands/do/create-theme.md +70 -0
- package/claude/commands/do/mock-data.md +67 -0
- package/claude/commands/do/reset-db.md +71 -0
- package/claude/commands/do/setup-scheduled-action.md +75 -0
- package/claude/commands/do/sync-code-review.md +117 -0
- package/claude/commands/do/update-selectors.md +112 -0
- package/claude/commands/do/use-skills.md +90 -0
- package/claude/commands/do/validate-blocks.md +69 -0
- package/claude/commands/how-to/README.md +261 -0
- package/claude/commands/how-to/add-metadata.md +692 -0
- package/claude/commands/how-to/add-taxonomies.md +806 -0
- package/claude/commands/how-to/add-translations.md +571 -0
- package/claude/commands/how-to/create-api.md +577 -0
- package/claude/commands/how-to/create-block.md +575 -0
- package/claude/commands/how-to/create-child-entities.md +771 -0
- package/claude/commands/how-to/create-entity.md +597 -0
- package/claude/commands/how-to/create-migrations.md +605 -0
- package/claude/commands/how-to/create-plugin.md +654 -0
- package/claude/commands/how-to/customize-app.md +481 -0
- package/claude/commands/how-to/customize-dashboard.md +553 -0
- package/claude/commands/how-to/customize-theme.md +438 -0
- package/claude/commands/how-to/define-features-flows.md +632 -0
- package/claude/commands/how-to/deploy.md +507 -0
- package/claude/commands/how-to/handle-file-uploads.md +746 -0
- package/claude/commands/how-to/implement-search.md +1001 -0
- package/claude/commands/how-to/install-plugins.md +352 -0
- package/claude/commands/how-to/manage-test-coverage.md +984 -0
- package/claude/commands/how-to/run-tests.md +400 -0
- package/claude/commands/how-to/set-app-languages.md +601 -0
- package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
- package/claude/commands/how-to/set-scheduled-actions.md +527 -0
- package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
- package/claude/commands/how-to/setup-authentication.md +388 -0
- package/claude/commands/how-to/setup-claude-code.md +440 -0
- package/claude/commands/how-to/setup-database.md +274 -0
- package/claude/commands/how-to/setup-email-providers.md +598 -0
- package/claude/commands/how-to/setup-mobile-dev.md +627 -0
- package/claude/commands/how-to/start.md +500 -0
- package/claude/commands/how-to/use-devtools.md +639 -0
- package/claude/commands/how-to/use-superadmin.md +622 -0
- package/claude/commands/session/README.md +193 -0
- package/claude/commands/session/block-create.md +190 -0
- package/claude/commands/session/block-list.md +203 -0
- package/claude/commands/session/block-update.md +192 -0
- package/claude/commands/session/block-validate.md +218 -0
- package/claude/commands/session/changelog.md +115 -0
- package/claude/commands/session/close.md +225 -0
- package/claude/commands/session/commit.md +174 -0
- package/claude/commands/session/db-entity.md +206 -0
- package/claude/commands/session/db-fix.md +212 -0
- package/claude/commands/session/db-sample.md +206 -0
- package/claude/commands/session/demo.md +178 -0
- package/claude/commands/session/doc-bdd.md +207 -0
- package/claude/commands/session/doc-feature.md +218 -0
- package/claude/commands/session/doc-read.md +225 -0
- package/claude/commands/session/execute.md +204 -0
- package/claude/commands/session/explain.md +202 -0
- package/claude/commands/session/fix-bug.md +210 -0
- package/claude/commands/session/fix-build.md +182 -0
- package/claude/commands/session/fix-test.md +189 -0
- package/claude/commands/session/pending.md +232 -0
- package/claude/commands/session/refine.md +188 -0
- package/claude/commands/session/resume.md +192 -0
- package/claude/commands/session/review.md +192 -0
- package/claude/commands/session/scope-change.md +181 -0
- package/claude/commands/session/start-blocks.md +347 -0
- package/claude/commands/session/start.md +604 -0
- package/claude/commands/session/status.md +169 -0
- package/claude/commands/session/test-fix.md +221 -0
- package/claude/commands/session/test-run.md +203 -0
- package/claude/commands/session/test-write.md +242 -0
- package/claude/commands/session/validate.md +162 -0
- package/claude/config/context.json +40 -0
- package/claude/config/github.json +69 -0
- package/claude/config/github.schema.json +106 -0
- package/claude/config/team.json +46 -0
- package/claude/config/team.schema.json +106 -0
- package/claude/config/workspace.json +43 -0
- package/claude/config/workspace.schema.json +75 -0
- package/claude/skills/README.md +228 -0
- package/claude/skills/accessibility/SKILL.md +573 -0
- package/claude/skills/api-bypass-layers/SKILL.md +550 -0
- package/claude/skills/asana-integration/SKILL.md +499 -0
- package/claude/skills/better-auth/SKILL.md +666 -0
- package/claude/skills/billing-subscriptions/SKILL.md +660 -0
- package/claude/skills/block-decision-matrix/SKILL.md +359 -0
- package/claude/skills/clickup-integration/SKILL.md +434 -0
- package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
- package/claude/skills/create-plugin/SKILL.md +425 -0
- package/claude/skills/create-theme/SKILL.md +331 -0
- package/claude/skills/cypress-api/SKILL.md +511 -0
- package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
- package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
- package/claude/skills/cypress-e2e/SKILL.md +526 -0
- package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
- package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
- package/claude/skills/cypress-selectors/SKILL.md +309 -0
- package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
- package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
- package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
- package/claude/skills/database-migrations/SKILL.md +335 -0
- package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
- package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
- package/claude/skills/design-system/SKILL.md +682 -0
- package/claude/skills/documentation/SKILL.md +540 -0
- package/claude/skills/entity-api/SKILL.md +482 -0
- package/claude/skills/entity-system/SKILL.md +635 -0
- package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
- package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
- package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
- package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
- package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
- package/claude/skills/github/SKILL.md +467 -0
- package/claude/skills/i18n-nextintl/SKILL.md +302 -0
- package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
- package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
- package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
- package/claude/skills/impact-analysis/SKILL.md +203 -0
- package/claude/skills/jest-unit/SKILL.md +306 -0
- package/claude/skills/jest-unit/references/component-testing.md +371 -0
- package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
- package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
- package/claude/skills/jira-integration/SKILL.md +539 -0
- package/claude/skills/media-library/SKILL.md +743 -0
- package/claude/skills/mock-analysis/SKILL.md +276 -0
- package/claude/skills/monorepo-architecture/SKILL.md +162 -0
- package/claude/skills/nextjs-api-development/SKILL.md +364 -0
- package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
- package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
- package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
- package/claude/skills/notion-integration/SKILL.md +641 -0
- package/claude/skills/npm-development-workflow/SKILL.md +480 -0
- package/claude/skills/page-builder-blocks/SKILL.md +530 -0
- package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
- package/claude/skills/permissions-system/SKILL.md +619 -0
- package/claude/skills/plugins/SKILL.md +340 -0
- package/claude/skills/plugins/references/plugin-templates.md +414 -0
- package/claude/skills/plugins/references/plugin-testing.md +353 -0
- package/claude/skills/plugins/references/plugin-types.md +198 -0
- package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
- package/claude/skills/pom-patterns/SKILL.md +452 -0
- package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
- package/claude/skills/rate-limiting/SKILL.md +342 -0
- package/claude/skills/react-best-practices/AGENTS.md +2410 -0
- package/claude/skills/react-best-practices/README.md +123 -0
- package/claude/skills/react-best-practices/SKILL.md +125 -0
- package/claude/skills/react-best-practices/metadata.json +15 -0
- package/claude/skills/react-best-practices/rules/_sections.md +46 -0
- package/claude/skills/react-best-practices/rules/_template.md +28 -0
- package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/claude/skills/react-patterns/SKILL.md +688 -0
- package/claude/skills/registry-system/SKILL.md +331 -0
- package/claude/skills/scheduled-actions/SKILL.md +671 -0
- package/claude/skills/scope-enforcement/SKILL.md +542 -0
- package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
- package/claude/skills/server-actions/SKILL.md +493 -0
- package/claude/skills/service-layer/SKILL.md +587 -0
- package/claude/skills/session-management/SKILL.md +266 -0
- package/claude/skills/session-management/scripts/create-session.py +166 -0
- package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
- package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
- package/claude/skills/session-management/scripts/session-archive.sh +87 -0
- package/claude/skills/session-management/scripts/session-close.sh +133 -0
- package/claude/skills/session-management/scripts/session-init.sh +225 -0
- package/claude/skills/session-management/scripts/session-list.sh +163 -0
- package/claude/skills/session-management/scripts/split-plan.sh +116 -0
- package/claude/skills/shadcn-components/SKILL.md +586 -0
- package/claude/skills/shadcn-theming/SKILL.md +446 -0
- package/claude/skills/suspense-loading/SKILL.md +280 -0
- package/claude/skills/tailwind-theming/SKILL.md +507 -0
- package/claude/skills/tanstack-query/SKILL.md +608 -0
- package/claude/skills/test-coverage/SKILL.md +239 -0
- package/claude/skills/web-design-guidelines/SKILL.md +39 -0
- package/claude/skills/zod-validation/SKILL.md +537 -0
- package/claude/templates/blocks/progress.md +86 -0
- package/claude/templates/iteration/changes.md +61 -0
- package/claude/templates/iteration/progress.md +55 -0
- package/claude/templates/log.md +31 -0
- package/claude/templates/story/context.md +77 -0
- package/claude/templates/story/pendings.md +37 -0
- package/claude/templates/story/plan.md +299 -0
- package/claude/templates/story/requirements.md +109 -0
- package/claude/templates/story/scope.json +10 -0
- package/claude/templates/story/tests.md +91 -0
- package/claude/templates/task/progress.md +58 -0
- package/claude/templates/task/requirements.md +54 -0
- package/claude/workflows/README.md +154 -0
- package/claude/workflows/blocks.md +614 -0
- package/claude/workflows/story.md +1207 -0
- package/claude/workflows/task.md +927 -0
- package/claude/workflows/tweak.md +527 -0
- package/cursor/.gitkeep +0 -0
- package/package.json +35 -0
- package/scripts/postinstall.mjs +198 -0
- package/scripts/setup.mjs +282 -0
- package/scripts/sync.mjs +209 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: accessibility
|
|
3
|
+
description: |
|
|
4
|
+
Accessibility (a11y) patterns for this Next.js application.
|
|
5
|
+
Covers WCAG 2.1 AA compliance, ARIA attributes, keyboard navigation, focus management, and screen reader support.
|
|
6
|
+
Use this skill when implementing accessible UI components or validating accessibility requirements.
|
|
7
|
+
allowed-tools: Read, Glob, Grep
|
|
8
|
+
version: 1.0.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Accessibility Skill
|
|
12
|
+
|
|
13
|
+
Patterns for implementing WCAG 2.1 Level AA compliant components with proper ARIA attributes, keyboard navigation, and screen reader support.
|
|
14
|
+
|
|
15
|
+
## Architecture Overview
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
ACCESSIBILITY LAYERS:
|
|
19
|
+
|
|
20
|
+
Semantic HTML Foundation:
|
|
21
|
+
├── <button> for buttons (not <div>)
|
|
22
|
+
├── <nav> for navigation regions
|
|
23
|
+
├── <form> and <fieldset> for forms
|
|
24
|
+
├── <a> for links
|
|
25
|
+
└── Heading hierarchy (h1-h6)
|
|
26
|
+
|
|
27
|
+
ARIA Enhancement:
|
|
28
|
+
├── Landmark roles (navigation, main, complementary)
|
|
29
|
+
├── State attributes (aria-expanded, aria-current)
|
|
30
|
+
├── Relationship attributes (aria-labelledby, aria-describedby)
|
|
31
|
+
└── Live regions (aria-live, role="alert")
|
|
32
|
+
|
|
33
|
+
Visual Accessibility:
|
|
34
|
+
├── Color contrast (4.5:1 minimum)
|
|
35
|
+
├── Focus indicators (focus-visible:ring-2)
|
|
36
|
+
├── Motion reduction (prefers-reduced-motion)
|
|
37
|
+
└── Text scaling support
|
|
38
|
+
|
|
39
|
+
Keyboard Accessibility:
|
|
40
|
+
├── Tab order management
|
|
41
|
+
├── Focus trapping in modals
|
|
42
|
+
├── Escape key handling
|
|
43
|
+
└── Arrow key navigation (where appropriate)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## When to Use This Skill
|
|
47
|
+
|
|
48
|
+
- Implementing interactive UI components
|
|
49
|
+
- Adding ARIA attributes to custom elements
|
|
50
|
+
- Validating accessibility requirements
|
|
51
|
+
- Ensuring keyboard navigation works
|
|
52
|
+
- Testing with screen readers
|
|
53
|
+
|
|
54
|
+
## WCAG Compliance Standards
|
|
55
|
+
|
|
56
|
+
### Level AA Requirements (Minimum)
|
|
57
|
+
|
|
58
|
+
| Criterion | Requirement | Implementation |
|
|
59
|
+
|-----------|-------------|----------------|
|
|
60
|
+
| **1.4.3** Color Contrast | 4.5:1 for normal text, 3:1 for large text | Use semantic color tokens |
|
|
61
|
+
| **1.4.11** Non-text Contrast | 3:1 for UI components | Focus rings, borders |
|
|
62
|
+
| **2.1.1** Keyboard | All functionality via keyboard | Tab navigation, Enter/Space |
|
|
63
|
+
| **2.4.3** Focus Order | Logical focus sequence | DOM order, tabIndex |
|
|
64
|
+
| **2.4.7** Focus Visible | Visible focus indicator | `focus-visible:ring-2` |
|
|
65
|
+
| **4.1.2** Name, Role, Value | Accessible names for elements | Labels, aria-label |
|
|
66
|
+
|
|
67
|
+
### POUR Principles
|
|
68
|
+
|
|
69
|
+
- **Perceivable**: Content visible to all users (contrast, alt text)
|
|
70
|
+
- **Operable**: Interface usable via keyboard and assistive tech
|
|
71
|
+
- **Understandable**: Clear, predictable interface
|
|
72
|
+
- **Robust**: Compatible with current and future assistive tech
|
|
73
|
+
|
|
74
|
+
## ARIA Attribute Patterns
|
|
75
|
+
|
|
76
|
+
### Landmark Roles
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// Navigation with accessible label
|
|
80
|
+
<nav role="navigation" aria-label="pagination">
|
|
81
|
+
<ul>
|
|
82
|
+
<li><a href="/page/1">1</a></li>
|
|
83
|
+
<li><a href="/page/2" aria-current="page">2</a></li>
|
|
84
|
+
</ul>
|
|
85
|
+
</nav>
|
|
86
|
+
|
|
87
|
+
// Breadcrumb navigation
|
|
88
|
+
<nav aria-label="breadcrumb">
|
|
89
|
+
<ol>
|
|
90
|
+
<li><a href="/">Home</a></li>
|
|
91
|
+
<li aria-current="page">Products</li>
|
|
92
|
+
</ol>
|
|
93
|
+
</nav>
|
|
94
|
+
|
|
95
|
+
// Main content area
|
|
96
|
+
<main role="main" id="main-content">
|
|
97
|
+
{/* Page content */}
|
|
98
|
+
</main>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Dynamic State Attributes
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Accordion/Collapsible
|
|
105
|
+
<button
|
|
106
|
+
aria-expanded={isOpen}
|
|
107
|
+
aria-controls={`content-${id}`}
|
|
108
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
109
|
+
>
|
|
110
|
+
Section Title
|
|
111
|
+
</button>
|
|
112
|
+
<div
|
|
113
|
+
id={`content-${id}`}
|
|
114
|
+
aria-hidden={!isOpen}
|
|
115
|
+
hidden={!isOpen}
|
|
116
|
+
>
|
|
117
|
+
{/* Collapsible content */}
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
// Tabs
|
|
121
|
+
<div role="tablist" aria-label="Settings tabs">
|
|
122
|
+
<button
|
|
123
|
+
role="tab"
|
|
124
|
+
aria-selected={activeTab === 'general'}
|
|
125
|
+
aria-controls="panel-general"
|
|
126
|
+
tabIndex={activeTab === 'general' ? 0 : -1}
|
|
127
|
+
>
|
|
128
|
+
General
|
|
129
|
+
</button>
|
|
130
|
+
</div>
|
|
131
|
+
<div
|
|
132
|
+
role="tabpanel"
|
|
133
|
+
id="panel-general"
|
|
134
|
+
aria-labelledby="tab-general"
|
|
135
|
+
hidden={activeTab !== 'general'}
|
|
136
|
+
>
|
|
137
|
+
{/* Tab content */}
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
// Menu/Dropdown
|
|
141
|
+
<button
|
|
142
|
+
aria-haspopup="menu"
|
|
143
|
+
aria-expanded={isOpen}
|
|
144
|
+
aria-controls="menu-items"
|
|
145
|
+
>
|
|
146
|
+
Options
|
|
147
|
+
</button>
|
|
148
|
+
<div
|
|
149
|
+
id="menu-items"
|
|
150
|
+
role="menu"
|
|
151
|
+
aria-hidden={!isOpen}
|
|
152
|
+
>
|
|
153
|
+
<button role="menuitem">Edit</button>
|
|
154
|
+
<button role="menuitem">Delete</button>
|
|
155
|
+
</div>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Form Accessibility
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// core/components/ui/form.tsx pattern
|
|
162
|
+
<FormItem>
|
|
163
|
+
<FormLabel htmlFor={formItemId}>
|
|
164
|
+
Email
|
|
165
|
+
</FormLabel>
|
|
166
|
+
<FormControl>
|
|
167
|
+
<Input
|
|
168
|
+
id={formItemId}
|
|
169
|
+
aria-describedby={
|
|
170
|
+
!error
|
|
171
|
+
? formDescriptionId
|
|
172
|
+
: `${formDescriptionId} ${formMessageId}`
|
|
173
|
+
}
|
|
174
|
+
aria-invalid={!!error}
|
|
175
|
+
aria-required={required}
|
|
176
|
+
/>
|
|
177
|
+
</FormControl>
|
|
178
|
+
<FormDescription id={formDescriptionId}>
|
|
179
|
+
We'll never share your email.
|
|
180
|
+
</FormDescription>
|
|
181
|
+
{error && (
|
|
182
|
+
<FormMessage id={formMessageId} role="alert">
|
|
183
|
+
{error.message}
|
|
184
|
+
</FormMessage>
|
|
185
|
+
)}
|
|
186
|
+
</FormItem>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Live Regions
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// Status announcements (polite)
|
|
193
|
+
<div aria-live="polite" aria-atomic="true" className="sr-only">
|
|
194
|
+
{statusMessage}
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
// Error alerts (assertive)
|
|
198
|
+
<div role="alert" className="text-destructive">
|
|
199
|
+
{errorMessage}
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
// Toast notifications
|
|
203
|
+
<div
|
|
204
|
+
role="status"
|
|
205
|
+
aria-live="polite"
|
|
206
|
+
aria-atomic="true"
|
|
207
|
+
>
|
|
208
|
+
{notification}
|
|
209
|
+
</div>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Keyboard Navigation Patterns
|
|
213
|
+
|
|
214
|
+
### Focus Indicators
|
|
215
|
+
|
|
216
|
+
All interactive elements use the standardized focus ring:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// Button focus pattern
|
|
220
|
+
<button
|
|
221
|
+
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
222
|
+
>
|
|
223
|
+
Click me
|
|
224
|
+
</button>
|
|
225
|
+
|
|
226
|
+
// Input focus pattern
|
|
227
|
+
<input
|
|
228
|
+
className="focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
229
|
+
/>
|
|
230
|
+
|
|
231
|
+
// Custom focus indicator
|
|
232
|
+
<div
|
|
233
|
+
tabIndex={0}
|
|
234
|
+
className="focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
|
235
|
+
onKeyDown={(e) => {
|
|
236
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
237
|
+
e.preventDefault()
|
|
238
|
+
handleAction()
|
|
239
|
+
}
|
|
240
|
+
}}
|
|
241
|
+
>
|
|
242
|
+
Interactive element
|
|
243
|
+
</div>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Tab Order Management
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// Logical tab order (follows DOM)
|
|
250
|
+
<header>
|
|
251
|
+
<nav tabIndex={0}>...</nav> {/* First */}
|
|
252
|
+
</header>
|
|
253
|
+
<main tabIndex={0}>...</main> {/* Second */}
|
|
254
|
+
<aside tabIndex={0}>...</aside> {/* Third */}
|
|
255
|
+
|
|
256
|
+
// Remove from tab order
|
|
257
|
+
<div tabIndex={-1}>
|
|
258
|
+
{/* Programmatically focusable but not in tab order */}
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
// Skip link pattern
|
|
262
|
+
<a
|
|
263
|
+
href="#main-content"
|
|
264
|
+
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:bg-background focus:px-4 focus:py-2"
|
|
265
|
+
>
|
|
266
|
+
Skip to main content
|
|
267
|
+
</a>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Focus Trap (Modals)
|
|
271
|
+
|
|
272
|
+
Radix UI handles focus trapping automatically in dialogs:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// core/components/ui/dialog.tsx
|
|
276
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
|
277
|
+
|
|
278
|
+
// Radix Dialog automatically:
|
|
279
|
+
// - Traps focus within modal
|
|
280
|
+
// - Returns focus on close
|
|
281
|
+
// - Handles Escape key
|
|
282
|
+
// - Manages aria-hidden on background
|
|
283
|
+
|
|
284
|
+
<DialogPrimitive.Root>
|
|
285
|
+
<DialogPrimitive.Trigger>Open</DialogPrimitive.Trigger>
|
|
286
|
+
<DialogPrimitive.Portal>
|
|
287
|
+
<DialogPrimitive.Overlay />
|
|
288
|
+
<DialogPrimitive.Content>
|
|
289
|
+
{/* Focus is trapped here */}
|
|
290
|
+
<DialogPrimitive.Close>
|
|
291
|
+
<span className="sr-only">Close</span>
|
|
292
|
+
<X aria-hidden="true" />
|
|
293
|
+
</DialogPrimitive.Close>
|
|
294
|
+
</DialogPrimitive.Content>
|
|
295
|
+
</DialogPrimitive.Portal>
|
|
296
|
+
</DialogPrimitive.Root>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Screen Reader Support
|
|
300
|
+
|
|
301
|
+
### Screen Reader Only Text
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// Visually hidden but announced by screen readers
|
|
305
|
+
<span className="sr-only">Close dialog</span>
|
|
306
|
+
|
|
307
|
+
// Icon button with accessible name
|
|
308
|
+
<Button variant="ghost" size="icon">
|
|
309
|
+
<X aria-hidden="true" />
|
|
310
|
+
<span className="sr-only">Close</span>
|
|
311
|
+
</Button>
|
|
312
|
+
|
|
313
|
+
// More pages indicator
|
|
314
|
+
<span aria-hidden="true">...</span>
|
|
315
|
+
<span className="sr-only">More pages</span>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Decorative Icons
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
// Hide decorative icons from screen readers
|
|
322
|
+
<ChevronRight aria-hidden="true" className="h-4 w-4" />
|
|
323
|
+
|
|
324
|
+
// Separator in breadcrumb
|
|
325
|
+
<span role="presentation" aria-hidden="true">/</span>
|
|
326
|
+
|
|
327
|
+
// Loading spinner
|
|
328
|
+
<Loader2
|
|
329
|
+
aria-hidden="true"
|
|
330
|
+
className="h-4 w-4 animate-spin"
|
|
331
|
+
/>
|
|
332
|
+
<span className="sr-only">Loading...</span>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Accessible Names Strategy
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
// 1. Visible text (preferred)
|
|
339
|
+
<button>Delete Item</button>
|
|
340
|
+
|
|
341
|
+
// 2. aria-label for icon-only buttons
|
|
342
|
+
<button aria-label="Delete item">
|
|
343
|
+
<TrashIcon aria-hidden="true" />
|
|
344
|
+
</button>
|
|
345
|
+
|
|
346
|
+
// 3. aria-labelledby for complex labels
|
|
347
|
+
<div id="dialog-title">Confirm Deletion</div>
|
|
348
|
+
<div aria-labelledby="dialog-title">
|
|
349
|
+
{/* Content described by title */}
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
// 4. Form labels
|
|
353
|
+
<Label htmlFor="email">Email address</Label>
|
|
354
|
+
<Input id="email" type="email" />
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Color Contrast
|
|
358
|
+
|
|
359
|
+
### OKLCH Color System
|
|
360
|
+
|
|
361
|
+
The theme uses OKLCH color space for perceptually uniform colors:
|
|
362
|
+
|
|
363
|
+
```css
|
|
364
|
+
/* contents/themes/default/styles/globals.css */
|
|
365
|
+
:root {
|
|
366
|
+
/* High contrast pairs */
|
|
367
|
+
--primary: oklch(0.2050 0 0); /* Dark */
|
|
368
|
+
--primary-foreground: oklch(0.9850 0 0); /* Light - 4.5:1+ contrast */
|
|
369
|
+
|
|
370
|
+
--destructive: oklch(0.5770 0.2450 27.3250);
|
|
371
|
+
--destructive-foreground: oklch(1 0 0);
|
|
372
|
+
|
|
373
|
+
/* Focus ring with sufficient contrast */
|
|
374
|
+
--ring: oklch(0.7080 0 0);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.dark {
|
|
378
|
+
/* Inverted for dark mode */
|
|
379
|
+
--primary: oklch(0.9220 0 0);
|
|
380
|
+
--primary-foreground: oklch(0.2050 0 0);
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Semantic Color Usage
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
// ✅ CORRECT - Use semantic tokens that guarantee contrast
|
|
388
|
+
<p className="text-foreground">Primary text</p>
|
|
389
|
+
<p className="text-muted-foreground">Secondary text</p>
|
|
390
|
+
<button className="bg-primary text-primary-foreground">
|
|
391
|
+
Action
|
|
392
|
+
</button>
|
|
393
|
+
|
|
394
|
+
// ❌ WRONG - Arbitrary colors may not have proper contrast
|
|
395
|
+
<p className="text-gray-400">Low contrast text</p>
|
|
396
|
+
<button className="bg-blue-300 text-blue-100">
|
|
397
|
+
Poor contrast
|
|
398
|
+
</button>
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## Component Patterns
|
|
402
|
+
|
|
403
|
+
### Slider/Range Component
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// core/components/ui/double-range.tsx
|
|
407
|
+
<div
|
|
408
|
+
ref={thumbMinRef}
|
|
409
|
+
role="slider"
|
|
410
|
+
aria-label="Minimum value"
|
|
411
|
+
aria-valuemin={min}
|
|
412
|
+
aria-valuemax={max}
|
|
413
|
+
aria-valuenow={localValue[0]}
|
|
414
|
+
tabIndex={disabled ? -1 : 0}
|
|
415
|
+
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
416
|
+
onKeyDown={handleKeyDown} // Arrow keys to adjust
|
|
417
|
+
/>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Pagination Component
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
// core/components/ui/pagination.tsx
|
|
424
|
+
<nav role="navigation" aria-label="pagination">
|
|
425
|
+
<PaginationPrevious aria-label="Go to previous page" />
|
|
426
|
+
|
|
427
|
+
{pages.map((page) => (
|
|
428
|
+
<PaginationLink
|
|
429
|
+
aria-current={page === current ? 'page' : undefined}
|
|
430
|
+
>
|
|
431
|
+
{page}
|
|
432
|
+
</PaginationLink>
|
|
433
|
+
))}
|
|
434
|
+
|
|
435
|
+
<PaginationNext aria-label="Go to next page" />
|
|
436
|
+
</nav>
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Alert Component
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
// core/components/ui/alert.tsx
|
|
443
|
+
<div
|
|
444
|
+
ref={ref}
|
|
445
|
+
role="alert"
|
|
446
|
+
className={cn(alertVariants({ variant }), className)}
|
|
447
|
+
{...props}
|
|
448
|
+
/>
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Testing Accessibility
|
|
452
|
+
|
|
453
|
+
### Tools
|
|
454
|
+
|
|
455
|
+
- **ESLint Plugin:** `eslint-plugin-jsx-a11y` for linting
|
|
456
|
+
- **Jest:** `jest-axe` for unit testing
|
|
457
|
+
- **Cypress:** `cypress-axe` for E2E testing
|
|
458
|
+
- **Manual:** Keyboard navigation, screen reader testing
|
|
459
|
+
|
|
460
|
+
### Jest-axe Example
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
464
|
+
|
|
465
|
+
expect.extend(toHaveNoViolations)
|
|
466
|
+
|
|
467
|
+
test('should have no accessibility violations', async () => {
|
|
468
|
+
const { container } = render(<MyComponent />)
|
|
469
|
+
const results = await axe(container)
|
|
470
|
+
expect(results).toHaveNoViolations()
|
|
471
|
+
})
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Manual Testing Checklist
|
|
475
|
+
|
|
476
|
+
1. **Keyboard Navigation:**
|
|
477
|
+
- Tab through all interactive elements
|
|
478
|
+
- Verify logical focus order
|
|
479
|
+
- Escape key closes modals
|
|
480
|
+
- Enter/Space activates buttons
|
|
481
|
+
|
|
482
|
+
2. **Screen Reader:**
|
|
483
|
+
- VoiceOver (Mac) or NVDA (Windows)
|
|
484
|
+
- Verify content is announced correctly
|
|
485
|
+
- Check form labels and error messages
|
|
486
|
+
- Verify live regions announce updates
|
|
487
|
+
|
|
488
|
+
3. **Visual:**
|
|
489
|
+
- Check focus indicators are visible
|
|
490
|
+
- Verify contrast with browser devtools
|
|
491
|
+
- Test with zoom (200%)
|
|
492
|
+
|
|
493
|
+
## Anti-Patterns
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
// ❌ NEVER: Div as button
|
|
497
|
+
<div onClick={handleClick}>Click me</div>
|
|
498
|
+
|
|
499
|
+
// ✅ CORRECT: Use semantic button
|
|
500
|
+
<button onClick={handleClick}>Click me</button>
|
|
501
|
+
|
|
502
|
+
// ❌ NEVER: Icon-only button without accessible name
|
|
503
|
+
<button><TrashIcon /></button>
|
|
504
|
+
|
|
505
|
+
// ✅ CORRECT: Add aria-label or sr-only text
|
|
506
|
+
<button aria-label="Delete item">
|
|
507
|
+
<TrashIcon aria-hidden="true" />
|
|
508
|
+
</button>
|
|
509
|
+
|
|
510
|
+
// ❌ NEVER: Remove focus indicator completely
|
|
511
|
+
<button className="outline-none focus:outline-none">
|
|
512
|
+
No focus indicator
|
|
513
|
+
</button>
|
|
514
|
+
|
|
515
|
+
// ✅ CORRECT: Custom focus indicator
|
|
516
|
+
<button className="focus-visible:ring-2 focus-visible:ring-ring">
|
|
517
|
+
Visible focus
|
|
518
|
+
</button>
|
|
519
|
+
|
|
520
|
+
// ❌ NEVER: Low contrast text
|
|
521
|
+
<p className="text-gray-300 bg-white">Hard to read</p>
|
|
522
|
+
|
|
523
|
+
// ✅ CORRECT: Use semantic tokens
|
|
524
|
+
<p className="text-muted-foreground bg-background">Readable</p>
|
|
525
|
+
|
|
526
|
+
// ❌ NEVER: Missing form labels
|
|
527
|
+
<input type="email" placeholder="Email" />
|
|
528
|
+
|
|
529
|
+
// ✅ CORRECT: Proper label association
|
|
530
|
+
<Label htmlFor="email">Email</Label>
|
|
531
|
+
<input id="email" type="email" />
|
|
532
|
+
|
|
533
|
+
// ❌ NEVER: Images without alt text
|
|
534
|
+
<img src="/logo.png" />
|
|
535
|
+
|
|
536
|
+
// ✅ CORRECT: Descriptive alt or empty for decorative
|
|
537
|
+
<img src="/logo.png" alt="Company Logo" />
|
|
538
|
+
<img src="/decorative.png" alt="" aria-hidden="true" />
|
|
539
|
+
|
|
540
|
+
// ❌ NEVER: Auto-playing media without controls
|
|
541
|
+
<video autoPlay src="/video.mp4" />
|
|
542
|
+
|
|
543
|
+
// ✅ CORRECT: User controls and captions
|
|
544
|
+
<video controls>
|
|
545
|
+
<source src="/video.mp4" />
|
|
546
|
+
<track kind="captions" src="/captions.vtt" />
|
|
547
|
+
</video>
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
## Checklist
|
|
551
|
+
|
|
552
|
+
Before finalizing component accessibility:
|
|
553
|
+
|
|
554
|
+
- [ ] Semantic HTML elements used (button, nav, form, etc.)
|
|
555
|
+
- [ ] All interactive elements keyboard accessible
|
|
556
|
+
- [ ] Focus indicators visible (focus-visible:ring-2)
|
|
557
|
+
- [ ] ARIA attributes correct (aria-expanded, aria-controls, etc.)
|
|
558
|
+
- [ ] Form fields have associated labels
|
|
559
|
+
- [ ] Error messages use role="alert"
|
|
560
|
+
- [ ] Icons have aria-hidden="true"
|
|
561
|
+
- [ ] Icon-only buttons have aria-label or sr-only text
|
|
562
|
+
- [ ] Color contrast meets 4.5:1 (text) / 3:1 (UI)
|
|
563
|
+
- [ ] Screen reader text (.sr-only) where needed
|
|
564
|
+
- [ ] Tab order is logical
|
|
565
|
+
- [ ] Modals trap focus and return it on close
|
|
566
|
+
- [ ] Live regions announce dynamic content
|
|
567
|
+
|
|
568
|
+
## Related Skills
|
|
569
|
+
|
|
570
|
+
- `shadcn-components` - Accessible UI component patterns
|
|
571
|
+
- `react-patterns` - Component architecture
|
|
572
|
+
- `tailwind-theming` - Color contrast tokens
|
|
573
|
+
- `cypress-e2e` - Accessibility testing
|