ios26-liquid-glass

iOS 26 Liquid Glass API reference for SwiftUI and UIKit. Use this skill when working with glassEffect, GlassEffectContainer, glassEffectID, glassEffectUnion, or any Liquid Glass related code on iOS 26+.

$ Installer

git clone https://github.com/praveenperera/dotfiles /tmp/dotfiles && cp -r /tmp/dotfiles/claude/skills/ios26-liquid-glass ~/.claude/skills/dotfiles

// tip: Run this command in your terminal to install the skill


name: ios26-liquid-glass description: iOS 26 Liquid Glass API reference for SwiftUI and UIKit. Use this skill when working with glassEffect, GlassEffectContainer, glassEffectID, glassEffectUnion, or any Liquid Glass related code on iOS 26+.

iOS 26 Liquid Glass Reference

Liquid Glass is Apple's design language introduced at WWDC 2025. Glass elements float above content with translucent, depth-aware surfaces that reflect and refract surrounding content.

SwiftUI - glassEffect Modifier

func glassEffect<S: Shape>(
    _ glass: Glass = .regular,
    in shape: S = DefaultGlassEffectShape,
    isEnabled: Bool = true
) -> some View

Apply .glassEffect() last in your modifier chain for correct rendering.

Glass Variants

VariantDescriptionUse Case
.regularDefault, balanced transparencyMost UI elements (buttons, controls, overlays)
.clearHigh transparency, limited adaptivityMedia-rich backgrounds (needs dimming layer)
.identityNo effect appliedConditional glass application

Glass Modifiers

// semantic color tinting - use only for meaning (success, warning, destructive)
.glassEffect(.regular.tint(.blue))

// iOS-only: enables scaling, bounce, shimmer on interaction
.glassEffect(.regular.interactive())

// combined
.glassEffect(.regular.tint(.blue).interactive())

Shapes

// default shape (capsule)
.glassEffect()
.glassEffect(.regular, in: DefaultGlassEffectShape())

// built-in shapes
.glassEffect(.regular, in: .capsule)
.glassEffect(.regular, in: .circle)
.glassEffect(.regular, in: .ellipse)
.glassEffect(.regular, in: .rect(cornerRadius: 12))
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: 16))

// adapts corner radius to parent container (for buttons in cards/sheets)
.glassEffect(.regular, in: .rect(cornerRadius: .containerConcentric))

GlassEffectContainer

Groups multiple glass elements for seamless blending and morphing. Glass cannot sample other glass, so use this when elements are positioned closely.

GlassEffectContainer(spacing: 8) {  // spacing = merge threshold in points
    Button("One") { }
        .glassEffect()

    Button("Two") { }
        .glassEffect()
}

When elements are closer than spacing, they visually blend and morph together like water droplets.

Rules

  • Don't nest GlassEffectContainer inside another
  • Don't place Menu inside GlassEffectContainer (breaks morphing in iOS 26.1)
  • Don't use .clipShape() on glassEffect views

glassEffectID - Morphing Transitions

Associates glass elements across states for fluid morphing animations. Requires @Namespace and GlassEffectContainer.

@Namespace var namespace
@State var expanded = false

GlassEffectContainer {
    if expanded {
        HStack {
            Button("Home") { }
                .glassEffect(.regular.interactive())
                .glassEffectID("home", in: namespace)

            Button("Settings") { }
                .glassEffect(.regular.interactive())
                .glassEffectID("settings", in: namespace)
        }
    } else {
        Button("Menu") { expanded = true }
            .glassEffect(.regular.interactive())
            .glassEffectID("menu", in: namespace)
    }
}
.animation(.easeInOut, value: expanded)

Requirements for Morphing

  1. Elements must be within the same GlassEffectContainer
  2. Each view needs glassEffectID with shared namespace
  3. Views must be conditionally shown/hidden
  4. Animation must be applied to the state change
  5. All morphing elements should use same Glass style and tint

glassEffectUnion - Grouping Elements

Makes multiple elements appear as a single glass shape (like Apple Maps zoom controls).

@Namespace var ns

