Liquid Glass Design System (iOS 26)
Patterns for implementing Apple's Liquid Glass — a dynamic material that blurs content behind it, reflects color and light from surrounding content, and reacts to touch and pointer interactions. Covers SwiftUI, UIKit, and WidgetKit integration.
When to Activate
- Building or updating apps for iOS 26+ with the new design language
- Implementing glass-style buttons, cards, toolbars, or containers
- Creating morphing transitions between glass elements
- Applying Liquid Glass effects to widgets
- Migrating existing blur/material effects to the new Liquid Glass API
Core Pattern — SwiftUI
Basic Glass Effect
The simplest way to add Liquid Glass to any view:
swift
1Text("Hello, World!")
2 .font(.title)
3 .padding()
4 .glassEffect() // Default: regular variant, capsule shape
Customizing Shape and Tint
swift
1Text("Hello, World!")
2 .font(.title)
3 .padding()
4 .glassEffect(.regular.tint(.orange).interactive(), in: .rect(cornerRadius: 16.0))
Key customization options:
.regular — standard glass effect
.tint(Color) — add color tint for prominence
.interactive() — react to touch and pointer interactions
- Shape:
.capsule (default), .rect(cornerRadius:), .circle
swift
1Button("Click Me") { /* action */ }
2 .buttonStyle(.glass)
3
4Button("Important") { /* action */ }
5 .buttonStyle(.glassProminent)
GlassEffectContainer for Multiple Elements
Always wrap multiple glass views in a container for performance and morphing:
swift
1GlassEffectContainer(spacing: 40.0) {
2 HStack(spacing: 40.0) {
3 Image(systemName: "scribble.variable")
4 .frame(width: 80.0, height: 80.0)
5 .font(.system(size: 36))
6 .glassEffect()
7
8 Image(systemName: "eraser.fill")
9 .frame(width: 80.0, height: 80.0)
10 .font(.system(size: 36))
11 .glassEffect()
12 }
13}
The spacing parameter controls merge distance — closer elements blend their glass shapes together.
Uniting Glass Effects
Combine multiple views into a single glass shape with glassEffectUnion:
swift
1@Namespace private var namespace
2
3GlassEffectContainer(spacing: 20.0) {
4 HStack(spacing: 20.0) {
5 ForEach(symbolSet.indices, id: \.self) { item in
6 Image(systemName: symbolSet[item])
7 .frame(width: 80.0, height: 80.0)
8 .glassEffect()
9 .glassEffectUnion(id: item < 2 ? "group1" : "group2", namespace: namespace)
10 }
11 }
12}
Morphing Transitions
Create smooth morphing when glass elements appear/disappear:
swift
1@State private var isExpanded = false
2@Namespace private var namespace
3
4GlassEffectContainer(spacing: 40.0) {
5 HStack(spacing: 40.0) {
6 Image(systemName: "scribble.variable")
7 .frame(width: 80.0, height: 80.0)
8 .glassEffect()
9 .glassEffectID("pencil", in: namespace)
10
11 if isExpanded {
12 Image(systemName: "eraser.fill")
13 .frame(width: 80.0, height: 80.0)
14 .glassEffect()
15 .glassEffectID("eraser", in: namespace)
16 }
17 }
18}
19
20Button("Toggle") {
21 withAnimation { isExpanded.toggle() }
22}
23.buttonStyle(.glass)
To allow horizontal scroll content to extend under a sidebar or inspector, ensure the ScrollView content reaches the leading/trailing edges of the container. The system automatically handles the under-sidebar scrolling behavior when the layout extends to the edges — no additional modifier is needed.
Core Pattern — UIKit
Basic UIGlassEffect
swift
1let glassEffect = UIGlassEffect()
2glassEffect.tintColor = UIColor.systemBlue.withAlphaComponent(0.3)
3glassEffect.isInteractive = true
4
5let visualEffectView = UIVisualEffectView(effect: glassEffect)
6visualEffectView.translatesAutoresizingMaskIntoConstraints = false
7visualEffectView.layer.cornerRadius = 20
8visualEffectView.clipsToBounds = true
9
10view.addSubview(visualEffectView)
11NSLayoutConstraint.activate([
12 visualEffectView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
13 visualEffectView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
14 visualEffectView.widthAnchor.constraint(equalToConstant: 200),
15 visualEffectView.heightAnchor.constraint(equalToConstant: 120)
16])
17
18// Add content to contentView
19let label = UILabel()
20label.text = "Liquid Glass"
21label.translatesAutoresizingMaskIntoConstraints = false
22visualEffectView.contentView.addSubview(label)
23NSLayoutConstraint.activate([
24 label.centerXAnchor.constraint(equalTo: visualEffectView.contentView.centerXAnchor),
25 label.centerYAnchor.constraint(equalTo: visualEffectView.contentView.centerYAnchor)
26])
UIGlassContainerEffect for Multiple Elements
swift
1let containerEffect = UIGlassContainerEffect()
2containerEffect.spacing = 40.0
3
4let containerView = UIVisualEffectView(effect: containerEffect)
5
6let firstGlass = UIVisualEffectView(effect: UIGlassEffect())
7let secondGlass = UIVisualEffectView(effect: UIGlassEffect())
8
9containerView.contentView.addSubview(firstGlass)
10containerView.contentView.addSubview(secondGlass)
swift
1scrollView.topEdgeEffect.style = .automatic
2scrollView.bottomEdgeEffect.style = .hard
3scrollView.leftEdgeEffect.isHidden = true
swift
1let favoriteButton = UIBarButtonItem(image: UIImage(systemName: "heart"), style: .plain, target: self, action: #selector(favoriteAction))
2favoriteButton.hidesSharedBackground = true // Opt out of shared glass background
Rendering Mode Detection
swift
1struct MyWidgetView: View {
2 @Environment(\.widgetRenderingMode) var renderingMode
3
4 var body: some View {
5 if renderingMode == .accented {
6 // Tinted mode: white-tinted, themed glass background
7 } else {
8 // Full color mode: standard appearance
9 }
10 }
11}
Accent Groups for Visual Hierarchy
swift
1HStack {
2 VStack(alignment: .leading) {
3 Text("Title")
4 .widgetAccentable() // Accent group
5 Text("Subtitle")
6 // Primary group (default)
7 }
8 Image(systemName: "star.fill")
9 .widgetAccentable() // Accent group
10}
Image Rendering in Accented Mode
swift
1Image("myImage")
2 .widgetAccentedRenderingMode(.monochrome)
Container Background
swift
1VStack { /* content */ }
2 .containerBackground(for: .widget) {
3 Color.blue.opacity(0.2)
4 }
Key Design Decisions
| Decision | Rationale |
|---|
| GlassEffectContainer wrapping | Performance optimization, enables morphing between glass elements |
spacing parameter | Controls merge distance — fine-tune how close elements must be to blend |
@Namespace + glassEffectID | Enables smooth morphing transitions on view hierarchy changes |
interactive() modifier | Explicit opt-in for touch/pointer reactions — not all glass should respond |
| UIGlassContainerEffect in UIKit | Same container pattern as SwiftUI for consistency |
| Accented rendering mode in widgets | System applies tinted glass when user selects tinted Home Screen |
Best Practices
- Always use GlassEffectContainer when applying glass to multiple sibling views — it enables morphing and improves rendering performance
- Apply
.glassEffect() after other appearance modifiers (frame, font, padding)
- Use
.interactive() only on elements that respond to user interaction (buttons, toggleable items)
- Choose spacing carefully in containers to control when glass effects merge
- Use
withAnimation when changing view hierarchies to enable smooth morphing transitions
- Test across appearances — light mode, dark mode, and accented/tinted modes
- Ensure accessibility contrast — text on glass must remain readable
Anti-Patterns to Avoid
- Using multiple standalone
.glassEffect() views without a GlassEffectContainer
- Nesting too many glass effects — degrades performance and visual clarity
- Applying glass to every view — reserve for interactive elements, toolbars, and cards
- Forgetting
clipsToBounds = true in UIKit when using corner radii
- Ignoring accented rendering mode in widgets — breaks tinted Home Screen appearance
- Using opaque backgrounds behind glass — defeats the translucency effect
When to Use
- Navigation bars, toolbars, and tab bars with the new iOS 26 design
- Floating action buttons and card-style containers
- Interactive controls that need visual depth and touch feedback
- Widgets that should integrate with the system's Liquid Glass appearance
- Morphing transitions between related UI states