Accessibility Audit
Accessibility Audit
Project: {{PROJECT_NAME}}Drop — Fintech Payment App
Version: {{VERSION}}0.1.0
Date: {{DATE}}2026-02-23
Author: {{AUTHOR}}John (AI Director, ALAI)
Status: Draft
| In Review | Approved
Reviewers: {{REVIEWERS}}Alem Bašić (CEO)
Document History
| Version |
Date |
Author |
Changes |
| 0.1 |
{{DATE}}2026-02-23 |
{{AUTHOR}}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 |
TargetAspirational for criticalpayment flows (checkout,send forms)money, QR payment) |
| EN 301 549 |
Applicable |
If serving EU public sectorproduct |
| Section 508 |
{{Yes/No}}No |
IfNo US federal contract |
Audit date: {{DATE}}2026-02-23 (initial code review — automated testing TBD)
Auditor: {{NAME}}John (AI Director)
Next scheduled audit: {{DATE}}Before v1.0 production release
2. Audit Methodology
2.1 Automated Testing
- axe-core:
IntegratedNot inyet integrated — planned for Storybook + CI (via @axe-core/playwright)
- Lighthouse:
AccessibilityNot scoreyet trackedrun inon CIproduction (target:build — target ≥ 95)95
- pa11y:
RunNot onyet full page URLs in staging environmentconfigured
2.2 Manual Testing
Keyboard-Code review of all page components in src/drop-app/src/app/
- No keyboard-only navigation
ontesting allperformed critical user flowsyet
ScreenNo screen reader testing (seeperformed matrix in Section 6)yet
Zoom test:No 200% browser zoom —testing noperformed loss of content or functionality
Reflow test: 320px viewport widthyet
2.3 User Testing
{{Yes — included users with disabilities in user testing | No — planned for future}}:Phase {{Notes}}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) |
{{Pass}}Partial |
AllCustom <img> have alt; decorative imagesdrop-icons use alt=""currentColor but lack aria-label / aria-hidden in several places. Lucide icons lack labels in some interactive contexts. |
A11Y-001 |
Checklist:
TODO:Issues:
- A11Y-001 (High):
RunDashboard axenotification bell and listlogout allbuttons violations.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) |
{{Pass/Fail/N/A}}A |
No audio or video content in app |
|
| 1.2.2 Captions (A) |
{{Pass/Fail/N/A}}A |
No video content |
|
| 1.2.3 Audio Description (A) |
{{Pass/Fail/N/A}}A |
No video content |
|
| 1.2.4 Captions Live (AA) |
{{Pass/Fail/N/A}}A |
No live media |
|
| 1.2.5 Audio Description Prerecorded (AA) |
{{Pass/Fail/N/A}}A |
No video content |
|
TODO: Audit all video/audio content on the platform.
1.3 Adaptable
| Criterion |
Status |
Notes |
Issue Ref |
| 1.3.1 Info and Relationships (A) |
{{Pass/Fail}}Partial |
SemanticShadcn/ui HTMLcomponents foruse tables,semantic lists,HTML. headingsTransaction amounts use color only (red/green) without non-color indicator. |
A11Y-002 |
| 1.3.2 Meaningful Sequence (A) |
{{Pass/Fail}}Pass |
DOM order matches visual order for all pages |
|
| 1.3.3 Sensory Characteristics (A) |
{{Pass/Fail}}Pass |
No "click the red button" instructions |
|
| 1.3.4 Orientation (AA) |
{{Pass/Fail}}Pass |
No locked orientation |
|
| 1.3.5 Identify Input Purpose (AA) |
{{Pass/Fail}}Partial |
Login/register inputs should use autocomplete onattributes all(email, personalgiven-name, data fieldsetc.) |
A11Y-003 |
Checklist:Issues:
1.4 Distinguishable
| Criterion |
Status |
Notes |
Issue Ref |
| 1.4.1 Use of Color (A) |
{{Pass/Fail}}Partial |
ColorTransaction notamounts: solecolor differentiatorused but +/- prefix also present. Tab active state: color only — lacks additional indicator. |
A11Y-004 |
| 1.4.2 Audio Control (A) |
{{Pass/Fail}}N/A |
Auto-No auto-play audio has stop mechanism |
|
| 1.4.3 Contrast Minimum (AA) |
{{Pass/Fail}}Partial |
See contrast table belowin Section 9. Muted gray (#9CA3AF) fails WCAG AA for small text. |
A11Y-005 |
| 1.4.4 Resize Text (AA) |
{{Pass/Fail}}Unknown |
Not tested at 200% zoom — no content loss |
|
| 1.4.5 Images of Text (AA) |
{{Pass/Fail}}Pass |
Real text used, not images |
|
| 1.4.10 Reflow (AA) |
{{Pass/Fail}}Unknown |
Not tested at 320px width — no horizontal scrollviewport |
|
| 1.4.11 Non-text Contrast (AA) |
{{Pass/Fail}}Partial |
UIInput componentsborders (#E5E7EB on #F9FAFB) may be below 3:1 ratio |
A11Y-006 |
| 1.4.12 Text Spacing (AA) |
{{Pass/Fail}}Unknown |
CustomNot spacing doesn't break layouttested |
|
| 1.4.13 Content on Hover/Focus (AA) |
{{Pass/Fail}}Pass |
TooltipsNo canhover-only be hovered/dismissedcontent |
|
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) |
{{Pass/Fail}}Unknown |
AllNot functionalitytested keyboard-operable— shadcn/ui uses Radix (keyboard accessible), but custom components need verification |
|
| 2.1.2 No Keyboard Trap (A) |
{{Pass/Fail}}Partial |
FocusCookieConsent canmodal alwaysuses beDialog moved(Radix) away— focus trap should work. Custom scan state machine may trap focus. |
A11Y-007 |
| 2.1.4 Character Key Shortcuts (A) |
{{Pass/Fail}}Pass |
Single-No single-key shortcuts can be disabledimplemented |
|
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/Fail}}Pass |
SessionNo timeoutautomatic warnssession withtimeouts optionin tocurrent extendversion (auth is httpOnly cookie, 7-day lifetime) |
| 2.2.2 Pause, Stop, Hide (A) |
{{Pass/Fail}}Pass |
Auto-No auto-updating content hasin pausecurrent controlversion |
2.3 Seizures and Physical Reactions
| Criterion |
Status |
Notes |
| 2.3.1 Three Flashes (A) |
{{Pass/Fail}}Pass |
No content flashes >3 times/secsec. animate-pulse on scan page is slow (not flashing). |
2.4 Navigable
| Criterion |
Status |
Notes |
| 2.4.1 Bypass Blocks (A) |
{{Pass/Fail}}Fail |
Skip-No skip-to-content link implemented |
| 2.4.2 Page Titled (A) |
{{Pass/Fail}}Partial |
UniqueRoot descriptivelayout pagehas titles"Drop" title. Individual pages do not set unique <title> tags. |
| 2.4.3 Focus Order (A) |
{{Pass/Fail}}Unknown |
FocusNot order matches reading ordertested |
| 2.4.4 Link Purpose (A) |
{{Pass/Fail}}Partial |
LinkNavigation textlinks descriptiveare inlabeled. contextSome icon-only buttons lack context. |
| 2.4.6 Headings and Labels (AA) |
{{Pass/Fail}}Partial |
DescriptiveHeading headinghierarchy andnot labelconsistently textused across pages |
| 2.4.7 Focus Visible (AA) |
{{Pass/Fail}}Partial |
Focusshadcn/ui indicatorinputs have focus:border-[#0B6E35] focus:ring-1. Custom buttons may lack visible onfocus all elementsring. |
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) |
{{Pass/Fail}}Fail |
<html lang="en"lang> setattribute not verified in root layout. Should be lang="nb" (Norwegian Bokmål). |
| 3.1.2 Language of Parts (AA) |
{{Pass/Fail}}Pass |
langApp onis foreignNorwegian-only languagein sectionsPhase 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/Fail}}Pass |
Focus does not trigger unexpected changenavigation |
| 3.2.2 On Input (A) |
{{Pass/Fail}}Pass |
InputToggle changeswitches doesn't(notifications) auto-submitupdate formimmediately — user expectation for toggles |
| 3.2.3 Consistent Navigation (AA) |
{{Pass/Fail}}Pass |
NavBottomNav consistent across all authenticated pages |
| 3.2.4 Consistent Identification (AA) |
{{Pass/Fail}}Pass |
Same functionicons =used sameconsistently labelacross pages |
| Criterion |
Status |
Notes |
| 3.3.1 Error Identification (A) |
{{Pass/Fail}}Partial |
Errors identifiedshown inas text,red nottext. justNot colorall error messages include field identification. |
| 3.3.2 Labels or Instructions (A) |
{{Pass/Fail}}Pass |
All fields haveForm labels present on login and register pages |
| 3.3.3 Error Suggestion (AA) |
{{Pass/Fail}}Partial |
ErrorSome error messages suggest correction (e.g., "Passord må inneholde minst 8 tegn") |
| 3.3.4 Error Prevention (AA) |
{{Pass/Fail}}Pass |
DestructiveSend actionsmoney requireand 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/Fail}}Pass |
ValidReact HTML,generates novalid HTML. No duplicate IDs via React's virtual DOM. |
| 4.1.2 Name, Role, Value (A) |
{{Pass/Fail}}Partial |
All UIshadcn/ui components have(Dialog, accessibleTabs) nameuse +correct roleARIA. Custom components need audit. |
| 4.1.3 Status Messages (AA) |
{{Pass/Fail}}Fail |
Status messages announced viaNo 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 1415 |
Safari |
HIGH |
{{Pass/Fail/Untested}}Untested |
|
|
| VoiceOver |
iOS 1718 |
Safari |
HIGH |
{{Pass/Fail/Untested}}Untested |
|
|
| NVDA |
Windows 11 |
Chrome |
HIGH |
{{Pass/Fail/Untested}}Untested |
|
|
| NVDA |
Windows 11 |
Firefox |
MEDIUM |
{{Pass/Fail/Untested}}Untested |
|
|
| JAWS |
Windows 11 |
Chrome |
HIGH |
{{Pass/Fail/Untested}}Untested |
|
|
| TalkBack |
Android 14 |
Chrome |
MEDIUM |
{{Pass/Fail/Untested}}Untested |
|
|
Tested user flows:
8. Keyboard Navigation Map
| Context |
Key |
Action |
| Global |
Tab |
Next focusable element |
| Global |
Shift+Tab |
Previous focusable element |
| Global |
EscapeEnter |
CloseActivate modal,button, dropdown,follow toastlink |
| Global |
Alt+SSpace |
SkipActivate to main content (custom shortcut)button |
DropdownBottomNav |
Arrow Down/UpTab |
NavigateNext optionsnav item |
DropdownBottomNav |
Enter/SpaceEnter |
SelectNavigate optionto route |
Table |
Arrow keys |
Navigate cells (if grid role) |
ModalDialog |
Tab |
Cycle within modal (focus trapped) |
Date pickerDialog |
Arrow keysEscape |
NavigateClose daysdialog |
| 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) |
BodyPrimary text |
#0F172A#1A1A1A |
#FFFFFF#FAFCF8 |
{{X:1}}~18.6:1 |
{{Y/N}}Yes |
{{Y/N}}Yes |
| Secondary text |
#334155#374151 |
#FAFCF8 |
~10.2:1 |
Yes |
Yes |
| Muted text |
#6B7280 |
#FAFCF8 |
~4.7:1 |
Yes |
No |
| Inactive nav |
#9CA3AF |
#FFFFFF |
{{X:1}}~2.9:1 |
{{Y/N}}No |
{{Y/N}} |
Disabled text |
#94A3B8 |
#FFFFFF |
{{X:1}} |
{{Y/N}} |
{{Y/N}}No |
| Primary button text |
#FFFFFF |
#0EA5E9#0B6E35 |
{{X:1}}~5.0:1 |
{{Y/N}}Yes |
{{Y/N}}No |
| Link color |
#0284C7#0B6E35 |
#FAFCF8 |
~5.0:1 |
Yes |
No |
| Success (amounts) |
#10B981 |
#FFFFFF |
{{X:1}}~3.0:1 |
{{Y/N}}No (small text) |
{{Y/N}}No |
| Error text |
#EF4444 |
#FFFFFF |
~3.9:1 |
No (small text) |
No |
| Error text (large) |
{{X:1}}#EF4444 |
{{Y/N}}#FFFFFF |
{{Y/N}}~3.9:1 |
Yes (large text ≥18px) |
No |
| Placeholder |
#94A3B8#6B7280 |
#FFFFFF#F9FAFB |
{{X:1}}~4.5:1 |
{{Y/N}}Borderline |
{{Y/N}}No |
TODO:Critical failures:
Measure
all#9CA3AF valueson usingwhite: 2.9:1 — fails AA. Used for inactive nav labels and muted hints.
{{Colour#10B981 Contraston Analyserwhite: |3.0:1 axe— DevTools}}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", aria-modal="true",via aria-labelledbyRadix |
{{Yes/No}}Yes |
Radix Dialog handles correctly |
NavigationBottomNav navigation |
role="navigation", — not verified |
Unknown |
Should have aria-labellabel="Navigasjon" |
{{Yes/No}} |
|
Alert/toastAlert / error messages |
role="alert" or— aria-live="polite"NOT implemented |
{{Yes/No}}No |
Error messages need live region |
| Tab panel (Transactions) |
role="tablist",Radix role="tab",Tabs role="tabpanel"— correct roles |
{{Yes/No}}Yes |
| Radix
Tabs handles Accordion |
aria-expanded, aria-controls |
{{Yes/No}} |
tablist/tab/tabpanel |
Custom selectscan states |
role="listbox",No role="option",ARIA roles |
No |
Needs aria-selectedlive | {{Yes/No}} | for state transitions |
| Loading states |
No aria-busy="true", live region |
{{Yes/No}}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 |
{{IssueA11Y-001: description}}Icon buttons lack aria-label |
{{1.4.3}}1.1 |
{{Critical/High/Medium/Low}}High |
P1 |
{{NAME}}Frontend dev |
{{DATE}}Pre-v1.0 |
{{Open/InOpen |
Progress/Fixed}}
| 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 |
|
|
|