
Swiftui Liquid Glass
- 2.9k installs
- 944 repo stars
- Updated July 15, 2026
- dpearson2699/swift-ios-skills
swiftui-liquid-glass is a SwiftUI skill for iOS 26 Liquid Glass effects, containers, button styles, morphing transitions, and availability fallbacks.
About
The swiftui-liquid-glass skill implements Apple's Liquid Glass translucent material introduced in iOS 26 across custom controls and navigation layers. Standard tab bars, toolbars, navigation bars, and sheets adopt the material automatically when built with the iOS 26 SDK, while custom views use glassEffect, GlassEffectContainer, and glass button styles. Workflows cover picking target surfaces, wrapping grouped glass in containers, applying glassEffect after layout modifiers, adding interactive only on tappable elements, and morphing with glassEffectID and glassEffectTransition matchedGeometry or materialize choices. API summary documents Glass variants regular, clear, identity, tint, and interactive chaining, plus ToolbarSpacer, scrollEdgeEffectStyle, and backgroundExtensionEffect companions. Common mistakes warn against glass on all content, static status masquerading as buttons, nested containers, interactive on read-only badges, wrong modifier order, and missing iOS 26 availability gates with fallbacks. Review checklist spans container usage, transition types, clear glass contrast, accessibility with Reduce Transparency, and Sendable ID requirements.
- glassEffect modifier with Glass variants and tint chaining.
- GlassEffectContainer grouping for blending and morphing performance.
- glass and glassProminent button styles with clear variant contrast rules.
- Morphing transitions via glassEffectID and matchedGeometry or materialize.
- iOS 26 availability gates with bordered fallback controls.
Swiftui Liquid Glass by the numbers
- 2,860 all-time installs (skills.sh)
- +128 installs in the week ending Jul 29, 2026 (Skillselion tracking)
- Ranked #58 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
swiftui-liquid-glass capabilities & compatibility
- Capabilities
- glasseffect and glass variant configuration · glasseffectcontainer grouping and spacing contro · morphing transitions with glasseffectid namespac · glass and glassprominent button style applicatio · scroll edge and background extension companion a · availability gating and accessibility contrast r
- Use cases
- ui design · frontend
- Platforms
- macOS
- Pricing
- Free
What swiftui-liquid-glass says it does
Liquid Glass belongs in the controls/navigation layer; do not use it to decorate static content containers.
`.interactive()` adds visual affordance suggesting tappability.
Gate with `if #available(iOS 26, *)` and provide a fallback for earlier versions.
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill swiftui-liquid-glassAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2.9k |
|---|---|
| repo stars | ★ 944 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 15, 2026 |
| Repository | dpearson2699/swift-ios-skills ↗ |
How do I apply Liquid Glass correctly on custom SwiftUI controls without misleading interactive affordances or layout bugs?
Implement iOS 26 Liquid Glass effects with glassEffect, containers, button styles, morphing transitions, and availability fallbacks.
Who is it for?
iOS developers adopting iOS 26 Liquid Glass on custom toolbars, floating controls, and morphing UI groups.
Skip if: Skip for pre-iOS 26-only apps without fallback UI, static content backgrounds, or UIKit-only screens without SwiftUI glass.
When should I use this skill?
User asks about Liquid Glass, glassEffect, GlassEffectContainer, glass buttons, morphing transitions, or iOS 26 design adoption.
What you get
Grouped glass controls with correct modifier order, morphing transitions, interactive only where needed, and iOS 26 gated fallbacks.
- Liquid Glass SwiftUI view implementations
- coordinated glass animation setup
By the numbers
- Documents 8+ Liquid Glass SwiftUI APIs including glassEffect, GlassEffectContainer, and glassEffectTransition
Files
SwiftUI Liquid Glass
Liquid Glass is the dynamic translucent material introduced in iOS 26 (and iPadOS 26, macOS 26, tvOS 26, watchOS 26). It blurs content behind it, reflects surrounding color and light, and reacts to touch and pointer interactions. Standard SwiftUI components (tab bars, toolbars, navigation bars, sheets) adopt Liquid Glass automatically when built with the iOS 26 SDK. Treat custom Liquid Glass as a controls/navigation-layer effect, not a general content background. Use the APIs below for custom views and controls that need that functional layer.
See references/liquid-glass.md for the full API reference with additional examples.
Contents
Workflow
Choose the path that matches the request:
1. Implement a new feature with Liquid Glass
1. Identify target surfaces (floating controls, custom bars, transient controls). 2. Decide shape, prominence, and whether each element is a real control or static status. 3. Wrap grouped glass elements in a GlassEffectContainer. 4. Apply .glassEffect() after layout and appearance modifiers. 5. Add .interactive() only to tappable/focusable elements. 6. Add morphing transitions with glassEffectID(_:in:) where the view hierarchy changes with animation. Put glassEffectTransition(_:) on the inserted or removed glass child, not on the always-present container. Name the transition choice: .matchedGeometry for nearby effects inside container spacing; .materialize for distant insertion/removal or no geometry match. 7. Gate with if #available(iOS 26, *) and provide a fallback for earlier versions.
2. Improve an existing feature with Liquid Glass
1. Find custom control or navigation backgrounds that can be replaced with .glassEffect(). 2. Wrap sibling glass elements in GlassEffectContainer for blending and performance. 3. Replace custom glass-like buttons with .buttonStyle(.glass), .buttonStyle(.glassProminent), or configurable styles such as .buttonStyle(.glass(.clear)). Use .glass(_:) when the button needs a specific tint or variant; reserve .glassProminent for high-emphasis primary actions. 4. Add morphing transitions where animated insertion/removal occurs.
3. Review existing Liquid Glass usage
Run through the Review Checklist below and verify each item.
Core API Summary
glassEffect(_:in:)
Applies Liquid Glass behind a view. Default: .regular variant in a Capsule shape.
nonisolated func glassEffect(
_ glass: Glass = .regular,
in shape: some Shape = DefaultGlassEffectShape()
) -> some ViewGlass struct
| Property / Method | Purpose |
|---|---|
.regular | Standard glass material |
.clear | Clear variant; add dimming/contrast treatment when legibility needs it |
.identity | No visual effect (pass-through) |
.tint(_:) | Add a color tint for prominence |
.interactive(_:) | React to touch and pointer interactions |
Chain them: .regular.tint(.blue).interactive()
GlassEffectContainer
Wraps multiple glass views for shared rendering, blending, and morphing.
GlassEffectContainer(spacing: 24) {
// child views with .glassEffect()
}The spacing controls when nearby glass shapes begin to blend. Match or exceed the interior layout spacing so shapes merge during animated transitions but remain separate at rest.
Morphing & Transitions
| Modifier | Purpose |
|---|---|
glassEffectID(_:in:) | Stable identity for morphing during view hierarchy changes |
glassEffectUnion(id:namespace:) | Merge multiple views into one glass shape |
glassEffectTransition(_:) | Control how glass appears/disappears |
Transition decision: use .matchedGeometry for nearby effects inside the container's spacing; use .materialize for distant insertion/removal or when no geometry match should occur; use .identity only when no transition animation is wanted.
Button Styles
Button("Action") { }
.buttonStyle(.glass) // standard glass button
Button("Primary") { }
.buttonStyle(.glassProminent) // prominent glass button
Button("Media") { }
.buttonStyle(.glass(.clear)) // configurable variant; verify contrastScroll Edge Effects and Background Extension (iOS 26+)
These complement Liquid Glass when building custom toolbars and scroll views:
ScrollView {
content
}
.scrollEdgeEffectStyle(.soft, for: .top) // Configures edge effect at scroll boundaries
// Duplicate view into mirrored copies at safe area edges with blur (e.g., under sidebars)
content
.backgroundExtensionEffect()ToolbarSpacer (iOS 26+)
Creates a visual break between items in toolbars:
.toolbar {
ToolbarItem { Button("Edit") { } }
ToolbarSpacer(.fixed)
ToolbarItem { Button("Share") { } }
}Code Examples
Glass button with availability gate
if #available(iOS 26, *) {
Button("Show Status") { showStatusDetails() }
.buttonStyle(.glass)
} else {
Button("Show Status") { showStatusDetails() }
.buttonStyle(.bordered)
}Grouped glass shapes in a container
let symbols = ["pencil", "eraser.fill", "lasso"]
GlassEffectContainer(spacing: 24) {
HStack(spacing: 24) {
ForEach(symbols, id: \.self) { symbol in
Image(systemName: symbol)
.frame(width: 56, height: 56)
.glassEffect()
}
}
}Nearby morphing transition
@State private var isExpanded = false
@Namespace private var ns
var body: some View {
GlassEffectContainer(spacing: 40) {
HStack(spacing: 40) {
Image(systemName: "pencil")
.frame(width: 80, height: 80)
.glassEffect()
.glassEffectID("pencil", in: ns)
if isExpanded {
Image(systemName: "eraser.fill")
.frame(width: 80, height: 80)
.glassEffect()
.glassEffectID("eraser", in: ns)
.glassEffectTransition(.matchedGeometry)
}
}
}
Button("Toggle") {
withAnimation { isExpanded.toggle() }
}
.buttonStyle(.glass)
}For distant inserted or removed glass that should not morph from a nearby control, use .glassEffectTransition(.materialize) on the conditional child instead.
Unioning views into a single glass shape
@Namespace private var ns
GlassEffectContainer(spacing: 20) {
HStack(spacing: 20) {
ForEach(items.indices, id: \.self) { i in
Image(systemName: items[i])
.frame(width: 80, height: 80)
.glassEffect()
.glassEffectUnion(id: i < 2 ? "group1" : "group2", namespace: ns)
}
}
}Tinted glass icon control
struct GlassIconControl: View {
let icon: String
let tint: Color
let action: () -> Void
var body: some View {
Button(action: action) {
Image(systemName: icon)
.font(.title2)
.padding()
}
.buttonStyle(.glass(.regular.tint(tint)))
}
}Clear glass action over bright content
if #available(iOS 26, *) {
ZStack {
Capsule()
.fill(.black.opacity(0.28))
Button {
playRecap()
} label: {
Label("Play", systemImage: "play.fill")
.font(.headline)
.padding(.horizontal, 8)
}
.buttonStyle(.glass(.clear))
}
.fixedSize()
} else {
Button("Play", systemImage: "play.fill") { playRecap() }
.buttonStyle(.borderedProminent)
}Use clear glass only when the background still leaves labels and symbols readable. On bright or busy backgrounds, add a subtle dimming layer or choose a more opaque button style.
Static status count, not a toolbar control
VStack(spacing: 8) {
Text("24")
.font(.headline.monospacedDigit())
.accessibilityLabel("24 options expiring")
Text("Options Expiring")
.font(.subheadline)
.foregroundStyle(.secondary)
}
// Keep read-only status out of toolbar control slots and do not add .interactive().Common Mistakes
DON'T: Apply Liquid Glass to every surface
Overuse distracts from content. Liquid Glass belongs in the controls/navigation layer; do not use it to decorate static content containers.
// WRONG: Glass on everything
VStack {
Text("Title").glassEffect()
Text("Subtitle").glassEffect()
Divider().glassEffect()
Text("Body").glassEffect()
}
// CORRECT: Static content stays in the content layer
VStack {
Text("Title").font(.title)
Text("Subtitle").font(.subheadline)
Divider()
Text("Body")
}
Button("Add") { addItem() }
.buttonStyle(.glass)DON'T: Make static status look tappable
Counts, badges, summaries, and read-only status chips should not live in toolbar action slots or use .interactive() unless they initiate an immediate action. If a badge belongs to a real toolbar action, make the action one accessible control and keep the badge subordinate to that control.
DON'T: Nest GlassEffectContainer for one related group
Use one container for the related glass group so rendering, blending, and morphing scope stay predictable.
// WRONG
GlassEffectContainer {
GlassEffectContainer {
content.glassEffect()
}
}
// CORRECT: Single container wrapping all glass views
GlassEffectContainer {
content.glassEffect()
}DON'T: Add .interactive() to non-interactive elements
.interactive() adds visual affordance suggesting tappability. Using it on decorative or read-only status glass misleads users.
DON'T: Apply .glassEffect() before layout modifiers
Glass calculates its shape from the final frame. Applying it before padding/frame produces incorrect bounds.
// WRONG: Glass applied before padding
Text("Label").glassEffect().padding()
// CORRECT: Glass applied after layout
Text("Label").padding().glassEffect()DON'T: Forget accessibility testing
Always test with Reduce Transparency and Reduce Motion enabled. System settings can remove or modify translucency and motion, so verify custom glass content remains readable.
DON'T: Skip availability checks
Liquid Glass requires iOS 26+. Gate with if #available(iOS 26, *) and provide a fallback.
Review Checklist
- [ ] Availability:
if #available(iOS 26, *)present with fallback UI. - [ ] Container: Multiple glass views wrapped in
GlassEffectContainer. - [ ] Modifier order:
.glassEffect()applied after layout/appearance modifiers. - [ ] Interactivity:
.interactive()used only where user interaction exists. - [ ] Status vs action: Static counts/status are not toolbar controls and do not expose press/hover affordance.
- [ ] Transitions:
glassEffectIDused with@Namespacefor morphing animations. - [ ] Transition type:
.matchedGeometryfor nearby effects;.materializefor distant ones, applied to the conditional glass child. - [ ] Consistency: Shapes, tints, and spacing are uniform across related elements.
- [ ] Performance: Glass effects are limited in number; container used for grouping.
- [ ] Accessibility: Tested with Reduce Transparency and Reduce Motion enabled.
- [ ] Button styles: Standard
.glass,.glassProminent, or configurable.glass(_:)used for buttons;.glass(_:)is primary when a tint or clear variant is required. - [ ] Clear glass contrast: Clear glass over bright content has a dimming/contrast treatment or uses a more legible style.
- [ ] Concurrency: IDs passed to
glassEffectID/glassEffectUnionareSendable; MainActor-annotated Liquid Glass APIs stay in SwiftUI UI code.
References
- Full API guide: references/liquid-glass.md
- Apple docs: Applying Liquid Glass to custom views
- Apple docs: Adopting Liquid Glass
{
"skill_name": "swiftui-liquid-glass",
"evals": [
{
"id": 0,
"name": "static-status-badge-toolbar-affordance",
"prompt": "Review this iOS 26 SwiftUI sheet header plan: the sheet has a centered title, a Done button, and a top-left numeric count like `24` showing how many options expire that day. The current implementation places the count in `ToolbarItem(placement: .topBarLeading)` as a custom capsule with Liquid Glass styling, but tapping it only makes it visually grow and does nothing. What should the implementation change?",
"expected_output": "A Liquid Glass-aware review that identifies the count as a non-action status indicator, avoids placing it in a toolbar/control slot when it has no action, removes interactive glass affordance, and recommends static header placement plus accessibility metadata and UI verification.",
"files": [],
"assertions": [
"Identifies the numeric count as a status indicator rather than a standalone action or toolbar command.",
"Explains that custom toolbar/navigation items in iOS 26 can inherit Liquid Glass control affordance, so a non-action count in a toolbar slot can misleadingly look tappable.",
"Recommends moving the static count out of the toolbar/control surface or attaching it only to a real actionable control if an action is intended.",
"Requires avoiding `.interactive()` glass, `Button`, `NavigationLink`, or other press/hover affordances for the non-action count.",
"Includes accessibility guidance such as a meaningful label like \"24 options expiring\", a stable automation identifier, and static/non-button semantics.",
"Calls for visual or UI automation verification that the badge displays as a plain number and does not react as a tappable target."
]
},
{
"id": 1,
"name": "badge-attached-to-real-action",
"prompt": "Design an iOS 26 SwiftUI notification bell in a toolbar. The bell opens a notifications screen, and a small unread-count badge should show `7`. The team is unsure whether Liquid Glass makes the badge look like a separate control. Give implementation guidance.",
"expected_output": "Guidance that treats the toolbar bell as the actionable control, keeps the unread badge as subordinate status information, preserves a single accessible action, and uses Liquid Glass only on the actual button/control.",
"files": [],
"assertions": [
"Keeps the bell button as the single actionable toolbar control that opens notifications.",
"Treats the unread-count badge as subordinate status information, not an independent tappable control.",
"Recommends a combined accessibility label/value such as \"Notifications, 7 unread\" or equivalent VoiceOver meaning.",
"Avoids giving the badge its own Button, NavigationLink, gesture, or independent interactive Liquid Glass affordance.",
"Uses an iOS 26-appropriate toolbar/button treatment for the actual action while keeping the badge visually lightweight.",
"Mentions testing that the toolbar exposes one action target rather than separate bell and badge targets."
]
},
{
"id": 2,
"name": "mixed-glass-controls-and-status-audit",
"prompt": "Audit this SwiftUI iOS 26 dashboard design before coding: a floating Liquid Glass toolbar has Add, Filter, and Share controls; nearby static chips show `Synced`, `3 warnings`, and `Market closed`; a selected filter pill changes color but does not open anything; and a decorative glass panel sits behind a chart. Which elements should be interactive glass, which should be static, and what should be routed or tested?",
"expected_output": "A boundary-aware Liquid Glass audit that limits interactive glass to real controls, treats status chips and decorative panels as static/noninteractive, flags ambiguous selected pills, avoids overusing glass, and routes accessibility or layout concerns where appropriate.",
"files": [],
"assertions": [
"Allows `.interactive()` or glass button styles only for real actions such as Add, Filter, and Share.",
"Classifies `Synced`, `3 warnings`, and `Market closed` as status indicators that should not receive press/hover/tap affordance unless a real action is added.",
"Flags the selected filter pill as ambiguous if it looks tappable but has no action, and recommends either making the action explicit or restyling it as noninteractive state.",
"Warns against using Liquid Glass as broad decoration behind the chart when it distracts from content or creates unnecessary rendering cost.",
"Mentions availability gating and fallbacks for iOS versions before Liquid Glass support.",
"Routes detailed accessibility semantics, labels, traits, and automation identifiers to the accessibility skill while still noting that controls and status indicators need different semantics."
]
},
{
"id": 3,
"name": "content-layer-and-clear-glass-review",
"prompt": "Review this iOS 26 SwiftUI card design: a large dashboard chart sits inside a `.glassEffect(.clear)` rounded rectangle so the graph looks fancy over a bright gradient background, and the card has no controls. A nearby Play button opens a media preview. What should change before implementation?",
"expected_output": "A Liquid Glass-aware review that keeps static chart content out of the Liquid Glass functional layer, applies glass styling only to the real Play action if appropriate, recognizes configurable glass button styling, and requires clear-glass legibility treatment if clear glass remains over rich media.",
"files": [],
"assertions": [
"Warns that Liquid Glass should not be used as decoration for a static content-layer chart/card.",
"Recommends leaving the chart/card in the content layer or using standard materials/content styling instead of custom Liquid Glass.",
"Treats the Play button as the real actionable control and allows a standard Liquid Glass button style there.",
"Recognizes `.buttonStyle(.glass(.clear))` or equivalent configurable glass button styling as the appropriate API when a clear glass button variant is needed.",
"Requires clear glass legibility treatment such as dimming or contrast verification over the bright/rich background.",
"Mentions availability gating or fallback behavior for platforms before iOS 26 Liquid Glass support."
]
}
]
}
Liquid Glass API Reference for SwiftUI
Contents
- Overview
- glassEffect(_:in:)
- Glass
- GlassEffectContainer
- glassEffectID(_:in:)
- glassEffectUnion(id:namespace:)
- glassEffectTransition(_:)
- GlassEffectTransition
- Button Styles
- DefaultGlassEffectShape
- Scroll Edge Effect
- Background Extension (Split Views)
- Availability Gating
- Performance Guidelines
- Accessibility Considerations
- Best Practices Summary
- Apple Documentation Links
Overview
Liquid Glass is a dynamic translucent material available on iOS 26.0+, iPadOS 26.0+, macOS 26.0+, Mac Catalyst 26.0+, tvOS 26.0+, and watchOS 26.0+. It blurs content behind it, reflects color and light from surrounding content, and reacts to touch and pointer interactions in real time. Standard SwiftUI components (tab bars, toolbars, navigation bars, sheets, popovers) adopt Liquid Glass automatically when built with the latest SDK.
This reference covers the complete API surface for applying Liquid Glass to custom views.
glassEffect(_:in:)
Applies the Liquid Glass effect behind a view.
nonisolated func glassEffect(
_ glass: Glass = .regular,
in shape: some Shape = DefaultGlassEffectShape()
) -> some ViewThe system renders a shape anchored behind the view with the Liquid Glass material and applies foreground effects over the view content. The default shape is Capsule.
Apply this modifier after other modifiers that affect the view's appearance (padding, frame, font, foregroundStyle).
Basic usage
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect()Custom shape
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect(in: .rect(cornerRadius: 16.0))Common shapes: .capsule (default), .rect(cornerRadius:), .circle.
Tinted and interactive
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect(.regular.tint(.orange).interactive())Use .interactive() only when the custom component is actually tappable, focusable, or otherwise interactive. For buttons, prefer the built-in glass button styles in the Button Styles section.
Glass
A structure that defines the configuration of the Liquid Glass material. Conforms to Equatable, Sendable, and SendableMetatype.
Type Properties
| Property | Description |
|---|---|
.regular | Standard Liquid Glass material |
.clear | Clear variant with high translucency; add dimming or other contrast treatment when legibility needs it |
.identity | No-op; content appears as if no glass effect was applied |
Instance Methods
| Method | Description |
|---|---|
.tint(_ color: Color) | Returns a copy with a color tint to suggest prominence |
.interactive(_ isInteractive: Bool = true) | Returns a copy that reacts to touch and pointer interactions |
Methods are chainable:
.glassEffect(.regular.tint(.blue).interactive())When using .clear, verify foreground legibility over the actual background. Add a dimming layer or another contrast treatment when bright or visually busy content sits behind the glass.
ZStack {
Capsule()
.fill(.black.opacity(0.28))
Label("Play", systemImage: "play.fill")
.padding(.horizontal, 12)
.padding(.vertical, 8)
.glassEffect(.clear)
}
.fixedSize()GlassEffectContainer
A view that combines multiple Liquid Glass shapes into a single rendering pass. Enables blending and morphing between individual shapes.
@MainActor @preconcurrency
struct GlassEffectContainer<Content> where Content : ViewConforms to View, Sendable, SendableMetatype.
Initializer
init(spacing: CGFloat? = nil, @ContentBuilder content: () -> Content)The spacing parameter controls how glass shapes interact:
- Larger spacing: Shapes begin blending at greater distances; morphing starts sooner.
- Smaller spacing: Shapes must be closer before blending occurs.
Match the container spacing to the interior layout spacing so shapes remain separate at rest but merge during animated transitions.
Example
GlassEffectContainer(spacing: 40.0) {
HStack(spacing: 40.0) {
Image(systemName: "scribble.variable")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
Image(systemName: "eraser.fill")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
}
}glassEffectID(_:in:)
Associates a stable identity with a Liquid Glass effect for morphing during view hierarchy transitions.
nonisolated func glassEffectID(
_ id: (some Hashable & Sendable)?,
in namespace: Namespace.ID
) -> some ViewUse with @Namespace, GlassEffectContainer, and withAnimation to animate shapes morphing into each other when views appear or disappear.
Morphing example
@State private var isExpanded = false
@Namespace private var namespace
var body: some View {
GlassEffectContainer(spacing: 40.0) {
HStack(spacing: 40.0) {
Image(systemName: "scribble.variable")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
.glassEffectID("pencil", in: namespace)
if isExpanded {
Image(systemName: "eraser.fill")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
.glassEffectID("eraser", in: namespace)
.glassEffectTransition(.matchedGeometry)
}
}
}
Button("Toggle") {
withAnimation {
isExpanded.toggle()
}
}
.buttonStyle(.glass)
}glassEffectUnion(id:namespace:)
Merges multiple views into a single Liquid Glass shape. All effects with the same shape, Glass variant, and union ID combine into one rendered shape.
@MainActor @preconcurrency func glassEffectUnion(
id: (some Hashable & Sendable)?,
namespace: Namespace.ID
) -> some ViewUseful for dynamically created views or views outside a shared layout container.
Example
@Namespace private var namespace
let symbols = ["cloud.bolt.rain.fill", "sun.rain.fill", "moon.stars.fill", "moon.fill"]
GlassEffectContainer(spacing: 20.0) {
HStack(spacing: 20.0) {
ForEach(symbols.indices, id: \.self) { i in
Image(systemName: symbols[i])
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
.glassEffectUnion(id: i < 2 ? "weather" : "night", namespace: namespace)
}
}
}glassEffectTransition(_:)
Controls how a glass effect appears or disappears during view hierarchy changes.
@MainActor @preconcurrency func glassEffectTransition(
_ transition: GlassEffectTransition
) -> some ViewAttach this modifier to the view whose glass effect is inserted or removed. Keep GlassEffectContainer around the related group to define the blending/morphing scope, but do not put the transition only on the always-present container.
GlassEffectTransition
A structure describing changes when a glass effect is added to or removed from the view hierarchy. Conforms to Sendable and SendableMetatype.
Type Properties
| Transition | Behavior |
|---|---|
.matchedGeometry | Morphs the shape to/from nearby glass effects. Default when within container spacing. |
.materialize | Fades content and animates the glass material in/out without geometry matching. Use for distant effects. |
.identity | No transition animation. |
Example with explicit transition
if isExpanded {
Image(systemName: "note")
.frame(width: 20, height: 20)
.glassEffect()
.glassEffectID("note", in: namespace)
.glassEffectTransition(.materialize)
}Button Styles
SwiftUI provides built-in Liquid Glass button styles plus a configurable style that accepts a Glass value.
GlassButtonStyle
Standard glass appearance for buttons.
Button("Action") { }
.buttonStyle(.glass)GlassProminentButtonStyle
A more prominent glass appearance for primary actions.
Button("Confirm") { }
.buttonStyle(.glassProminent)Configurable Glass Button Style
Use .glass(_:) when a button needs a specific Glass variant or tint:
Button("Media") { }
.buttonStyle(.glass(.clear))When the button sits over a bright or visually busy background, include a contrast treatment underneath the clear glass or choose a more opaque style:
ZStack {
Capsule()
.fill(.black.opacity(0.28))
Button {
playRecap()
} label: {
Label("Play", systemImage: "play.fill")
.font(.headline)
.padding(.horizontal, 8)
}
.buttonStyle(.glass(.clear))
}
.fixedSize()Use .glass(.regular.tint(color)) for tinted tool controls. Reserve .glassProminent for high-emphasis primary actions rather than using it as the default answer whenever a button needs visual weight.
These button styles automatically include interactivity (touch/pointer reactions). Prefer these over manually applying .glassEffect(.regular.interactive()) to buttons.
DefaultGlassEffectShape
The default shape used by glassEffect(_:in:) when no shape is specified. Resolves to Capsule.
Scroll Edge Effect
Configures the scroll edge effect style for scroll views within a view hierarchy.
nonisolated func scrollEdgeEffectStyle(
_ style: ScrollEdgeEffectStyle?,
for edges: Edge.Set
) -> some ViewAvailable styles include .soft (soft edge) and .hard. System bars adopt this automatically. Apply to custom bars that float over scrollable content.
.scrollEdgeEffectStyle(.soft, for: .top)Background Extension (Split Views)
Extend content visually under sidebars and inspectors by duplicating the view into mirrored copies with a blur effect applied on top:
NavigationSplitView {
// sidebar content
} detail: {
ZStack {
BannerView()
.backgroundExtensionEffect()
}
}Also available as backgroundExtensionEffect(isEnabled:) for conditional use. Apply with discretion — typically to a single background view in the detail column.
Availability Gating
All Liquid Glass APIs require iOS 26.0+. Always provide a fallback:
if #available(iOS 26, *) {
content
.padding()
.glassEffect(.regular, in: .rect(cornerRadius: 16))
} else {
content
.padding()
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
}Performance Guidelines
- Use
GlassEffectContainerto batch multiple glass effects into a single render pass. - Limit the total number of glass effects visible on screen at once.
- Avoid creating many separate
GlassEffectContainerinstances when one can suffice. - Profile with Instruments to check rendering performance.
Accessibility Considerations
- Test with Reduce Transparency enabled (glass effects adapt automatically for
standard components; verify custom implementations).
- Test with Reduce Motion enabled (morphing and fluid animations are simplified).
- Ensure sufficient contrast for text and icons rendered over glass; clear glass may
need a dimming layer or another contrast treatment.
- Standard components from SwiftUI adapt to these settings automatically.
Best Practices Summary
1. Use GlassEffectContainer when multiple glass views coexist. 2. Apply .glassEffect() after layout and appearance modifiers. 3. Match container spacing to interior layout spacing. 4. Use .interactive() only on tappable/focusable elements. 5. Use withAnimation when toggling views with glassEffectID for morphing. 6. Prefer .buttonStyle(.glass), .buttonStyle(.glassProminent), or configurable styles such as .buttonStyle(.glass(.clear)) for buttons. 7. Avoid overusing Liquid Glass -- reserve it for key functional elements. 8. Always gate with if #available(iOS 26, *) and provide fallback UI. 9. Test with Reduce Transparency and Reduce Motion accessibility settings. 10. Apply glassEffectTransition(_:) to the conditional glass view that appears or disappears.
Apple Documentation Links
Related skills
FAQ
Where should Liquid Glass be applied?
On controls and navigation layers, not as decoration across static content containers.
When should interactive be used?
Only on tappable or focusable elements; read-only status badges should not look pressable.
What modifier order does glassEffect require?
Apply glassEffect after layout and appearance modifiers such as padding and frame so bounds are final.
Is Swiftui Liquid Glass safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.