Liquid Glass Design System: Adopting Apple's iOS 26 Visual Language


You spent years mastering flat design, vibrancy layers, and .ultraThinMaterial. Then Apple walked on stage at WWDC25 and replaced all of it with a single word: glass. Liquid Glass is the most sweeping visual redesign since iOS 7 killed skeuomorphism, and unlike that transition, this one ships with first-class SwiftUI APIs that make adoption remarkably straightforward — if you understand how the pieces fit together.

This post covers the core Liquid Glass APIs — glassEffect(_:in:), GlassEffectContainer, .glassEffectID(), and the glass button styles — along with migration guidance for existing apps. We will not cover navigation-specific changes (tab bars, sidebars) or UIKit bridging; those have their own dedicated posts.

Contents

The Problem

Consider a typical detail screen in a Pixar movie catalog app. You have layered materials, custom blur effects, and carefully tuned opacity values to create depth:

struct MovieDetailHeader: View {
    let movie: PixarMovie

    var body: some View {
        ZStack(alignment: .bottom) {
            movie.heroImage
                .resizable()
                .scaledToFill()

            VStack(alignment: .leading) {
                Text(movie.title)
                    .font(.largeTitle.bold())
                Text(movie.tagline)
                    .font(.subheadline)
            }
            .padding()
            .frame(maxWidth: .infinity, alignment: .leading)
            .background(.ultraThinMaterial) // iOS 15+ material
            .clipShape(RoundedRectangle(cornerRadius: 16))
        }
    }
}

This worked fine on iOS 18. On iOS 26, it still compiles and runs, but it looks wrong. The rest of the system — navigation bars, tab bars, toolbars, alerts — all render with the new Liquid Glass treatment. Your custom .ultraThinMaterial overlay now clashes with every system surface on screen. The visual language is fractured.

Apple’s message is clear: Liquid Glass is not an optional style. It is the design system. Apps that do not adopt it will feel dated within a single release cycle.

Understanding Liquid Glass

Liquid Glass is a rendering technique that simulates light passing through a translucent, refractive surface. Unlike the flat materials introduced in iOS 15, glass surfaces bend and distort the content behind them, creating a sense of physical depth. The effect responds dynamically to the content underneath, the device’s ambient light, and the viewing angle on supported hardware.

Note: Liquid Glass is available on iOS 26, iPadOS 26, macOS 26, watchOS 26, tvOS 26, and visionOS 26. On older deployment targets, glass modifiers are unavailable at compile time. Use @available(iOS 26.0, *) checks when supporting multiple OS versions.

The design system rests on three pillars:

  1. Glass surfaces — individual views that render with the glass effect
  2. Glass containers — parent views that group glass surfaces so they interact correctly with each other
  3. Glass transitions — morphing animations that smoothly transform one glass surface into another

The glassEffect Modifier

The glassEffect(_:in:) modifier is the fundamental building block. It applies the Liquid Glass treatment to any view.

@available(iOS 26.0, *)
struct MovieCard: View {
    let movie: PixarMovie

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(movie.title)
                .font(.headline)
            Text(movie.director)
                .font(.subheadline)
                .foregroundStyle(.secondary)
        }
        .padding()
        .glassEffect(.regular, in: .rect(cornerRadius: 20))
    }
}

The modifier takes two arguments:

  • Effect style.regular provides the standard translucent glass. .clear produces a more transparent variant that lets more background content through, useful for overlays where readability of the underlying content matters.
  • Shape — Any InsettableShape. The glass renders within this shape’s bounds, and the refraction is clipped to it. Common choices are .rect(cornerRadius:), .capsule, and .circle.

The glass effect automatically adapts its appearance based on the content behind it. Place the same MovieCard over a bright Inside Out poster and over a dark WALL-E scene, and the glass will adjust its tint, blur intensity, and refraction to remain legible in both cases.

Controlling Glass Intensity

You can influence how prominently the glass renders by combining it with standard SwiftUI modifiers:

VStack {
    Text("Now Playing")
        .font(.title2.bold())
}
.padding()
.glassEffect(.regular, in: .capsule)
.opacity(0.9) // Subtle reduction in glass prominence

Tip: Avoid stacking a .background(.ultraThinMaterial) underneath a .glassEffect. The two systems are not designed to compose — you will get visual artifacts where the material and the glass refraction compete. Choose one or the other.

Grouping Glass with GlassEffectContainer

When multiple glass surfaces appear near each other, they need to coordinate. Without grouping, overlapping glass elements produce double-refraction artifacts that look like visual noise rather than intentional layering.

GlassEffectContainer solves this by establishing a shared rendering context for its children:

@available(iOS 26.0, *)
struct MovieControlBar: View {
    let movie: PixarMovie
    @State private var isPlaying = false

    var body: some View {
        GlassEffectContainer {
            HStack(spacing: 16) {
                Button(action: { isPlaying.toggle() }) {
                    Image(systemName: isPlaying ? "pause.fill" : "play.fill")
                }
                .glassEffect(.regular, in: .circle)

                VStack(alignment: .leading) {
                    Text(movie.title)
                        .font(.headline)
                    Text("Directed by \(movie.director)")
                        .font(.caption)
                }
                .padding(.horizontal)
                .glassEffect(.regular, in: .capsule)

                Button("Details") {
                    // Navigate to detail
                }
                .glassEffect(.regular, in: .capsule)
            }
            .padding()
        }
    }
}

Inside a GlassEffectContainer, the system treats all .glassEffect surfaces as part of a unified visual group. Where two glass surfaces overlap or sit adjacent, the system merges their refraction rather than stacking it. This is essential for toolbars, control groups, and any layout where glass elements are positioned close together.

Warning: Nesting GlassEffectContainer views is supported but rarely necessary. Each container creates its own compositing group, so deeply nested containers may produce unexpected layering. Keep your container hierarchy flat unless you specifically need isolated glass groups.

Morphing Transitions with .glassEffectID()

One of the most striking Liquid Glass capabilities is morphing — smoothly transitioning one glass surface into another across navigation or state changes. The .glassEffectID() modifier enables this by tagging glass surfaces with stable identifiers.

@available(iOS 26.0, *)
struct MovieBrowser: View {
    @State private var selectedMovie: PixarMovie?
    @Namespace private var glassNamespace

    var body: some View {
        GlassEffectContainer {
            if let movie = selectedMovie {
                MovieDetailCard(movie: movie)
                    .glassEffect(.regular, in: .rect(cornerRadius: 24))
                    .glassEffectID(movie.id, in: glassNamespace)
                    .onTapGesture { selectedMovie = nil }
            } else {
                LazyVGrid(columns: [.init(.adaptive(minimum: 150))]) {
                    ForEach(pixarMovies) { movie in
                        MovieThumbnail(movie: movie)
                            .glassEffect(.regular, in: .rect(cornerRadius: 12))
                            .glassEffectID(movie.id, in: glassNamespace)
                            .onTapGesture { selectedMovie = movie }
                    }
                }
            }
        }
        .animation(.smooth(duration: 0.5), value: selectedMovie)
    }
}

When the user taps a movie thumbnail, the glass surface morphs from the small grid card into the expanded detail card. The refraction, shape, and content all transition smoothly because both views share the same glassEffectID. The Namespace works identically to how you use it with .matchedGeometryEffect, but instead of just matching geometry, it also interpolates the glass rendering properties.

Tip: For the smoothest morphing transitions, keep the content inside both glass surfaces structurally similar. If the source has a Text and an Image and the destination has only a Text, the system can still morph the glass shape, but the content transition will be less cohesive. Use .contentTransition(.interpolate) on shared elements for best results.

Glass Button Styles

iOS 26 introduces two new button styles purpose-built for the Liquid Glass design system: .glass and .glassProminent.

@available(iOS 26.0, *)
struct RenderControlPanel: View {
    @State private var isRendering = false

    var body: some View {
        VStack(spacing: 16) {
            // Standard glass button -- blends with surroundings
            Button("Preview Render") {
                // Start preview
            }
            .buttonStyle(.glass)

            // Prominent glass button -- stands out as primary action
            Button("Start Final Render") {
                isRendering = true
            }
            .buttonStyle(.glassProminent)

            // Glass toggle button
            Button(isRendering ? "Cancel" : "Render All Frames") {
                isRendering.toggle()
            }
            .buttonStyle(.glass)
            .tint(isRendering ? .red : .accentColor)
        }
        .padding()
    }
}

The difference between the two styles is hierarchy:

  • .glass produces a subtle, translucent button that blends with its surroundings. Use it for secondary actions — the buttons that should be available but not visually dominant.
  • .glassProminent adds stronger visual weight with increased opacity and a more pronounced refraction edge. Use it for primary calls to action — the one thing you want the user to tap.

Both styles automatically adapt to the content behind them and respect the current tint color. They also respond to dynamic type, dark mode, and accessibility settings without additional work.

Apple Docs: ButtonStyle — SwiftUI

How Glass Interacts with System UI

On iOS 26, system chrome — navigation bars, tab bars, toolbars, and search bars — automatically adopts Liquid Glass. This is not opt-in; it happens the moment your app links against the iOS 26 SDK.

Here is what changes without any code modifications:

  • Navigation bars render with a glass background that refracts the scrolling content beneath them. The title and bar button items float on the glass surface.
  • Tab bars become glass surfaces at the bottom of the screen. The selected tab indicator integrates with the glass refraction.
  • Toolbars applied via .toolbar render their items on glass surfaces.
  • Search bars placed via .searchable adopt the glass treatment for the search field background.
@available(iOS 26.0, *)
struct PixarStudioApp: View {
    var body: some View {
        TabView {
            Tab("Movies", systemImage: "film") {
                NavigationStack {
                    MovieListView()
                        .navigationTitle("Pixar Movies")
                        .toolbar {
                            ToolbarItem(placement: .primaryAction) {
                                Button("Add", systemImage: "plus") { }
                            }
                        }
                }
            }
            Tab("Studios", systemImage: "building.2") {
                StudioListView()
            }
        }
        // Navigation bar, tab bar, and toolbar items are all
        // automatically rendered with Liquid Glass on iOS 26.
        // No additional modifiers needed.
    }
}

The system manages glass coordination between these chrome elements automatically. The tab bar and navigation bar share a compositing context so their glass effects do not double-refract when they are near each other.

Note: For deep customization of navigation bars and tab bars with Liquid Glass, see SwiftUI Navigation Migration for iOS 26 and SwiftUI Toolbars on iOS 26.

Migration Considerations

Adopting Liquid Glass in an existing app ranges from trivial to substantial depending on how much custom chrome you have built. Here is a practical migration checklist:

Step 1: Remove Competing Materials

Search your codebase for .ultraThinMaterial, .thinMaterial, .regularMaterial, .thickMaterial, and .ultraThickMaterial. Evaluate each usage:

// Before: iOS 15-18 material approach
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: 16))

// After: iOS 26 glass approach
.glassEffect(.regular, in: .rect(cornerRadius: 16))

Not every material needs to become glass. Materials used for subtle background tinting (e.g., a grouped list section header) may work fine as-is. Focus on surfaces that act as interactive chrome — toolbars, floating controls, cards the user taps.

Identify groups of glass surfaces that appear together and wrap them in GlassEffectContainer:

// Before: Independent glass elements
HStack {
    playButton.glassEffect(.regular, in: .circle)
    titleLabel.glassEffect(.regular, in: .capsule)
}

// After: Coordinated glass group
GlassEffectContainer {
    HStack {
        playButton.glassEffect(.regular, in: .circle)
        titleLabel.glassEffect(.regular, in: .capsule)
    }
}

Step 3: Audit Custom Navigation Chrome

