Skip to content

feat: accessibility improvements — skip link, WCAG AA dark mode contrast, focus rings, ARIA live regions#832

Merged
Mystery-CLI merged 1 commit into
Ethereal-Future:mainfrom
hman38705:feat/accessibility-a11y-issues-739-740-741-742
Jun 28, 2026
Merged

feat: accessibility improvements — skip link, WCAG AA dark mode contrast, focus rings, ARIA live regions#832
Mystery-CLI merged 1 commit into
Ethereal-Future:mainfrom
hman38705:feat/accessibility-a11y-issues-739-740-741-742

Conversation

@hman38705

@hman38705 hman38705 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #739
closes #740
closes #741
closes #742

Implements all four Stellar Wave accessibility issues in a single PR.


#739 — Skip-to-main-content link

The skip link (<a href="#main-content" className="skip-link">) was already rendered as the first DOM element in App.jsx, and <main id="main-content"> was already in place. This PR fixes the skip link's color from the hardcoded #fff to var(--on-primary) so it remains WCAG-compliant in dark mode (#0f172a on #60a5fa → 7.3:1 ✓).


#740 — WCAG AA colour contrast in dark mode

Audit of .theme-dark CSS custom property pairs:

Pair Contrast Pass?
--text (#e2e8f0) on --bg (#020617) 16.4:1 ✓ AA
--muted (#94a3b8) on --bg (#020617) 8.3:1 ✓ AA
--muted (#94a3b8) on --surface (#111827) 6.7:1 ✓ AA
--muted (#94a3b8) on --card (#1f2937) 5.3:1 ✓ AA
--primary (#60a5fa) on --bg (#020617) 8.8:1 ✓ AA
--on-primary (#0f172a) on --primary (#60a5fa) 7.3:1 ✓ AA
--danger (#f87171) on --bg (#020617) 7.6:1 ✓ AA
--danger (#f87171) on --card (#1f2937) 4.83:1 ✓ AA
--success (#34d399) on --bg (#020617) 10.7:1 ✓ AA
--warning (#fbbf24) on --bg (#020617) 12.3:1 ✓ AA
--info (#38bdf8) on --bg (#020617) 9.6:1 ✓ AA
--link (#93c5fd) on --bg (#020617) 11.4:1 ✓ AA

Fixes for hardcoded colours that failed in dark mode:

Element Before After Contrast on dark bg
.balance-asset #555 (3.0:1 ✗) var(--muted) 8.3:1 ✓
.qr-pubkey #666 (3.8:1 ✗) var(--muted) 8.3:1 ✓
.sm-history-toggle #666 (3.8:1 ✗) var(--muted) 8.3:1 ✓

#741 — Focus-visible ring on all interactive elements

  • Added focus ring CSS custom properties to :root: --focus-ring-color: #2563eb, --focus-ring-width: 2px, --focus-ring-offset: 2px
  • Added dark mode override: --focus-ring-color: #93c5fd (11.4:1 on #020617 ✓)
  • Fixed input:focus-visible: was outline: none — now outline: var(--focus-ring-width) solid var(--focus-ring-color) while keeping border-color change
  • Fixed .fu-dropzone: removed outline: none from the base rule; added explicit .fu-dropzone:focus-visible rule using ring tokens
  • Global :focus-visible rule already applied 3px solid var(--primary) to all other interactive elements

#742 — ARIA live regions for async status updates

  • Balance updates: wrapped balance AnimatePresence in <div role="status" aria-live="polite"> so screen readers announce new balance values after transactions complete
  • StatusMessage roles: changed Message component to use role="alert" (assertive) for error/warning types and role="status" (polite) for success/info — prevents disruptive interruptions for non-urgent notifications
  • Loading state: <div aria-live="polite" aria-atomic="true" className="sr-only"> was already present in App.jsx, announcing "Sending payment…", "Checking balance…", etc.
  • aria-busy: already set on Create, Balance, and Send buttons

Test plan

  • Tab to the page — first focused element should be "Skip to main content"; pressing Enter should move focus to <main>
  • Toggle dark mode; check .balance-asset, QR modal, and message history toggle text are readable
  • Tab through all interactive elements in dark and light mode — every element should show a visible focus ring
  • Submit a payment; verify screen reader announces "Sending payment…" (polite), then success/error message
  • On payment success, the toast should be announced politely; on payment error, immediately (assertive)
  • After checking balance, the new balance values should be announced

…real-Future#740, Ethereal-Future#741, Ethereal-Future#742

- Ethereal-Future#739 (skip link): skip-to-main-content link is already first in DOM with
  <main id="main-content"> target; fix skip-link color to use --on-primary
  token so it passes WCAG AA in both light and dark mode

- Ethereal-Future#740 (dark mode contrast): replace hardcoded Ethereal-Future#555/Ethereal-Future#666 colors on
  .balance-asset, .qr-pubkey, and .sm-history-toggle with var(--muted)
  so they meet ≥4.5:1 contrast ratio on dark backgrounds; audit of all
  dark mode CSS custom property pairs confirms they all pass WCAG AA

- Ethereal-Future#741 (focus-visible ring): add --focus-ring-color/width/offset tokens to
  :root; add --focus-ring-color override in .theme-dark (#93c5fd, 11.4:1);
  fix input:focus-visible to use ring tokens instead of outline:none;
  fix .fu-dropzone to use :focus-visible with ring instead of outline:none

- Ethereal-Future#742 (live regions): balance display wrapped in role="status" aria-live
  region so updated balance is announced after transactions; StatusMessage
  now uses role="alert" for error/warning (assertive) and role="status"
  for success/info (polite), preventing disruptive interruptions for
  non-urgent notifications; loading state live region was already present

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@drips-wave

drips-wave Bot commented Jun 27, 2026

Copy link
Copy Markdown

@hman38705 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Mystery-CLI Mystery-CLI merged commit 1b3919d into Ethereal-Future:main Jun 28, 2026
13 of 39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants