Writing Technical guide

Mobile Development · 3 min read

I Had to Turn Down Liquid Glass to Test It Properly

The full Liquid Glass effect became distracting while I built with it. Here is how I handled Reduce Motion and Dynamic Type in SwiftUI.

In this article3 sections

I started building a UI with iOS 26’s Liquid Glass design language: .glassEffect() modifiers, blur animations, refraction, and parallax. After a while, the full effect became distracting even to me. That made Reduce Motion part of the implementation, not a cleanup task. This first part covers 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 effect combines refraction, light response, morphing, and adaptive blur. Larger glass elements simulate thicker glass with deeper shadows and more pronounced lensing.

Test custom motion 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.

Leaving blur, refraction, and parallax untested creates accessibility problems. Three settings matter here: Reduce Motion, Reduce Transparency, and VoiceOver.

Liquid Glass adapts to system accessibility preferences, but that does not settle the behavior of custom animations. Test Reduce Motion and Reduce Transparency independently and together. I would not infer battery life or a universally best contrast setting from appearance alone.

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 {
        Circle()
            .fill(Color.blue)
            .frame(width: 12, height: 12)
            .scaleEffect(animate ? 1.2 : 1.0)
            .animation(
                reduceMotion ? nil : Animation.easeInOut(duration: 0.8)
                    .repeatForever(autoreverses: true),
                value: animate
            )
            .onAppear { animate = !reduceMotion }
            .onChange(of: reduceMotion) { _, enabled in
                animate = !enabled
            }
    }
}

Read @Environment(\.accessibilityReduceMotion) and use an explicit optional animation. Handle preference changes as well as first appearance; onChange is the relevant SwiftUI hook.

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.

Handle changes while the app is open 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.

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 it also prevents the larger accessibility sizes. I treat that cap as a last resort after trying to make the layout adapt.

Continue to Part 2 for VoiceOver support and testing strategies.

Was this useful?

What was missing?

Thanks for the feedback.