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
- Understanding Liquid Glass
- The
glassEffectModifier - Grouping Glass with
GlassEffectContainer - Morphing Transitions with
.glassEffectID() - Glass Button Styles
- How Glass Interacts with System UI
- Migration Considerations
- Performance Considerations
- When to Use (and When Not To)
- Summary
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:
- Glass surfaces — individual views that render with the glass effect
- Glass containers — parent views that group glass surfaces so they interact correctly with each other
- 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 —
.regularprovides the standard translucent glass..clearproduces 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
GlassEffectContainerviews 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
Textand anImageand the destination has only aText, 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:
.glassproduces a subtle, translucent button that blends with its surroundings. Use it for secondary actions — the buttons that should be available but not visually dominant..glassProminentadds 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
.toolbarrender their items on glass surfaces. - Search bars placed via
.searchableadopt 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.
Step 2: Wrap Related Glass Elements
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:
- Replace custom chrome with system components. This is Apple’s recommended path. System navigation bars and tab bars get glass for free.
- Apply glass manually. Use
.glassEffecton 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
ListorScrollViewscrolls underneath a glass navigation bar or toolbar. This common pattern runs at 120fps on ProMotion displays. - Avoid glass in reusable cells. Applying
.glassEffectto every cell in a largeLazyVStackorListis 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
.glassEffectinside aForEachthat 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)
| Scenario | Recommendation |
|---|---|
| Navigation chrome | System does this automatically on iOS 26. |
| Floating action buttons | Use .glassProminent for primary actions. |
| Modal overlays and sheets | Use .glassEffect(.regular) for overlays. |
| Cards in a scrollable grid | Apply to selected/featured cards only. |
| List cell backgrounds | Avoid glass. Use materials or opaque fills. |
| Full-screen backgrounds | Avoid. Glass needs content behind it. |
| Accessibility contexts | Test with Reduce Transparency enabled. |
| Deployment target < iOS 26 | Use 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.GlassEffectContainergroups glass surfaces so they share a compositing context and avoid double-refraction artifacts..glassEffectID()with aNamespaceenables morphing transitions between glass surfaces, similar tomatchedGeometryEffectbut 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:
- Build a SwiftUI App with the New Design (Session 323)
- Get to Know the New Design System (Session 356)