Accessibility Guide - WCAG 2.1 Level AA Compliance

Overview

KoproGo is committed to providing an accessible platform that meets WCAG 2.1 Level AA standards. This document outlines our accessibility features, best practices, and testing guidelines.

✅ Implemented Features

1. Keyboard Navigation

  • Skip Links: Press Tab on any page to reveal “Skip to main content” link

  • Keyboard-only Navigation: All interactive elements accessible via Tab, Enter, Space, Arrow keys

  • Focus Indicators: Visible focus rings on all interactive elements (2px outline, green color)

  • Focus Management: Proper focus trapping in modals and dialogs

2. Screen Reader Support

  • ARIA Landmarks: Proper role attributes (banner, main, contentinfo, navigation)

  • ARIA Labels: All buttons and interactive elements have descriptive labels

  • Live Regions: Screen reader announcements for dynamic content updates

  • Semantic HTML: Proper use of headings, lists, buttons, and form elements

3. Color Contrast

All text meets WCAG AA contrast ratios:

  • Normal Text: 4.5:1 minimum contrast ratio

  • Large Text (18pt+ or 14pt+ bold): 3.1 minimum contrast ratio

  • Interactive Elements: 3:1 contrast ratio for focus indicators

4. Visual Design

  • Focus Visible: Clear focus indicators (:focus-visible pseudo-class)

  • No Color-Only Information: Information never conveyed by color alone

  • Resizable Text: Text can be resized up to 200% without loss of functionality

  • Responsive Design: Works on all screen sizes and orientations

🛠️ Accessibility Components

AccessibleButton

Fully accessible button component with ARIA support:

<AccessibleButton
  variant="primary"
  ariaLabel="Save changes to expense"
  disabled={false}
  loading={isLoading}
  on:click={handleSave}
>
  Save
</AccessibleButton>

Props:

  • variant: ‘primary’ | ‘secondary’ | ‘danger’ | ‘success’

  • ariaLabel: Descriptive label for screen readers

  • ariaPressed: Boolean state for toggle buttons

  • ariaExpanded: Boolean state for expandable buttons

  • disabled: Disabled state

  • loading: Shows loading spinner with announcement

AccessibleModal

Modal dialog with focus trap and keyboard navigation:

<AccessibleModal
  isOpen={showModal}
  title="Edit Expense"
  description="Update expense details"
  onClose={handleClose}
  size="md"
>
  <form>...</form>

  <svelte:fragment slot="footer">
    <AccessibleButton on:click={handleSave}>Save</AccessibleButton>
    <AccessibleButton variant="secondary" on:click={handleClose}>Cancel</AccessibleButton>
  </svelte:fragment>
</AccessibleModal>

Features:

  • Automatic focus trap

  • Escape key to close

  • Click outside to close

  • Screen reader announcements

  • Saves and restores previous focus

📚 Accessibility Utilities

Focus Management

import { trapFocus, FocusManager } from '../lib/accessibility';

// Trap focus within a container
const cleanup = trapFocus(containerElement);

// Save and restore focus
const manager = new FocusManager();
manager.save();
// ... do something ...
manager.restore();

Screen Reader Announcements

import { announce } from '../lib/accessibility';

// Polite announcement (doesn't interrupt)
announce('Changes saved successfully', 'polite');

// Assertive announcement (interrupts current reading)
announce('Error: Form validation failed', 'assertive');

Color Contrast Checking

import { meetsContrastRatio } from '../lib/accessibility';

const isAccessible = meetsContrastRatio('#10b981', '#ffffff', false);
// Returns true if contrast ratio meets WCAG AA standards

Keyboard List Navigation

import { handleListKeyboard } from '../lib/accessibility';

function onKeyDown(event: KeyboardEvent) {
  const newIndex = handleListKeyboard(
    event,
    items,
    currentIndex,
    (index) => selectItem(items[index])
  );
  currentIndex = newIndex;
}

🧪 Testing

Automated Testing (axe-core)

Run automated accessibility tests:

npm run test:a11y

Manual Testing Checklist

Keyboard Navigation

  • Can access all interactive elements with Tab

  • Can activate buttons with Enter/Space

  • Can navigate lists with Arrow keys

  • Can close modals with Escape

  • Focus indicators visible on all elements

Screen Reader Testing

  • Test with NVDA (Windows)

  • Test with VoiceOver (macOS/iOS)

  • All images have alt text

  • Forms have proper labels

  • Dynamic content announces changes

Visual Testing

  • Check color contrast with browser DevTools

  • Test with 200% text zoom

  • Test in high contrast mode

  • Verify no information by color alone

📋 WCAG 2.1 AA Compliance Checklist

Perceivable

  • 1.1.1 Non-text Content: All images have alt text

  • 1.3.1 Info and Relationships: Semantic HTML structure

  • 1.3.2 Meaningful Sequence: Logical reading order

  • 1.3.3 Sensory Characteristics: No instructions based on shape/location alone

  • 1.4.1 Use of Color: Information not conveyed by color alone

  • 1.4.3 Contrast (Minimum): 4.5:1 for normal text, 3:1 for large text

  • 1.4.4 Resize Text: Text can be resized to 200%

  • 1.4.10 Reflow: Content reflows at 320px width

  • 1.4.11 Non-text Contrast: 3:1 contrast for UI components

  • 1.4.12 Text Spacing: Text remains readable with increased spacing

  • 1.4.13 Content on Hover: Tooltips dismissable and persistent

Operable

  • 2.1.1 Keyboard: All functionality available via keyboard

  • 2.1.2 No Keyboard Trap: Focus can move away from all elements

  • 2.4.1 Bypass Blocks: Skip navigation links provided

  • 2.4.2 Page Titled: All pages have descriptive titles

  • 2.4.3 Focus Order: Logical focus order

  • 2.4.4 Link Purpose: Link text describes destination

  • 2.4.5 Multiple Ways: Multiple ways to find pages (navigation, search)

  • 2.4.6 Headings and Labels: Descriptive headings and labels

  • 2.4.7 Focus Visible: Visible focus indicators

Understandable

  • 3.1.1 Language of Page: HTML lang attribute set

  • 3.2.1 On Focus: No context changes on focus alone

  • 3.2.2 On Input: No context changes on input alone

  • 3.2.3 Consistent Navigation: Navigation consistent across pages

  • 3.2.4 Consistent Identification: Components identified consistently

  • 3.3.1 Error Identification: Form errors clearly identified

  • 3.3.2 Labels or Instructions: Form inputs have labels

  • 3.3.3 Error Suggestion: Error messages suggest corrections

  • 3.3.4 Error Prevention: Confirmations for critical actions

Robust

  • 4.1.1 Parsing: Valid HTML

  • 4.1.2 Name, Role, Value: All UI components have accessible names

  • 4.1.3 Status Messages: Status messages use ARIA live regions

🎨 Design System Accessibility

Color Palette

All colors meet WCAG AA contrast requirements:

Background

Text Color

Contrast Ratio

WCAG AA

#ffffff (White)

#1f2937 (Gray-900)

14.7:1

✅ Pass

#f9fafb (Gray-50)

#1f2937 (Gray-900)

13.8:1

✅ Pass

#10b981 (Primary)

#ffffff (White)

3.4:1

✅ Pass (Large Text)

#ef4444 (Danger)

#ffffff (White)

4.5:1

✅ Pass

Typography

  • Base Font Size: 16px (1rem)

  • Line Height: 1.5 (minimum for readability)

  • Font Family: System fonts for maximum compatibility

  • Letter Spacing: Normal (not too tight)

Interactive Elements

  • Minimum Touch Target: 44x44px (WCAG 2.5.5)

  • Focus Indicator: 2px solid ring, offset by 2px

  • Hover State: Visible color change

  • Active State: Distinct from hover

🚀 Best Practices for Developers

1. Use Semantic HTML

<!-- ✅ Good -->
<nav><ul><li><a href="...">Home</a></li></ul></nav>
<main><h1>Page Title</h1><p>Content...</p></main>
<footer>...</footer>

<!-- ❌ Bad -->
<div class="nav">...</div>
<div class="content">...</div>
<div class="footer">...</div>

2. Add ARIA Labels

<!-- ✅ Good -->
<button aria-label="Close dialog">
  <svg aria-hidden="true">...</svg>
</button>

<!-- ❌ Bad -->
<button><svg>...</svg></button>

3. Manage Focus

// ✅ Good - Focus management in modals
onMount(() => {
  if (isOpen) {
    cleanupFocusTrap = trapFocus(modalEl);
  }
});

onDestroy(() => {
  if (cleanupFocusTrap) {
    cleanupFocusTrap();
  }
});

4. Announce Dynamic Changes

// ✅ Good - Announce form submission
async function handleSubmit() {
  try {
    await saveData();
    announce('Changes saved successfully', 'polite');
  } catch (error) {
    announce('Error: Failed to save changes', 'assertive');
  }
}

5. Keyboard Event Handling

<!-- ✅ Good - Support both mouse and keyboard -->
<div
  role="button"
  tabindex="0"
  on:click={handleClick}
  on:keydown={(e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      handleClick();
    }
  }}
>
  Click me
</div>

📞 Reporting Accessibility Issues

If you encounter any accessibility barriers while using KoproGo, please report them:

  1. Email: accessibility@koprogo.com

  2. GitHub Issues: Label with accessibility

  3. Expected Response: Within 2 business days

📖 Resources

🔄 Continuous Improvement

This is a living document. We continuously improve our accessibility:

  • Monthly Audits: Automated axe-core scans

  • Quarterly Reviews: Manual testing with assistive technologies

  • User Feedback: Incorporating feedback from users with disabilities

  • Training: Regular accessibility training for all developers