KahWee - Web Development, AI Tools & Tech Trends

Expert takes on AI tools like Claude and Sora, modern web development with React and Vite, and tech trends. By KahWee.

SwiftUI Liquid Glass and Accessibility - Part 1

I started building a UI with iOS 26's Liquid Glass design language.glassEffect() modifiers, blur animations, refraction, and parallax. The whole glass effect gets distracting after a while. I wanted Reduce Motion to work properly. This is Part 1 covering Reduce Motion and Dynamic Type. Part 2 covers VoiceOver and testing.

What Liquid Glass Does

iOS 26 introduces Liquid Glass, Apple's unified design language influenced by visionOS. It replaces iOS 7's flat design with rounded, translucent elements that refract, blur, and respond to motion, content, and user input.

The visual properties: environmental refraction (refracts and reflects content behind it), dynamic light reflection (responds to light sources), responsive fluidity (morphs based on size and interaction), and adaptive blur (adapts to background content). Larger glass elements simulate thicker glass with deeper shadows and more pronounced lensing.

Important

iOS 26's Liquid Glass design can cause accessibility issues if not handled properly. Blur, refraction, and parallax effects may trigger motion sickness for users with vestibular disorders.

iOS 26's Liquid Glass blur, refraction, and parallax effects break accessibility when unhandled. Three areas matter: Reduce Motion (disable or simplify animations), Reduce Transparency (make glass less translucent for better contrast), and VoiceOver (give glass-styled components proper labels).

Apple built accessibility into Liquid Glass directly. Reduce Motion decreases glass effect intensity, disables elastic properties, and removes parallax and refraction animations. Reduce Transparency makes Liquid Glass "frostier" and more opaque, obscuring more content behind glass to improve contrast. Combined settings (Reduce Transparency + Reduce Motion + Dark Mode) deliver the best visibility, performance, and battery life. Liquid Glass then behaves more like iOS 25's flat design.

Disable Liquid Glass Animations with Reduce Motion

When users enable Reduce Motion, disable or simplify blur animations, parallax, refraction, and elastic morphing. Liquid Glass's dynamic refraction and morphing animations can trigger motion sickness in users with vestibular disorders.

Implementation:

struct AnimatedBadge: View {
    @Environment(\.accessibilityReduceMotion) private var reduceMotion
    @State private var animate = false

    var body: some View {
        let animation = reduceMotion
            ? .none
            : .easeInOut(duration: 0.8).repeatForever(autoreverses: true)

        Circle()
            .fill(Color.blue)
            .frame(width: 12, height: 12)
            .scaleEffect(animate ? 1.2 : 1.0)
            .onAppear {
                guard !reduceMotion else { return }
                withAnimation(animation) {
                    animate = true
                }
            }
    }
}

The pattern: read @Environment(\.accessibilityReduceMotion), set animation to .none when enabled, and early return in onAppear to prevent state changes.

Liquid Glass with Reduce Motion:

struct GlassCard: View {
    @Environment(\.accessibilityReduceMotion) private var reduceMotion

    var body: some View {
        VStack {
            Text("Content")
        }
        .padding()
        .background {
            if reduceMotion {
                // Fallback: simple blur without morphing
                RoundedRectangle(cornerRadius: 16)
                    .fill(.ultraThinMaterial)
            } else {
                // Full Liquid Glass effect
                RoundedRectangle(cornerRadius: 16)
                    .glassEffect(.regular.interactive())
            }
        }
    }
}

When Reduce Motion is on, replace .glassEffect() with .ultraThinMaterial or .regularMaterial. You keep the blur without the morphing and refraction.

Warning

Do not wrap the Reduce Motion check in onAppear alone. If the user toggles Reduce Motion while your app is open, @Environment updates the view automatically but onAppear does not re-fire.

Scalable Text with Dynamic Type

Dynamic Type lets users adjust text size system-wide. SwiftUI text styles (.headline, .body, .caption) scale automatically, but you still need to handle layout constraints. Users with visual impairments rely on larger sizes up to accessibility5.

Implementation:

Text(item.name)
    .font(.headline)
    .fontWeight(.semibold)
    .lineLimit(2)
    .multilineTextAlignment(.leading)
    .dynamicTypeSize(...DynamicTypeSize.accessibility5)

Use SwiftUI's built-in text styles instead of fixed point sizes. The range operator ...DynamicTypeSize.accessibility5 caps the maximum text size. Allow at least 2 lines to prevent truncation at larger sizes. Test with Settings -> Accessibility -> Display & Text Size -> Larger Text.

Tip

Sometimes unlimited scaling breaks layouts. Use .dynamicTypeSize(.medium...DynamicTypeSize.xxxLarge) to cap at a maximum size while still supporting accessibility.

Capping Dynamic Type: You can cap at a maximum size when unlimited scaling breaks layouts:

Text("Fixed Layout Text")
    .font(.body)
    .dynamicTypeSize(.medium...DynamicTypeSize.xxxLarge)

This allows scaling up to xxxLarge but prevents accessibility sizes that might break the design.

Continue to Part 2 for VoiceOver support and testing strategies.


Related posts: