SwiftUI Liquid Glass and Accessibility - Part 2
This is Part 2 covering VoiceOver support and testing. Part 1 covered Reduce Motion and Dynamic Type.
VoiceOver - Labels, Hints, and Grouping
VoiceOver reads UI elements aloud for blind and low-vision users. SwiftUI provides default labels for text and images, but custom views need explicit accessibility annotations. Without proper labels, VoiceOver reads raw view hierarchies ("Image, Text, Text, Text") instead of meaningful descriptions.
Combining elements: Group related views so VoiceOver reads them as one logical element:
HStack {
Image(systemName: "photo")
.resizable()
.frame(width: 60, height: 60)
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
Text("\(item.quantity) items")
.font(.subheadline)
if let notes = item.notes {
Text(notes)
.font(.caption)
}
}
}
.accessibilityElement(children: .combine)
.accessibilityLabel("\(item.name), \(item.quantity) items")
.accessibilityHint(item.notes ?? "No notes")
.accessibilityValue(item.condition?.rawValue ?? "")
The modifiers: .accessibilityElement(children: .combine) merges child elements into one, .accessibilityLabel() is primary text read immediately by VoiceOver (keep it short), .accessibilityHint() is secondary context read after a delay, and .accessibilityValue() is current state or value.
Example: Stat Card with Icon:
VStack {
Image(systemName: "star.fill")
.font(.title)
Text("\(count)")
.font(.title2)
.fontWeight(.bold)
Text(label)
.font(.caption)
}
.accessibilityElement(children: .combine)
.accessibilityLabel("\(label): \(count)")
Without .accessibilityElement(children: .combine), VoiceOver reads "Star, 42, Favorites". With proper grouping: "Favorites: 42".
iOS 26 Liquid Glass Best Practices
New in iOS 26: .glassEffect() automatically respects Reduce Motion and Reduce Transparency system settings. Liquid Glass materials adapt opacity and blur intensity based on accessibility preferences. GlassEffectContainer groups glass views for shared rendering and better performance. .interactive() modifier makes glass respond to touch and focus with subtle lighting (disabled when Reduce Motion is on).
Liquid Glass best practices: Apply .glassEffect() to floating elements (toolbars, tab bars, cards). Avoid placing solid fills behind glass views. Don't combine .glassEffect() with manual .blur() or .opacity() modifiers. Use GlassEffectContainer when multiple glass elements share the same space.
Testing Your Liquid Glass Implementation
Reduce Motion: Settings → Accessibility → Motion → Reduce Motion → ON. Verify .glassEffect() is replaced with static materials and check that parallax and refraction animations are disabled.
Reduce Transparency: Settings → Accessibility → Display & Text Size → Reduce Transparency → ON. Verify glass elements become more opaque and frosty, and check that content behind glass is more obscured for better contrast.
Dynamic Type: Settings → Accessibility → Display & Text Size → Larger Text. Drag slider to maximum (accessibility sizes) and check that text on glass backgrounds remains readable and layouts don't break.
VoiceOver: Settings → Accessibility → VoiceOver → Enable. Navigate through glass UI elements and listen for meaningful descriptions (not just "Glass effect, Button").
Combined testing (recommended): Enable Reduce Motion + Reduce Transparency + Dark Mode to test the most accessibility-friendly configuration.
My Take
I went into this wanting to build a polished UI with iOS 26's Liquid Glass design. What I learned is that accessibility isn't an afterthought—it's baked into Liquid Glass from the start, but you have to explicitly respect it in custom implementations.
The @Environment(\.accessibilityReduceMotion) pattern is simple and effective. When Reduce Motion is enabled, I replace .glassEffect() with .ultraThinMaterial for a static blur without the morphing animations. VoiceOver grouping with .accessibilityElement(children: .combine) solved the problem of screen readers treating each glass element separately. Dynamic Type worked automatically once I switched from fixed font sizes to SwiftUI's built-in text styles.
Liquid Glass looks incredible, but it's visually intense. Some users find it distracting or hard to read, which is why iOS 26 includes settings to tone it down. Testing with Reduce Motion + Reduce Transparency showed me how much the system adapts—glass becomes more opaque, animations stop, and the UI feels closer to iOS 25's flat design.
What I'd do differently next time: Check accessibilityReduceMotion and fallback to .ultraThinMaterial instead of .glassEffect(), use SwiftUI text styles with dynamicTypeSize ranges for text on glass backgrounds, group compound glass views with accessibilityElement(children: .combine), test with both Reduce Motion and Reduce Transparency enabled, and don't manually combine .glassEffect() with .blur() or .opacity()—let the system handle it.
The iOS 26 Liquid Glass design is visually impressive and accessibility is built-in. The hard part isn't the API—it's remembering to test with Reduce Motion and Reduce Transparency enabled.