# Accessibility (A11y) Skill

> **Standard:** WCAG 2.1 Level AA compliance for inclusive user experience

---

## Purpose

This skill covers accessibility requirements, ARIA attributes, keyboard navigation, and screen reader support for the Comments Press Zone plugin.

---

## Core Principles

1. **Keyboard Navigation** - All interactive elements accessible via keyboard
2. **Screen Reader Support** - Meaningful labels and announcements
3. **Focus Management** - Visible focus indicators, logical tab order
4. **Color Contrast** - WCAG AA minimum (4.5:1 for normal text)
5. **Reduced Motion** - Respect user preferences

---

## Keyboard Navigation

### Interactive Elements

```html
<!-- Button (automatically keyboard accessible) -->
<button 
    type="button"
    class="presszone-comments-vote-btn"
    onclick="handleVote()">
    Vote
</button>

<!-- Link -->
<a href="#" class="presszone-comments-link" onclick="handleAction(event)">
    Click me
</a>
```

### Custom Clickable Elements

```javascript
// Add keyboard handler for custom elements
element.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        handleAction();
    }
});

// Or make it focusable and clickable
element.setAttribute('tabindex', '0');
element.setAttribute('role', 'button');
element.addEventListener('click', handleAction);
element.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        handleAction();
    }
});
```

---

## ARIA Attributes

### Buttons

```html
<!-- Icon-only button MUST have aria-label -->
<button 
    type="button"
    class="presszone-comments-btn"
    aria-label="<?php echo esc_attr__('Upvote comment', 'comments-press-zone'); ?>">
    👍
</button>

<!-- Toggle button -->
<button 
    type="button"
    aria-pressed="false"
    onclick="toggleCollapse()">
    Show More
</button>
```

### Dropdown/Accordion

```html
<!-- Dropdown trigger -->
<button 
    type="button"
    aria-expanded="false"
    aria-controls="dropdown-menu"
    onclick="toggleDropdown()">
    Menu
</button>

<div id="dropdown-menu" class="presszone-comments-dropdown" hidden>
    <!-- Dropdown content -->
</div>
```

### Live Regions

```html
<!-- Screen reader announcements -->
<div 
    role="status"
    aria-live="polite"
    aria-atomic="true"
    class="presszone-comments-sr-only">
    <!-- Dynamic content announced to screen readers -->
</div>
```

### Loading States

```html
<!-- Loading indicator -->
<div 
    role="status"
    aria-live="polite"
    aria-busy="true">
    <span class="presszone-comments-spinner"></span>
    <span><?php echo esc_html__('Loading...', 'comments-press-zone'); ?></span>
</div>
```

---

## Focus Management

### Visible Focus Indicators

```scss
.presszone-comments-btn {
    &:focus {
        outline: 2px solid $presszone-comments-primary;
        outline-offset: 2px;
    }
    
    // Modern browsers with :focus-visible
    &:focus-visible {
        outline: 2px solid $presszone-comments-primary;
        outline-offset: 2px;
    }
    
    // Remove outline when clicked (not keyboard focused)
    &:focus:not(:focus-visible) {
        outline: none;
    }
}
```

### Modal Focus Trapping

```javascript
class Modal {
    open() {
        this.previousFocus = document.activeElement;
        this.element.removeAttribute('hidden');
        this.element.querySelector('[autofocus]')?.focus();
        this.trapFocus();
    }
    
    close() {
        this.element.setAttribute('hidden', '');
        this.previousFocus?.focus();
    }
    
    trapFocus() {
        const focusable = this.element.querySelectorAll(
            'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
        );
        
        if (focusable.length === 0) return;
        
        const first = focusable[0];
        const last = focusable[focusable.length - 1];
        
        this.element.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                this.close();
                return;
            }
            
            if (e.key === 'Tab') {
                if (e.shiftKey && document.activeElement === first) {
                    e.preventDefault();
                    last.focus();
                } else if (!e.shiftKey && document.activeElement === last) {
                    e.preventDefault();
                    first.focus();
                }
            }
        });
    }
}
```

---

## Screen Reader Support

### Hidden Content (Visual Only)

```scss
.presszone-comments-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
```

### Skip Links

