Skip to content

[A11Y] [High] Form inputs missing associated labels #2

@continue

Description

@continue

Accessibility Issue: Form Inputs Without Associated Labels

WCAG Level: A
Severity: High
Category: Form Accessibility (WCAG 1.3.1, 4.1.2)

Issue Description

Multiple form inputs across the application lack properly associated <label> elements. While some inputs use placeholder text, this is insufficient for screen reader users and does not meet WCAG requirements for form accessibility.

User Impact

  • Affected Users: Blind users, low vision users, cognitive disability users
  • Severity: Users cannot understand the purpose of form fields when using screen readers

Violations Found

File: src/pages/Settings.tsx

Lines: 193-201, 241-248, 353-362

<!-- Current Code - Missing labels -->
<input
  type="text"
  placeholder="Strava Client ID"
  value={stravaClientId}
  onChange={(e) => setStravaClientId(e.target.value)}
  style={{ ... }}
/>
<input
  type="password"
  placeholder="Strava Client Secret"
  value={stravaSecret}
  onChange={(e) => setStravaSecret(e.target.value)}
  style={{ ... }}
/>

Issue: Inputs use placeholder as the only label, which disappears when user starts typing and is not announced properly by screen readers.

File: src/pages/WelcomeFlow.tsx

Lines: 137-148, 285-327

<!-- Current Code -->
<label>
  <span className="start-date-label">Current weekly miles</span>
  <input type="number" ... />
</label>

Issue: While these use implicit label association (wrapping), some inputs lack proper label text visibility for screen readers.

File: src/pages/Insights.tsx

Lines: 569-574

<!-- Current Code -->
<input type="number" value={hrMax} onChange={(e) => setHRMax(e.target.value)} ... />
<input type="number" value={hrResting} onChange={(e) => setHRResting(e.target.value)} ... />

Issue: HR max and resting inputs lack associated labels.


Recommended Fix

<!-- Fixed Code - Settings.tsx -->
<label htmlFor="strava-client-id" className="visually-hidden">Strava Client ID</label>
<input
  id="strava-client-id"
  type="text"
  placeholder="Strava Client ID"
  aria-label="Strava Client ID"
  value={stravaClientId}
  onChange={(e) => setStravaClientId(e.target.value)}
  style={{ ... }}
/>

<!-- Or use visible labels (preferred) -->
<div className="form-field">
  <label htmlFor="strava-client-id">Strava Client ID</label>
  <input
    id="strava-client-id"
    type="text"
    value={stravaClientId}
    onChange={(e) => setStravaClientId(e.target.value)}
    style={{ ... }}
  />
</div>

Changes Required:

  1. Add explicit <label> elements with htmlFor attribute linking to input id
  2. Or add aria-label attribute to inputs as a minimum fix
  3. Add visible labels where space permits (better UX)
  4. Ensure placeholder text is supplementary, not the only label

Additional Instances

  • src/pages/Settings.tsx - Garmin Client ID/Secret inputs (lines 241-248)
  • src/pages/Settings.tsx - Heart rate inputs (lines 353-362)
  • src/pages/Settings.tsx - Daily recap time input (line 294)
  • src/pages/Training.tsx - Date picker inputs
  • src/pages/Analytics.tsx - Period selector buttons (should have group label)

Testing Instructions

  1. Navigate to Settings page using keyboard only
  2. Use screen reader (NVDA/VoiceOver) to navigate form fields
  3. Verify each input announces its purpose
  4. Test with forms mode enabled in screen reader

Resources

Acceptance Criteria

  • All form inputs have associated labels (visible or aria-label)
  • Labels are properly linked via htmlFor/id or implicit wrapping
  • Screen reader announces field purpose when focused
  • Tested with NVDA or VoiceOver
  • Automated tests pass (axe DevTools)

Metadata

Metadata

Assignees

No one assigned

    Labels

    accessibilityAccessibility improvementsseverity-highHigh severity - significant impactwcag-aWCAG Level A compliance

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions