@girardmedia/bootspring 3.3.2 → 3.4.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/assets/agents/accessibility-auditor.md +39 -0
- package/assets/agents/api-designer.md +40 -0
- package/assets/agents/auth-implementer.md +64 -0
- package/assets/agents/bug-hunter.md +42 -0
- package/assets/agents/bundle-analyzer.md +40 -0
- package/assets/agents/cache-optimizer.md +55 -0
- package/assets/agents/changelog-writer.md +55 -0
- package/assets/agents/ci-cd-builder.md +40 -0
- package/assets/agents/code-explainer.md +39 -0
- package/assets/agents/code-reviewer.md +39 -0
- package/assets/agents/cost-optimizer.md +57 -0
- package/assets/agents/cron-scheduler.md +51 -0
- package/assets/agents/data-seeder.md +56 -0
- package/assets/agents/database-architect.md +40 -0
- package/assets/agents/dependency-updater.md +40 -0
- package/assets/agents/deploy-checker.md +40 -0
- package/assets/agents/docker-optimizer.md +40 -0
- package/assets/agents/documentation-writer.md +40 -0
- package/assets/agents/email-builder.md +55 -0
- package/assets/agents/env-setup.md +40 -0
- package/assets/agents/error-handler.md +40 -0
- package/assets/agents/eslint-fixer.md +46 -0
- package/assets/agents/feature-flagger.md +69 -0
- package/assets/agents/git-detective.md +39 -0
- package/assets/agents/graphql-builder.md +60 -0
- package/assets/agents/incident-responder.md +59 -0
- package/assets/agents/log-analyzer.md +39 -0
- package/assets/agents/migration-planner.md +41 -0
- package/assets/agents/monorepo-navigator.md +39 -0
- package/assets/agents/nextjs-expert.md +57 -0
- package/assets/agents/notification-builder.md +56 -0
- package/assets/agents/onboarding-guide.md +39 -0
- package/assets/agents/performance-profiler.md +40 -0
- package/assets/agents/prisma-expert.md +57 -0
- package/assets/agents/rate-limiter.md +58 -0
- package/assets/agents/react-expert.md +58 -0
- package/assets/agents/refactorer.md +42 -0
- package/assets/agents/regex-builder.md +46 -0
- package/assets/agents/release-manager.md +40 -0
- package/assets/agents/s3-manager.md +58 -0
- package/assets/agents/schema-validator.md +40 -0
- package/assets/agents/search-builder.md +62 -0
- package/assets/agents/security-auditor.md +39 -0
- package/assets/agents/sitemap-generator.md +53 -0
- package/assets/agents/stripe-integrator.md +59 -0
- package/assets/agents/tailwind-expert.md +55 -0
- package/assets/agents/tech-debt-tracker.md +39 -0
- package/assets/agents/test-writer.md +42 -0
- package/assets/agents/type-fixer.md +45 -0
- package/assets/agents/webhook-builder.md +54 -0
- package/assets/rules/cpp.md +53 -0
- package/assets/rules/css.md +52 -0
- package/assets/rules/go.md +50 -0
- package/assets/rules/html.md +52 -0
- package/assets/rules/java.md +51 -0
- package/assets/rules/kotlin.md +50 -0
- package/assets/rules/php.md +51 -0
- package/assets/rules/python.md +51 -0
- package/assets/rules/ruby.md +51 -0
- package/assets/rules/rust.md +49 -0
- package/assets/rules/shell.md +52 -0
- package/assets/rules/sql.md +49 -0
- package/assets/rules/swift.md +50 -0
- package/assets/rules/typescript.md +52 -0
- package/assets/rules/yaml-json.md +51 -0
- package/assets/skills/accessibility.md +210 -0
- package/assets/skills/agent-patterns.md +387 -0
- package/assets/skills/ai-integration.md +263 -0
- package/assets/skills/animation-patterns.md +224 -0
- package/assets/skills/api-design.md +218 -0
- package/assets/skills/api-gateway.md +341 -0
- package/assets/skills/api-versioning.md +226 -0
- package/assets/skills/astro-patterns.md +233 -0
- package/assets/skills/auth-patterns.md +248 -0
- package/assets/skills/aws-patterns.md +171 -0
- package/assets/skills/background-jobs.md +162 -0
- package/assets/skills/browser-extensions.md +309 -0
- package/assets/skills/caching-patterns.md +253 -0
- package/assets/skills/ci-cd.md +251 -0
- package/assets/skills/cli-development.md +296 -0
- package/assets/skills/code-review.md +185 -0
- package/assets/skills/cron-patterns.md +327 -0
- package/assets/skills/data-fetching.md +231 -0
- package/assets/skills/database-migrations.md +346 -0
- package/assets/skills/database-patterns.md +219 -0
- package/assets/skills/debugging.md +281 -0
- package/assets/skills/design-system.md +289 -0
- package/assets/skills/django-patterns.md +182 -0
- package/assets/skills/docker-patterns.md +235 -0
- package/assets/skills/e2e-testing.md +287 -0
- package/assets/skills/edge-computing.md +268 -0
- package/assets/skills/electron-patterns.md +266 -0
- package/assets/skills/email-templates.md +206 -0
- package/assets/skills/error-handling.md +265 -0
- package/assets/skills/event-driven.md +232 -0
- package/assets/skills/express-patterns.md +239 -0
- package/assets/skills/fastapi-patterns.md +198 -0
- package/assets/skills/feature-flags.md +212 -0
- package/assets/skills/figma-to-code.md +298 -0
- package/assets/skills/file-upload.md +228 -0
- package/assets/skills/forms-patterns.md +264 -0
- package/assets/skills/gcp-patterns.md +189 -0
- package/assets/skills/git-workflow.md +187 -0
- package/assets/skills/golang-patterns.md +185 -0
- package/assets/skills/graphql-patterns.md +244 -0
- package/assets/skills/i18n-patterns.md +172 -0
- package/assets/skills/image-processing.md +350 -0
- package/assets/skills/java-springboot.md +226 -0
- package/assets/skills/kotlin-patterns.md +207 -0
- package/assets/skills/kubernetes-patterns.md +326 -0
- package/assets/skills/laravel-patterns.md +261 -0
- package/assets/skills/llm-fine-tuning.md +335 -0
- package/assets/skills/load-testing.md +303 -0
- package/assets/skills/logging-observability.md +228 -0
- package/assets/skills/markdown-processing.md +318 -0
- package/assets/skills/mcp-server-patterns.md +292 -0
- package/assets/skills/microservices.md +272 -0
- package/assets/skills/migration-patterns.md +239 -0
- package/assets/skills/mongodb-patterns.md +189 -0
- package/assets/skills/monorepo-patterns.md +287 -0
- package/assets/skills/nextjs-app-router.md +237 -0
- package/assets/skills/notification-patterns.md +348 -0
- package/assets/skills/oauth-patterns.md +246 -0
- package/assets/skills/payment-integration.md +222 -0
- package/assets/skills/pdf-generation.md +307 -0
- package/assets/skills/performance-optimization.md +277 -0
- package/assets/skills/php-patterns.md +210 -0
- package/assets/skills/prisma-patterns.md +241 -0
- package/assets/skills/prompt-engineering.md +193 -0
- package/assets/skills/pwa-patterns.md +247 -0
- package/assets/skills/python-patterns.md +158 -0
- package/assets/skills/python-testing.md +172 -0
- package/assets/skills/queue-patterns.md +295 -0
- package/assets/skills/rag-patterns.md +159 -0
- package/assets/skills/rate-limiting.md +319 -0
- package/assets/skills/react-components.md +201 -0
- package/assets/skills/react-native-patterns.md +299 -0
- package/assets/skills/real-time-patterns.md +181 -0
- package/assets/skills/redis-patterns.md +188 -0
- package/assets/skills/refactoring.md +218 -0
- package/assets/skills/regex-patterns.md +191 -0
- package/assets/skills/remix-patterns.md +262 -0
- package/assets/skills/responsive-design.md +199 -0
- package/assets/skills/ruby-rails-patterns.md +178 -0
- package/assets/skills/rust-patterns.md +211 -0
- package/assets/skills/search-patterns.md +227 -0
- package/assets/skills/security-hardening.md +237 -0
- package/assets/skills/seo-patterns.md +179 -0
- package/assets/skills/serverless-patterns.md +223 -0
- package/assets/skills/sql-optimization.md +154 -0
- package/assets/skills/state-management.md +254 -0
- package/assets/skills/storybook-patterns.md +330 -0
- package/assets/skills/svelte-patterns.md +258 -0
- package/assets/skills/swift-patterns.md +227 -0
- package/assets/skills/tailwind-patterns.md +272 -0
- package/assets/skills/tdd-workflow.md +199 -0
- package/assets/skills/terraform-patterns.md +270 -0
- package/assets/skills/testing-react.md +240 -0
- package/assets/skills/testing-vitest.md +232 -0
- package/assets/skills/typescript-strict.md +159 -0
- package/assets/skills/video-processing.md +340 -0
- package/assets/skills/vue-patterns.md +247 -0
- package/assets/skills/web-workers.md +327 -0
- package/assets/skills/webhooks-patterns.md +283 -0
- package/assets/skills/websocket-patterns.md +306 -0
- package/dist/cli/index.js +941 -958
- package/dist/core/index.d.ts +341 -11
- package/dist/core.js +58 -95
- package/dist/mcp/index.d.ts +33 -1
- package/dist/mcp-server.js +177 -255
- package/package.json +4 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: typescript
|
|
3
|
+
description: Code style and best practice rules for TypeScript.
|
|
4
|
+
globs:
|
|
5
|
+
- "**/*.ts"
|
|
6
|
+
- "**/*.tsx"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# TypeScript Rules
|
|
10
|
+
|
|
11
|
+
## Style
|
|
12
|
+
- Enable `strict: true` in every tsconfig; never weaken with `skipLibCheck` in application code
|
|
13
|
+
- Use `PascalCase` for types, interfaces, enums, and classes; `camelCase` for variables and functions; `UPPER_SNAKE_CASE` for constants
|
|
14
|
+
- Prefix interfaces only when distinguishing from a class of the same name; prefer no `I` prefix
|
|
15
|
+
- One exported declaration per file for major types; co-locate small helper types in the same file
|
|
16
|
+
- Use barrel exports (`index.ts`) per module boundary, not per directory
|
|
17
|
+
- Keep import order: node builtins, external packages, internal packages, relative imports, separated by blank lines
|
|
18
|
+
- Limit files to a single responsibility; split when a file exceeds 300 lines
|
|
19
|
+
- Use explicit return types on exported functions; inferred types are acceptable for internal helpers
|
|
20
|
+
- Prefer `type` over `interface` unless declaration merging is needed
|
|
21
|
+
- Group related type definitions at the top of the file, before runtime code
|
|
22
|
+
|
|
23
|
+
## Patterns
|
|
24
|
+
- Use `unknown` instead of `any` for values of uncertain type; narrow with type guards before use
|
|
25
|
+
- Use discriminated unions with exhaustive `switch` statements; add a `default: never` check to catch unhandled cases
|
|
26
|
+
- Prefer branded types (`type UserId = string & { __brand: 'UserId' }`) for domain identifiers to prevent accidental mixing
|
|
27
|
+
- Use `readonly` arrays and properties by default; mutate only when performance requires it
|
|
28
|
+
- Handle errors with typed Result patterns (`{ ok: true; value: T } | { ok: false; error: E }`) instead of thrown exceptions for expected failures
|
|
29
|
+
- Use `async/await` over raw Promises; never mix `.then()` chains with `await` in the same function
|
|
30
|
+
- Prefer `Map` and `Set` over plain objects when keys are dynamic or non-string
|
|
31
|
+
- Use `satisfies` operator to validate object shapes without widening the inferred type
|
|
32
|
+
- Use template literal types for string patterns that follow a known format
|
|
33
|
+
- Prefer `const` assertions (`as const`) for literal tuples and fixed configuration objects
|
|
34
|
+
- Use `Zod` or similar runtime validators at system boundaries; never trust external input matches your types
|
|
35
|
+
|
|
36
|
+
## Avoid
|
|
37
|
+
- Never use `any`; use `unknown` and narrow, or define a proper type
|
|
38
|
+
- Never use `@ts-ignore`; use `@ts-expect-error` with a comment explaining why
|
|
39
|
+
- Never use `enum` with computed values; prefer `const` objects with `as const` or string literal unions
|
|
40
|
+
- Never reassign function parameters; create a new variable instead
|
|
41
|
+
- Never use `delete` operator on objects; restructure with spread or create a new object
|
|
42
|
+
- Never use non-null assertion (`!`) unless you can prove the value exists in the same scope
|
|
43
|
+
- Never export mutable state; export functions that return or modify encapsulated state
|
|
44
|
+
- Never use `Function` type; spell out the full signature `(arg: Type) => ReturnType`
|
|
45
|
+
|
|
46
|
+
## Testing
|
|
47
|
+
- Use `describe`/`it` blocks with descriptive names: `it('returns 404 when user does not exist')`
|
|
48
|
+
- Test the public API of each module; avoid testing private internals directly
|
|
49
|
+
- Use factory functions to build test fixtures; avoid shared mutable state between tests
|
|
50
|
+
- Assert specific values, not just truthiness; prefer `toEqual` over `toBeTruthy`
|
|
51
|
+
- Mock external dependencies at the boundary (HTTP, DB, filesystem); never mock the module under test
|
|
52
|
+
- Aim for one assertion per test; split multi-assert tests into separate `it` blocks when they test different behaviors
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yaml-json
|
|
3
|
+
description: Code style and best practice rules for YAML and JSON configuration files.
|
|
4
|
+
globs:
|
|
5
|
+
- "**/*.yml"
|
|
6
|
+
- "**/*.yaml"
|
|
7
|
+
- "**/*.json"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# YAML & JSON Rules
|
|
11
|
+
|
|
12
|
+
## Style
|
|
13
|
+
- Use 2-space indentation in YAML; never use tabs
|
|
14
|
+
- Use 2-space indentation in JSON for human-readable files; use minified JSON only for API responses and build artifacts
|
|
15
|
+
- Use `snake_case` for keys in configuration files; use `camelCase` only when matching a language SDK convention (e.g., `tsconfig.json`)
|
|
16
|
+
- Keep file names lowercase with hyphens: `docker-compose.yml`, `app-config.yaml`
|
|
17
|
+
- Use `.yml` extension consistently within a project; do not mix `.yml` and `.yaml`
|
|
18
|
+
- Place the most important or required keys at the top of each object; group related keys together
|
|
19
|
+
- Use blank lines to separate logical sections in YAML; add `# comment` headers above each section
|
|
20
|
+
- Wrap long strings in YAML with `>-` (folded, no trailing newline) or `|-` (literal, no trailing newline)
|
|
21
|
+
- Use explicit `true`/`false` in YAML for booleans; never rely on `yes`/`no`/`on`/`off` implicit conversions
|
|
22
|
+
- Keep JSON files under 500 lines; split large configurations into multiple files with `$ref` or tool-specific includes
|
|
23
|
+
|
|
24
|
+
## Patterns
|
|
25
|
+
- Use YAML anchors (`&anchor`) and aliases (`*anchor`) to eliminate duplication in repeated configuration blocks
|
|
26
|
+
- Use `<<: *anchor` merge key to inherit and override properties from a base configuration
|
|
27
|
+
- Define a JSON Schema or YAML schema for every configuration file; validate in CI with `ajv` or `yamllint`
|
|
28
|
+
- Use environment variable interpolation (`${VAR}` or `!env VAR`) where supported by the tool, instead of hardcoded values
|
|
29
|
+
- Use multi-document YAML (`---` separator) only when the consuming tool requires it (e.g., Kubernetes manifests)
|
|
30
|
+
- Use JSON with comments (`jsonc`) for configuration files that benefit from inline documentation (e.g., `tsconfig.json`, `.vscode/settings.json`)
|
|
31
|
+
- Use arrays for ordered collections and objects for named key-value pairs; never use an object when order matters
|
|
32
|
+
- Use `null` explicitly in JSON for absent values; in YAML use `~` or omit the key entirely depending on schema requirements
|
|
33
|
+
- Pin versions in dependency files to exact versions or narrow ranges; never use `latest` or `*`
|
|
34
|
+
- Use `$schema` property in JSON files to enable IDE validation and autocompletion where supported
|
|
35
|
+
- Prefer flat structures over deeply nested objects; limit nesting to 4 levels maximum
|
|
36
|
+
|
|
37
|
+
## Avoid
|
|
38
|
+
- Never use duplicate keys in the same object; most parsers silently use the last value, hiding bugs
|
|
39
|
+
- Never store secrets, API keys, or passwords in YAML or JSON files; use environment variables or a secrets manager
|
|
40
|
+
- Never use YAML implicit typing that converts strings to unexpected types: quote `"yes"`, `"no"`, `"on"`, `"off"`, `"null"`, `"true"`, `"false"` when they are string values
|
|
41
|
+
- Never use YAML flow style (`{key: value}`) for objects with more than 3 keys; use block style for readability
|
|
42
|
+
- Never commit `package-lock.json` or `yarn.lock` changes that are unrelated to the current PR
|
|
43
|
+
- Never use trailing commas in JSON; they are invalid per the JSON spec
|
|
44
|
+
- Never leave commented-out configuration blocks in committed files; remove or use a feature flag
|
|
45
|
+
|
|
46
|
+
## Testing
|
|
47
|
+
- Validate YAML files with `yamllint` using a shared `.yamllint.yml` config; enforce consistent quoting and indentation
|
|
48
|
+
- Validate JSON files with `jsonlint` or `ajv validate` against the declared schema
|
|
49
|
+
- Test configuration loading in application unit tests; verify that missing required keys produce clear error messages
|
|
50
|
+
- Use snapshot tests for generated configuration files to detect unintended changes
|
|
51
|
+
- Verify that environment variable placeholders resolve correctly in staging before deploying to production
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: accessibility
|
|
3
|
+
description: Accessibility patterns with ARIA, keyboard navigation, screen readers, color contrast, and focus management.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Web Accessibility
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
Apply to every web interface you build. Accessibility is not a feature -- it is a baseline requirement. Over 15% of users have some form of disability. Accessible sites rank better in SEO, work on more devices, and avoid legal liability under ADA and EAA.
|
|
10
|
+
|
|
11
|
+
## How It Works
|
|
12
|
+
|
|
13
|
+
### Semantic HTML First
|
|
14
|
+
|
|
15
|
+
Use the right element before reaching for ARIA. Native elements provide keyboard handling, focus management, and screen reader announcements for free.
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<!-- BAD -- div soup requires manual keyboard/ARIA work -->
|
|
19
|
+
<div class="btn" onclick="submit()">Submit</div>
|
|
20
|
+
<div class="input-wrapper"><div contenteditable="true"></div></div>
|
|
21
|
+
<div class="nav"><div class="link" onclick="go('/about')">About</div></div>
|
|
22
|
+
|
|
23
|
+
<!-- GOOD -- native elements handle accessibility automatically -->
|
|
24
|
+
<button type="submit">Submit</button>
|
|
25
|
+
<input type="text" aria-label="Search" />
|
|
26
|
+
<nav><a href="/about">About</a></nav>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Semantic elements: `<button>`, `<a>`, `<input>`, `<select>`, `<nav>`, `<main>`, `<header>`, `<footer>`, `<article>`, `<section>`, `<dialog>`, `<details>`, `<table>`, `<form>`, `<label>`, `<fieldset>`, `<legend>`.
|
|
30
|
+
|
|
31
|
+
### ARIA -- When HTML Is Not Enough
|
|
32
|
+
|
|
33
|
+
Use ARIA to fill gaps that semantic HTML cannot cover:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
// Tabs -- no native HTML element for tab interfaces
|
|
37
|
+
<div role="tablist" aria-label="Account settings">
|
|
38
|
+
<button
|
|
39
|
+
role="tab"
|
|
40
|
+
aria-selected={activeTab === "profile"}
|
|
41
|
+
aria-controls="panel-profile"
|
|
42
|
+
id="tab-profile"
|
|
43
|
+
onClick={() => setTab("profile")}
|
|
44
|
+
>
|
|
45
|
+
Profile
|
|
46
|
+
</button>
|
|
47
|
+
<button
|
|
48
|
+
role="tab"
|
|
49
|
+
aria-selected={activeTab === "security"}
|
|
50
|
+
aria-controls="panel-security"
|
|
51
|
+
id="tab-security"
|
|
52
|
+
onClick={() => setTab("security")}
|
|
53
|
+
>
|
|
54
|
+
Security
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
<div
|
|
58
|
+
role="tabpanel"
|
|
59
|
+
id="panel-profile"
|
|
60
|
+
aria-labelledby="tab-profile"
|
|
61
|
+
hidden={activeTab !== "profile"}
|
|
62
|
+
>
|
|
63
|
+
{/* profile content */}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
// Live regions -- announce dynamic content to screen readers
|
|
67
|
+
<div aria-live="polite" aria-atomic="true">
|
|
68
|
+
{notification && <p>{notification}</p>}
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
// Loading states
|
|
72
|
+
<button aria-busy={isLoading} disabled={isLoading}>
|
|
73
|
+
{isLoading ? "Saving..." : "Save"}
|
|
74
|
+
</button>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Keyboard Navigation
|
|
78
|
+
|
|
79
|
+
Every interactive element must be operable with keyboard alone:
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
function Modal({ isOpen, onClose, children }: ModalProps) {
|
|
83
|
+
const modalRef = useRef<HTMLDivElement>(null);
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (!isOpen) return;
|
|
87
|
+
const focusable = modalRef.current?.querySelectorAll<HTMLElement>(
|
|
88
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
89
|
+
);
|
|
90
|
+
if (!focusable?.length) return;
|
|
91
|
+
|
|
92
|
+
const first = focusable[0];
|
|
93
|
+
const last = focusable[focusable.length - 1];
|
|
94
|
+
first.focus();
|
|
95
|
+
|
|
96
|
+
function handleKeyDown(e: KeyboardEvent) {
|
|
97
|
+
if (e.key === "Escape") { onClose(); return; }
|
|
98
|
+
if (e.key !== "Tab") return;
|
|
99
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
last.focus();
|
|
102
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
first.focus();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
109
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
110
|
+
}, [isOpen, onClose]);
|
|
111
|
+
|
|
112
|
+
if (!isOpen) return null;
|
|
113
|
+
return (
|
|
114
|
+
<div ref={modalRef} role="dialog" aria-modal="true" aria-label="Dialog">
|
|
115
|
+
{children}
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Skip navigation link -- first focusable element on the page:
|
|
122
|
+
|
|
123
|
+
```html
|
|
124
|
+
<a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2">
|
|
125
|
+
Skip to main content
|
|
126
|
+
</a>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Color Contrast
|
|
130
|
+
|
|
131
|
+
WCAG AA requires 4.5:1 for normal text, 3:1 for large text (18px+ or 14px+ bold):
|
|
132
|
+
|
|
133
|
+
```css
|
|
134
|
+
/* BAD -- light gray on white: 2.1:1 ratio */
|
|
135
|
+
.subtle-text { color: #999; background: #fff; }
|
|
136
|
+
|
|
137
|
+
/* GOOD -- dark gray on white: 7.4:1 ratio */
|
|
138
|
+
.body-text { color: #374151; background: #fff; }
|
|
139
|
+
|
|
140
|
+
/* Never use color alone to convey information */
|
|
141
|
+
.error { color: #dc2626; }
|
|
142
|
+
.error::before { content: "! "; font-weight: bold; }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Screen Reader Best Practices
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
// Visually hidden but announced by screen readers
|
|
149
|
+
<span className="sr-only">3 items in cart</span>
|
|
150
|
+
|
|
151
|
+
// Image alt text -- describe the content, not the format
|
|
152
|
+
<img alt="Bar chart showing revenue growth from $2M to $5M in Q3" src="chart.png" />
|
|
153
|
+
// Decorative images get empty alt
|
|
154
|
+
<img alt="" src="decorative-border.png" role="presentation" />
|
|
155
|
+
|
|
156
|
+
// Form labels -- every input needs one
|
|
157
|
+
<label htmlFor="email">Email address</label>
|
|
158
|
+
<input id="email" type="email" aria-describedby="email-hint" />
|
|
159
|
+
<p id="email-hint">We will never share your email.</p>
|
|
160
|
+
|
|
161
|
+
// Error messages linked to inputs
|
|
162
|
+
<input
|
|
163
|
+
id="password"
|
|
164
|
+
type="password"
|
|
165
|
+
aria-invalid={!!error}
|
|
166
|
+
aria-describedby={error ? "password-error" : undefined}
|
|
167
|
+
/>
|
|
168
|
+
{error && <p id="password-error" role="alert">{error}</p>}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Focus Management
|
|
172
|
+
|
|
173
|
+
When content changes dynamically, manage focus so users are not lost:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// After deleting an item from a list, focus the next item
|
|
177
|
+
function handleDelete(index: number) {
|
|
178
|
+
deleteItem(index);
|
|
179
|
+
const nextItem = listRef.current?.children[Math.min(index, items.length - 1)];
|
|
180
|
+
(nextItem as HTMLElement)?.focus();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// After SPA navigation, focus the new page heading
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
const heading = document.querySelector("h1");
|
|
186
|
+
heading?.setAttribute("tabindex", "-1");
|
|
187
|
+
heading?.focus();
|
|
188
|
+
}, [pathname]);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Examples
|
|
192
|
+
|
|
193
|
+
| Issue | Fix | WCAG Criterion |
|
|
194
|
+
|-------|-----|---------------|
|
|
195
|
+
| Clickable div | Replace with `<button>` | 2.1.1 Keyboard |
|
|
196
|
+
| No visible focus indicator | Add `:focus-visible` outline | 2.4.7 Focus Visible |
|
|
197
|
+
| Error shown only in red | Add icon + text + `aria-invalid` | 1.4.1 Use of Color |
|
|
198
|
+
| Modal does not trap focus | Add focus trap + Escape to close | 2.4.3 Focus Order |
|
|
199
|
+
| Dynamic content not announced | Use `aria-live="polite"` | 4.1.3 Status Messages |
|
|
200
|
+
| Image without alt text | Add descriptive alt | 1.1.1 Non-text Content |
|
|
201
|
+
|
|
202
|
+
## Checklist
|
|
203
|
+
- [ ] All interactive elements reachable and operable via keyboard
|
|
204
|
+
- [ ] Every form input has a visible `<label>` or `aria-label`
|
|
205
|
+
- [ ] Color contrast meets WCAG AA (4.5:1 normal, 3:1 large text)
|
|
206
|
+
- [ ] Color is never the sole means of conveying information
|
|
207
|
+
- [ ] Images have descriptive alt text (or empty alt for decorative)
|
|
208
|
+
- [ ] Modals trap focus and close with Escape
|
|
209
|
+
- [ ] Skip navigation link is the first focusable element
|
|
210
|
+
- [ ] Dynamic content uses `aria-live` for announcements
|