iOS VoiceOver accessibility overhaul — 67 files

New framework:
- AccessibilityLabels.swift: centralized A11y struct with VoiceOver strings
- AccessibilityModifiers.swift: reusable .a11yHeader, .a11yDecorative,
  .a11yButton, .a11yCard, .a11yStatValue View extensions

Shared components: decorative elements hidden, stat views combined,
status/priority badges labeled, error views announced, empty states grouped

Cards: ResidenceCard, TaskCard, DynamicTaskCard, ContractorCard,
DocumentCard, WarrantyCard — all grouped with combined labels,
chevrons hidden, action buttons labeled

Main screens: Login, Register, Residences, Tasks, Contractors, Documents —
toolbar buttons labeled, section headers marked, form field hints added

Onboarding: all 10 views — header traits, button hints, task selection
state, progress indicator, decorative backgrounds hidden

Profile/Subscription: toggle hints, theme selection state, feature
comparison table accessibility, subscription button labels

iOS build verified: BUILD SUCCEEDED
This commit is contained in:
Trey T
2026-03-26 14:51:29 -05:00
parent 0d80df07f6
commit af73f8861b
67 changed files with 394 additions and 8 deletions

View File

@@ -17,6 +17,7 @@ struct OnboardingLocationContent: View {
var body: some View {
ZStack {
WarmGradientBackground()
.a11yDecorative()
// Decorative blobs
GeometryReader { geo in
@@ -54,6 +55,7 @@ struct OnboardingLocationContent: View {
.offset(x: geo.size.width * 0.65, y: geo.size.height * 0.75)
.blur(radius: 15)
}
.a11yDecorative()
VStack(spacing: 0) {
Spacer()
@@ -125,6 +127,7 @@ struct OnboardingLocationContent: View {
.font(.system(size: 26, weight: .bold, design: .rounded))
.foregroundColor(Color.appTextPrimary)
.multilineTextAlignment(.center)
.a11yHeader()
Text("Enter your ZIP code so we can suggest\nmaintenance tasks for your climate region.")
.font(.system(size: 15, weight: .medium))
@@ -163,6 +166,7 @@ struct OnboardingLocationContent: View {
.keyboardType(.numberPad)
.focused($isTextFieldFocused)
.multilineTextAlignment(.center)
.accessibilityHint("Enter your ZIP code")
.onChange(of: zipCode) { _, newValue in
// Only allow digits, max 5
let filtered = String(newValue.filter(\.isNumber).prefix(5))