All skills

Build iOS 26 Liquid Glass views in SwiftUI, UIKit, and WidgetKit. Use blur, tint, light, touch effects, shared glass groups, and smooth shape changes.

  • 1 file
  • 12.1 KB
  • Updated last week
  • GitHub

Use this Skill: https://skilld.dev/gh/agenticluke/liquid-glass-ui-plus/skill

This session only. Nothing lands on disk.

SKILL.md

≈39 tokens always: the name and description. ≈3k when used: this file.

Liquid Glass Design System for iOS 26

Use this skill to build Apple's Liquid Glass style. Glass blurs the view behind it. It can pick up nearby color and light. It can also react to touch and pointer input.

This guide covers SwiftUI, UIKit, and WidgetKit.

When to Use This Skill

Use this skill when you:

  • Build or update an app for iOS 26 or later
  • Add glass buttons, cards, bars, or groups
  • Make one glass shape change into another
  • Add glass to a widget
  • Replace an old blur or material effect

Do not use these APIs on an older system without a version check.

if #available(iOS 26.0, *) {
    // Use Liquid Glass.
} else {
    // Use an older material or blur.
}

Check the app's target, SDK, and platform before you write code. Some APIs may not exist on every Apple platform.

SwiftUI

Add a Basic Glass Effect

Add size, font, and padding before .glassEffect().

Text("Hello, World!")
    .font(.title)
    .padding()
    .glassEffect()

The default effect uses regular glass and a capsule shape.

Set the Shape and Tint

Text("Hello, World!")
    .font(.title)
    .padding()
    .glassEffect(
        .regular
            .tint(.orange)
            .interactive(),
        in: .rect(cornerRadius: 16)
    )

Main options:

  • .regular adds the normal glass look.
  • .tint(Color) adds color.
  • .interactive() reacts to touch or pointer input.
  • .capsule makes a pill shape.
  • .rect(cornerRadius:) makes a round box.
  • .circle makes a circle.

Use .interactive() only when the view can be used. Do not add it to plain text or art.

Add Glass Buttons

Button("Open") {
    openItem()
}
.buttonStyle(.glass)

Button("Delete") {
    deleteItem()
}
.buttonStyle(.glassProminent)

Use .glassProminent for the main action. Do not use it for every button.

Group More Than One Glass View

Put nearby glass views in one GlassEffectContainer. This helps the system draw them as one group. It also lets close shapes blend.

GlassEffectContainer(spacing: 40) {
    HStack(spacing: 40) {
        Image(systemName: "scribble.variable")
            .font(.system(size: 36))
            .frame(width: 80, height: 80)
            .glassEffect()

        Image(systemName: "eraser.fill")
            .font(.system(size: 36))
            .frame(width: 80, height: 80)
            .glassEffect()
    }
}

The spacing value helps set when shapes blend. Test it at the real view size. A value that works on one screen may look wrong on another.

Do not make one large container for the whole app. Group only glass views that belong together.

Join Views Into One Glass Shape

Use glassEffectUnion when many views should look like one glass group.

@Namespace private var glassNamespace

let symbols = ["pencil", "eraser", "lasso", "paintbrush"]

GlassEffectContainer(spacing: 20) {
    HStack(spacing: 20) {
        ForEach(symbols.indices, id: \.self) { index in
            Image(systemName: symbols[index])
                .frame(width: 80, height: 80)
                .glassEffect()
                .glassEffectUnion(
                    id: index < 2 ? "edit" : "draw",
                    namespace: glassNamespace
                )
        }
    }
}

Use a stable group ID. If the ID changes by mistake, the glass group may jump or split.

Make a Shape Change Smoothly

Give each glass view a stable ID in the same namespace. Add or remove views inside an animation.

@State private var isExpanded = false
@Namespace private var glassNamespace

VStack {
    GlassEffectContainer(spacing: 40) {
        HStack(spacing: 40) {
            Image(systemName: "scribble.variable")
                .frame(width: 80, height: 80)
                .glassEffect()
                .glassEffectID("pencil", in: glassNamespace)

            if isExpanded {
                Image(systemName: "eraser.fill")
                    .frame(width: 80, height: 80)
                    .glassEffect()
                    .glassEffectID("eraser", in: glassNamespace)
            }
        }
    }

    Button("Toggle Tools") {
        withAnimation {
            isExpanded.toggle()
        }
    }
    .buttonStyle(.glass)
}

Each ID must be unique in its namespace. Keep the container and namespace alive during the change.

Let Content Scroll Under a Side Bar

Make the ScrollView content reach the leading and trailing edges of its parent. The system can then draw the scroll under a side bar or inspector.

Do not add extra side padding to the whole scroll view. Add padding to the items inside it when needed.

Full SwiftUI Example

This example builds a small floating tool bar.

import SwiftUI

struct ToolBarView: View {
    @State private var selectedTool = "pencil"

    private let tools = ["pencil", "eraser", "lasso"]

    var body: some View {
        if #available(iOS 26.0, *) {
            GlassEffectContainer(spacing: 12) {
                HStack(spacing: 12) {
                    ForEach(tools, id: \.self) { tool in
                        Button {
                            selectedTool = tool
                        } label: {
                            Image(systemName: tool)
                                .font(.title2)
                                .frame(width: 44, height: 44)
                        }
                        .buttonStyle(
                            tool == selectedTool
                                ? .glassProminent
                                : .glass
                        )
                        .accessibilityLabel(tool.capitalized)
                    }
                }
                .padding(8)
            }
        } else {
            HStack(spacing: 12) {
                ForEach(tools, id: \.self) { tool in
                    Button {
                        selectedTool = tool
                    } label: {
                        Image(systemName: tool)
                            .frame(width: 44, height: 44)
                    }
                }
            }
            .padding(8)
            .background(.regularMaterial, in: Capsule())
        }
    }
}

UIKit

Add a Basic Glass View

let glassEffect = UIGlassEffect()
glassEffect.tintColor = UIColor.systemBlue.withAlphaComponent(0.3)
glassEffect.isInteractive = true

let glassView = UIVisualEffectView(effect: glassEffect)
glassView.translatesAutoresizingMaskIntoConstraints = false
glassView.layer.cornerRadius = 20
glassView.clipsToBounds = true

view.addSubview(glassView)

NSLayoutConstraint.activate([
    glassView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    glassView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    glassView.widthAnchor.constraint(equalToConstant: 200),
    glassView.heightAnchor.constraint(equalToConstant: 120)
])

let label = UILabel()
label.text = "Liquid Glass"
label.translatesAutoresizingMaskIntoConstraints = false

glassView.contentView.addSubview(label)

NSLayoutConstraint.activate([
    label.centerXAnchor.constraint(equalTo: glassView.contentView.centerXAnchor),
    label.centerYAnchor.constraint(equalTo: glassView.contentView.centerYAnchor)
])

Add child views to contentView, not to the effect view itself.

Set clipsToBounds when the glass must follow the corner shape. Check shadows after you turn it on, since clipping can cut them off. Put a shadow on an outer wrapper view if needed.

Group UIKit Glass Views

let containerEffect = UIGlassContainerEffect()
containerEffect.spacing = 40

let containerView = UIVisualEffectView(effect: containerEffect)

let firstGlass = UIVisualEffectView(effect: UIGlassEffect())
let secondGlass = UIVisualEffectView(effect: UIGlassEffect())

containerView.contentView.addSubview(firstGlass)
containerView.contentView.addSubview(secondGlass)

Add layout rules for the container and each child. Do not leave their size or place unclear.

Set Scroll Edge Effects

scrollView.topEdgeEffect.style = .automatic
scrollView.bottomEdgeEffect.style = .hard
scrollView.leftEdgeEffect.isHidden = true

Hide an edge effect only when the layout calls for it. Test with long text, large text, and right-to-left text.

Change a Tool Bar Item Background

let favoriteButton = UIBarButtonItem(
    image: UIImage(systemName: "heart"),
    style: .plain,
    target: self,
    action: #selector(favoriteAction)
)

favoriteButton.hidesSharedBackground = true

Set hidesSharedBackground only when the item should not share the glass behind nearby items.

WidgetKit

Check the Widget Render Mode

struct MyWidgetView: View {
    @Environment(\.widgetRenderingMode) private var renderingMode

    var body: some View {
        if renderingMode == .accented {
            Text("Tinted mode")
        } else {
            Text("Full color mode")
        }
    }
}

Do not depend on one exact background color. The user and system can change the widget look.

Set Accent Groups

HStack {
    VStack(alignment: .leading) {
        Text("Title")
            .widgetAccentable()

        Text("Subtitle")
    }

    Image(systemName: "star.fill")
        .widgetAccentable()
}

Use accent groups to show rank. Keep key text clear in both full color and tinted modes.

Set Image Style in Accent Mode

Image("myImage")
    .widgetAccentedRenderingMode(.monochrome)

Test photos, logos, and symbols on a real tinted Home Screen. A full-color image may not stay clear when made into one color.

Add a Widget Background

VStack {
    Text("Today")
}
.containerBackground(for: .widget) {
    Color.blue.opacity(0.2)
}

Use containerBackground(for:) for the main widget background. Do not place a second solid background over it.

Design Rules

Choice Rule
Glass container Group nearby glass views that belong together.
Container spacing Tune when close shapes start to blend.
Stable IDs Use fixed IDs for smooth shape changes.
Interactive glass Use it only for controls that the user can use.
Tint Use a light tint to show rank or state.
Widget accent mode Keep the widget clear when the system adds a tint.
Fallback view Give older systems a plain material or blur.

Best Practices

  • Apply size, font, and padding before .glassEffect().
  • Use one glass container for each related group.
  • Keep text short and clear on glass.
  • Keep strong contrast between text and its background.
  • Test light mode, dark mode, and tinted widget mode.
  • Test with Reduce Transparency on.
  • Test with Reduce Motion on.
  • Do not use motion as the only way to show a state change.
  • Add clear access names to icon-only buttons.
  • Keep touch targets at least 44 by 44 points.
  • Test large text and long translated text.
  • Test on a real device. Blur and light may look different from the preview.
  • Keep a plain fallback for iOS versions before 26.
  • Check the final API names in the SDK used by the project.

Common Mistakes

Avoid these mistakes:

  • Using many nearby glass views without a container
  • Putting the whole app in one glass container
  • Nesting glass effects many levels deep
  • Adding glass to every view
  • Making plain art react to touch
  • Using too much tint
  • Placing a solid background behind glass
  • Reusing the same glass ID for two live views
  • Changing IDs during an animation
  • Adding child views outside a UIKit effect view's contentView
  • Using rounded UIKit glass without clipping its content
  • Forgetting that clipping can cut off shadows
  • Ignoring tinted widget mode
  • Using glass as the only sign that a control is selected
  • Shipping without an older-system fallback

Good Uses

Liquid Glass works well for:

  • Navigation bars, tool bars, and tab bars
  • Floating action buttons
  • Small tool groups
  • Cards that sit over rich content
  • Controls that need touch feedback
  • Widgets that match the system look
  • Smooth changes between related interface states

Use glass with care. It should help the user see groups and actions. It should not make the screen hard to read.

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at 15c2b2c. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last week.

Activeupdated last week

README badge

README badge for agenticluke/liquid-glass-ui-plus