| name | liquid-glass-design |
| description | iOS 26/macOS 26 Liquid Glass design system with complete API coverage. Use when user asks about iOS 26 design, Liquid Glass, glassEffect modifier, GlassEffectContainer, morphing animations, HIG compliance, visual styling, or the new Apple design language. |
| allowed-tools | Bash, Read, Write, Edit |
Liquid Glass Design System
Comprehensive guide to iOS 26 and macOS Tahoe's revolutionary Liquid Glass design system, including complete SwiftUI API coverage, Human Interface Guidelines, morphing animations, and implementation best practices.
Prerequisites
- Xcode 26+
- iOS 26 / macOS Tahoe deployment target
- SwiftUI framework
Overview
Liquid Glass is Apple's new design language introduced at WWDC 2025. It creates a lightweight, dynamic material that:
- Bends light in real-time (lensing effect)
- Responds to motion with specular highlights
- Adapts to content behind it
- Morphs between states fluidly
- Respects accessibility settings automatically
Design Philosophy
Liquid Glass establishes a clear visual hierarchy:
- Content Layer - Your app's main content sits at the bottom
- Navigation Layer - Glass controls float above content
Key Principle: Reserve glass for navigation and controls, NOT for content.
Material Properties
Light Behavior
┌─────────────────────────────────────┐
│ Liquid Glass Material Properties │
├─────────────────────────────────────┤
│ • Translucency with depth │
│ • Real-time light refraction │
│ • Specular highlights on motion │
│ • Adaptive shadows │
│ • Color informed by backdrop │
│ • Light/dark environment aware │
└─────────────────────────────────────┘
Visual Characteristics
- Lensing: Content behind glass appears subtly magnified/distorted
- Specular Highlights: Bright spots that respond to device tilt
- Adaptive Tint: Glass picks up colors from underlying content
- Depth: Material has apparent thickness and dimension
Glass Variants
Regular Glass (Default)
The versatile, adaptive variant. Use for most UI elements.
Button("Action") {
performAction()
}
.buttonStyle(.glass)
Characteristics:
- Adapts to light/dark mode automatically
- Picks up underlying content colors
- Standard blur and translucency
- Best for most use cases
Clear Glass
Permanently transparent variant with minimal visual impact.
Button("Subtle Action") {
performAction()
}
.buttonStyle(.glassClear)
Characteristics:
- Higher transparency
- Requires background dimming for contrast
- Use when content visibility is paramount
- Less prominent than regular glass
Identity Glass
No glass effect applied. Use for comparisons or opt-out.
.glassEffect(.identity)
Critical Design Rule
NEVER mix Regular and Clear glass variants in the same interface.
This creates visual inconsistency and violates HIG principles.
HStack {
Button("Save") { }
.buttonStyle(.glass)
Button("Cancel") { }
.buttonStyle(.glassClear)
}
HStack {
Button("Save") { }
.buttonStyle(.glass)
Button("Cancel") { }
.buttonStyle(.glass)
}
SwiftUI API Reference
Basic Glass Effect
View()
.glassEffect()
View()
.glassEffect(in: RoundedRectangle(cornerRadius: 16))
View()
.glassEffect(.regular, in: Capsule())
View()
.glassEffect(in: Circle(), isEnabled: showGlass)
Glass Effect Signature
func glassEffect(
_ glass: Glass = .regular,
in shape: some Shape = .rect,
isEnabled: Bool = true
) -> some View
Glass Enum
enum Glass {
case regular
case clear
case identity
}
Glass Effect Modifiers
Tint
Add color tint to glass elements:
Button("Tinted") { }
.buttonStyle(.glass)
.tint(.blue)
View()
.glassEffect(in: RoundedRectangle(cornerRadius: 12))
.tint(.green)
Interactive
Enable interactive behaviors for glass:
View()
.glassEffect(.regular.interactive(), in: Capsule())
Button Styles
Button("Glass") { }
.buttonStyle(.glass)
Button("Prominent") { }
.buttonStyle(.glassProminent)
Button("Borderless") { }
.buttonStyle(.glassBorderless)
GlassEffectContainer
GlassEffectContainer combines multiple glass shapes into a single morphable unit with shared visual properties.
Basic Usage
GlassEffectContainer {
HStack {
Button("First") { }
.glassEffect(in: Capsule())
Button("Second") { }
.glassEffect(in: Capsule())
Button("Third") { }
.glassEffect(in: Capsule())
}
}
Spacing Parameter
The spacing parameter controls the threshold distance for morphing:
GlassEffectContainer(spacing: 8) {
HStack(spacing: 4) {
Button("A") { }
.glassEffect(in: Capsule())
Button("B") { }
.glassEffect(in: Capsule())
}
}
GlassEffectContainer(spacing: 8) {
HStack(spacing: 20) {
Button("A") { }
.glassEffect(in: Capsule())
Button("B") { }
.glassEffect(in: Capsule())
}
}
Container Benefits
When views are inside a GlassEffectContainer:
- Automatic Blending - Overlapping shapes blend seamlessly
- Consistent Effects - Shared blur and lighting
- Morphing Transitions - Smooth animations between states
- Performance - Optimized rendering for multiple glass elements
Container Example: Expandable Menu
struct ExpandableMenu: View {
@State private var isExpanded = false
@Namespace private var animation
var body: some View {
GlassEffectContainer {
if isExpanded {
VStack {
Button("Option 1") { }
.glassEffect(in: Capsule())
.glassEffectID("menu", in: animation)
Button("Option 2") { }
.glassEffect(in: Capsule())
Button("Option 3") { }
.glassEffect(in: Capsule())
}
} else {
Button("Menu") {
withAnimation(.spring) {
isExpanded.toggle()
}
}
.glassEffect(in: Capsule())
.glassEffectID("menu", in: animation)
}
}
}
}
Morphing Animations
Glass Effect ID
Link glass elements across states for fluid morphing:
@Namespace private var animation
Button("Collapsed") { }
.glassEffect(in: Capsule())
.glassEffectID("button", in: animation)
HStack {
Button("Edit") { }
.glassEffect(in: Capsule())
.glassEffectID("button", in: animation)
Button("Delete") { }
.glassEffect(in: Capsule())
}
Glass Effect Union
Combine multiple glass shapes into one:
GlassEffectContainer {
ForEach(items) { item in
ItemView(item: item)
.glassEffect(in: RoundedRectangle(cornerRadius: 12))
.glassEffectUnion(id: "group", namespace: animation)
}
}
Glass Effect Transition
Control how glass appears/disappears:
View()
.glassEffect(in: Capsule())
.glassEffectTransition(.scale, isEnabled: true)
.glassEffectTransition(.opacity)
.glassEffectTransition(.scale)
.glassEffectTransition(.slide)
.glassEffectTransition(.identity)
Complete Morphing Example
struct MorphingToolbar: View {
@State private var mode: Mode = .browse
@Namespace private var morphing
enum Mode {
case browse, edit, select
}
var body: some View {
GlassEffectContainer {
switch mode {
case .browse:
HStack {
Button("Edit") {
withAnimation(.spring(duration: 0.4)) {
mode = .edit
}
}
.glassEffect(in: Capsule())
.glassEffectID("primary", in: morphing)
}
case .edit:
HStack {
Button("Done") {
withAnimation(.spring(duration: 0.4)) {
mode = .browse
}
}
.glassEffect(in: Capsule())
.glassEffectID("primary", in: morphing)
Button("Select All") { }
.glassEffect(in: Capsule())
.glassEffectTransition(.scale)
}
case .select:
HStack {
Button() {
withAnimation(.spring(duration: )) {
mode .browse
}
}
.glassEffect(in: ())
.glassEffectID(, in: morphing)
()
() { }
.glassEffect(in: ())
.tint(.red)
}
}
}
.padding()
}
}
Known Issues
iOS 26.1: Menu in GlassEffectContainer
Bug: Placing a Menu inside a GlassEffectContainer breaks morphing animations.
GlassEffectContainer {
Menu("Options") {
Button("Edit") { }
Button("Delete") { }
}
.glassEffect(in: Capsule())
}
VStack {
Menu("Options") {
Button("Edit") { }
Button("Delete") { }
}
.buttonStyle(.glass)
GlassEffectContainer {
}
}
Toolbar Integration
Glass Toolbars
Toolbars automatically adopt Liquid Glass in iOS 26:
struct ContentView: View {
var body: some View {
NavigationStack {
ContentList()
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("Add", systemImage: "plus") {
addItem()
}
}
ToolbarSpacer(.fixed)
ToolbarItem(placement: .secondaryAction) {
Button("Edit") {
editMode.toggle()
}
}
}
}
}
}
Toolbar Spacer
New in iOS 26 for grouping toolbar items:
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("Save") { }
}
ToolbarSpacer(.fixed)
ToolbarItem(placement: .secondaryAction) {
Button("Share") { }
}
ToolbarItem(placement: .secondaryAction) {
Button("Delete") { }
}
}
Close Button Role
New button role for dismiss actions with glass X styling:
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Close", role: .close) {
dismiss()
}
}
}
Toolbar Glass Visibility
Control glass background visibility:
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("Action") { }
}
}
.toolbarBackgroundVisibility(.visible, for: .navigationBar)
Navigation with Glass
Navigation Bar
NavigationStack {
ContentView()
.navigationTitle("My App")
.navigationBarTitleDisplayMode(.large)
}
Tab Bar
TabView {
HomeView()
.tabItem {
Label("Home", systemImage: "house")
}
SearchView()
.tabItem {
Label("Search", systemImage: "magnifyingglass")
}
.tab(role: .search)
}
Split View
NavigationSplitView {
Sidebar()
} content: {
ContentList()
} detail: {
DetailView()
}
Accessibility
Liquid Glass automatically respects accessibility settings:
Reduce Transparency
When enabled:
- Glass becomes more opaque/frosty
- Background content is more obscured
- Better contrast for readability
@Environment(\.accessibilityReduceTransparency) var reduceTransparency
var body: some View {
if reduceTransparency {
}
}
Increase Contrast
When enabled:
- Glass shifts to predominantly black/white
- Borders become more prominent
- Higher contrast ratios
@Environment(\.colorSchemeContrast) var contrast
var body: some View {
if contrast == .increased {
}
}
Reduce Motion
When enabled:
- Morphing animations are subdued
- Transitions are shorter/simpler
- Less visual movement
@Environment(\.accessibilityReduceMotion) var reduceMotion
var body: some View {
withAnimation(reduceMotion ? .none : .spring) {
}
}
UIKit/AppKit Integration
Scene Bridging
Bring SwiftUI glass into UIKit apps:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swiftUIView = GlassButtonView()
let hostingController = UIHostingController(rootView: swiftUIView)
addChild(hostingController)
view.addSubview(hostingController.view)
hostingController.didMove(toParent: self)
}
}
struct GlassButtonView: View {
var body: some View {
Button("SwiftUI Glass") { }
.buttonStyle(.glass)
}
}
UIViewControllerRepresentable
Wrap UIKit in SwiftUI with glass:
struct LegacyViewWrapper: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> LegacyViewController {
LegacyViewController()
}
func updateUIViewController(_ vc: LegacyViewController, context: Context) {}
}
struct ContentView: View {
var body: some View {
ZStack {
LegacyViewWrapper()
VStack {
Spacer()
HStack {
Button("Control") { }
.buttonStyle(.glass)
}
.padding()
}
}
}
}
App Icon Guidelines
Redesigning for Liquid Glass
iOS 26 introduces new icon aesthetics:
- Grid System - Follow the updated icon grid
- Safe Areas - Respect new safe margins
- Translucency - Consider subtle glass effects in icon
- Simplicity - Reduce complexity for glass aesthetic
- Color - Use colors that complement glass UI
Icon Specifications
┌─────────────────────────────────┐
│ iOS 26 App Icon Grid │
│ │
│ ┌─────────────────────────┐ │
│ │ │ │
│ │ Safe Content Area │ │
│ │ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ │ │ │
│ │ │ Main Element │ │ │
│ │ │ │ │ │
│ │ └─────────────────┘ │ │
│ │ │ │
│ └─────────────────────────┘ │
│ │
│ 1024x1024 @ 1x │
└─────────────────────────────────┘
Best Practices
DO
- Use glass for navigation elements - Toolbars, tab bars, floating buttons
- Keep content behind glass - Let users see through to their content
- Use morphing for state changes - Connect related UI with glassEffectID
- Test accessibility - Verify with Reduce Transparency enabled
- Maintain visual hierarchy - Glass floats, content grounds
DON'T
- Glass on content - Don't apply glass to cards, lists, text containers
- Mix variants - Never combine regular and clear glass
- Nest glass - Avoid glass-on-glass layering
- Overuse morphing - Reserve for meaningful state transitions
- Ignore accessibility - Always test with accessibility settings
Complete Example: Glass Interface
import SwiftUI
struct GlassInterfaceView: View {
@State private var isEditing = false
@State private var selectedTab = 0
@Namespace private var morphing
var body: some View {
TabView(selection: $selectedTab) {
NavigationStack {
ScrollView {
ContentGrid()
}
.navigationTitle("Home")
.toolbar {
ToolbarItem(placement: .primaryAction) {
GlassEffectContainer {
if isEditing {
HStack {
Button("Done") {
withAnimation(.spring(duration: 0.35)) {
isEditing = false
}
}
.glassEffect(in: Capsule())
.glassEffectID("edit", in: morphing)
Button("Select All") { }
.glassEffect(in: Capsule())
.glassEffectTransition(.scale)
}
} {
() {
withAnimation(.spring(duration: )) {
isEditing
}
}
.glassEffect(in: ())
.glassEffectID(, in: morphing)
}
}
}
}
}
.tabItem {
(, systemImage: )
}
.tag()
()
.tabItem {
(, systemImage: )
}
.tab(role: .search)
.tag()
()
.tabItem {
(, systemImage: )
}
.tag()
}
}
}
: {
items ().map { }
body: {
(columns: [
(.adaptive(minimum: ))
], spacing: ) {
(items, id: \.) { item
(title: item)
}
}
.padding()
}
}
: {
title:
body: {
{
(cornerRadius: )
.fill(.secondary.opacity())
.frame(height: )
(title)
.font(.headline)
}
.padding()
.background(.regularMaterial)
.clipShape((cornerRadius: ))
}
}
: {
query
body: {
{
{
(searchResults, id: \.) { result
(result)
}
}
.navigationTitle()
.searchable(text: )
}
}
searchResults: [] {
[]
}
}
: {
body: {
{
{
() {
() { () }
() { () }
}
() {
() { () }
() { () }
}
}
.navigationTitle()
}
}
}
# {
()
}
Debugging Glass Effects
Visual Debugging
View()
.glassEffect(in: RoundedRectangle(cornerRadius: 12))
.border(.red)
Check Container Scope
GlassEffectContainer {
VStack {
}
}
.border(.blue)
Animation Debugging
withAnimation(.spring(duration: 2.0)) {
state.toggle()
}
Official Resources