```html
<a href="#main-content" class="presszone-comments-skip-link">
    <?php echo esc_html__('Skip to main content', 'comments-press-zone'); ?>
</a>
```

```scss
.presszone-comments-skip-link {
    position: absolute;
    top: -40px;
    left: 0;
    z-index: 100;
    
    &:focus {
        top: 0;
    }
}
```

---

## Semantic HTML

### Headings Hierarchy

```html
<!-- Proper heading hierarchy -->
<h2><?php echo esc_html__('Comments', 'comments-press-zone'); ?></h2>
    <h3><?php echo esc_html__('Reply to John', 'comments-press-zone'); ?></h3>
    
<!-- WRONG - Skipping levels -->
<h2>Comments</h2>
    <h4>Reply to John</h4>  <!-- Bad: skipped h3 -->
```

### Lists

```html
<!-- Proper list markup -->
<ul class="presszone-comments-list">
    <li class="presszone-comments-item">
        Comment content
    </li>
</ul>
```

### Forms

```html
<!-- Proper form labels -->
<label for="comment-text">
    <?php echo esc_html__('Your comment', 'comments-press-zone'); ?>
</label>
<textarea 
    id="comment-text"
    name="comment_text"
    aria-describedby="comment-help"
    required></textarea>
<p id="comment-help" class="presszone-comments-help">
    <?php echo esc_html__('Maximum 2000 characters', 'comments-press-zone'); ?>
</p>
```

---

## Color Contrast

### WCAG AA Requirements

- **Normal text (< 18pt):** 4.5:1 minimum
- **Large text (≥ 18pt):** 3:1 minimum
- **UI components:** 3:1 minimum

```scss
// Example: Sufficient contrast
.presszone-comments-text {
    color: $presszone-comments-text; // #0f172a on white = 15.8:1 ✓
    
    .dark-mode & {
        color: $presszone-comments-text-dark; // #e3e3e3 on #131314 = 13.1:1 ✓
    }
}
```

---

## Reduced Motion

```scss
@media (prefers-reduced-motion: reduce) {
    * {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}
```

---

## Common Patterns

### Toast Notification

```html
<div 
    role="alert"
    aria-live="assertive"
    class="presszone-comments-toast">
    <?php echo esc_html($message); ?>
</div>
```

### Tabs

```html
<div class="presszone-comments-tabs">
    <div role="tablist" aria-label="<?php echo esc_attr__('Settings tabs', 'comments-press-zone'); ?>">
        <button 
            role="tab"
            aria-selected="true"
            aria-controls="panel-1"
            id="tab-1">
            Tab 1
        </button>
        <button 
            role="tab"
            aria-selected="false"
            aria-controls="panel-2"
            id="tab-2">
            Tab 2
        </button>
    </div>
    
    <div 
        role="tabpanel"
        id="panel-1"
        aria-labelledby="tab-1">
        Content 1
    </div>
    
    <div 
        role="tabpanel"
        id="panel-2"
        aria-labelledby="tab-2"
        hidden>
        Content 2
    </div>
</div>
```

---

## Common Mistakes to Avoid

| Mistake | Fix |
|---------|-----|
| Icon-only button without label | Add `aria-label` |
| No keyboard handler on clickable divs | Use `<button>` or add keyboard handler |
| Missing focus indicators | Add visible `:focus` styles |
| Not restoring focus after modal | Save and restore `document.activeElement` |
| Missing alt text on images | Add descriptive `alt` attribute |
| Low contrast text | Use WCAG AA minimum (4.5:1) |
| Skipping heading levels | Follow h1 → h2 → h3 hierarchy |
| Using `<div>` for buttons | Use semantic `<button>` element |

---

## Testing Checklist

- [ ] All interactive elements keyboard accessible
- [ ] Icon-only buttons have `aria-label`
- [ ] Focus visible on all focusable elements
- [ ] Modals trap focus and restore on close
- [ ] Color contrast meets WCAG AA (4.5:1)
- [ ] Reduced motion respected
- [ ] Semantic HTML used (headings, lists, forms)
- [ ] Screen reader tested (NVDA/JAWS/VoiceOver)
- [ ] Tab order logical
- [ ] Form fields properly labeled
