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(_ glass: Glass = .regular, in shape: some Shape = .rect, isEnabled: Bool = true)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### Glass7273#### Available Styles7475The `Glass` type exposes three static values — there is no `.prominent`:7677```swift78.glassEffect(.regular) // Standard glass appearance (most common)79.glassEffect(.clear) // Nearly invisible glass surface80.glassEffect(.identity) // No-op / pass-through glass81```8283To make a surface appear more prominent, increase the tint opacity instead of reaching for a non-existent `.prominent` property.8485#### Tinting8687Add color tint to the glass:8889```swift90.glassEffect(.regular.tint(.blue))91.glassEffect(.regular.tint(.red.opacity(0.3)))92```9394#### Interactivity9596Make glass respond to touch/pointer hover:9798```swift99// Interactive glass - responds to user interaction100.glassEffect(.regular.interactive())101102// Combined with tint103.glassEffect(.regular.tint(.blue).interactive())104```105106**Important**: Only use `.interactive()` on elements that actually respond to user input (buttons, tappable views, focusable elements).107108## GlassEffectContainer109110Wraps multiple glass elements for proper visual grouping and spacing.111112**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.113114```swift115GlassEffectContainer {116HStack {117Button("One") { }118.glassEffect()119Button("Two") { }120.glassEffect()121}122}123```124125### With Spacing126127Control the visual spacing between glass elements:128129```swift130GlassEffectContainer(spacing: 24) {131HStack(spacing: 24) {132GlassChip(icon: "pencil")133GlassChip(icon: "eraser")134GlassChip(icon: "trash")135}136}137```138139**Note**: The container's `spacing` parameter should match the actual spacing in your layout for proper glass effect rendering.140141> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)142143## Glass Button Styles144145Built-in button styles for glass appearance:146147```swift148// Standard glass button149Button("Action") { }150.buttonStyle(.glass)151152// Prominent glass button (higher visibility)153Button("Primary Action") { }154.buttonStyle(.glassProminent)155```156157### Custom Glass Buttons158159For more control, apply glass effect manually:160161```swift162Button(action: { }) {163Label("Settings", systemImage: "gear")164.padding()165}166.glassEffect(.regular.interactive(), in: .capsule)167```168169## Morphing Transitions170171Create smooth transitions between glass elements using `glassEffectID` and `@Namespace`:172173```swift174struct MorphingExample: View {175@Namespace private var animation176@State private var isExpanded = false177178var body: some View {179GlassEffectContainer {180if isExpanded {181ExpandedCard()182.glassEffect()183.glassEffectID("card", in: animation)184} else {185CompactCard()186.glassEffect()187.glassEffectID("card", in: animation)188}189}190.animation(.smooth, value: isExpanded)191}192}193```194195### Requirements for Morphing1961971. Both views must have the same `glassEffectID`1982. Use the same `@Namespace`1993. Wrap in `GlassEffectContainer`2004. Apply animation to the container or parent201202## Modifier Order203204**Critical**: Apply `glassEffect` after layout and visual modifiers:205206```swift207// CORRECT order208Text("Label")209.font(.headline) // 1. Typography210.foregroundStyle(.primary) // 2. Color211.padding() // 3. Layout212.glassEffect() // 4. Glass effect LAST213214// WRONG order - glass applied too early215Text("Label")216.glassEffect() // Wrong position217.padding()218.font(.headline)219```220221## Complete Examples222223### Toolbar with Glass Buttons224225```swift226struct GlassToolbar: View {227var body: some View {228if #available(iOS 26, *) {229GlassEffectContainer(spacing: 16) {230HStack(spacing: 16) {231ToolbarButton(icon: "pencil", action: { })232ToolbarButton(icon: "eraser", action: { })233ToolbarButton(icon: "scissors", action: { })234Spacer()235ToolbarButton(icon: "square.and.arrow.up", action: { })236}237.padding(.horizontal)238}239} else {240// Fallback toolbar241HStack(spacing: 16) {242// ... fallback implementation243}244}245}246}247248struct ToolbarButton: View {249let icon: String250let action: () -> Void251252var body: some View {253Button(action: action) {254Image(systemName: icon)255.font(.title2)256.frame(width: 44, height: 44)257}258.glassEffect(.regular.interactive(), in: .circle)259}260}261```262263### Card with Glass Effect264265```swift266struct GlassCard: View {267let title: String268let subtitle: String269270var body: some View {271if #available(iOS 26, *) {272cardContent273.glassEffect(.regular, in: .rect(cornerRadius: 20))274} else {275cardContent276.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 20))277}278}279280private var cardContent: some View {281VStack(alignment: .leading, spacing: 8) {282Text(title)283.font(.headline)284Text(subtitle)285.font(.subheadline)286.foregroundStyle(.secondary)287}288.padding()289.frame(maxWidth: .infinity, alignment: .leading)290}291}292```293294### Segmented Control295296```swift297struct GlassSegmentedControl: View {298@Binding var selection: Int299let options: [String]300@Namespace private var animation301302var body: some View {303if #available(iOS 26, *) {304GlassEffectContainer(spacing: 4) {305HStack(spacing: 4) {306ForEach(options.indices, id: \.self) { index in307Button(options[index]) {308withAnimation(.smooth) {309selection = index310}311}312.padding(.horizontal, 16)313.padding(.vertical, 8)314.glassEffect(315selection == index316? .regular.tint(.accentColor.opacity(0.4)).interactive()317: .regular.interactive(),318in: .capsule319)320.glassEffectID(selection == index ? "selected" : "option\(index)", in: animation)321}322}323.padding(4)324}325} else {326Picker("Options", selection: $selection) {327ForEach(options.indices, id: \.self) { index in328Text(options[index]).tag(index)329}330}331.pickerStyle(.segmented)332}333}334}335```336337## Fallback Strategies338339### Using Materials340341```swift342if #available(iOS 26, *) {343content.glassEffect()344} else {345content.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))346}347```348349### Available Materials for Fallback350351- `.ultraThinMaterial` - Closest to glass appearance352- `.thinMaterial` - Slightly more opaque353- `.regularMaterial` - Standard blur354- `.thickMaterial` - More opaque355- `.ultraThickMaterial` - Most opaque356357### Conditional Modifier Extension358359```swift360extension View {361@ViewBuilder362func glassEffectWithFallback(363_ glass: Glass = .regular,364in shape: some Shape = .rect,365fallbackMaterial: Material = .ultraThinMaterial366) -> some View {367if #available(iOS 26, *) {368self.glassEffect(glass, in: shape)369} else {370self.background(fallbackMaterial, in: shape)371}372}373}374```375376## Design System Notes377378### Toolbar Icons379380In 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.381382### Sheet Presentations383384Partial-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`.385386### Scroll Edge Effects387388An 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.389390> Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)391392## Best Practices393394### Do395396- Use `GlassEffectContainer` for grouped glass elements (glass cannot sample other glass)397- Apply glass after layout modifiers398- Use `.interactive()` only on tappable elements399- Match container spacing with layout spacing400- Provide material-based fallbacks for older iOS401- Keep glass shapes consistent within a feature402- Remove custom `presentationBackground(_:)` on sheets to use the default glass material403404### Don't405406- Apply glass to every element (use sparingly)407- Use `.interactive()` on static content408- Mix different corner radii arbitrarily409- Forget iOS version checks410- Apply glass before padding/frame modifiers411- Nest `GlassEffectContainer` unnecessarily412- Add custom darkening backgrounds behind toolbars (conflicts with scroll edge effect)413414## Checklist415416- [ ] `#available(iOS 26, *)` with fallback417- [ ] `GlassEffectContainer` wraps grouped elements418- [ ] `.glassEffect()` applied after layout modifiers419- [ ] `.interactive()` only on user-interactable elements420- [ ] `glassEffectID` with `@Namespace` for morphing421- [ ] Consistent shapes and spacing across feature422- [ ] Container spacing matches layout spacing423- [ ] Tint opacity used instead of non-existent `.prominent` for emphasis424