Skip to content

fix: WCAG 2.1 AA accessibility audit — contrast fixes, ARIA labels, keyboard nav, mobile a11y & ACCESSIBILITY.md#388

Open
Itzzavdheshh wants to merge 6 commits into
Dev-Card:mainfrom
Itzzavdheshh:de
Open

fix: WCAG 2.1 AA accessibility audit — contrast fixes, ARIA labels, keyboard nav, mobile a11y & ACCESSIBILITY.md#388
Itzzavdheshh wants to merge 6 commits into
Dev-Card:mainfrom
Itzzavdheshh:de

Conversation

@Itzzavdheshh
Copy link
Copy Markdown
Contributor

Summary

Conducts a complete WCAG 2.1 AA accessibility audit across apps/web and apps/mobile as specified in Product Doc Section 5.11 — zero prior accessibility work existed. Fixes all AA-level color contrast failures, adds missing ARIA labels on icon-only buttons, strengthens invisible focus rings, adds skip navigation, completes all mobile accessibilityLabel/accessibilityRole/accessibilityHint attributes, describes QR codes for screen readers, extends font size tokens to prevent inline hardcoding, and documents everything in a new ACCESSIBILITY.md. Also fixes a pre-existing duplicate import bug in SettingsScreen.tsx.

Closes #39


Type of Change

  • Bug fix — contrast failures, invisible focus rings, duplicate import
  • New feature
  • Refactor (no functional change)
  • UI / Design change
  • Tests only
  • Documentation — ACCESSIBILITY.md
  • Infrastructure / DevOps
  • Security

What Changed

apps/web/src/app.css

  • --text-muted light: #64748b#536071 — passes 4.5:1 on white
  • --text-secondary light: #475569#334155 — passes 4.5:1
  • .copy-message.error: #ef4444#dc2626 — passes 4.5:1 on white
  • All focus-visible outlines: near-invisible rgba(..., 0.18–0.35) → solid 3px solid #6366f1
  • .role-badge, .card-footer, .username on dark card — contrast verified ≥ 4.5:1

apps/web/src/routes/+layout.svelte

  • Visually-hidden skip-to-main-content link added — appears on keyboard focus, href="#main-content"

apps/web/src/routes/+page.svelte (homepage)

  • Theme toggle: aria-label="Toggle dark mode" — was emoji-only 🌙/☀️
  • <main id="main-content"> + <nav aria-label="Main navigation"> added

apps/web/src/routes/u/[username]/+page.svelte

  • Links section: <section aria-label="Platform connections"> added
  • "Copy Link" button: aria-label added; aria-live="polite" region confirmed

apps/web/src/routes/devcard/[id]/+page.svelte

  • Platform buttons: aria-label="{platform} — {username}, opens in new tab" on each
  • <section aria-label="Platform Connections"> + <main> wrapper added

apps/mobile/src/screens/DevCardViewScreen.tsx

  • Close button, platform tiles, error back button — accessibilityLabel + accessibilityRole="button" + accessibilityHint added
  • QR code: accessible={true} + accessibilityRole="image" + descriptive label

apps/mobile/src/screens/HomeScreen.tsx

  • QR toggle, Share, Analytics, Preview, Search Go — all get accessibilityLabel + accessibilityRole="button"
  • QR toggle: accessibilityHint="Double-tap to toggle QR code visibility"
  • TextInput: accessibilityLabel="Enter DevCard username"

apps/mobile/src/screens/ScanScreen.tsx

  • Switch Card, Go button, QR container, TextInput — all accessibility attributes added
  • Switch Card: accessibilityHint="Opens a list of your DevCards to choose which to display"

apps/mobile/src/screens/SettingsScreen.tsx

  • Connected Platforms, Save Changes, Log Out, all TextInputs — accessibilityLabel + accessibilityRole added
  • Pre-existing bug fixed: duplicate import { useNavigation } on lines 14 and 19 removed

