smithery/co-labs-co

ios26-liquid-glass-design

Apple's Liquid Glass design system for iOS 26+ with backward-compatible fallbacks for iOS 17-25. Use this skill when implementing glass effects, translucent buttons, adaptive glass styling, or building UIs that gracefully degrade across iOS versions.

Installation

$ npx skills add smithery/co-labs-co --skill ios26-liquid-glass-design

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/co-labs-co.

npx skills add smithery/co-labs-co

Browse all from smithery/co-labs-co

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Version0.1.0

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 14,252 B
  • docs SUMMARY.md 283 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

iOS 26 Liquid Glass Design Pattern

Implement Apple's Liquid Glass design system introduced in iOS 26 (WWDC25) with graceful fallbacks for iOS 17-25. This skill covers adaptive glass styling, button styles, container effects, and the architectural patterns for building version-aware SwiftUI components.

Design Principles (WWDC25 Sessions 219, 323, 356)

Core Guidelines

  1. Content-hugging shapes: Glass buttons should be capsule-shaped, NOT full-width
  2. Hierarchy through prominence:

- Primary actions → .glassProminent with tinting - Secondary actions → .glass without tinting

  1. Never stack glass on glass: Avoid layering multiple glass effects
  2. Capsule is default: Large/Extra-Large buttons automatically use capsule shapes

Visual Hierarchy

Element Type iOS 26 Style Fallback Style
Primary action .buttonStyle(.glassProminent) Filled capsule with color
Secondary action .buttonStyle(.glass) Semi-transparent capsule
Status badge .glassEffect() Color.black.opacity(0.6) capsule
Card container .glassEffect() Color.black.opacity(0.7) rounded rect

Architecture Pattern

The Availability Check Problem

Direct if #available checks in SwiftUI body cause performance issues due to view diffing. Instead, use wrapper components with separate iOS 26+ and fallback implementations.

// ❌ AVOID: if #available in body causes performance issues
struct BadButton: View {
    var body: some View {
        if #available(iOS 26.0, *) {
            Button("Tap") { }
                .buttonStyle(.glass)  // Recomputed every render
        } else {
            Button("Tap") { }
                .background(Capsule().fill(.black.opacity(0.6)))
        }
    }
}

// ✅ CORRECT: Wrapper component with separate implementations
struct GlassButton: View {
    let title: String
    let action: () -> Void
    
    var body: some View {
        if #available(iOS 26.0, *) {
            GlassButtoniOS26(title: title, action: action)
        } else {
            GlassButtonFallback(title: title, action: action)
        }
    }
}

View Extension for Adaptive Glass

Basic Glass Effect

extension View {
    /// Applies glass effect on iOS 26+, or semi-transparent capsule on older versions
    /// Use for badges, status indicators, and small UI elements
    @ViewBuilder
    func adaptiveGlass() -> some View {
        if #available(iOS 26.0, *) {
            self.glassEffect()
        } else {
            self.background(Capsule().fill(Color.black.opacity(0.6)))
        }
    }
    
    /// Applies glass effect with rounded rectangle on iOS 26+, or fallback styling
    /// Use for cards and larger container elements
    @ViewBuilder
    func adaptiveGlassCard() -> some View {
        if #available(iOS 26.0, *) {
            self.glassEffect()
        } else {
            self.background(
                RoundedRectangle(cornerRadius: 16)
                    .fill(Color.black.opacity(0.7))
            )
        }
    }
}

Usage

// Status badge with glass effect
Text("LIVE")
    .font(.caption.bold())
    .foregroundColor(.white)
    .padding(.horizontal, 12)
    .padding(.vertical, 6)
    .adaptiveGlass()

// Card with glass effect
VStack {
    Text("Stream Info")
    Text("1920x1080 @ 30fps")
}
.padding()
.adaptiveGlassCard()

Glass Button Components

Primary Action Button (GlassProminent)

/// Primary action button with glass prominent style (iOS 26+)
@available(iOS 26.0, *)
struct GlassProminentButtoniOS26: View {
    let title: String
    let icon: String
    var tintColor: Color = .blue
    let action: () -> Void
    
    var body: some View {
        Button(action: action) {
            Label(title, systemImage: icon)
                .font(.title2.bold())
                .padding(.horizontal, 8)
                .padding(.vertical, 4)
        }
        .buttonStyle(.glassProminent)
        .tint(tintColor)
    }
}

/// Primary action button fallback (iOS 17-25)
struct GlassProminentButtonFallback: View {
    let title: String
    let icon: String
    var tintColor: Color = .blue
    let action: () -> Void
    
    var body: some View {
        Button(action: action) {
            HStack(spacing: 10) {
                Image(systemName: icon)
                    .font(.title2)
                Text(title)
                    .font(.title3.bold())
            }
            .foregroundColor(.white)
            .padding(.horizontal, 28)
            .padding(.vertical, 18)
            .background(
                Capsule()
                    .fill(tintColor.opacity(0.85))
            )
        }
    }
}

/// Wrapper that chooses the right implementation
struct GlassProminentButton: View {
    let title: String
    let icon: String
    var tintColor: Color = .blue
    let action: () -> Void
    
    var body: some View {
        if #available(iOS 26.0, *) {
            GlassProminentButtoniOS26(title: title, icon: icon, tintColor: tintColor, action: action)
        } else {
            GlassProminentButtonFallback(title: title, icon: icon, tintColor: tintColor, action: action)
        }
    }
}

Secondary Action Button (Glass)

/// Secondary action button with glass style (iOS 26+)
@available(iOS 26.0, *)
struct GlassButtoniOS26: View {
    let title: String
    let icon: String
    let action: () -> Void
    
    var body: some View {
        Button(action: action) {
            Label(title, systemImage: icon)
                .font(.title3.weight(.semibold))
                .padding(.horizontal, 6)
                .padding(.vertical, 2)
        }
        .buttonStyle(.glass)
    }
}

/// Secondary action button fallback (iOS 17-25)
struct GlassButtonFallback: View {
    let title: String
    let icon: String
    let action: () -> Void
    
    var body: some View {
        Button(action: action) {
            HStack(spacing: 10) {
                Image(systemName: icon)
                    .font(.title3)
                Text(title)
                    .font(.title3.weight(.semibold))
            }
            .foregroundColor(.white)
            .padding(.horizontal, 24)
            .padding(.vertical, 16)
            .background(
                Capsule()
                    .fill(Color.black.opacity(0.6))
            )
        }
    }
}

/// Wrapper that chooses the right implementation
struct GlassButton: View {
    let title: String
    let icon: String
    let action: () -> Void
    
    var body: some View {
        if #available(iOS 26.0, *) {
            GlassButtoniOS26(title: title, icon: icon, action: action)
        } else {
            GlassButtonFallback(title: title, icon: icon, action: action)
        }
    }
}

Full-Width Bottom Button

For full-width primary actions (like Start/Stop streaming), use .ultraThinMaterial with a color overlay instead of pure glass:

struct FullWidthBottomButton: View {
    let title: String
    let icon: String
    var tintColor: Color = .blue
    var isLoading: Bool = false
    let action: () -> Void
    
    private var isLargeScreen: Bool {
        UIDevice.current.userInterfaceIdiom == .pad
    }
    
    var body: some View {
        Button(action: action) {
            HStack(spacing: 10) {
                Image(systemName: icon)
                    .font(.title2)
                Text(title)
                    .font(.title3.bold())
            }
            .foregroundColor(.white)
            .frame(maxWidth: isLargeScreen ? 300 : .infinity)
            .padding(.vertical, 18)
            .padding(.horizontal, 24)
            .background(
                RoundedRectangle(cornerRadius: 16)
                    .fill(.ultraThinMaterial)
                    .overlay(
                        RoundedRectangle(cornerRadius: 16)
                            .fill(tintColor.opacity(0.3))
                    )
            )
        }
        .disabled(isLoading)
        .padding(.horizontal, isLargeScreen ? 0 : 16)
        .padding(.bottom, isLargeScreen ? 40 : 24)
    }
}

CI/CD Considerations

The Xcode Version Problem

GitHub Actions and most CI services use stable Xcode versions. iOS 26 SDK requires Xcode 26, which may not be available in CI when iOS 26 is first released.

Conditional Compilation Strategy

// Option 1: Runtime availability check (always compiles)
if #available(iOS 26.0, *) {
    self.glassEffect()
}

// Option 2: Compiler version check (for APIs that don't exist in older SDKs)
#if compiler(>=6.2)  // Swift 6.2 = Xcode 26
    .buttonStyle(.glassProminent)
#else
    .buttonStyle(.borderedProminent)
#endif

Recommended Approach

Use runtime #available checks that compile on any Xcode version, with fallback implementations that look good on iOS 17-25:

