Add comprehensive WCAG 2.1 AA accessibility support

- Add VoiceOver labels and hints to all voting layouts, settings, widgets,
  onboarding screens, and entry cells
- Add Reduce Motion support to button animations throughout the app
- Ensure 44x44pt minimum touch targets on widget mood buttons
- Enhance AccessibilityHelpers with Dynamic Type support, ScaledValue wrapper,
  and VoiceOver detection utilities
- Gate premium features (Insights, Month/Year views) behind subscription
- Update widgets to show subscription prompts for non-subscribers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-23 23:26:21 -06:00
parent a6a6912183
commit 086f8b8807
24 changed files with 741 additions and 283 deletions

View File

@@ -93,8 +93,12 @@ struct HorizontalVotingView: View {
}
.buttonStyle(MoodButtonStyle())
.frame(maxWidth: .infinity)
.accessibilityLabel(mood.strValue)
.accessibilityHint(String(localized: "Select this mood"))
}
}
.accessibilityElement(children: .contain)
.accessibilityLabel(String(localized: "Mood selection"))
}
}
@@ -136,8 +140,12 @@ struct CardVotingView: View {
)
}
.buttonStyle(CardButtonStyle())
.accessibilityLabel(mood.strValue)
.accessibilityHint(String(localized: "Select this mood"))
}
}
.accessibilityElement(children: .contain)
.accessibilityLabel(String(localized: "Mood selection"))
}
}
@@ -177,10 +185,14 @@ struct RadialVotingView: View {
}
.buttonStyle(MoodButtonStyle())
.position(position)
.accessibilityLabel(mood.strValue)
.accessibilityHint(String(localized: "Select this mood"))
}
}
}
.frame(height: 180)
.accessibilityElement(children: .contain)
.accessibilityLabel(String(localized: "Mood selection"))
}
private func angleForIndex(_ index: Int, total: Int) -> Double {
@@ -233,8 +245,12 @@ struct StackedVotingView: View {
)
}
.buttonStyle(CardButtonStyle())
.accessibilityLabel(mood.strValue)
.accessibilityHint(String(localized: "Select this mood"))
}
}
.accessibilityElement(children: .contain)
.accessibilityLabel(String(localized: "Mood selection"))
}
}
@@ -323,36 +339,43 @@ struct AuraVotingView: View {
}
}
.buttonStyle(AuraButtonStyle(color: color))
.accessibilityLabel(mood.strValue)
.accessibilityHint(String(localized: "Select this mood"))
}
}
// Custom button style for aura with glow effect on press
struct AuraButtonStyle: ButtonStyle {
let color: Color
@Environment(\.accessibilityReduceMotion) private var reduceMotion
func makeBody(configuration: Configuration) -> some View {
configuration.label
.scaleEffect(configuration.isPressed ? 0.92 : 1.0)
.brightness(configuration.isPressed ? 0.1 : 0)
.animation(.easeInOut(duration: 0.15), value: configuration.isPressed)
.animation(reduceMotion ? nil : .easeInOut(duration: 0.15), value: configuration.isPressed)
}
}
// MARK: - Button Styles
struct MoodButtonStyle: ButtonStyle {
@Environment(\.accessibilityReduceMotion) private var reduceMotion
func makeBody(configuration: Configuration) -> some View {
configuration.label
.scaleEffect(configuration.isPressed ? 0.9 : 1.0)
.animation(.easeInOut(duration: 0.15), value: configuration.isPressed)
.animation(reduceMotion ? nil : .easeInOut(duration: 0.15), value: configuration.isPressed)
}
}
struct CardButtonStyle: ButtonStyle {
@Environment(\.accessibilityReduceMotion) private var reduceMotion
func makeBody(configuration: Configuration) -> some View {
configuration.label
.scaleEffect(configuration.isPressed ? 0.96 : 1.0)
.opacity(configuration.isPressed ? 0.8 : 1.0)
.animation(.easeInOut(duration: 0.15), value: configuration.isPressed)
.animation(reduceMotion ? nil : .easeInOut(duration: 0.15), value: configuration.isPressed)
}
}