apps/mobile/src/theme/tokens.ts

  • FONT_SIZE.micro = 10 and FONT_SIZE.nano = 8 added — inline fontSize: 8/9/10 now use tokens
  • Comment added: allowFontScaling={false} must never be used — dynamic type scaling preserved

ACCESSIBILITY.md (NEW)

  • Full audit grouped by page and severity with resolution status
  • Screen reader navigation walkthrough for "View a DevCard" flow
  • Future contributor guidelines for contrast, ARIA, and mobile a11y

How to Test

# Web
cd apps/web && pnpm dev
# Install axe DevTools browser extension → run on /, /u/[username], /devcard/[id]

# Typecheck
pnpm -r run typecheck

# Lint
pnpm -r run lint

# Mobile
cd apps/mobile && pnpm start
  1. Press Tab on homepage → verify skip link appears → press Enter → focus jumps to #main-content
  2. Tab to theme toggle → verify aria-label="Toggle dark mode" announced by screen reader
  3. Tab through all web pages → verify solid 3px focus ring visible on every interactive element
  4. Run axe DevTools on /, /u/[username], /devcard/[id] → verify zero AA violations
  5. Open /u/[username] → click "Copy Link" → verify aria-live region announces success
  6. Open /devcard/[id] → tab to platform buttons → verify label reads "{platform} — {username}, opens in new tab"
  7. On mobile: navigate DevCardViewScreen with TalkBack/VoiceOver → verify all buttons announced with label + role
  8. On mobile: verify QR code announced as "image" with descriptive label
  9. On mobile: increase system font size to 150% → verify all text scales correctly (no clipping)
  10. Run pnpm -r run typecheck → verify zero errors after duplicate import fix

Checklist

  • My code follows the project's coding style (pnpm -r run lint passes)
  • TypeScript compiles without errors (pnpm -r run typecheck)
  • I have added or updated tests for the changes I made
  • All tests pass locally (pnpm -r run test)
  • I have updated documentation where necessary — ACCESSIBILITY.md created
  • No new console.log or debug statements left in the code
  • Breaking changes are documented in this PR description
  • Zero AA-level axe violations on all 3 web routes
  • All focus-visible outlines visible — solid 3px solid #6366f1
  • All color contrast ratios ≥ 4.5:1 — verified via WebAIM Contrast Checker
  • Skip-to-main-content link functional on keyboard navigation
  • All icon-only buttons have aria-label
  • All mobile TouchableOpacity/Pressable have accessibilityLabel + accessibilityRole
  • accessibilityHint added for non-obvious mobile controls
  • QR code elements described with accessibilityRole="image" on web and mobile
  • allowFontScaling={false} not introduced — dynamic type preserved
  • Duplicate useNavigation import in SettingsScreen.tsx fixed

Screenshots / Recordings

1. Before — near-invisible focus ring
image


2. After — solid 3px focus ring
image

2. axe DevTools — zero AA violations on /
image


3. axe DevTools — zero AA violations on /u/[username]
image


4. Skip link visible on first Tab keypress
image



Additional Context

  • Why #536071 not #475569 for --text-muted? The fix targets the minimum ratio (4.5:1) while staying visually close to the original. #475569 would also pass but is harder to distinguish from --text-secondary — using #536071 keeps the two tokens visually separated.
  • Screen reader device testing: TalkBack/VoiceOver testing requires a physical Android/iOS device. Code correctness is verified; expected screen reader behavior is documented in ACCESSIBILITY.md. This is the standard approach for code-level a11y reviews without a device.
  • @axe-core/playwright CI: Not added in this PR to keep scope focused. ACCESSIBILITY.md documents the integration path for a follow-up PR.
  • socket.io-client and large asset optimization are tracked as separate issues — out of scope here.
  • Future work: Add @axe-core/playwright automated CI check; audit any new pages added after this PR against the guidelines in ACCESSIBILITY.md.

Submitted as part of Open Source Contribution — GSSoC (GirlScript Summer of Code)

@Itzzavdheshh
Copy link
Copy Markdown
Contributor Author


🙌 Contribution Note

Hi ** @ShantKhatri ** 👋

  • Zero AA violations — all 8 contrast failures fixed; axe DevTools clean on all 3 web routes
  • Keyboard complete — solid focus rings, skip link, logical tab order
  • Mobile complete — every TouchableOpacity/Pressable across 4 screens has full a11y attributes
  • QR describedaccessibilityRole="image" + labels on web and mobile
  • Dynamic type preserved — token system extended; no allowFontScaling={false}
  • Pre-existing bug fixed — duplicate import in SettingsScreen.tsx
  • ACCESSIBILITY.md — full audit, resolutions, screen reader walkthrough

Happy to address any feedback! 🚀


Comment thread ACCESSIBILITY.md Outdated
Comment thread apps/web/playwright.config.ts Outdated
Signed-off-by: AVDHESH KUMAR DADHICH <aavdhesh.dadhich@gmail.com>
Signed-off-by: AVDHESH KUMAR DADHICH <aavdhesh.dadhich@gmail.com>
Signed-off-by: AVDHESH KUMAR DADHICH <aavdhesh.dadhich@gmail.com>
@Itzzavdheshh
Copy link
Copy Markdown
Contributor Author

Hi @ShantKhatri please review again sir

@Itzzavdheshh Itzzavdheshh requested a review from ShantKhatri May 29, 2026 19:41
@Harxhit Harxhit added the gssoc:approved Required label for every approved PR. Gives the base +50 points and enables contribution tracking. label May 30, 2026
Signed-off-by: AVDHESH KUMAR DADHICH <aavdhesh.dadhich@gmail.com>
@ShantKhatri
Copy link
Copy Markdown
Contributor

Hi @Itzzavdheshh, will not be using Expo for any functionality, so please do not include that in the devcard.

@Itzzavdheshh
Copy link
Copy Markdown
Contributor Author

Itzzavdheshh commented May 30, 2026

Hi @Itzzavdheshh, will not be using Expo for any functionality, so please do not include that in the devcard.


Hi @ShantKhatri! Understood on not using Expo for product features.

Right now, Expo is only used as a developer helper in the background to dynamically resolve local IP addresses for testing the app locally. We aren't using any of the main Expo SDK features.

Would you still like me to completely remove these helper packages and rewrite the configurations, or is it fine to keep them just for local development convenience?


@ShantKhatri
Copy link
Copy Markdown
Contributor

Hi @Itzzavdheshh, will not be using Expo for any functionality, so please do not include that in the devcard.

Hi @ShantKhatri! Understood on not using Expo for product features.
Right now, Expo is only used as a developer helper in the background to dynamically resolve local IP addresses for testing the app locally. We aren't using any of the main Expo SDK features.
Would you still like me to completely remove these helper packages and rewrite the configurations, or is it fine to keep them just for local development convenience?

We need to remove the expo completely and rewrite the configuration. As that breaks our functionalities or makes the configuration break when we try to deal with it natively. Thanks!

@Itzzavdheshh
Copy link
Copy Markdown
Contributor Author

HI @ShantKhatri please check , if there still any more feedback please tell me , Happy to address them too..........also please checkout my other PRs #365 & #364

@ShantKhatri
Copy link
Copy Markdown
Contributor

HI @ShantKhatri please check , if there still any more feedback please tell me , Happy to address them too..........also please checkout my other PRs #365 & #364

Allow 2-3 days atleast, as I'll be pushing big real factoring for mobile side,so please allow some time.

@Itzzavdheshh
Copy link
Copy Markdown
Contributor Author

@ShantKhatri what about this one .....

@ShantKhatri
Copy link
Copy Markdown
Contributor

Allow some time, need some refactoring here too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved Required label for every approved PR. Gives the base +50 points and enables contribution tracking.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mobile + web: implement end-to-end Accessibility audit and WCAG 2.1 AA compliance

4 participants