// This compiles on Xcode 15/16 AND Xcode 26
// On iOS 26+: Uses native glass effect
// On iOS 17-25: Uses semi-transparent capsule
@ViewBuilder
func adaptiveGlass() -> some View {
    if #available(iOS 26.0, *) {
        self.glassEffect()
    } else {
        self.background(Capsule().fill(Color.black.opacity(0.6)))
    }
}

Status Badge Example

struct StatusBadge: View {
    let isLive: Bool
    let text: String
    
    var body: some View {
        HStack(spacing: 6) {
            Circle()
                .fill(isLive ? Color.red : Color.gray)
                .frame(width: 8, height: 8)
            Text(text)
                .font(.caption.bold())
                .foregroundColor(.white)
        }
        .padding(.horizontal, 12)
        .padding(.vertical, 6)
        .adaptiveGlass()
    }
}

// Usage
StatusBadge(isLive: true, text: "LIVE")
StatusBadge(isLive: false, text: "OFFLINE")

Card Container Example

struct InfoCard: View {
    let title: String
    let value: String
    
    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(title)
                .font(.caption)
                .foregroundColor(.secondary)
            Text(value)
                .font(.headline)
                .foregroundColor(.primary)
        }
        .padding()
        .frame(maxWidth: .infinity, alignment: .leading)
        .adaptiveGlassCard()
    }
}

Complete Usage Example

struct StreamingControlView: View {
    @State private var isStreaming = false
    
    var body: some View {
        ZStack {
            // Camera preview background
            CameraPreviewView()
            
            VStack {
                // Top status bar with glass badges
                HStack {
                    StatusBadge(isLive: isStreaming, text: isStreaming ? "LIVE" : "READY")
                    Spacer()
                    
                    // Settings button with glass style
                    GlassButton(title: "Settings", icon: "gear") {
                        // Open settings
                    }
                }
                .padding()
                
                Spacer()
                
                // Bottom action button
                if isStreaming {
                    GlassProminentButton(
                        title: "Stop Streaming",
                        icon: "stop.fill",
                        tintColor: .red
                    ) {
                        isStreaming = false
                    }
                } else {
                    GlassProminentButton(
                        title: "Start Streaming",
                        icon: "play.fill",
                        tintColor: .green
                    ) {
                        isStreaming = true
                    }
                }
            }
        }
    }
}

Fallback Color Recommendations

Use Case Fallback Background
Dark overlay (camera UI) Color.black.opacity(0.6)
Card background Color.black.opacity(0.7)
Light mode badge Color.white.opacity(0.8)
Tinted primary button tintColor.opacity(0.85)

Testing Checklist

  • Test on iOS 26 simulator/device with native glass effects
  • Test on iOS 17-25 with fallback styling
  • Verify no visual glitches when switching between versions
  • Check dark mode and light mode appearance
  • Validate contrast ratios for accessibility
  • Test on iPad (larger touch targets, different layouts)

Common Mistakes to Avoid

1. Stacking Glass on Glass

// ❌ DON'T: Multiple glass layers
VStack {
    Text("Hello")
        .adaptiveGlass()
}
.adaptiveGlassCard()

// ✅ DO: Single glass layer
VStack {
    Text("Hello")
}
.adaptiveGlassCard()

2. Full-Width Glass Buttons

// ❌ DON'T: Glass buttons shouldn't be full-width
Button("Start") { }
    .frame(maxWidth: .infinity)
    .buttonStyle(.glass)

// ✅ DO: Content-hugging capsule shape
Button("Start") { }
    .buttonStyle(.glass)  // Capsule is default

3. Missing Availability Checks

// ❌ DON'T: Will crash on iOS 17-25
Button("Tap") { }
    .buttonStyle(.glass)

// ✅ DO: Always check availability
if #available(iOS 26.0, *) {
    Button("Tap") { }
        .buttonStyle(.glass)
} else {
    Button("Tap") { }
        .background(Capsule().fill(.black.opacity(0.6)))
}

Reference Implementation

See CarSeet/CarSeet/ContentView.swift for the complete production implementation including:

  • adaptiveGlass() and adaptiveGlassCard() view extensions
  • GlassProminentButton and GlassButton wrapper components
  • FullWidthBottomButton for streaming controls
  • Status badges and info cards with glass effects

WWDC25 Sessions

  • Session 219: Introduction to Liquid Glass
  • Session 323: Designing with Liquid Glass
  • Session 356: Implementing Liquid Glass in SwiftUI