Accessibility Standards

WCAG 2.1 AA Last updated: December 2025

Accessibility Overview

QR Igniter is committed to ensuring digital accessibility for people with disabilities. We aim to meet WCAG 2.1 Level AA compliance across all web and mobile interfaces.

Accessibility Target

All QR Igniter interfaces must meet WCAG 2.1 Level AA compliance. New features cannot be released without passing accessibility review.

Why Accessibility Matters

  • Legal Compliance: UK Equality Act 2010, EU Accessibility Act
  • User Inclusion: 15% of the world's population has some form of disability
  • Better UX: Accessible design improves usability for everyone
  • SEO Benefits: Many accessibility practices improve search rankings

WCAG 2.1 Compliance

The Four Principles (POUR)

Perceivable
Information and UI components must be presentable in ways users can perceive.
Operable
UI components and navigation must be operable by all users.
Understandable
Information and operation of the UI must be understandable.
Robust
Content must be robust enough to be interpreted by assistive technologies.

Level AA Requirements

Criterion Requirement Implementation
1.1.1 Non-text Content All images have alt text Alt attributes on all <img> elements
1.3.1 Info and Relationships Structure conveyed programmatically Semantic HTML, ARIA landmarks
1.4.3 Contrast (Minimum) 4.5:1 for normal text, 3:1 for large text Design system color palette verified
1.4.4 Resize Text Text resizable up to 200% Relative units (rem), fluid layouts
2.1.1 Keyboard All functionality keyboard accessible Focus management, keyboard handlers
2.4.4 Link Purpose Link purpose determinable from text Descriptive link text, aria-label where needed
2.4.7 Focus Visible Visible focus indicator Custom focus styles defined in CSS
3.1.1 Language of Page Default language defined lang attribute on <html>
4.1.2 Name, Role, Value Name and role of components exposed ARIA attributes, semantic elements

Web Accessibility

Semantic HTML

<!-- Good: Semantic structure -->
<header role="banner">
    <nav aria-label="Main navigation">
        <ul>
            <li><a href="/dashboard">Dashboard</a></li>
            <li><a href="/qr-codes">QR Codes</a></li>
        </ul>
    </nav>
</header>

<main role="main">
    <h1>Dashboard</h1>
    <section aria-labelledby="stats-heading">
        <h2 id="stats-heading">Statistics</h2>
        <!-- Content -->
    </section>
</main>

<!-- Bad: Non-semantic structure -->
<div class="header">
    <div class="nav">
        <span onclick="navigate('/dashboard')">Dashboard</span>
    </div>
</div>

Focus Management

/* Visible focus indicators */
:focus {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* Don't remove focus for keyboard users */
:focus:not(:focus-visible) {
    outline: none; /* Only for mouse users */
}

:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

Color Contrast

Color Pair Contrast Ratio Status
Primary (#F05A28) on White 4.58:1 Pass AA
Text (#1f2937) on White 14.07:1 Pass AAA
Muted (#6b7280) on White 5.35:1 Pass AA
Success (#22c55e) on White 3.02:1 Pass Large Text

Form Accessibility

<!-- Accessible form with labels and error handling -->
<form aria-labelledby="form-title">
    <h2 id="form-title">Create QR Code</h2>

    <div class="form-group">
        <label for="gtin">
            GTIN
            <span class="required" aria-label="required">*</span>
        </label>
        <input
            type="text"
            id="gtin"
            name="gtin"
            aria-describedby="gtin-help gtin-error"
            aria-required="true"
            aria-invalid="false"
        >
        <p id="gtin-help" class="help-text">
            Enter a 14-digit Global Trade Item Number
        </p>
        <p id="gtin-error" class="error-text" role="alert" hidden>
            Please enter a valid GTIN
        </p>
    </div>

    <button type="submit">Create QR Code</button>
</form>

ARIA Usage

<!-- Live regions for dynamic content -->
<div aria-live="polite" aria-atomic="true" class="status-message">
    QR code generated successfully
</div>

<!-- Modal dialog -->
<div
    role="dialog"
    aria-modal="true"
    aria-labelledby="dialog-title"
    aria-describedby="dialog-description"
>
    <h2 id="dialog-title">Confirm Delete</h2>
    <p id="dialog-description">
        Are you sure you want to delete this QR code?
    </p>
    <button type="button">Cancel</button>
    <button type="button">Delete</button>
</div>

Mobile Accessibility

Flutter Accessibility

// Semantic labels for widgets
Semantics(
  label: 'Scan QR Code',
  hint: 'Opens camera to scan a QR code',
  button: true,
  child: FloatingActionButton(
    onPressed: _openScanner,
    child: const Icon(Icons.qr_code_scanner),
  ),
)

// Exclude decorative elements from accessibility
ExcludeSemantics(
  child: Image.asset('assets/decorative_background.png'),
)

// Merge semantics for complex widgets
MergeSemantics(
  child: Row(
    children: [
      const Icon(Icons.check_circle, color: Colors.green),
      const Text('Scan successful'),
    ],
  ),
)

Touch Target Size

  • Minimum touch target: 48x48 dp (44x44 pt on iOS)
  • Adequate spacing between interactive elements
  • Large buttons for primary actions
// Ensure adequate touch target size
SizedBox(
  width: 48,
  height: 48,
  child: IconButton(
    icon: const Icon(Icons.settings),
    onPressed: _openSettings,
    tooltip: 'Settings',
  ),
)

Screen Reader Support

  • VoiceOver (iOS) fully supported
  • TalkBack (Android) fully supported
  • Logical reading order maintained
  • Focus order follows visual layout

Testing Tools

Automated Testing

Tool Type Integration
axe-core Browser extension / library CI/CD pipeline
Lighthouse Browser DevTools Automated audits
WAVE Browser extension Manual testing
Pa11y CLI tool CI/CD pipeline

CI/CD Integration

# .gitlab-ci.yml accessibility stage
accessibility:
  stage: test
  script:
    # Run Pa11y accessibility tests
    - pa11y-ci --config pa11y.config.js

    # Run Lighthouse accessibility audit
    - lhci autorun --collect.url="http://localhost:8080"
      --assert.preset="lighthouse:recommended"
      --assert.assertions.accessibility=0.9

  artifacts:
    paths:
      - accessibility-report/
    expire_in: 7 days

Manual Testing

  • Keyboard navigation: Tab through all interactive elements
  • Screen reader: Test with VoiceOver, NVDA, or TalkBack
  • Zoom: Test at 200% zoom level
  • Color blindness: Use simulation tools
  • Motion sensitivity: Test with reduced motion

Accessibility Checklist

Development Checklist

Category Check Required
Images All images have alt text Yes
Decorative images have empty alt="" Yes
Complex images have extended descriptions Yes
Forms All inputs have labels Yes
Error messages are associated with inputs Yes
Required fields are indicated Yes
Navigation All functionality keyboard accessible Yes
Focus visible at all times Yes
Skip links provided Yes
Content Heading hierarchy is logical (h1-h6) Yes
Link text is descriptive Yes
Color is not the only indicator Yes

Release Accessibility Sign-off

Before each release, the following must be completed:

  1. Automated accessibility tests pass (Lighthouse score > 90)
  2. Manual keyboard navigation test completed
  3. Screen reader testing with VoiceOver/TalkBack
  4. Color contrast verification
  5. Mobile accessibility testing
  6. Accessibility statement updated (if needed)