From 7cb1fe939337dd2df0fe0c6d642334147952ab0e Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Sun, 8 Mar 2026 19:16:11 -0400 Subject: [PATCH 1/2] chore: add accessibility labels to widget views Add .accessibilityLabel(), .accessibilityHidden(), .accessibilityElement(), and .accessibilityValue() to interactive and decorative elements across all widget SwiftUI views: - HomeWidgetView: streak badge (combine + label), flame icon (hidden), focus rows (combine + label), checkmark icons (hidden) - SmallHomeWidgetView: same pattern for streak badge and focus rows - LockWidgetView: gauge label/value, fallback target image label - ProgressRing: progress label and percentage value --- Widgets/Views/HomeWidgetView.swift | 9 +++++++++ Widgets/Views/LockWidgetView.swift | 3 +++ Widgets/Views/ProgressRing.swift | 2 ++ Widgets/Views/SmallHomeWidgetView.swift | 6 ++++++ 4 files changed, 20 insertions(+) diff --git a/Widgets/Views/HomeWidgetView.swift b/Widgets/Views/HomeWidgetView.swift index 05a14b6..deda567 100644 --- a/Widgets/Views/HomeWidgetView.swift +++ b/Widgets/Views/HomeWidgetView.swift @@ -83,6 +83,7 @@ struct HomeWidgetView: View { Image(systemName: "flame.fill") .font(WidgetTypography.badge) .foregroundStyle(snapshot.streak > 0 ? palette.accent : palette.textMuted) + .accessibilityHidden(true) Text("\(snapshot.streak)") .font(WidgetTypography.badge) .foregroundStyle(palette.textPrimary) @@ -91,6 +92,8 @@ struct HomeWidgetView: View { .padding(.vertical, 4) .background(palette.surface.opacity(0.9)) .clipShape(Capsule()) + .accessibilityElement(children: .combine) + .accessibilityLabel("\(snapshot.streak) day streak") } } private var defaultURL: URL? { @@ -181,11 +184,14 @@ private struct FocusListView: View { HStack(spacing: 8) { Image(systemName: focus.isCompleted ? "checkmark.circle.fill" : "circle") .foregroundStyle(focus.isCompleted ? palette.accent : palette.textMuted) + .accessibilityHidden(true) Text(focus.title) .lineLimit(1) } .font(WidgetTypography.body) .foregroundStyle(palette.textPrimary) + .accessibilityElement(children: .combine) + .accessibilityLabel("\(focus.title), \(focus.isCompleted ? "completed" : "not completed")") } } } @@ -201,6 +207,7 @@ private struct StreakBadgeView: View { Image(systemName: "flame.fill") .font(WidgetTypography.badge) .foregroundStyle(streak > 0 ? palette.accent : palette.textMuted) + .accessibilityHidden(true) Text("\(streak)") .font(WidgetTypography.badge) .foregroundStyle(palette.textPrimary) @@ -209,6 +216,8 @@ private struct StreakBadgeView: View { .padding(.vertical, 4) .background(palette.surface.opacity(0.9)) .clipShape(Capsule()) + .accessibilityElement(children: .combine) + .accessibilityLabel("\(streak) day streak") } .frame(width: 70, alignment: .topTrailing) } diff --git a/Widgets/Views/LockWidgetView.swift b/Widgets/Views/LockWidgetView.swift index 12a1b4f..f91a771 100644 --- a/Widgets/Views/LockWidgetView.swift +++ b/Widgets/Views/LockWidgetView.swift @@ -21,8 +21,11 @@ struct LockWidgetView: View { } .gaugeStyle(.accessoryCircular) .tint(snapshot.completedCount >= total ? palette.complete : palette.accent) + .accessibilityLabel("Focus progress") + .accessibilityValue("\(snapshot.completedCount) of \(total) completed") } else { Image(systemName: "target") + .accessibilityLabel("10x") } } .containerBackground(Color.clear, for: .widget) diff --git a/Widgets/Views/ProgressRing.swift b/Widgets/Views/ProgressRing.swift index 9863803..3ceadcd 100644 --- a/Widgets/Views/ProgressRing.swift +++ b/Widgets/Views/ProgressRing.swift @@ -14,5 +14,7 @@ struct ProgressRing: View { .stroke(palette.textPrimary, style: StrokeStyle(lineWidth: 4, lineCap: .round)) .rotationEffect(.degrees(-90)) } + .accessibilityLabel("Progress") + .accessibilityValue("\(Int(progress * 100)) percent") } } diff --git a/Widgets/Views/SmallHomeWidgetView.swift b/Widgets/Views/SmallHomeWidgetView.swift index dec8f56..1a4790e 100644 --- a/Widgets/Views/SmallHomeWidgetView.swift +++ b/Widgets/Views/SmallHomeWidgetView.swift @@ -79,6 +79,7 @@ struct SmallHomeWidgetView: View { Image(systemName: "flame.fill") .font(WidgetTypography.badge) .foregroundStyle(snapshot.streak > 0 ? palette.accent : palette.textMuted) + .accessibilityHidden(true) Text("\(snapshot.streak)") .font(WidgetTypography.badge) .foregroundStyle(palette.textPrimary) @@ -87,6 +88,8 @@ struct SmallHomeWidgetView: View { .padding(.vertical, 3) .background(palette.surface.opacity(0.9)) .clipShape(Capsule()) + .accessibilityElement(children: .combine) + .accessibilityLabel("\(snapshot.streak) day streak") } } @@ -100,11 +103,14 @@ struct SmallHomeWidgetView: View { Image(systemName: focus.isCompleted ? "checkmark.circle.fill" : "circle") .imageScale(.small) .foregroundStyle(focus.isCompleted ? palette.accent : palette.textMuted) + .accessibilityHidden(true) Text(focus.title) .lineLimit(1) } .font(WidgetTypography.body) .foregroundStyle(palette.textPrimary) + .accessibilityElement(children: .combine) + .accessibilityLabel("\(focus.title), \(focus.isCompleted ? "completed" : "not completed")") } } } From 1c7c8c435454f365ca75d6d667eb1b7216be84db Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Sun, 8 Mar 2026 20:01:56 -0400 Subject: [PATCH 2/2] fix: hide dot grid from VoiceOver and fix footer accessibility label --- Widgets/Views/HomeWidgetView.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Widgets/Views/HomeWidgetView.swift b/Widgets/Views/HomeWidgetView.swift index deda567..a0b6914 100644 --- a/Widgets/Views/HomeWidgetView.swift +++ b/Widgets/Views/HomeWidgetView.swift @@ -250,6 +250,7 @@ private struct YearPreviewWidgetView: View { .frame(height: gridHeight) .background(palette.surface) .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) + .accessibilityHidden(true) } else { Text(WidgetCopy.openToSyncYear) .font(WidgetTypography.caption) @@ -265,6 +266,7 @@ private struct YearPreviewWidgetView: View { .font(WidgetTypography.caption) .foregroundStyle(palette.textSecondary) .lineLimit(1) + .accessibilityLabel(footerAccessibilityLabel) } } @@ -296,6 +298,11 @@ private struct YearPreviewWidgetView: View { return "\(String(format: "%.0f%%", preview.yearCompletionPercent)) • \(preview.daysLeft)d left" } + private var footerAccessibilityLabel: String { + guard let preview, preview.totalDays > 0 else { return WidgetCopy.yearProgressFallback } + return "\(preview.daysLeft) days left, \(String(format: "%.0f", preview.yearCompletionPercent)) percent complete" + } + private var gridHeight: CGFloat { switch layout { case .medium: