feat: add WCAG AA accessibility app-wide, fix CloudKit container config, remove debug logs
- Add VoiceOver labels, hints, and element grouping across all 60+ views - Add Reduce Motion support (Theme.Animation.prefersReducedMotion) to all animations - Replace fixed font sizes with semantic Dynamic Type styles - Hide decorative elements from VoiceOver with .accessibilityHidden(true) - Add .minimumHitTarget() modifier ensuring 44pt touch targets - Add AccessibilityAnnouncer utility for VoiceOver announcements - Improve color contrast values in Theme.swift for WCAG AA compliance - Extract CloudKitContainerConfig for explicit container identity - Remove PostHog debug console log from AnalyticsManager Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -47,7 +47,9 @@ struct PlaceholderRectangle: View {
|
||||
.fill(placeholderColor)
|
||||
.frame(width: width, height: height)
|
||||
.opacity(isAnimating ? LoadingPlaceholder.maxOpacity : LoadingPlaceholder.minOpacity)
|
||||
.accessibilityHidden(true)
|
||||
.onAppear {
|
||||
guard !Theme.Animation.prefersReducedMotion else { return }
|
||||
withAnimation(.easeInOut(duration: LoadingPlaceholder.animationDuration).repeatForever(autoreverses: true)) {
|
||||
isAnimating = true
|
||||
}
|
||||
@@ -72,7 +74,9 @@ struct PlaceholderCircle: View {
|
||||
.fill(placeholderColor)
|
||||
.frame(width: diameter, height: diameter)
|
||||
.opacity(isAnimating ? LoadingPlaceholder.maxOpacity : LoadingPlaceholder.minOpacity)
|
||||
.accessibilityHidden(true)
|
||||
.onAppear {
|
||||
guard !Theme.Animation.prefersReducedMotion else { return }
|
||||
withAnimation(.easeInOut(duration: LoadingPlaceholder.animationDuration).repeatForever(autoreverses: true)) {
|
||||
isAnimating = true
|
||||
}
|
||||
@@ -98,7 +102,9 @@ struct PlaceholderCapsule: View {
|
||||
.fill(placeholderColor)
|
||||
.frame(width: width, height: height)
|
||||
.opacity(isAnimating ? LoadingPlaceholder.maxOpacity : LoadingPlaceholder.minOpacity)
|
||||
.accessibilityHidden(true)
|
||||
.onAppear {
|
||||
guard !Theme.Animation.prefersReducedMotion else { return }
|
||||
withAnimation(.easeInOut(duration: LoadingPlaceholder.animationDuration).repeatForever(autoreverses: true)) {
|
||||
isAnimating = true
|
||||
}
|
||||
@@ -145,7 +151,10 @@ struct PlaceholderCard: View {
|
||||
RoundedRectangle(cornerRadius: Theme.CornerRadius.large)
|
||||
.stroke(Theme.surfaceGlow(colorScheme), lineWidth: 1)
|
||||
}
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel("Loading content")
|
||||
.onAppear {
|
||||
guard !Theme.Animation.prefersReducedMotion else { return }
|
||||
withAnimation(.easeInOut(duration: LoadingPlaceholder.animationDuration).repeatForever(autoreverses: true)) {
|
||||
isAnimating = true
|
||||
}
|
||||
@@ -185,6 +194,7 @@ struct PlaceholderListRow: View {
|
||||
}
|
||||
.padding(Theme.Spacing.md)
|
||||
.onAppear {
|
||||
guard !Theme.Animation.prefersReducedMotion else { return }
|
||||
withAnimation(.easeInOut(duration: LoadingPlaceholder.animationDuration).repeatForever(autoreverses: true)) {
|
||||
isAnimating = true
|
||||
}
|
||||
|
||||
@@ -25,10 +25,11 @@ struct LoadingSheet: View {
|
||||
// Dimmed background
|
||||
Color.black.opacity(Self.backgroundOpacity)
|
||||
.ignoresSafeArea()
|
||||
.accessibilityHidden(true)
|
||||
|
||||
// Centered card
|
||||
VStack(spacing: Theme.Spacing.lg) {
|
||||
LoadingSpinner(size: .large)
|
||||
LoadingSpinner(size: .large, label: label)
|
||||
|
||||
VStack(spacing: Theme.Spacing.xs) {
|
||||
Text(label)
|
||||
|
||||
@@ -56,6 +56,7 @@ struct LoadingSpinner: View {
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
}
|
||||
.accessibilityLabel(label ?? "Loading")
|
||||
}
|
||||
|
||||
private var spinnerView: some View {
|
||||
@@ -63,15 +64,18 @@ struct LoadingSpinner: View {
|
||||
// Background track - subtle gray like Apple's native spinner
|
||||
Circle()
|
||||
.stroke(Color.secondary.opacity(0.2), lineWidth: size.strokeWidth)
|
||||
.accessibilityHidden(true)
|
||||
|
||||
// Rotating arc (270 degrees) - gray like Apple's ProgressView
|
||||
Circle()
|
||||
.trim(from: 0, to: 0.75)
|
||||
.stroke(Color.secondary, style: StrokeStyle(lineWidth: size.strokeWidth, lineCap: .round))
|
||||
.rotationEffect(.degrees(rotation))
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
.frame(width: size.diameter, height: size.diameter)
|
||||
.onAppear {
|
||||
guard !Theme.Animation.prefersReducedMotion else { return }
|
||||
withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) {
|
||||
rotation = 360
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user