If you built custom navigation bars or tab bars, they will not automatically adopt glass. You have two options:

  1. Replace custom chrome with system components. This is Apple’s recommended path. System navigation bars and tab bars get glass for free.
  2. Apply glass manually. Use .glassEffect on your custom chrome views. This requires more testing but preserves your custom layout.

Step 4: Support Multiple OS Versions

When your deployment target is below iOS 26, use availability checks to provide glass on iOS 26+ and materials on older versions:

struct AdaptiveCard<Content: View>: View {
    let content: Content

    var body: some View {
        if #available(iOS 26.0, *) {
            content
                .glassEffect(.regular, in: .rect(cornerRadius: 16))
        } else {
            content
                .background(.ultraThinMaterial)
                .clipShape(RoundedRectangle(cornerRadius: 16))
        }
    }
}

Tip: Wrap this pattern in a custom view modifier to keep your call sites clean. A single .adaptiveGlass() modifier is much more maintainable than scattering availability checks throughout your views.

Performance Considerations

Liquid Glass is GPU-intensive. Each glass surface requires the system to sample and refract the content behind it in real time. Here are the key performance characteristics to be aware of:

  • Glass surfaces are composited on the GPU. On A15 and later chips, the refraction pipeline is hardware-accelerated and performs well even with multiple glass layers. On older hardware (A12-A14), you may see frame drops with more than 3-4 overlapping glass surfaces.
  • Scrolling under glass is optimized. Apple specifically optimized the case where a List or ScrollView scrolls underneath a glass navigation bar or toolbar. This common pattern runs at 120fps on ProMotion displays.
  • Avoid glass in reusable cells. Applying .glassEffect to every cell in a large LazyVStack or List is expensive. The system cannot efficiently recycle glass compositing contexts during fast scrolling. Reserve glass for chrome and floating controls, not for repeated list content.
  • Profile with Instruments. Use the Core Animation instrument’s “Offscreen Rendered” overlay to identify glass surfaces that are causing unexpected compositing passes.

Warning: Using .glassEffect inside a ForEach that renders hundreds of items will cause significant frame drops. If you need a glass-like appearance on list cells, consider using .background(.regularMaterial) on older elements and reserving true glass for selected or highlighted states.

When to Use (and When Not To)

ScenarioRecommendation
Navigation chromeSystem does this automatically on iOS 26.
Floating action buttonsUse .glassProminent for primary actions.
Modal overlays and sheetsUse .glassEffect(.regular) for overlays.
Cards in a scrollable gridApply to selected/featured cards only.
List cell backgroundsAvoid glass. Use materials or opaque fills.
Full-screen backgroundsAvoid. Glass needs content behind it.
Accessibility contextsTest with Reduce Transparency enabled.
Deployment target < iOS 26Use availability checks with fallbacks.

The guiding principle: glass communicates interactive surface and navigational chrome. It is not a general-purpose background style. Use it where you want the user to perceive a floating, interactive layer. Use opaque or material backgrounds for content that should feel grounded.

Summary

  • Liquid Glass is iOS 26’s design system, automatically applied to system navigation bars, tab bars, and toolbars when linking against the iOS 26 SDK.
  • glassEffect(_:in:) is the core modifier — apply it with a shape to turn any view into a glass surface.
  • GlassEffectContainer groups glass surfaces so they share a compositing context and avoid double-refraction artifacts.
  • .glassEffectID() with a Namespace enables morphing transitions between glass surfaces, similar to matchedGeometryEffect but with interpolated glass rendering.
  • .buttonStyle(.glass) and .buttonStyle(.glassProminent) provide the standard and emphasized button treatments for the new design system.
  • Migrate incrementally: remove competing materials, wrap related elements in containers, and use availability checks for backward compatibility.

For navigation-specific glass adoption — tab bars with tabBarMinimizeBehavior, sidebar glass effects, and updated NavigationSplitView patterns — continue to SwiftUI Navigation Migration for iOS 26.

WWDC25 Sessions: