diff --git a/musify_flutter/.agents/skills/accessibility/SKILL.md b/musify_flutter/.agents/skills/accessibility/SKILL.md deleted file mode 100644 index af2299a..0000000 --- a/musify_flutter/.agents/skills/accessibility/SKILL.md +++ /dev/null @@ -1,440 +0,0 @@ ---- -name: accessibility -description: Audit and improve web accessibility following WCAG 2.2 guidelines. Use when asked to "improve accessibility", "a11y audit", "WCAG compliance", "screen reader support", "keyboard navigation", or "make accessible". -license: MIT -metadata: - author: web-quality-skills - version: "1.1" ---- - -# Accessibility (a11y) - -Comprehensive accessibility guidelines based on WCAG 2.2 and Lighthouse accessibility audits. Goal: make content usable by everyone, including people with disabilities. - -## WCAG Principles: POUR - -| Principle | Description | -|-----------|-------------| -| **P**erceivable | Content can be perceived through different senses | -| **O**perable | Interface can be operated by all users | -| **U**nderstandable | Content and interface are understandable | -| **R**obust | Content works with assistive technologies | - -## Conformance levels - -| Level | Requirement | Target | -|-------|-------------|--------| -| **A** | Minimum accessibility | Must pass | -| **AA** | Standard compliance | Should pass (legal requirement in many jurisdictions) | -| **AAA** | Enhanced accessibility | Nice to have | - ---- - -## Perceivable - -### Text alternatives (1.1) - -**Images require alt text:** -```html - - - - -Bar chart showing 40% increase in Q3 sales - - - - - -
- 2024 market trends infographic -
- -
-
-``` - -**Icon buttons need accessible names:** -```html - - - - - - - - -``` - -**Visually hidden class:** -```css -.visually-hidden { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} -``` - -### Color contrast (1.4.3, 1.4.6) - -| Text Size | AA minimum | AAA enhanced | -|-----------|------------|--------------| -| Normal text (< 18px / < 14px bold) | 4.5:1 | 7:1 | -| Large text (≥ 18px / ≥ 14px bold) | 3:1 | 4.5:1 | -| UI components & graphics | 3:1 | 3:1 | - -```css -/* ❌ Low contrast (2.5:1) */ -.low-contrast { - color: #999; - background: #fff; -} - -/* ✅ Sufficient contrast (7:1) */ -.high-contrast { - color: #333; - background: #fff; -} - -/* ✅ Focus states need contrast too */ -:focus-visible { - outline: 2px solid #005fcc; - outline-offset: 2px; -} -``` - -**Don't rely on color alone:** -```html - - - - - -
- - - - Please enter a valid email address - -
-``` - -### Media alternatives (1.2) - -```html - - - - - -
- Transcript -

Full transcript text...

-
-``` - ---- - -## Operable - -### Keyboard accessible (2.1) - -**All functionality must be keyboard accessible:** -```javascript -// ❌ Only handles click -element.addEventListener('click', handleAction); - -// ✅ Handles both click and keyboard -element.addEventListener('click', handleAction); -element.addEventListener('keydown', (e) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - handleAction(); - } -}); -``` - -**No keyboard traps.** Users must be able to Tab into and out of every component. Use the [modal focus trap pattern](references/A11Y-PATTERNS.md#modal-focus-trap) for dialogs—the native `` element handles this automatically. - -### Focus visible (2.4.7) - -```css -/* ❌ Never remove focus outlines */ -*:focus { outline: none; } - -/* ✅ Use :focus-visible for keyboard-only focus */ -:focus { - outline: none; -} - -:focus-visible { - outline: 2px solid #005fcc; - outline-offset: 2px; -} - -/* ✅ Or custom focus styles */ -button:focus-visible { - box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5); -} -``` - -### Focus not obscured (2.4.11) — new in 2.2 - -When an element receives keyboard focus, it must not be entirely hidden by other author-created content such as sticky headers, footers, or overlapping panels. At Level AAA (2.4.12), no part of the focused element may be hidden. - -```css -/* ✅ Account for sticky headers when scrolling to focused elements */ -:target { - scroll-margin-top: 80px; -} - -/* ✅ Ensure focused items clear fixed/sticky bars */ -:focus { - scroll-margin-top: 80px; - scroll-margin-bottom: 60px; -} -``` - -### Skip links (2.4.1) - -Provide a skip link so keyboard users can bypass repetitive navigation. See the [skip link pattern](references/A11Y-PATTERNS.md#skip-link) for full markup and styles. - -### Target size (2.5.8) — new in 2.2 - -Interactive targets must be at least **24 × 24 CSS pixels** (AA). Exceptions: inline text links, elements where the browser controls the size, and targets where a 24px circle centered on the bounding box does not overlap another target. - -```css -/* ✅ Minimum target size */ -button, -[role="button"], -input[type="checkbox"] + label, -input[type="radio"] + label { - min-width: 24px; - min-height: 24px; -} - -/* ✅ Comfortable target size (recommended 44×44) */ -.touch-target { - min-width: 44px; - min-height: 44px; - display: inline-flex; - align-items: center; - justify-content: center; -} -``` - -### Dragging movements (2.5.7) — new in 2.2 - -Any action that requires dragging must have a single-pointer alternative (e.g., buttons, inputs). See the [dragging movements pattern](references/A11Y-PATTERNS.md#dragging-movements) for a sortable-list example. - -### Timing (2.2) - -```javascript -// Allow users to extend time limits -function showSessionWarning() { - const modal = createModal({ - title: 'Session Expiring', - content: 'Your session will expire in 2 minutes.', - actions: [ - { label: 'Extend session', action: extendSession }, - { label: 'Log out', action: logout } - ], - timeout: 120000 - }); -} -``` - -### Motion (2.3) - -```css -/* Respect reduced motion preference */ -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - scroll-behavior: auto !important; - } -} -``` - ---- - -## Understandable - -### Page language (3.1.1) - -```html - - - - - - - -

The French word for hello is bonjour.

-``` - -### Consistent navigation (3.2.3) - -```html - - -``` - -### Consistent help (3.2.6) — new in 2.2 - -If a help mechanism (contact info, chat widget, FAQ link, self-help option) is repeated across multiple pages, it must appear in the **same relative order** each time. Users who rely on consistent placement shouldn't have to hunt for help on every page. - -### Form labels (3.3.2) - -Every input needs a programmatically associated label. See the [form labels pattern](references/A11Y-PATTERNS.md#form-labels) for explicit, implicit, and instructional examples. - -### Error handling (3.3.1, 3.3.3) - -Announce errors to screen readers with `role="alert"` or `aria-live`, set `aria-invalid="true"` on invalid fields, and focus the first error on submit. See the [error handling pattern](references/A11Y-PATTERNS.md#error-handling) for full markup and JS. - -### Redundant entry (3.3.7) — new in 2.2 - -Don't force users to re-enter information they already provided in the same session. Auto-populate from earlier steps, or let users select from previously entered values. Exceptions: security re-confirmation and content that has expired. - -```html - -
- Shipping address - - -
-``` - -### Accessible authentication (3.3.8) — new in 2.2 - -Login flows must not rely on cognitive function tests (e.g., remembering a password, solving a puzzle) unless at least one of: -- A copy-paste or autofill mechanism is available -- An alternative method exists (e.g., passkey, SSO, email link) -- The test uses object recognition or personal content (AA only; AAA removes this exception) - -```html - - - - - - -``` - ---- - -## Robust - -### ARIA usage (4.1.2) - -**Prefer native elements:** -```html - -
Click me
- - - - - -
Option
- - - -``` - -**When ARIA is needed,** use the correct roles and states. See the [ARIA tabs pattern](references/A11Y-PATTERNS.md#aria-tabs) for a complete tablist example. - -### Live regions (4.1.3) - -Use `aria-live` regions to announce dynamic content changes without moving focus. See the [live regions pattern](references/A11Y-PATTERNS.md#live-regions-and-notifications) for markup and a `showNotification()` helper. - ---- - -## Testing checklist - -### Automated testing -```bash -# Lighthouse accessibility audit -npx lighthouse https://example.com --only-categories=accessibility - -# axe-core -npm install @axe-core/cli -g -axe https://example.com -``` - -### Manual testing - -- [ ] **Keyboard navigation:** Tab through entire page, use Enter/Space to activate -- [ ] **Screen reader:** Test with VoiceOver (Mac), NVDA (Windows), or TalkBack (Android) -- [ ] **Zoom:** Content usable at 200% zoom -- [ ] **High contrast:** Test with Windows High Contrast Mode -- [ ] **Reduced motion:** Test with `prefers-reduced-motion: reduce` -- [ ] **Focus order:** Logical and follows visual order -- [ ] **Target size:** Interactive elements meet 24×24px minimum - -See the [screen reader commands reference](references/A11Y-PATTERNS.md#screen-reader-commands) for VoiceOver and NVDA shortcuts. - ---- - -## Common issues by impact - -### Critical (fix immediately) -1. Missing form labels -2. Missing image alt text -3. Insufficient color contrast -4. Keyboard traps -5. No focus indicators - -### Serious (fix before launch) -1. Missing page language -2. Missing heading structure -3. Non-descriptive link text -4. Auto-playing media -5. Missing skip links - -### Moderate (fix soon) -1. Missing ARIA labels on icons -2. Inconsistent navigation -3. Missing error identification -4. Timing without controls -5. Missing landmark regions - -## References - -- [WCAG 2.2 Quick Reference](https://www.w3.org/WAI/WCAG22/quickref/) -- [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) -- [Deque axe Rules](https://dequeuniversity.com/rules/axe/) -- [Web Quality Audit](../web-quality-audit/SKILL.md) -- [WCAG criteria reference](references/WCAG.md) -- [Accessibility code patterns](references/A11Y-PATTERNS.md) diff --git a/musify_flutter/.agents/skills/accessibility/references/A11Y-PATTERNS.md b/musify_flutter/.agents/skills/accessibility/references/A11Y-PATTERNS.md deleted file mode 100644 index 6d500ef..0000000 --- a/musify_flutter/.agents/skills/accessibility/references/A11Y-PATTERNS.md +++ /dev/null @@ -1,233 +0,0 @@ -# Accessibility Code Patterns - -Practical, copy-paste-ready patterns for common accessibility requirements. Each pattern is self-contained and linked from the main [SKILL.md](../SKILL.md). - ---- - -## Modal focus trap - -Trap keyboard focus inside a modal dialog so Tab/Shift+Tab cycle through its focusable elements and Escape closes it. - -```javascript -function openModal(modal) { - const focusableElements = modal.querySelectorAll( - 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' - ); - const firstElement = focusableElements[0]; - const lastElement = focusableElements[focusableElements.length - 1]; - - modal.addEventListener('keydown', (e) => { - if (e.key === 'Tab') { - if (e.shiftKey && document.activeElement === firstElement) { - e.preventDefault(); - lastElement.focus(); - } else if (!e.shiftKey && document.activeElement === lastElement) { - e.preventDefault(); - firstElement.focus(); - } - } - if (e.key === 'Escape') { - closeModal(); - } - }); - - firstElement.focus(); -} -``` - -The native `` element handles focus trapping automatically—prefer it when browser support allows. - ---- - -## Skip link - -Allows keyboard users to bypass repetitive navigation and jump straight to main content. - -```html - - -
-
- -
- -``` - -```css -.skip-link { - position: absolute; - top: -40px; - left: 0; - background: #000; - color: #fff; - padding: 8px 16px; - z-index: 100; -} - -.skip-link:focus { - top: 0; -} -``` - ---- - -## Error handling - -Announce errors to screen readers and focus the first invalid field on submit. - -```html -
-
- - - -
-
-``` - -```javascript -form.addEventListener('submit', (e) => { - const firstError = form.querySelector('[aria-invalid="true"]'); - if (firstError) { - e.preventDefault(); - firstError.focus(); - - const errorSummary = document.getElementById('error-summary'); - errorSummary.textContent = - `${errors.length} errors found. Please fix them and try again.`; - errorSummary.focus(); - } -}); -``` - ---- - -## Form labels - -Every input needs an associated label—either explicit (`for`/`id`) or implicit (wrapping `