Initial commit
This commit is contained in:
14
.claude-plugin/plugin.json
Normal file
14
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "accessibility-design",
|
||||||
|
"description": "Accessibility and inclusive design patterns following WCAG 2.2 and ARIA best practices",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "Brock"
|
||||||
|
},
|
||||||
|
"agents": [
|
||||||
|
"./agents"
|
||||||
|
],
|
||||||
|
"commands": [
|
||||||
|
"./commands"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# accessibility-design
|
||||||
|
|
||||||
|
Accessibility and inclusive design patterns following WCAG 2.2 and ARIA best practices
|
||||||
95
agents/a11y-specialist.md
Normal file
95
agents/a11y-specialist.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Accessibility Specialist Agent
|
||||||
|
|
||||||
|
You are an autonomous agent specialized in implementing accessibility features and inclusive design following WCAG 2.2 guidelines.
|
||||||
|
|
||||||
|
## Your Mission
|
||||||
|
|
||||||
|
Make applications accessible to all users, including those using assistive technologies, by implementing WCAG 2.2 AA/AAA standards.
|
||||||
|
|
||||||
|
## Core Responsibilities
|
||||||
|
|
||||||
|
### 1. Audit Accessibility
|
||||||
|
- Run automated tests (axe, Lighthouse)
|
||||||
|
- Manual keyboard navigation testing
|
||||||
|
- Screen reader testing
|
||||||
|
- Color contrast analysis
|
||||||
|
- Identify WCAG violations
|
||||||
|
|
||||||
|
### 2. Implement Semantic HTML
|
||||||
|
```html
|
||||||
|
<header>
|
||||||
|
<nav aria-label="Main">
|
||||||
|
<ul><li><a href="/">Home</a></li></ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<article>
|
||||||
|
<h1>Title</h1>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Add ARIA Attributes
|
||||||
|
```html
|
||||||
|
<button
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="menu"
|
||||||
|
>
|
||||||
|
Menu
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="menu"
|
||||||
|
role="menu"
|
||||||
|
aria-label="Options"
|
||||||
|
>
|
||||||
|
<button role="menuitem">Option 1</button>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Implement Keyboard Navigation
|
||||||
|
- All interactive elements accessible via Tab
|
||||||
|
- Arrow keys for menus/lists
|
||||||
|
- Escape to close modals/dropdowns
|
||||||
|
- Enter/Space to activate
|
||||||
|
- Focus management
|
||||||
|
|
||||||
|
### 5. Ensure Color Contrast
|
||||||
|
- 4.5:1 minimum for normal text (AA)
|
||||||
|
- 3:1 for large text (AA)
|
||||||
|
- 7:1 for enhanced contrast (AAA)
|
||||||
|
- Don't rely on color alone
|
||||||
|
|
||||||
|
### 6. Add Alternative Text
|
||||||
|
```html
|
||||||
|
<img src="chart.png" alt="Sales increased 25% in Q4" />
|
||||||
|
<img src="decorative.png" alt="" role="presentation" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Test with Assistive Tech
|
||||||
|
- Screen readers (NVDA, JAWS, VoiceOver)
|
||||||
|
- Keyboard only
|
||||||
|
- Voice control
|
||||||
|
- Screen magnification
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- Use semantic HTML
|
||||||
|
- Provide text alternatives
|
||||||
|
- Ensure keyboard access
|
||||||
|
- Maintain color contrast
|
||||||
|
- Logical heading structure
|
||||||
|
- Proper form labels
|
||||||
|
- Visible focus indicators
|
||||||
|
- Support screen readers
|
||||||
|
- Test thoroughly
|
||||||
|
|
||||||
|
## Deliverables
|
||||||
|
|
||||||
|
1. Accessibility audit report
|
||||||
|
2. WCAG compliant implementation
|
||||||
|
3. ARIA annotations
|
||||||
|
4. Keyboard navigation
|
||||||
|
5. Testing documentation
|
||||||
|
6. Remediation plan
|
||||||
542
commands/a11y-patterns.md
Normal file
542
commands/a11y-patterns.md
Normal file
@@ -0,0 +1,542 @@
|
|||||||
|
# Accessibility & Inclusive Design Patterns
|
||||||
|
|
||||||
|
Comprehensive accessibility patterns following WCAG 2.2 guidelines and ARIA best practices.
|
||||||
|
|
||||||
|
## WCAG 2.2 Principles (POUR)
|
||||||
|
|
||||||
|
### Perceivable
|
||||||
|
Information and UI components must be presentable to users in ways they can perceive.
|
||||||
|
|
||||||
|
### Operable
|
||||||
|
UI components and navigation must be operable.
|
||||||
|
|
||||||
|
### Understandable
|
||||||
|
Information and operation of UI must be understandable.
|
||||||
|
|
||||||
|
### Robust
|
||||||
|
Content must be robust enough to be interpreted by various user agents, including assistive technologies.
|
||||||
|
|
||||||
|
## Semantic HTML
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Good: Semantic structure -->
|
||||||
|
<header>
|
||||||
|
<nav aria-label="Main navigation">
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<article>
|
||||||
|
<h1>Page Title</h1>
|
||||||
|
<p>Content...</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<aside aria-label="Related content">
|
||||||
|
<h2>Related Articles</h2>
|
||||||
|
</aside>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2024 Company Name</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Bad: Non-semantic -->
|
||||||
|
<div class="header">
|
||||||
|
<div class="nav">
|
||||||
|
<div class="link">Home</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
## ARIA Attributes
|
||||||
|
|
||||||
|
### Landmarks
|
||||||
|
```html
|
||||||
|
<nav aria-label="Primary navigation">
|
||||||
|
<main role="main">
|
||||||
|
<aside aria-label="Sidebar">
|
||||||
|
<footer role="contentinfo">
|
||||||
|
```
|
||||||
|
|
||||||
|
### Live Regions
|
||||||
|
```html
|
||||||
|
<!-- Announce changes immediately -->
|
||||||
|
<div role="alert" aria-live="assertive">
|
||||||
|
Error: Please correct the form errors.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Announce when convenient -->
|
||||||
|
<div aria-live="polite" aria-atomic="true">
|
||||||
|
5 new messages
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Status messages -->
|
||||||
|
<div role="status" aria-live="polite">
|
||||||
|
Item added to cart
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Form Accessibility
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Proper labeling -->
|
||||||
|
<label for="email">Email Address</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
required
|
||||||
|
aria-required="true"
|
||||||
|
aria-describedby="email-error email-help"
|
||||||
|
/>
|
||||||
|
<span id="email-help">We'll never share your email</span>
|
||||||
|
<span id="email-error" role="alert" aria-live="polite">
|
||||||
|
<!-- Error message appears here -->
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Grouped inputs -->
|
||||||
|
<fieldset>
|
||||||
|
<legend>Contact Preferences</legend>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="email-pref" name="contact" value="email" />
|
||||||
|
<label for="email-pref">Email</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="phone-pref" name="contact" value="phone" />
|
||||||
|
<label for="phone-pref">Phone</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<!-- Radio groups -->
|
||||||
|
<fieldset>
|
||||||
|
<legend>Subscription Type</legend>
|
||||||
|
<div>
|
||||||
|
<input type="radio" id="free" name="subscription" value="free" checked />
|
||||||
|
<label for="free">Free</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" id="premium" name="subscription" value="premium" />
|
||||||
|
<label for="premium">Premium</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Keyboard Navigation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// React component with keyboard support
|
||||||
|
function DropdownMenu({ items }) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [focusedIndex, setFocusedIndex] = useState(0);
|
||||||
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
switch (e.key) {
|
||||||
|
case 'Enter':
|
||||||
|
case ' ':
|
||||||
|
e.preventDefault();
|
||||||
|
setIsOpen(!isOpen);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Escape':
|
||||||
|
setIsOpen(false);
|
||||||
|
buttonRef.current?.focus();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowDown':
|
||||||
|
e.preventDefault();
|
||||||
|
if (!isOpen) {
|
||||||
|
setIsOpen(true);
|
||||||
|
} else {
|
||||||
|
setFocusedIndex((prev) => (prev + 1) % items.length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowUp':
|
||||||
|
e.preventDefault();
|
||||||
|
if (isOpen) {
|
||||||
|
setFocusedIndex((prev) => (prev - 1 + items.length) % items.length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Home':
|
||||||
|
e.preventDefault();
|
||||||
|
setFocusedIndex(0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'End':
|
||||||
|
e.preventDefault();
|
||||||
|
setFocusedIndex(items.length - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div onKeyDown={handleKeyDown}>
|
||||||
|
<button
|
||||||
|
ref={buttonRef}
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded={isOpen}
|
||||||
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
|
>
|
||||||
|
Menu
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{isOpen && (
|
||||||
|
<div
|
||||||
|
ref={menuRef}
|
||||||
|
role="menu"
|
||||||
|
aria-orientation="vertical"
|
||||||
|
>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<button
|
||||||
|
key={item.id}
|
||||||
|
role="menuitem"
|
||||||
|
tabIndex={index === focusedIndex ? 0 : -1}
|
||||||
|
onClick={() => item.onClick()}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Focus Management
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Trap focus in modal
|
||||||
|
function Modal({ isOpen, onClose, children }) {
|
||||||
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) return;
|
||||||
|
|
||||||
|
const modal = modalRef.current;
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const focusableElements = modal.querySelectorAll(
|
||||||
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
||||||
|
);
|
||||||
|
|
||||||
|
const firstElement = focusableElements[0] as HTMLElement;
|
||||||
|
const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;
|
||||||
|
|
||||||
|
// Focus first element
|
||||||
|
firstElement?.focus();
|
||||||
|
|
||||||
|
const handleTabKey = (e: KeyboardEvent) => {
|
||||||
|
if (e.key !== 'Tab') return;
|
||||||
|
|
||||||
|
if (e.shiftKey) {
|
||||||
|
// Shift + Tab
|
||||||
|
if (document.activeElement === firstElement) {
|
||||||
|
e.preventDefault();
|
||||||
|
lastElement?.focus();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Tab
|
||||||
|
if (document.activeElement === lastElement) {
|
||||||
|
e.preventDefault();
|
||||||
|
firstElement?.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
modal.addEventListener('keydown', handleTabKey);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
modal.removeEventListener('keydown', handleTabKey);
|
||||||
|
};
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={modalRef}
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="modal-title"
|
||||||
|
>
|
||||||
|
<h2 id="modal-title">Modal Title</h2>
|
||||||
|
{children}
|
||||||
|
<button onClick={onClose}>Close</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Color Contrast
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* WCAG AA requires 4.5:1 for normal text, 3:1 for large text */
|
||||||
|
/* WCAG AAA requires 7:1 for normal text, 4.5:1 for large text */
|
||||||
|
|
||||||
|
.button-primary {
|
||||||
|
/* Good contrast: 7.2:1 */
|
||||||
|
background: #0066cc;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-secondary {
|
||||||
|
/* Good contrast: 4.6:1 */
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't rely on color alone */
|
||||||
|
.error {
|
||||||
|
color: #cc0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error::before {
|
||||||
|
content: '⚠ '; /* Visual indicator */
|
||||||
|
}
|
||||||
|
|
||||||
|
.error[aria-invalid="true"] {
|
||||||
|
/* Also announced to screen readers */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Skip Links
|
||||||
|
|
||||||
|
```html
|
||||||
|
<a href="#main-content" class="skip-link">
|
||||||
|
Skip to main content
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.skip-link {
|
||||||
|
position: absolute;
|
||||||
|
top: -40px;
|
||||||
|
left: 0;
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
padding: 8px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skip-link:focus {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Images and Alt Text
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Informative image -->
|
||||||
|
<img src="chart.png" alt="Sales increased by 25% in Q4 2024" />
|
||||||
|
|
||||||
|
<!-- Decorative image -->
|
||||||
|
<img src="decorative.png" alt="" role="presentation" />
|
||||||
|
|
||||||
|
<!-- Complex image -->
|
||||||
|
<figure>
|
||||||
|
<img src="complex-chart.png" alt="Detailed sales data" />
|
||||||
|
<figcaption>
|
||||||
|
<details>
|
||||||
|
<summary>Chart description</summary>
|
||||||
|
<p>Detailed text description of the chart data...</p>
|
||||||
|
</details>
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<!-- Icon with text -->
|
||||||
|
<button>
|
||||||
|
<svg aria-hidden="true"><path d="..." /></svg>
|
||||||
|
<span>Delete</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Icon without text -->
|
||||||
|
<button aria-label="Delete item">
|
||||||
|
<svg aria-hidden="true"><path d="..." /></svg>
|
||||||
|
</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Screen Reader-Only Content
|
||||||
|
|
||||||
|
```css
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sr-only-focusable:focus {
|
||||||
|
position: static;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
overflow: visible;
|
||||||
|
clip: auto;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```html
|
||||||
|
<button>
|
||||||
|
<svg aria-hidden="true">...</svg>
|
||||||
|
<span class="sr-only">Settings</span>
|
||||||
|
</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Responsive and Touch-Friendly
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Minimum touch target size: 44x44px */
|
||||||
|
button,
|
||||||
|
a {
|
||||||
|
min-height: 44px;
|
||||||
|
min-width: 44px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure adequate spacing */
|
||||||
|
.button-group button {
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Headings Hierarchy
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Good: Logical hierarchy -->
|
||||||
|
<h1>Page Title</h1>
|
||||||
|
<h2>Section 1</h2>
|
||||||
|
<h3>Subsection 1.1</h3>
|
||||||
|
<h3>Subsection 1.2</h3>
|
||||||
|
<h2>Section 2</h2>
|
||||||
|
|
||||||
|
<!-- Bad: Skipped levels -->
|
||||||
|
<h1>Page Title</h1>
|
||||||
|
<h3>Section</h3> <!-- Skipped h2 -->
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tables
|
||||||
|
|
||||||
|
```html
|
||||||
|
<table>
|
||||||
|
<caption>Monthly Sales Report</caption>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Month</th>
|
||||||
|
<th scope="col">Sales</th>
|
||||||
|
<th scope="col">Growth</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">January</th>
|
||||||
|
<td>$10,000</td>
|
||||||
|
<td>+5%</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loading States
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function DataList() {
|
||||||
|
const { data, isLoading } = useQuery('data');
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div role="status" aria-live="polite" aria-busy="true">
|
||||||
|
<span className="sr-only">Loading data...</span>
|
||||||
|
<Spinner aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div role="region" aria-label="Data list">
|
||||||
|
{data.map(item => <Item key={item.id} {...item} />)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Accessibility
|
||||||
|
|
||||||
|
### Automated Testing
|
||||||
|
```typescript
|
||||||
|
import { axe, toHaveNoViolations } from 'jest-axe';
|
||||||
|
|
||||||
|
expect.extend(toHaveNoViolations);
|
||||||
|
|
||||||
|
test('page has no accessibility violations', async () => {
|
||||||
|
const { container } = render(<App />);
|
||||||
|
const results = await axe(container);
|
||||||
|
expect(results).toHaveNoViolations();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Testing Checklist
|
||||||
|
- [ ] Keyboard navigation works
|
||||||
|
- [ ] Screen reader announces correctly
|
||||||
|
- [ ] Color contrast meets WCAG AA
|
||||||
|
- [ ] Forms have proper labels
|
||||||
|
- [ ] Images have alt text
|
||||||
|
- [ ] Headings are hierarchical
|
||||||
|
- [ ] Focus indicators are visible
|
||||||
|
- [ ] Touch targets are adequate
|
||||||
|
- [ ] No color-only information
|
||||||
|
- [ ] Text can be resized to 200%
|
||||||
|
|
||||||
|
## Reduced Motion
|
||||||
|
|
||||||
|
```css
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function AnimatedComponent() {
|
||||||
|
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ duration: prefersReducedMotion ? 0 : 0.3 }}
|
||||||
|
>
|
||||||
|
Content
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Use semantic HTML**
|
||||||
|
2. **Provide text alternatives**
|
||||||
|
3. **Ensure keyboard accessibility**
|
||||||
|
4. **Maintain sufficient color contrast**
|
||||||
|
5. **Create logical heading structure**
|
||||||
|
6. **Label form inputs properly**
|
||||||
|
7. **Make focus indicators visible**
|
||||||
|
8. **Support screen readers**
|
||||||
|
9. **Test with real assistive technologies**
|
||||||
|
10. **Follow WCAG 2.2 guidelines**
|
||||||
49
plugin.lock.json
Normal file
49
plugin.lock.json
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:Dieshen/claude_marketplace:plugins/accessibility-design",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "59a790231bd3248131fc8735a376a95803384969",
|
||||||
|
"treeHash": "d7b7f73e436ef8af7b8df1299a323dc1ca76fa5edd653133bb825629ad4367e7",
|
||||||
|
"generatedAt": "2025-11-28T10:10:24.637350Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "accessibility-design",
|
||||||
|
"description": "Accessibility and inclusive design patterns following WCAG 2.2 and ARIA best practices",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "f3460be179d27f5dc51986dacd6d8b1a71a2a11b667dd484b0eede3d0fcee0bb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "agents/a11y-specialist.md",
|
||||||
|
"sha256": "ac9271aeae54c1d4ce843733bedbd1b9aae6d51f81f427bbd049716706d80af5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "1c2d6d6a981e899a1f1b9f61d91334711480d49e7a5b7ba8bfdc8bde60cfc3d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/a11y-patterns.md",
|
||||||
|
"sha256": "d18058d0271a3c6f4012fd692815c1fcba5736bd478f4b0652a078d208c16cc2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "d7b7f73e436ef8af7b8df1299a323dc1ca76fa5edd653133bb825629ad4367e7"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user