Accessibility Audit
Accessibility Audit
Project: Drop — Fintech Payment App
Version: 0.1.0
Date: 2026-02-23
Author: John (AI Director, ALAI)
Status: Draft
Reviewers: Alem Bašić (CEO)
Document History
| Version |
Date |
Author |
Changes |
| 0.1 |
2026-02-23 |
John |
Initial draft — code-review based (no automated tools run yet) |
1. Compliance Target
| Standard |
Level |
Notes |
| WCAG 2.1 |
AA |
Minimum required |
| WCAG 2.1 |
AAA |
Aspirational for payment flows (send money, QR payment) |
| EN 301 549 |
Applicable |
EU product |
| Section 508 |
No |
No US federal contract |
Audit date: 2026-02-23 (initial code review — automated testing TBD)
Auditor: John (AI Director)
Next scheduled audit: Before v1.0 production release
2. Audit Methodology
2.1 Automated Testing
- axe-core: Not yet integrated — planned for Storybook + CI via
@axe-core/playwright
- Lighthouse: Not yet run on production build — target ≥ 95
- pa11y: Not yet configured
2.2 Manual Testing
- Code review of all page components in
src/drop-app/src/app/
- No keyboard-only navigation testing performed yet
- No screen reader testing performed yet
- No 200% zoom testing performed yet
2.3 User Testing
- No — planned for Phase 2 before production launch.
Action: Run automated accessibility tools before v1.0 release. This document reflects code-review findings only.
3. Perceivable
1.1 Text Alternatives
| Criterion |
Status |
Notes |
Issue Ref |
| 1.1.1 Non-text Content (A) |
Partial |
Custom drop-icons use currentColor but lack aria-label / aria-hidden in several places. Lucide icons lack labels in some interactive contexts. |
A11Y-001 |
Checklist:
Issues:
- A11Y-001 (High): Dashboard notification bell and logout buttons use icon-only UI without
aria-label. Screen reader users cannot identify these controls.
| Criterion |
Status |
Notes |
Issue Ref |
| 1.2.1 Audio-only / Video-only (A) |
N/A |
No audio or video content in app |
|
| 1.2.2 Captions (A) |
N/A |
No video content |
|
| 1.2.3 Audio Description (A) |
N/A |
No video content |
|
| 1.2.4 Captions Live (AA) |
N/A |
No live media |
|
| 1.2.5 Audio Description Prerecorded (AA) |
N/A |
No video content |
|
1.3 Adaptable
| Criterion |
Status |
Notes |
Issue Ref |
| 1.3.1 Info and Relationships (A) |
Partial |
Shadcn/ui components use semantic HTML. Transaction amounts use color only (red/green) without non-color indicator. |
A11Y-002 |
| 1.3.2 Meaningful Sequence (A) |
Pass |
DOM order matches visual order for all pages |
|
| 1.3.3 Sensory Characteristics (A) |
Pass |
No "click the red button" instructions |
|
| 1.3.4 Orientation (AA) |
Pass |
No locked orientation |
|
| 1.3.5 Identify Input Purpose (AA) |
Partial |
Login/register inputs should use autocomplete attributes (email, given-name, etc.) |
A11Y-003 |
Issues:
- A11Y-002 (Medium): Transaction amounts use color (green/red) as the only differentiator for sent vs received. Needs
+ / - prefix (already present in transactions page) and screen reader-accessible labels.
- A11Y-003 (Low): Form inputs on
/login and /register lack autocomplete attributes for name, email, phone.
1.4 Distinguishable
| Criterion |
Status |
Notes |
Issue Ref |
| 1.4.1 Use of Color (A) |
Partial |
Transaction amounts: color used but +/- prefix also present. Tab active state: color only — lacks additional indicator. |
A11Y-004 |
| 1.4.2 Audio Control (A) |
N/A |
No auto-play audio |
|
| 1.4.3 Contrast Minimum (AA) |
Partial |
See contrast table in Section 9. Muted gray (#9CA3AF) fails WCAG AA for small text. |
A11Y-005 |
| 1.4.4 Resize Text (AA) |
Unknown |
Not tested at 200% zoom |
|
| 1.4.5 Images of Text (AA) |
Pass |
Real text used, not images |
|
| 1.4.10 Reflow (AA) |
Unknown |
Not tested at 320px viewport |
|
| 1.4.11 Non-text Contrast (AA) |
Partial |
Input borders (#E5E7EB on #F9FAFB) may be below 3:1 ratio |
A11Y-006 |
| 1.4.12 Text Spacing (AA) |
Unknown |
Not tested |
|
| 1.4.13 Content on Hover/Focus (AA) |
Pass |
No hover-only content |
|
Issues:
- A11Y-004 (Medium): BottomNav active tab indicator uses color only. Needs additional indicator (bold, underline, or icon change).
- A11Y-005 (High): Muted text color
#9CA3AF on white (#FFFFFF) = ~2.9:1 contrast ratio — fails WCAG AA 4.5:1. Used for timestamps, hints, inactive nav labels.
- A11Y-006 (Medium): Input border color
#E5E7EB on background #F9FAFB may not meet 3:1 for non-text contrast.
4. Operable
2.1 Keyboard Accessible
| Criterion |
Status |
Notes |
Issue Ref |
| 2.1.1 Keyboard (A) |
Unknown |
Not tested — shadcn/ui uses Radix (keyboard accessible), but custom components need verification |
|
| 2.1.2 No Keyboard Trap (A) |
Partial |
CookieConsent modal uses Dialog (Radix) — focus trap should work. Custom scan state machine may trap focus. |
A11Y-007 |
| 2.1.4 Character Key Shortcuts (A) |
Pass |
No single-key shortcuts implemented |
|
Checklist:
Issues:
- A11Y-007 (High): Multi-step flows (/register, /send, /scan) use state machines that may not be keyboard-accessible. Screen transitions between steps need verification.
2.2 Enough Time
| Criterion |
Status |
Notes |
| 2.2.1 Timing Adjustable (A) |
Pass |
No automatic session timeouts in current version (auth is httpOnly cookie, 7-day lifetime) |
| 2.2.2 Pause, Stop, Hide (A) |
Pass |
No auto-updating content in current version |
2.3 Seizures and Physical Reactions
| Criterion |
Status |
Notes |
| 2.3.1 Three Flashes (A) |
Pass |
No content flashes >3 times/sec. animate-pulse on scan page is slow (not flashing). |
2.4 Navigable
| Criterion |
Status |
Notes |
| 2.4.1 Bypass Blocks (A) |
Fail |
No skip-to-content link implemented |
| 2.4.2 Page Titled (A) |
Partial |
Root layout has "Drop" title. Individual pages do not set unique <title> tags. |
| 2.4.3 Focus Order (A) |
Unknown |
Not tested |
| 2.4.4 Link Purpose (A) |
Partial |
Navigation links are labeled. Some icon-only buttons lack context. |
| 2.4.6 Headings and Labels (AA) |
Partial |
Heading hierarchy not consistently used across pages |
| 2.4.7 Focus Visible (AA) |
Partial |
shadcn/ui inputs have focus:border-[#0B6E35] focus:ring-1. Custom buttons may lack visible focus ring. |
Issues:
- A11Y-008 (Medium): No skip-to-content link. Keyboard users must Tab through BottomNav on every page.
- A11Y-009 (Medium): Pages lack unique
<title> metadata. All pages inherit root "Drop" title.
- A11Y-010 (Medium): Custom buttons and interactive elements may lack visible focus indicators in some contexts.
5. Understandable
3.1 Readable
| Criterion |
Status |
Notes |
| 3.1.1 Language of Page (A) |
Fail |
<html lang> attribute not verified in root layout. Should be lang="nb" (Norwegian Bokmål). |
| 3.1.2 Language of Parts (AA) |
Pass |
App is Norwegian-only in Phase 1 |
Issues:
- A11Y-011 (High): Root layout
<html> element may not have correct lang="nb" attribute set. Must be verified.
3.2 Predictable
| Criterion |
Status |
Notes |
| 3.2.1 On Focus (A) |
Pass |
Focus does not trigger unexpected navigation |
| 3.2.2 On Input (A) |
Pass |
Toggle switches (notifications) update immediately — user expectation for toggles |
| 3.2.3 Consistent Navigation (AA) |
Pass |
BottomNav consistent across all authenticated pages |
| 3.2.4 Consistent Identification (AA) |
Pass |
Same icons used consistently across pages |
| Criterion |
Status |
Notes |
| 3.3.1 Error Identification (A) |
Partial |
Errors shown as red text. Not all error messages include field identification. |
| 3.3.2 Labels or Instructions (A) |
Pass |
Form labels present on login and register pages |
| 3.3.3 Error Suggestion (AA) |
Partial |
Some error messages suggest correction (e.g., "Passord må inneholde minst 8 tegn") |
| 3.3.4 Error Prevention (AA) |
Pass |
Send money and QR payment show confirmation step before transaction |
Issues:
- A11Y-012 (Medium): Error messages use color red only in some cases. Need to verify
aria-live or aria-describedby on error messages.
6. Robust
| Criterion |
Status |
Notes |
| 4.1.1 Parsing (A) |
Pass |
React generates valid HTML. No duplicate IDs via React's virtual DOM. |
| 4.1.2 Name, Role, Value (A) |
Partial |
shadcn/ui components (Dialog, Tabs) use correct ARIA. Custom components need audit. |
| 4.1.3 Status Messages (AA) |
Fail |
No ARIA live regions implemented for transaction success/error messages. |
Issues:
- A11Y-013 (High): Custom scan page states (scanning, payment, paying, success) lack ARIA roles and announcements.
- A11Y-014 (High): Transaction success/error messages not announced via
aria-live regions. Screen reader users won't hear payment confirmations.
7. Screen Reader Testing Matrix
| Screen Reader |
OS |
Browser |
Priority |
Status |
Tester |
Date |
| VoiceOver |
macOS 15 |
Safari |
HIGH |
Untested |
|
|
| VoiceOver |
iOS 18 |
Safari |
HIGH |
Untested |
|
|
| NVDA |
Windows 11 |
Chrome |
HIGH |
Untested |
|
|
| NVDA |
Windows 11 |
Firefox |
MEDIUM |
Untested |
|
|
| JAWS |
Windows 11 |
Chrome |
HIGH |
Untested |
|
|
| TalkBack |
Android 14 |
Chrome |
MEDIUM |
Untested |
|
|
Tested user flows:
8. Keyboard Navigation Map
| Context |
Key |
Action |
| Global |
Tab |
Next focusable element |
| Global |
Shift+Tab |
Previous focusable element |
| Global |
Enter |
Activate button, follow link |
| Global |
Space |
Activate button |
| BottomNav |
Tab |
Next nav item |
| BottomNav |
Enter |
Navigate to route |
| Dialog |
Tab |
Cycle within modal (focus trapped) |
| Dialog |
Escape |
Close dialog |
| Tabs (Transactions filter) |
Arrow keys |
Switch tab (Radix behavior) |
| Form multi-step |
Enter |
Submit / proceed to next step |
| Toggle switch |
Space |
Toggle on/off |
| Select dropdown |
Arrow Down/Up |
Navigate options |
| Select dropdown |
Enter |
Select option |
9. Color Contrast Verification Table
| Element |
Text Color |
Background |
Ratio |
AA Pass (4.5:1) |
AAA Pass (7:1) |
| Primary text |
#1A1A1A |
#FAFCF8 |
~18.6:1 |
Yes |
Yes |
| Secondary text |
#374151 |
#FAFCF8 |
~10.2:1 |
Yes |
Yes |
| Muted text |
#6B7280 |
#FAFCF8 |
~4.7:1 |
Yes |
No |
| Inactive nav |
#9CA3AF |
#FFFFFF |
~2.9:1 |
No |
No |
| Primary button text |
#FFFFFF |
#0B6E35 |
~5.0:1 |
Yes |
No |
| Link color |
#0B6E35 |
#FAFCF8 |
~5.0:1 |
Yes |
No |
| Success (amounts) |
#10B981 |
#FFFFFF |
~3.0:1 |
No (small text) |
No |
| Error text |
#EF4444 |
#FFFFFF |
~3.9:1 |
No (small text) |
No |
| Error text (large) |
#EF4444 |
#FFFFFF |
~3.9:1 |
Yes (large text ≥18px) |
No |
| Placeholder |
#6B7280 |
#F9FAFB |
~4.5:1 |
Borderline |
No |
Critical failures:
#9CA3AF on white: 2.9:1 — fails AA. Used for inactive nav labels and muted hints.
#10B981 on white: 3.0:1 — fails AA for small text. Used for positive transaction amounts.
#EF4444 on white: 3.9:1 — fails AA for small text (14px). Used for error messages and negative amounts.
10. ARIA Usage Audit
| Pattern |
ARIA Used |
Correct? |
Notes |
| Modal dialog (CookieConsent) |
role="dialog" via Radix |
Yes |
Radix Dialog handles correctly |
| BottomNav navigation |
role="navigation" — not verified |
Unknown |
Should have aria-label="Navigasjon" |
| Alert / error messages |
role="alert" — NOT implemented |
No |
Error messages need live region |
| Tab panel (Transactions) |
Radix Tabs — correct roles |
Yes |
Radix Tabs handles tablist/tab/tabpanel |
| Custom scan states |
No ARIA roles |
No |
Needs aria-live for state transitions |
| Loading states |
No aria-busy |
No |
Skeleton and loading text lack ARIA |
| Toggle switches (settings) |
Unknown — needs review |
Unknown |
Should use role="switch" or aria-checked |
| Issue |
WCAG Criterion |
Severity |
Priority |
Assigned To |
Target Date |
Status |
| A11Y-001: Icon buttons lack aria-label |
1.1.1 |
High |
P1 |
Frontend dev |
Pre-v1.0 |
Open |
| A11Y-005: Muted text #9CA3AF fails contrast |
1.4.3 |
High |
P1 |
Frontend dev |
Pre-v1.0 |
Open |
| A11Y-007: Keyboard access in multi-step flows |
2.1.1 |
High |
P1 |
Frontend dev |
Pre-v1.0 |
Open |
| A11Y-011: Missing lang="nb" on html element |
3.1.1 |
High |
P1 |
Frontend dev |
Immediate |
Open |
| A11Y-013: Scan page states lack ARIA |
4.1.2 |
High |
P1 |
Frontend dev |
Pre-v1.0 |
Open |
| A11Y-014: No aria-live for transaction results |
4.1.3 |
High |
P1 |
Frontend dev |
Pre-v1.0 |
Open |
| A11Y-002: Color-only transaction indicators |
1.3.1 |
Medium |
P2 |
Frontend dev |
Pre-v1.0 |
Partial (has +/- prefix) |
| A11Y-004: BottomNav active state color-only |
1.4.1 |
Medium |
P2 |
Frontend dev |
Pre-v1.0 |
Open |
| A11Y-008: No skip-to-content link |
2.4.1 |
Medium |
P2 |
Frontend dev |
Pre-v1.0 |
Open |
| A11Y-009: No unique page titles |
2.4.2 |
Medium |
P2 |
Frontend dev |
Pre-v1.0 |
Open |
| A11Y-010: Focus visibility inconsistent |
2.4.7 |
Medium |
P2 |
Frontend dev |
Pre-v1.0 |
Open |
| A11Y-012: Error messages lack aria-describedby |
3.3.1 |
Medium |
P2 |
Frontend dev |
Pre-v1.0 |
Open |
| A11Y-003: Missing autocomplete attributes |
1.3.5 |
Low |
P3 |
Frontend dev |
Post-v1.0 |
Open |
| A11Y-006: Input border contrast |
1.4.11 |
Low |
P3 |
Frontend dev |
Post-v1.0 |
Open |
Severity definitions:
- Critical: Blocks users with disabilities from core functionality
- High: Significant barrier, workaround exists
- Medium: Inconvenience, does not block task completion
- Low: Minor enhancement opportunity
Approval
| Role |
Name |
Date |
Signature |
| Author |
John (AI Director) |
2026-02-23 |
|
| Accessibility Lead |
|
|
|
| QA Lead |
|
|
|
| Product Owner |
|
|
|