| name | liquid-glass-design |
| description | iOS 26 Liquid Glass Design System — Dynamic glass materials for SwiftUI, UIKit, and WidgetKit with blur, reflection, and interactive morphing effects. |
Liquid Glass Design System (iOS 26)
Pattern guidelines for implementing Apple's Liquid Glass—a dynamic material that blurs content behind it, reflects surrounding colors and light, and reacts to touch and pointer interactions. Covers SwiftUI, UIKit, and WidgetKit integration.
When to Use This Skill
- Building or updating applications for iOS 26+ adopting 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 Patterns — SwiftUI
Basic Glass Effect
The simplest way to add Liquid Glass to any view:
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect()
Custom Shapes and Tints
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect(.regular.tint(.orange).interactive(), in: .rect(cornerRadius: 16.0))
Key customization options:
.regular — Standard glass effect
.tint(Color) — Add color tint to enhance prominence
.interactive() — React to touch and pointer interactions
- Shapes:
.capsule (default), .rect(cornerRadius:), .circle
Glass Button Styles
Button("Click Me") { }
.buttonStyle(.glass)
Button("Important") { }
.buttonStyle(.glassProminent)
GlassEffectContainer for Multiple Elements
For performance and morphing considerations, always wrap multiple glass views inside a container:
GlassEffectContainer(spacing: 40.0) {
HStack(spacing: 40.0) {
Image(systemName: "scribble.variable")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
Image(systemName: "eraser.fill")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
}
}
The spacing parameter controls the merge distance—elements that are closer together will fuse their glass shapes.
Unifying Glass Effects
Combine multiple views into a single glass shape using glassEffectUnion:
@Namespace private var namespace
GlassEffectContainer(spacing: 20.0) {
HStack(spacing: 20.0) {
ForEach(symbolSet.indices, id: \.self) { item in
Image(systemName: symbolSet[item])
.frame(width: 80.0, height: 80.0)
.glassEffect()
.glassEffectUnion(id: item < 2 ? "group1" : "group2", namespace: namespace)
}
}
}
Morphing Transitions
Create smooth morphing effects when glass elements appear/disappear:
@State private var isExpanded = false
@Namespace private var namespace
GlassEffectContainer(spacing: 40.0) {
HStack(spacing: 40.0) {
Image(systemName: "scribble.variable")
.frame(width: 80.0, height: 80.0)
.glassEffect()
.glassEffectID("pencil", in: namespace)
if isExpanded {
Image(systemName: "eraser.fill")
.frame(width: 80.0, height: 80.0)
.glassEffect()
.glassEffectID("eraser", in: namespace)
}
}
}
Button("Toggle") {
withAnimation { isExpanded.toggle() }
}
.buttonStyle(.glass)
Extending Horizontal Scrolling Under Sidebar
To allow horizontally scrolling content to extend under a sidebar or inspector, ensure the ScrollView content reaches the leading/trailing edges of the container. The system automatically handles scroll behavior under the sidebar when layouts extend to the edge—no extra modifiers needed.
Core Patterns — UIKit
Basic UIGlassEffect
let glassEffect = UIGlassEffect()
glassEffect.tintColor = UIColor.systemBlue.withAlphaComponent(0.3)
glassEffect.isInteractive = true
let visualEffectView = UIVisualEffectView(effect: glassEffect)
visualEffectView.translatesAutoresizingMaskIntoConstraints = false
visualEffectView.layer.cornerRadius = 20
visualEffectView.clipsToBounds = true
view.addSubview(visualEffectView)
NSLayoutConstraint.activate([
visualEffectView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
visualEffectView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
visualEffectView.widthAnchor.constraint(equalToConstant: 200),
visualEffectView.heightAnchor.constraint(equalToConstant: 120)
])
let label = UILabel()
label.text = "Liquid Glass"
label.translatesAutoresizingMaskIntoConstraints = false
visualEffectView.contentView.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: visualEffectView.contentView.centerXAnchor),
label.centerYAnchor.constraint(equalTo: visualEffectView.contentView.centerYAnchor)
])
UIGlassContainerEffect for Multiple Elements
let containerEffect = UIGlassContainerEffect()
containerEffect.spacing = 40.0
let containerView = UIVisualEffectView(effect: containerEffect)
let firstGlass = UIVisualEffectView(effect: UIGlassEffect())
let secondGlass = UIVisualEffectView(effect: UIGlassEffect())
containerView.contentView.addSubview(firstGlass)
containerView.contentView.addSubview(secondGlass)
Scroll Edge Effects
scrollView.topEdgeEffect.style = .automatic
scrollView.bottomEdgeEffect.style = .hard
scrollView.leftEdgeEffect.isHidden = true
Toolbar Glass Integration
let favoriteButton = UIBarButtonItem(image: UIImage(systemName: "heart"), style: .plain, target: self, action: #selector(favoriteAction))
favoriteButton.hidesSharedBackground = true
Core Patterns — WidgetKit
Rendering Mode Detection
struct MyWidgetView: View {
@Environment(\.widgetRenderingMode) var renderingMode
var body: some View {
if renderingMode == .accented {
} else {
}
}
}
Accent Groups for Visual Hierarchy
HStack {
VStack(alignment: .leading) {
Text("Title")
.widgetAccentable()
Text("Subtitle")
}
Image(systemName: "star.fill")
.widgetAccentable()
}
Image Rendering in Accented Mode
Image("myImage")
.widgetAccentedRenderingMode(.monochrome)
Container Background
VStack { }
.containerBackground(for: .widget) {
Color.blue.opacity(0.2)
}
Key Design Decisions
| Decision | Rationale |
|---|
| Wrapping with GlassEffectContainer | Performance optimization and enables morphing between glass elements |
spacing parameter | Controls merge distance—fine-tunes how close elements must be to fuse |
@Namespace + glassEffectID | Achieves smooth morphing transitions when the view hierarchy changes |
interactive() modifier | Explicit opt-in for touch/pointer reactions—not all glass should respond |
| UIGlassContainerEffect in UIKit | Consistent container pattern aligned with SwiftUI |
| Accented rendering mode in widgets | System applies tinted glass effects when the user chooses a tinted home screen |
Best Practices
- Always use GlassEffectContainer to apply glass effects across 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 interactions (buttons, toggleable items).
- Carefully select container spacing to control when glass effects merge.
- Use
withAnimation when changing view hierarchies to enable smooth morphing transitions.
- Test across appearance modes—light mode, dark mode, and accented/tinted modes.
- Ensure accessibility contrast—text over glass must remain legible.
Anti-Patterns to Avoid
- Using multiple independent
.glassEffect() views without a GlassEffectContainer.
- Nesting too many glass effects—degrades performance and visual clarity.
- Applying glass effects to every view—reserve them for interactive elements, toolbars, and cards.
- Forgetting
clipsToBounds = true when using corner radii in UIKit.
- Ignoring accented rendering modes in widgets—breaks the tinted home screen appearance.
- Using opaque backgrounds behind glass effects—destroys the translucency.
Use Cases
- Navigation bars, toolbars, and tab bars adopting the new iOS 26 design.
- Floating action buttons and card-like containers.
- Interactive controls requiring visual depth and tactile feedback.
- Widgets that should integrate with the system Liquid Glass appearance.
- Morphing transitions between related UI states.