VStack(spacing: 0) {
    Button(action: { /* zoom in */ }) {
        Image(systemName: "plus")
    }
    .glassEffect(.regular.tint(.white.opacity(0.8)))
    .glassEffectUnion(id: "zoom", namespace: ns)

    Divider()

    Button(action: { /* zoom out */ }) {
        Image(systemName: "minus")
    }
    .glassEffect(.regular.tint(.white.opacity(0.8)))
    .glassEffectUnion(id: "zoom", namespace: ns)
}

Requirements

All elements in a union must have:

  • Same union id
  • Same glass effect variant
  • Same tint (color and opacity)

UIKit Support

UIGlassEffect

if #available(iOS 26.0, *) {
    let glassEffect = UIGlassEffect()
    let effectView = UIVisualEffectView(effect: glassEffect)
    effectView.frame = CGRect(x: 20, y: 100, width: 200, height: 50)
    view.addSubview(effectView)

    // add content to contentView
    let label = UILabel()
    label.text = "Glass Effect"
    effectView.contentView.addSubview(label)
}

UIGlassContainerEffect - Merging Glass Views

if #available(iOS 26.0, *) {
    let containerEffect = UIGlassContainerEffect()
    containerEffect.spacing = 12  // merge distance threshold

    let containerView = UIVisualEffectView(effect: containerEffect)

    let glass1 = UIVisualEffectView(effect: UIGlassEffect())
    let glass2 = UIVisualEffectView(effect: UIGlassEffect())

    containerView.contentView.addSubview(glass1)
    containerView.contentView.addSubview(glass2)
}

Corner Configuration

if #available(iOS 26.0, *) {
    let effectView = UIVisualEffectView(effect: UIGlassEffect())
    effectView.cornerConfiguration = UIViewCornerConfiguration(
        corners: .allCorners,
        cornerRadius: 16
    )
}

UIHostingController Integration

When embedding SwiftUI glass in UIKit:

  • Place in navigationItem.titleView (not leftBarButtonItem/rightBarButtonItem)
  • Set sizingOptions = [.intrinsicContentSize]

Backward Compatibility

The Glass type only exists on iOS 26+. Create wrappers with #available:

extension View {
    @ViewBuilder
    func applyGlassEffect() -> some View {
        if #available(iOS 26.0, *) {
            self.glassEffect()
        } else {
            self  // no-op on older iOS
        }
    }

    // with parameters - requires @available at call site
    @available(iOS 26.0, *)
    @ViewBuilder
    func applyGlassEffect(
        _ glass: Glass,
        in shape: some Shape = DefaultGlassEffectShape()
    ) -> some View {
        self.glassEffect(glass, in: shape)
    }
}

Fallback for older iOS

if #available(iOS 26.0, *) {
    content.glassEffect()
} else {
    content.background(.ultraThinMaterial, in: .capsule)
}

Design Guidelines

When to Use

  • Navigation elements (tab bars, toolbars, nav bars)
  • Floating action buttons
  • Control panels and overlays
  • Search bars and input fields
  • Modal/sheet presentations
  • Interactive elements that sit "above" content

When NOT to Use

  • Core content (list rows, table cells)
  • Card backgrounds with primary information
  • Media-rich content areas
  • Text-heavy sections
  • Decorative elements with no function

Best Practices

  • Use .interactive() for buttons/controls on iOS
  • Use .tint() only for semantic meaning
  • Test in light mode, dark mode, and with "Reduce Transparency"
  • Maintain 4.5:1 contrast ratio (WCAG AA)
  • Use .containerConcentric corner radius for elements in containers
  • Monitor performance in scrollable lists on older iPhones

Known Issues

IssueWorkaround
Menu in GlassEffectContainer breaks morphing (iOS 26.1)Don't nest Menu in container
Morphing circle ↔ rectangle has glitchesAvoid drastically different shape transitions
glassEffectID inconsistentConsider .matchedGeometryEffect as alternative
UIHostingController in bar items causes side effectsUse titleView instead

Button Styles

// translucent glass button
Button("Glass") { }
    .buttonStyle(.glass)

// opaque prominent glass button
Button("Prominent") { }
    .buttonStyle(.glassProminent)

Tab Bar Behavior

// tab bar minimizes when scrolling down
.tabBarMinimizeBehavior(.onScrollDown)

Resources