Liquid Glass Design System for iOS 26
Use this skill to build Apple's Liquid Glass style. Glass blurs the view behind it. It can pick up nearby color and light. It can also react to touch and pointer input.
This guide covers SwiftUI, UIKit, and WidgetKit.
When to Use This Skill
Use this skill when you:
- Build or update an app for iOS 26 or later
- Add glass buttons, cards, bars, or groups
- Make one glass shape change into another
- Add glass to a widget
- Replace an old blur or material effect
Do not use these APIs on an older system without a version check.
if #available(iOS 26.0, *) {
// Use Liquid Glass.
} else {
// Use an older material or blur.
}Check the app's target, SDK, and platform before you write code. Some APIs may not exist on every Apple platform.
SwiftUI
Add a Basic Glass Effect
Add size, font, and padding before .glassEffect().
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect()The default effect uses regular glass and a capsule shape.
Set the Shape and Tint
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect(
.regular
.tint(.orange)
.interactive(),
in: .rect(cornerRadius: 16)
)Main options:
.regularadds the normal glass look..tint(Color)adds color..interactive()reacts to touch or pointer input..capsulemakes a pill shape..rect(cornerRadius:)makes a round box..circlemakes a circle.
Use .interactive() only when the view can be used. Do not add it to plain text or art.
Add Glass Buttons
Button("Open") {
openItem()
}
.buttonStyle(.glass)
Button("Delete") {
deleteItem()
}
.buttonStyle(.glassProminent)Use .glassProminent for the main action. Do not use it for every button.
Group More Than One Glass View
Put nearby glass views in one GlassEffectContainer. This helps the system draw them as one group. It also lets close shapes blend.
GlassEffectContainer(spacing: 40) {
HStack(spacing: 40) {
Image(systemName: "scribble.variable")
.font(.system(size: 36))
.frame(width: 80, height: 80)
.glassEffect()
Image(systemName: "eraser.fill")
.font(.system(size: 36))
.frame(width: 80, height: 80)
.glassEffect()
}
}The spacing value helps set when shapes blend. Test it at the real view size. A value that works on one screen may look wrong on another.
Do not make one large container for the whole app. Group only glass views that belong together.
Join Views Into One Glass Shape
Use glassEffectUnion when many views should look like one glass group.
@Namespace private var glassNamespace
let symbols = ["pencil", "eraser", "lasso", "paintbrush"]
GlassEffectContainer(spacing: 20) {
HStack(spacing: 20) {
ForEach(symbols.indices, id: \.self) { index in
Image(systemName: symbols[index])
.frame(width: 80, height: 80)
.glassEffect()
.glassEffectUnion(
id: index < 2 ? "edit" : "draw",
namespace: glassNamespace
)
}
}
}Use a stable group ID. If the ID changes by mistake, the glass group may jump or split.
Make a Shape Change Smoothly
Give each glass view a stable ID in the same namespace. Add or remove views inside an animation.
@State private var isExpanded = false
@Namespace private var glassNamespace
VStack {
GlassEffectContainer(spacing: 40) {
HStack(spacing: 40) {
Image(systemName: "scribble.variable")
.frame(width: 80, height: 80)
.glassEffect()
.glassEffectID("pencil", in: glassNamespace)
if isExpanded {
Image(systemName: "eraser.fill")
.frame(width: 80, height: 80)
.glassEffect()
.glassEffectID("eraser", in: glassNamespace)
}
}
}
Button("Toggle Tools") {
withAnimation {
isExpanded.toggle()
}
}
.buttonStyle(.glass)
}Each ID must be unique in its namespace. Keep the container and namespace alive during the change.
Let Content Scroll Under a Side Bar
Make the ScrollView content reach the leading and trailing edges of its parent. The system can then draw the scroll under a side bar or inspector.
Do not add extra side padding to the whole scroll view. Add padding to the items inside it when needed.
Full SwiftUI Example
This example builds a small floating tool bar.
import SwiftUI
struct ToolBarView: View {
@State private var selectedTool = "pencil"
private let tools = ["pencil", "eraser", "lasso"]
var body: some View {
if #available(iOS 26.0, *) {
GlassEffectContainer(spacing: 12) {
HStack(spacing: 12) {
ForEach(tools, id: \.self) { tool in
Button {
selectedTool = tool
} label: {
Image(systemName: tool)
.font(.title2)
.frame(width: 44, height: 44)
}
.buttonStyle(
tool == selectedTool
? .glassProminent
: .glass
)
.accessibilityLabel(tool.capitalized)
}
}
.padding(8)
}
} else {
HStack(spacing: 12) {
ForEach(tools, id: \.self) { tool in
Button {
selectedTool = tool
} label: {
Image(systemName: tool)
.frame(width: 44, height: 44)
}
}
}
.padding(8)
.background(.regularMaterial, in: Capsule())
}
}
}UIKit
Add a Basic Glass View
let glassEffect = UIGlassEffect()
glassEffect.tintColor = UIColor.systemBlue.withAlphaComponent(0.3)
glassEffect.isInteractive = true
let glassView = UIVisualEffectView(effect: glassEffect)
glassView.translatesAutoresizingMaskIntoConstraints = false
glassView.layer.cornerRadius = 20
glassView.clipsToBounds = true
view.addSubview(glassView)
NSLayoutConstraint.activate([
glassView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
glassView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
glassView.widthAnchor.constraint(equalToConstant: 200),
glassView.heightAnchor.constraint(equalToConstant: 120)
])
let label = UILabel()
label.text = "Liquid Glass"
label.translatesAutoresizingMaskIntoConstraints = false
glassView.contentView.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: glassView.contentView.centerXAnchor),
label.centerYAnchor.constraint(equalTo: glassView.contentView.centerYAnchor)
])Add child views to contentView, not to the effect view itself.
Set clipsToBounds when the glass must follow the corner shape. Check shadows after you turn it on, since clipping can cut them off. Put a shadow on an outer wrapper view if needed.
Group UIKit Glass Views
let containerEffect = UIGlassContainerEffect()
containerEffect.spacing = 40
let containerView = UIVisualEffectView(effect: containerEffect)
let firstGlass = UIVisualEffectView(effect: UIGlassEffect())
let secondGlass = UIVisualEffectView(effect: UIGlassEffect())
containerView.contentView.addSubview(firstGlass)
containerView.contentView.addSubview(secondGlass)Add layout rules for the container and each child. Do not leave their size or place unclear.
Set Scroll Edge Effects
scrollView.topEdgeEffect.style = .automatic
scrollView.bottomEdgeEffect.style = .hard
scrollView.leftEdgeEffect.isHidden = trueHide an edge effect only when the layout calls for it. Test with long text, large text, and right-to-left text.
Change a Tool Bar Item Background
let favoriteButton = UIBarButtonItem(
image: UIImage(systemName: "heart"),
style: .plain,
target: self,
action: #selector(favoriteAction)
)
favoriteButton.hidesSharedBackground = trueSet hidesSharedBackground only when the item should not share the glass behind nearby items.
WidgetKit
Check the Widget Render Mode
struct MyWidgetView: View {
@Environment(\.widgetRenderingMode) private var renderingMode
var body: some View {
if renderingMode == .accented {
Text("Tinted mode")
} else {
Text("Full color mode")
}
}
}Do not depend on one exact background color. The user and system can change the widget look.
Set Accent Groups
HStack {
VStack(alignment: .leading) {
Text("Title")
.widgetAccentable()
Text("Subtitle")
}
Image(systemName: "star.fill")
.widgetAccentable()
}Use accent groups to show rank. Keep key text clear in both full color and tinted modes.
Set Image Style in Accent Mode
Image("myImage")
.widgetAccentedRenderingMode(.monochrome)Test photos, logos, and symbols on a real tinted Home Screen. A full-color image may not stay clear when made into one color.
Add a Widget Background
VStack {
Text("Today")
}
.containerBackground(for: .widget) {
Color.blue.opacity(0.2)
}Use containerBackground(for:) for the main widget background. Do not place a second solid background over it.
Design Rules
| Choice | Rule |
|---|---|
| Glass container | Group nearby glass views that belong together. |
| Container spacing | Tune when close shapes start to blend. |
| Stable IDs | Use fixed IDs for smooth shape changes. |
| Interactive glass | Use it only for controls that the user can use. |
| Tint | Use a light tint to show rank or state. |
| Widget accent mode | Keep the widget clear when the system adds a tint. |
| Fallback view | Give older systems a plain material or blur. |
Best Practices
- Apply size, font, and padding before
.glassEffect(). - Use one glass container for each related group.
- Keep text short and clear on glass.
- Keep strong contrast between text and its background.
- Test light mode, dark mode, and tinted widget mode.
- Test with Reduce Transparency on.
- Test with Reduce Motion on.
- Do not use motion as the only way to show a state change.
- Add clear access names to icon-only buttons.
- Keep touch targets at least 44 by 44 points.
- Test large text and long translated text.
- Test on a real device. Blur and light may look different from the preview.
- Keep a plain fallback for iOS versions before 26.
- Check the final API names in the SDK used by the project.
Common Mistakes
Avoid these mistakes:
- Using many nearby glass views without a container
- Putting the whole app in one glass container
- Nesting glass effects many levels deep
- Adding glass to every view
- Making plain art react to touch
- Using too much tint
- Placing a solid background behind glass
- Reusing the same glass ID for two live views
- Changing IDs during an animation
- Adding child views outside a UIKit effect view's
contentView - Using rounded UIKit glass without clipping its content
- Forgetting that clipping can cut off shadows
- Ignoring tinted widget mode
- Using glass as the only sign that a control is selected
- Shipping without an older-system fallback
Good Uses
Liquid Glass works well for:
- Navigation bars, tool bars, and tab bars
- Floating action buttons
- Small tool groups
- Cards that sit over rich content
- Controls that need touch feedback
- Widgets that match the system look
- Smooth changes between related interface states
Use glass with care. It should help the user see groups and actions. It should not make the screen hard to read.