Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Reviews, improves, and writes SwiftUI code following state management, view composition, performance, and iOS 26+ Liquid Glass best practices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/liquid-glass.md
1# SwiftUI Liquid Glass Reference (iOS 26+)23## Table of Contents45- [Overview](#overview)6- [Availability](#availability)7- [Core APIs](#core-apis)8- [GlassEffectContainer](#glasseffectcontainer)9- [Glass Button Styles](#glass-button-styles)10- [Morphing Transitions](#morphing-transitions)11- [Modifier Order](#modifier-order)12- [Complete Examples](#complete-examples)13- [Fallback Strategies](#fallback-strategies)14- [Design System Notes](#design-system-notes)15- [Best Practices](#best-practices)16- [Checklist](#checklist)1718## Overview1920Liquid Glass is Apple's new design language introduced in iOS 26. It provides translucent, dynamic surfaces that respond to content and user interaction. This reference covers the native SwiftUI APIs for implementing Liquid Glass effects.2122**Only adopt Liquid Glass when explicitly requested by the user.** Do not proactively convert existing UI to glass effects.2324## Availability2526All Liquid Glass APIs require iOS 26 or later. Always provide fallbacks:2728```swift29if #available(iOS 26, *) {30// Liquid Glass implementation31} else {32// Fallback using materials33}34```3536## Core APIs3738### glassEffect Modifier3940The primary modifier for applying glass effects to views:4142```swift43.glassEffect(_ style: GlassEffectStyle = .regular, in shape: some Shape = .rect)44```4546#### Basic Usage4748```swift49Text("Hello")50.padding()51.glassEffect() // Default regular style, rect shape52```5354#### With Shape5556```swift57Text("Rounded Glass")58.padding()59.glassEffect(in: .rect(cornerRadius: 16))6061Image(systemName: "star")62.padding()63.glassEffect(in: .circle)6465Text("Capsule")66.padding(.horizontal, 20)67.padding(.vertical, 10)68.glassEffect(in: .capsule)69```7071### GlassEffectStyle7273#### Prominence Levels7475```swift76.glassEffect(.regular) // Standard glass appearance77.glassEffect(.prominent) // More visible, higher contrast78```7980#### Tinting8182Add color tint to the glass:8384```swift85.glassEffect(.regular.tint(.blue))86.glassEffect(.prominent.tint(.red.opacity(0.3)))87```8889#### Interactivity9091Make glass respond to touch/pointer hover:9293```swift94// Interactive glass - responds to user interaction95.glassEffect(.regular.interactive())9697// Combined with tint98.glassEffect(.regular.tint(.blue).interactive())99```100101**Important**: Only use `.interactive()` on elements that actually respond to user input (buttons, tappable views, focusable elements).102103## GlassEffectContainer104105Wraps multiple glass elements for proper visual grouping and spacing.106107**Glass cannot sample other glass.** The glass material reflects and refracts light by sampling content from an area larger than itself. Nearby glass elements in different containers will produce inconsistent visual results because they cannot sample each other. `GlassEffectContainer` gives grouped elements a shared sampling region, ensuring a consistent appearance.108109```swift110GlassEffectContainer {111HStack {112Button("One") { }113.glassEffect()114Button("Two") { }115.glassEffect()116}117}118```119120### With Spacing121122Control the visual spacing between glass elements:123124```swift125GlassEffectContainer(spacing: 24) {126HStack(spacing: 24) {127GlassChip(icon: "pencil")128GlassChip(icon: "eraser")129GlassChip(icon: "trash")130}131}132```133134**Note**: The container's `spacing` parameter should match the actual spacing in your layout for proper glass effect rendering.135136> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)137138## Glass Button Styles139140Built-in button styles for glass appearance:141142```swift143// Standard glass button144Button("Action") { }145.buttonStyle(.glass)146147// Prominent glass button (higher visibility)148Button("Primary Action") { }149.buttonStyle(.glassProminent)150```151152### Custom Glass Buttons153154For more control, apply glass effect manually:155156```swift157Button(action: { }) {158Label("Settings", systemImage: "gear")159.padding()160}161.glassEffect(.regular.interactive(), in: .capsule)162```163164## Morphing Transitions165166Create smooth transitions between glass elements using `glassEffectID` and `@Namespace`:167168```swift169struct MorphingExample: View {170@Namespace private var animation171@State private var isExpanded = false172173var body: some View {174GlassEffectContainer {175if isExpanded {176ExpandedCard()177.glassEffect()178.glassEffectID("card", in: animation)179} else {180CompactCard()181.glassEffect()182.glassEffectID("card", in: animation)183}184}185.animation(.smooth, value: isExpanded)186}187}188```189190### Requirements for Morphing1911921. Both views must have the same `glassEffectID`1932. Use the same `@Namespace`1943. Wrap in `GlassEffectContainer`1954. Apply animation to the container or parent196197## Modifier Order198199**Critical**: Apply `glassEffect` after layout and visual modifiers:200201```swift202// CORRECT order203Text("Label")204.font(.headline) // 1. Typography205.foregroundStyle(.primary) // 2. Color206.padding() // 3. Layout207.glassEffect() // 4. Glass effect LAST208209// WRONG order - glass applied too early210Text("Label")211.glassEffect() // Wrong position212.padding()213.font(.headline)214```215216## Complete Examples217218### Toolbar with Glass Buttons219220```swift221struct GlassToolbar: View {222var body: some View {223if #available(iOS 26, *) {224GlassEffectContainer(spacing: 16) {225HStack(spacing: 16) {226ToolbarButton(icon: "pencil", action: { })227ToolbarButton(icon: "eraser", action: { })228ToolbarButton(icon: "scissors", action: { })229Spacer()230ToolbarButton(icon: "square.and.arrow.up", action: { })231}232.padding(.horizontal)233}234} else {235// Fallback toolbar236HStack(spacing: 16) {237// ... fallback implementation238}239}240}241}242243struct ToolbarButton: View {244let icon: String245let action: () -> Void246247var body: some View {248Button(action: action) {249Image(systemName: icon)250.font(.title2)251.frame(width: 44, height: 44)252}253.glassEffect(.regular.interactive(), in: .circle)254}255}256```257258### Card with Glass Effect259260```swift261struct GlassCard: View {262let title: String263let subtitle: String264265var body: some View {266if #available(iOS 26, *) {267cardContent268.glassEffect(.regular, in: .rect(cornerRadius: 20))269} else {270cardContent271.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 20))272}273}274275private var cardContent: some View {276VStack(alignment: .leading, spacing: 8) {277Text(title)278.font(.headline)279Text(subtitle)280.font(.subheadline)281.foregroundStyle(.secondary)282}283.padding()284.frame(maxWidth: .infinity, alignment: .leading)285}286}287```288289### Segmented Control290291```swift292struct GlassSegmentedControl: View {293@Binding var selection: Int294let options: [String]295@Namespace private var animation296297var body: some View {298if #available(iOS 26, *) {299GlassEffectContainer(spacing: 4) {300HStack(spacing: 4) {301ForEach(options.indices, id: \.self) { index in302Button(options[index]) {303withAnimation(.smooth) {304selection = index305}306}307.padding(.horizontal, 16)308.padding(.vertical, 8)309.glassEffect(310selection == index ? .prominent.interactive() : .regular.interactive(),311in: .capsule312)313.glassEffectID(selection == index ? "selected" : "option\(index)", in: animation)314}315}316.padding(4)317}318} else {319Picker("Options", selection: $selection) {320ForEach(options.indices, id: \.self) { index in321Text(options[index]).tag(index)322}323}324.pickerStyle(.segmented)325}326}327}328```329330## Fallback Strategies331332### Using Materials333334```swift335if #available(iOS 26, *) {336content.glassEffect()337} else {338content.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))339}340```341342### Available Materials for Fallback343344- `.ultraThinMaterial` - Closest to glass appearance345- `.thinMaterial` - Slightly more opaque346- `.regularMaterial` - Standard blur347- `.thickMaterial` - More opaque348- `.ultraThickMaterial` - Most opaque349350### Conditional Modifier Extension351352```swift353extension View {354@ViewBuilder355func glassEffectWithFallback(356_ style: GlassEffectStyle = .regular,357in shape: some Shape = .rect,358fallbackMaterial: Material = .ultraThinMaterial359) -> some View {360if #available(iOS 26, *) {361self.glassEffect(style, in: shape)362} else {363self.background(fallbackMaterial, in: shape)364}365}366}367```368369## Design System Notes370371### Toolbar Icons372373In the new design, toolbar icons use **monochrome rendering** by default. The monochrome palette reduces visual noise and maintains legibility. Use `tint(_:)` only to convey meaning (e.g., a call to action), not for visual effect.374375### Sheet Presentations376377Partial-height sheets use a Liquid Glass background by default. If you previously used `presentationBackground(_:)` with a custom background, consider removing it to let the new material shine. Sheets can morph out of the glass controls that present them using `navigationZoomTransition`.378379### Scroll Edge Effects380381An automatic scroll edge effect blurs and fades content under system toolbars to keep controls legible. Remove any custom background-darkening effects behind bar items, as they will interfere.382383> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)384385## Best Practices386387### Do388389- Use `GlassEffectContainer` for grouped glass elements (glass cannot sample other glass)390- Apply glass after layout modifiers391- Use `.interactive()` only on tappable elements392- Match container spacing with layout spacing393- Provide material-based fallbacks for older iOS394- Keep glass shapes consistent within a feature395- Remove custom `presentationBackground(_:)` on sheets to use the default glass material396397### Don't398399- Apply glass to every element (use sparingly)400- Use `.interactive()` on static content401- Mix different corner radii arbitrarily402- Forget iOS version checks403- Apply glass before padding/frame modifiers404- Nest `GlassEffectContainer` unnecessarily405- Add custom darkening backgrounds behind toolbars (conflicts with scroll edge effect)406407## Checklist408409- [ ] `#available(iOS 26, *)` with fallback410- [ ] `GlassEffectContainer` wraps grouped elements411- [ ] `.glassEffect()` applied after layout modifiers412- [ ] `.interactive()` only on user-interactable elements413- [ ] `glassEffectID` with `@Namespace` for morphing414- [ ] Consistent shapes and spacing across feature415- [ ] Container spacing matches layout spacing416- [ ] Appropriate prominence levels used417