Use when planning comprehensive Liquid Glass adoption across an app, auditing existing interfaces for Liquid Glass compatibility, implementing app icon updates, or understanding platform-specific Liquid Glass behavior - comprehensive reference guide covering all aspects of Liquid Glass adoption from WWDC 2025
Inherits all available tools
Additional assets for this skill
This skill inherits all available tools. When active, it can use any tool Claude has access to.
Use when:
liquid-glass for implementing the Liquid Glass material itself and design review pressure scenariosswiftui-performance for profiling Liquid Glass rendering performanceaccessibility-diag for accessibility testingAdopting Liquid Glass doesn't mean reinventing your app from the ground up. Start by building your app in the latest version of Xcode to see the changes. If your app uses standard components from SwiftUI, UIKit, or AppKit, your interface picks up the latest look and feel automatically on the latest platform releases.
Standard components from SwiftUI, UIKit, and AppKit automatically adopt Liquid Glass with minimal code changes.
// ✅ Standard components get Liquid Glass automatically
NavigationView {
List(items) { item in
Text(item.name)
}
.toolbar {
ToolbarItem {
Button("Add") { }
}
}
}
// Recompile with Xcode 26 → Liquid Glass applied
// ❌ Custom backgrounds interfere with Liquid Glass
NavigationView { }
.background(Color.blue.opacity(0.5)) // Breaks Liquid Glass effects
.toolbar {
ToolbarItem { }
.background(LinearGradient(...)) // Overlays system effects
}
Solution Remove custom effects and let the system determine background appearance.
// Launch arguments for accessibility testing
app.launchArguments += [
"-UIAccessibilityIsReduceTransparencyEnabled", "1",
"-UIAccessibilityButtonShapesEnabled", "1",
"-UIAccessibilityIsReduceMotionEnabled", "1"
]
Liquid Glass seeks to bring attention to underlying content. Overusing this material in multiple custom controls distracts from content and provides subpar user experience.
// ✅ Selective application to navigation layer
struct ContentView: View {
var body: some View {
ZStack {
// Content layer - no glass
ScrollView {
ForEach(articles) { article in
ArticleCard(article) // No glass
}
}
// Navigation layer — Liquid Glass
VStack {
Spacer()
HStack {
Button("Filter") { }
.glassEffect() // ✅ Important functional element
Spacer()
Button("Sort") { }
.glassEffect() // ✅ Important functional element
}
.padding()
}
}
}
}
App icons now take on a design that's dynamic and expressive. Updates to the icon grid result in standardized iconography that's visually consistent across devices. App icons contain layers that dynamically respond to lighting and visual effects.
❌ Design includes:
- Pre-applied blur
- Manual shadows
- Hardcoded highlights
- Fixed masking
✅ Design includes:
- Clean vector shapes
- Solid fills
- Semi-transparent overlays
- Foreground/middle/background separation
System automatically adds:
- Reflection, refraction
- Shadow, blur, highlights
- Masking to final shape
Export settings:
- Format: PNG or vector (SVG recommended)
- Resolution: @1x, @2x, @3x
- Transparency: Preserved
- One file per layer
Icon Composer app (included in Xcode 26+):
https://developer.apple.com/design/resources/
❌ Problem: Logo text clipped at edges
✅ Solution: Center design, reduce size to fit safe area
Controls have refreshed look across platforms and come to life during interaction. Knobs transform into Liquid Glass during interaction, buttons fluidly morph into menus/popovers. Hardware shape informs curvature of controls (rounder forms nestle into corners).
// ✅ Standard controls adopt changes automatically
Slider(value: $volume)
Toggle("Enabled", isOn: $isEnabled)
Button("Action") { }
// ❌ Hard-coded dimensions may break
Slider(value: $volume)
.frame(width: 250, height: 44) // May not match new dimensions
// ✅ Use automatic sizing
Slider(value: $volume)
// ✅ System colors adapt to light/dark contexts
Button("Primary") { }
.tint(.accentColor)
Toggle("Feature", isOn: $enabled)
.tint(.blue) // System blue adapts
// ❌ May not adapt to context
Button("Primary") { }
.foregroundColor(Color(red: 0.2, green: 0.4, blue: 0.8))
// ❌ Controls too close together
HStack(spacing: 4) { // Too tight
Button("Action 1") { }.glassEffect()
Button("Action 2") { }.glassEffect()
Button("Action 3") { }.glassEffect()
}
// ✅ System spacing feels natural
HStack {
Button("Action 1") { }.glassEffect()
Button("Action 2") { }.glassEffect()
Button("Action 3") { }.glassEffect()
}
Why Liquid Glass elements need breathing room. Overcrowding or layering glass on glass creates visual noise.
Problem Content scrolling beneath controls can reduce legibility.
Solution scrollEdgeEffectStyle(_:for:)
// ✅ Obscure content scrolling beneath controls
ScrollView {
LazyVStack {
ForEach(items) { item in
ItemRow(item)
}
}
}
.scrollEdgeEffectStyle(.hard, for: .top) // Maintain legibility at top edge
// Custom bar with controls/text/icons
CustomToolbar()
.scrollEdgeEffectStyle(.hard, for: .top)
Principle Shape of hardware informs curvature throughout interface.
containerRelativeShape()// ✅ Control shape aligns with container curvature
Button("Action") { }
.containerRelativeShape(.roundedRectangle)
Result Nested elements feel visually harmonious.
.borderedProminentButton("Primary Action") { }
.buttonStyle(.borderedProminent)
.tint(.blue)
// Filled button with Liquid Glass adaptation
.borderedButton("Secondary Action") { }
.buttonStyle(.bordered)
// Bordered button with Liquid Glass effects
.plain with Liquid GlassButton("Tertiary") { }
.buttonStyle(.plain)
.glassEffect()
// Custom glass application for specific needs
Best Practice Use .borderedProminent for primary actions, .bordered for secondary, .plain for tertiary.
Liquid Glass applies to topmost layer where you define navigation. Key navigation elements like tab bars and sidebars float in this Liquid Glass layer to help people focus on underlying content.
┌─────────────────────────────────┐
│ Navigation Layer (Liquid Glass)│ ← Tab bar, sidebar, toolbar
│ • Clear functional layer │
│ • Floats above content │
├─────────────────────────────────┤
│ Content Layer (No Glass) │ ← Articles, photos, data
│ • Underlying content │
│ • Focus of user attention │
└─────────────────────────────────┘
// ❌ Content and navigation compete
List(items) { item in
ItemRow(item)
.glassEffect() // ❌ Content layer shouldn't have glass
}
Why Clear separation establishes distinct functional layers, helping users understand what's navigation vs content.
// ✅ Tab bar adapts to sidebar depending on context
TabView {
ContentView()
.tabItem { Label("Home", systemImage: "house") }
SearchView()
.tabItem { Label("Search", systemImage: "magnifyingglass") }
}
.tabViewStyle(.sidebarAdaptable) // NEW in iOS 26
// ✅ Split view with sidebar + content + inspector
NavigationSplitView {
// Sidebar
List(folders, selection: $selectedFolder) { folder in
Label(folder.name, systemImage: folder.icon)
}
.navigationTitle("Folders")
} content: {
// Main content
List(items, selection: $selectedItem) { item in
ItemRow(item)
}
} detail: {
// Inspector
InspectorView(item: selectedItem)
}
Problem Content might not peek through appropriately beneath sidebars/inspectors.
// ✅ Respect safe areas for proper content peeking
ScrollView {
LazyVStack {
ForEach(items) { item in
ItemRow(item)
}
}
.safeAreaInset(edge: .leading) {
// Sidebar occupies this space
Color.clear.frame(width: 0)
}
}
When implementing Liquid Glass effects that extend edge-to-edge, use .safeAreaPadding() instead of .padding() to ensure content respects device safe areas (notch, Dynamic Island, home indicator).
// ❌ WRONG - Content hits notch/home indicator with Liquid Glass background
ZStack {
// Liquid Glass background extends edge-to-edge
RoundedRectangle(cornerRadius: 12)
.fill(.thinMaterial)
.ignoresSafeArea()
VStack {
content
}
.padding(.horizontal, 20) // Doesn't account for safe areas!
}
// ✅ CORRECT - Content properly inset from safe areas + custom margin
ZStack {
RoundedRectangle(cornerRadius: 12)
.fill(.thinMaterial)
.ignoresSafeArea()
VStack {
content
}
.safeAreaPadding(.horizontal, 20) // 20pt beyond safe areas
}
Key pattern for Liquid Glass: When your material extends edge-to-edge with .ignoresSafeArea(), always use .safeAreaPadding() on the content layer to maintain proper spacing from screen edges and device-specific features.
Common Liquid Glass scenarios requiring .safeAreaPadding():
.ultraThinMaterial backgroundsPlatform availability: .safeAreaPadding() requires iOS 17+. For iOS 16 and earlier, use .safeAreaInset() or manual GeometryReader calculations. See swiftui-layout-ref skill for complete .safeAreaPadding() vs .padding() guidance.
.safeAreaPadding() with edge-to-edge Liquid Glass)Background extension effect creates impression of stretching content under sidebar/inspector without actually scrolling it there. Mirrors adjacent content + applies blur for legibility.
// ✅ Background extends under sidebar
NavigationSplitView {
SidebarView()
} detail: {
DetailView()
.backgroundExtension(.enabled) // NEW API (placeholder)
}
Tab bars can recede when scrolling to elevate underlying content.
TabView {
ContentView()
.tabItem { Label("Home", systemImage: "house") }
}
.tabBarMinimizationBehavior(.onScrollDown) // NEW in iOS 26
.onScrollDown - Minimize when scrolling down.onScrollUp - Minimize when scrolling up.automatic - System determines.never - Always visibleBest Practice Use .onScrollDown for content-focused apps (reading, media).
Menus have refreshed look across platforms. They adopt Liquid Glass, and menu items for common actions use icons to help people quickly scan and identify actions. iPadOS now has menu bar for faster access to common commands.
// ✅ Standard selectors get icons automatically
Menu("Actions") {
Button(action: cut) {
Text("Cut")
}
Button(action: copy) {
Text("Copy")
}
Button(action: paste) {
Text("Paste")
}
}
// System uses selector to determine icon
// cut() → scissors icon
// copy() → documents icon
// paste() → clipboard icon
cut() → ✂️ scissorscopy() → 📄 documentspaste() → 📋 clipboarddelete() → 🗑️ trashshare() → ↗️ share arrow// ✅ Provide icon for custom actions
Button {
customAction()
} label: {
Label("Custom Action", systemImage: "star.fill")
}
// ✅ Swipe actions match contextual menu
List(emails) { email in
EmailRow(email)
.swipeActions(edge: .leading) {
Button("Archive", systemImage: "archivebox") {
archive(email)
}
}
.swipeActions(edge: .trailing) {
Button("Delete", systemImage: "trash", role: .destructive) {
delete(email)
}
}
.contextMenu {
// ✅ Same actions appear at top
Button("Archive", systemImage: "archivebox") {
archive(email)
}
Button("Delete", systemImage: "trash", role: .destructive) {
delete(email)
}
Divider()
// Additional actions below
Button("Mark Unread") { }
}
}
Why Users expect swipe actions and menu actions to match. Consistency builds trust and predictability.
// ✅ Fixed spacer separates groups
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
// Navigation group
Button("Up") { }
Button("Down") { }
Spacer(.fixed) // NEW in iOS 26 - separates groups
// Action group
Button("Settings") { }
}
}
.fixed spacer creates visual separation.fixed for logical separation// ✅ Icons declutter interface
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
share()
} label: {
Label("Share", systemImage: "square.and.arrow.up")
}
}
}
// ❌ Inconsistent visual style
.toolbar {
ToolbarItemGroup {
Button("Save") { } // Text
Button {
share()
} label: {
Image(systemName: "square.and.arrow.up") // Icon
}
}
}
Guideline Pick one style (icons OR text) per background group, not both.
// ✅ Icon has accessibility label
Button {
share()
} label: {
Image(systemName: "square.and.arrow.up")
}
.accessibilityLabel("Share")
Why People using VoiceOver or Voice Control need text labels even when icons are shown visually.
// ❌ Hiding view instead of item
.toolbar {
ToolbarItem {
if showButton {
Button("Action") { }
} else {
EmptyView() // ❌ Creates empty toolbar item
}
}
}
// ✅ Hide entire toolbar item
.toolbar {
if showButton {
ToolbarItem {
Button("Action") { }
}
}
}
Windows adopt rounder corners to fit controls and navigation elements. iPadOS apps show window controls and support continuous window resizing. Sheets and action sheets adopt Liquid Glass with increased corner radius.
// ✅ Content adapts to arbitrary window sizes
WindowGroup {
ContentView()
}
.defaultSize(width: 600, height: 800)
.windowResizability(.contentSize) // NEW in iPadOS 26
// ✅ Split view handles continuous resizing
NavigationSplitView(columnVisibility: $columnVisibility) {
SidebarView()
} detail: {
DetailView()
}
// ✅ Content respects safe areas
VStack {
ContentView()
}
.safeAreaInset(edge: .top) {
// Window controls and title bar
Color.clear.frame(height: 0)
}
// Test half sheet presentation
.sheet(isPresented: $showSheet) {
SheetContent()
.presentationDetents([.medium, .large])
}
// ❌ Custom visual effect view in popover
.popover(isPresented: $showPopover) {
ContentView()
.background(
VisualEffectView(effect: UIBlurEffect(style: .systemMaterial))
) // ❌ Interferes with Liquid Glass
}
// ✅ System applies Liquid Glass automatically
.popover(isPresented: $showPopover) {
ContentView()
// No background modifier needed
}
Action sheets originate from the element that initiates the action (not bottom edge of display). When active, action sheets let people interact with other parts of interface.
// ✅ Action sheet anchored to source
Button("Options") {
showActionSheet = true
}
.confirmationDialog("Options", isPresented: $showActionSheet) {
Button("Option 1") { }
Button("Option 2") { }
Button("Cancel", role: .cancel) { }
}
// System positions sheet next to button automatically
Lists, tables, and forms have larger row height and padding to give content room to breathe. Sections have increased corner radius to match curvature of controls.
// ✅ Standard components adopt new metrics
List(items) { item in
Text(item.name)
}
// ❌ May look cramped with new design
List(items) { item in
Text(item.name)
.frame(height: 44) // ❌ Hard-coded height
.padding(.vertical, 4) // ❌ Hard-coded padding
}
// ✅ Use automatic sizing
List(items) { item in
Text(item.name)
// System determines row height and padding
}
Section headers no longer render entirely in capital letters. They now respect title-style capitalization you provide.
Section(header: Text("User settings")) {
// Rendered as "USER SETTINGS"
}
Section(header: Text("User Settings")) {
// Rendered as "User Settings" (title-style)
}
Update section headers to title-style capitalization:
// ❌ Old style (all lowercase)
Section(header: Text("user settings")) {
// Renders as "user settings" (looks wrong)
}
// ✅ New style (title-style)
Section(header: Text("User Settings")) {
// Renders as "User Settings" (correct)
}
.grouped form style// ✅ Form adopts platform-optimized layout
Form {
Section("Personal Information") {
TextField("Name", text: $name)
TextField("Email", text: $email)
}
Section("Preferences") {
Toggle("Notifications", isOn: $notificationsEnabled)
Picker("Theme", selection: $theme) {
Text("Light").tag(Theme.light)
Text("Dark").tag(Theme.dark)
Text("Auto").tag(Theme.auto)
}
}
}
.formStyle(.grouped) // Platform-optimized metrics
Platform conventions for search location and behavior optimize experience for each device. Review search field design conventions to provide engaging search experience.
When a person taps search field to give it focus, it slides upwards as keyboard appears.
// ✅ Existing searchable modifier adopts new behavior
List(items) { item in
Text(item.name)
}
.searchable(text: $searchText)
For Tab API patterns including .tabRole(.search), see swiftui-nav-ref Section 5.
Liquid Glass can have distinct appearance and behavior across platforms, contexts, and input methods. Test across devices to understand material appearance.
Liquid Glass changes are minimal in watchOS. They appear automatically when you open app on latest release even without building against latest SDK.
// ✅ Standard button styles on watchOS
Button("Action") { }
.buttonStyle(.bordered)
Standard buttons and controls take on Liquid Glass appearance when focus moves to them.
// ✅ Standard focus APIs for Liquid Glass on focus
Button("Action") { }
.focusable()
Apply Liquid Glass effects when they gain focus:
// ✅ Custom control with focus-based glass
struct CustomControl: View {
@FocusState private var isFocused: Bool
var body: some View {
Text("Custom")
.glassEffect()
.opacity(isFocused ? 1.0 : 0.5)
.focused($isFocused)
}
}
GlassEffectContainer for Performance// ✅ Combine effects in container for optimization
GlassEffectContainer {
HStack {
Button("Action 1") { }
.glassEffect()
Button("Action 2") { }
.glassEffect()
Button("Action 3") { }
.glassEffect()
}
}
Building with latest SDKs is opportunity to assess and improve performance.
swiftui-performance skill)swiftui-performance - SwiftUI Instrument workflowsperformance-profiling - Instruments decision treesTo ship with latest SDKs while keeping app as it looked when built against previous SDKs:
UIDesignRequiresCompatibility key<!-- Info.plist -->
<key>UIDesignRequiresCompatibility</key>
<true/>
UIDesignRequiresCompatibility enabledglassEffect() - Apply Liquid Glass materialglassEffect(.clear) - Clear variant (requires 3 conditions)glassEffect(in: Shape) - Custom shapeglassBackgroundEffect() - For custom views reflecting contentscrollEdgeEffectStyle(_:for:) - Maintain legibility where glass meets scrolling content.hard style for pinned accessory views.soft style for gradual fadecontainerRelativeShape() - Align control shapes with containers.borderedProminent button style.bordered button style.tint() for adaptation.tabViewStyle(.sidebarAdaptable) - Tab bar adapts to sidebar.tabBarMinimizationBehavior(_:) - Minimize on scroll.tabRole(.search) - Semantic search tabsNavigationSplitView for sidebar + inspector layoutsSpacer(.fixed) - Separate toolbar groups.formStyle(.grouped) - Platform-optimized form layoutsGlassEffectContainer - Combine multiple glass effectsUIDesignRequiresCompatibility in Info.plist (if needed)Use this checklist when auditing app for Liquid Glass adoption:
.borderedProminent, .bordered)Spacer(.fixed).formStyle(.grouped) adopted for forms.tabRole(.search) used for search tabs (if tab-based)GlassEffectContainer used for combining custom effectsUIDesignRequiresCompatibility key considered (if backward compatibility needed)liquid-glass - Implementing Liquid Glass material, design review pressureswiftui-performance - Profiling Liquid Glass rendering performanceswiftui-debugging - Debugging view updates with Liquid Glassaccessibility-diag - Accessibility testingLast Updated: 2025-12-01 Minimum Platform: iOS 26, iPadOS 26, macOS Tahoe, tvOS, watchOS, visionOS 3 Xcode Version: Xcode 26+ Skill Type: Reference (comprehensive adoption guide)