Use when implementing Liquid Glass effects, reviewing UI for Liquid Glass adoption, debugging visual artifacts, optimizing performance, or requesting expert review of Liquid Glass implementation - provides comprehensive design principles, API patterns, and troubleshooting guidance from WWDC 2025. Includes design review pressure handling and professional push-back frameworks
/plugin marketplace add CharlesWiltgen/Axiom/plugin install axiom@axiom-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Use when:
axiom-liquid-glass-ref for comprehensive app-wide adoption guidance (app icons, controls, navigation, menus, windows, platform considerations)These are real questions developers ask that this skill is designed to answer:
→ The skill explains Liquid Glass as a lensing-based material (not blur), shows design philosophy, and when adoption makes sense
→ The skill covers the visual properties (lensing vs motion vs environment), Regular vs Clear variants, and debugging visual artifacts
→ The skill demonstrates adaptive Liquid Glass patterns and platform-specific guidance (iOS 26+, macOS Tahoe+, axiom-visionOS 3+)
→ The skill covers tinting strategies, adaptive color choices, and opacity patterns for maintaining readability across light/dark modes
→ The skill provides the comprehensive review checklist and professional push-back frameworks for design review meetings
Liquid Glass is Apple's next-generation material design system introduced at WWDC 2025. It represents a significant evolution from previous materials (Aqua, iOS 7 blurs, Dynamic Island) by creating a new digital meta-material that:
Core Philosophy: Liquid Glass complements the evolution of rounded, immersive screens with rounded, floating forms that feel natural to touch interaction while letting content shine through.
Liquid Glass defines itself through lensing — the warping and bending of light that communicates presence, motion, and form.
How it works:
Design Implication: Unlike previous materials that scattered light, Liquid Glass uses instinctive visual cues from the natural world to provide separation.
Motion and visuals were designed as one unified experience:
Liquid Glass continuously adapts without fixed light/dark appearance:
Content-aware adaptation:
Platform adaptation:
glassEffect Modifier// Basic usage - applies glass within capsule shape
Text("Hello")
.glassEffect()
// Custom shape
Text("Hello")
.glassEffect(in: RoundedRectangle(cornerRadius: 12))
// Interactive elements (iOS - for controls/containers)
Button("Tap Me") {
// action
}
.glassEffect()
.interactive() // Add for custom controls on iOS
Automatic Adoption: Simply recompiling with Xcode 26 brings Liquid Glass to standard controls automatically.
CRITICAL DECISION: Never mix Regular and Clear in the same interface.
Characteristics:
When to use: Navigation bars, tab bars, toolbars, buttons, menus, sidebars
// Regular is the default
NavigationView {
// Content
}
.glassEffect() // Uses Regular variant
Characteristics:
Use ONLY when ALL three conditions are met:
// Clear variant with localized dimming for small footprints
ZStack {
MediaRichBackground()
.overlay(.black.opacity(0.3)) // Dimming layer
BoldBrightControl()
.glassEffect(.clear)
}
⚠️ WARNING: Using Clear without meeting all three conditions results in poor legibility.
Liquid Glass is composed of multiple layers working together:
Correct Usage:
[Content Layer — No Glass]
↓
[Navigation Layer — Liquid Glass]
• Tab bars
• Navigation bars
• Toolbars
• Floating controls
Why: Liquid Glass floats above content, creating clear hierarchy.
Wrong:
// DON'T apply to table views, lists, or content
List(items) { item in
Text(item.name)
}
.glassEffect() // ❌ Competes with navigation, muddy hierarchy
Why: Makes elements compete, creates visual confusion.
Wrong:
ZStack {
NavigationBar()
.glassEffect() // ❌
FloatingButton()
.glassEffect() // ❌ Glass on glass
}
Correct:
ZStack {
NavigationBar()
.glassEffect()
FloatingButton()
.foregroundStyle(.primary) // Use fills, transparency, vibrancy
// Feels like thin overlay part of the material
}
Wrong: Content intersects with Liquid Glass when app launches
Correct: Reposition or scale content to maintain separation in steady states
Why: Prevents unwanted visual noise; intersections acceptable during scrolling/transitions.
Work in concert with Liquid Glass to maintain separation and legibility with scrolling content.
How they work:
Use when pinned accessory views exist (e.g., column headers):
ScrollView {
// Content
}
.scrollEdgeEffect(.hard) // Uniform across toolbar + pinned accessories
When to use: Extra visual separation between floating elements in accessory view and scrolling content.
Liquid Glass introduces adaptive tinting that respects material principles and maximizes legibility.
How it works:
Compatible with all glass behaviors (morphing, adaptation, interaction).
Button("Primary Action") {
// action
}
.tint(.red) // Adaptive tinting automatically applied
.glassEffect()
// Good — Emphasizes primary action
Button("View Bag") {
// action
}
.tint(.red)
.glassEffect()
// Wrong — When everything is tinted, nothing stands out
VStack {
Button("Action 1").tint(.blue).glassEffect()
Button("Action 2").tint(.green).glassEffect()
Button("Action 3").tint(.purple).glassEffect()
} // ❌ Confusing, no hierarchy
Solution: Use color in content layer instead, reserve tinting for primary UI actions.
Solid fills break Liquid Glass character:
// ❌ Opaque, breaks visual character
Button("Action") {}
.background(.red) // Solid, opaque
// ✅ Transparent, grounded in environment
Button("Action") {}
.tint(.red)
.glassEffect()
Small elements (navbars, tabbars):
Large elements (menus, sidebars):
Symbols/glyphs:
Use selectively for distinct functional purpose:
// Selective tinting for emphasis
NavigationView {
List {
// Content
}
.toolbar {
ToolbarItem {
Button("Important") {}
.tint(.orange) // Brings attention
}
}
}
Applies to: Labels, text, fully tinted buttons, time on lock screen, etc.
Liquid Glass offers several accessibility features that modify material without sacrificing its magic:
Developer Action Required: None - all features available automatically when using Liquid Glass.
Concern: Liquid Glass rendering cost in complex view hierarchies
Guidance:
Optimization:
// ❌ Avoid deep nesting
ZStack {
GlassContainer1()
.glassEffect()
ZStack {
GlassContainer2()
.glassEffect()
// More nesting...
}
}
// ✅ Flatten hierarchy
VStack {
GlassContainer1()
.glassEffect()
GlassContainer2()
.glassEffect()
}
Adaptive behaviors have computational cost:
System handles optimization, but be mindful:
Capture screenshots in multiple states:
func testLiquidGlassAppearance() {
let app = XCUIApplication()
app.launch()
// Test light mode
XCTContext.runActivity(named: "Light Mode Glass") { _ in
let screenshot = app.screenshot()
// Compare with baseline
}
// Test dark mode
app.launchArguments = ["-UIUserInterfaceStyle", "dark"]
app.launch()
XCTContext.runActivity(named: "Dark Mode Glass") { _ in
let screenshot = app.screenshot()
// Compare with baseline
}
}
Critical test cases:
func testLiquidGlassAccessibility() {
// Enable accessibility features via launch arguments
app.launchArguments += [
"-UIAccessibilityIsReduceTransparencyEnabled", "1",
"-UIAccessibilityButtonShapesEnabled", "1",
"-UIAccessibilityIsReduceMotionEnabled", "1"
]
// Verify glass still functional and legible
XCTAssertTrue(glassElement.exists)
XCTAssertTrue(glassElement.isHittable)
}
Under design review pressure, you'll face requests to:
These sound reasonable. But they violate the framework. Your job: defend using evidence, not opinion.
If you hear ANY of these, STOP and reference the skill:
"I want to make this change, but let me show you Apple's guidance on Clear variant.
It requires THREE conditions:
1. Media-rich content background
2. Dimming layer for legibility
3. Bold, bright controls on top
Let me show which screens meet all three..."
Open the app on a device. Show:
"Clear can work beautifully in these 6 hero sections where all three conditions apply.
Regular handles everything else with automatic legibility. Best of both worlds."
If overruled (designer insists on Clear everywhere):
Slack message to PM + designer:
"Design review decided to use Clear variant across all controls.
Important: Clear variant requires legibility testing in low-contrast scenarios
(bright sunlight, dark content). If we see accessibility issues after launch,
we'll need an expedited follow-up. I'm flagging this proactively."
// In the meeting, demo side-by-side:
// Regular variant (current implementation)
NavigationBar()
.glassEffect() // Automatic legibility
// Clear variant (requested)
NavigationBar()
.glassEffect(.clear) // Requires dimming layer below
// Show the three-condition checklist
// Demonstrate which screens pass/fail
// Offer: Clear in hero sections, Regular elsewhere
Sometimes designers have valid reasons to override the skill. Accept if:
"Design review decided to use Clear variant [in these locations].
We understand this requires:
- All three conditions met: [list them]
- Potential legibility issues in low-contrast scenarios
- Accessibility testing across brightness levels
Monitoring plan:
- Gather user feedback first 48 hours
- Run accessibility audit
- Have fallback to Regular variant ready for push if needed"
This protects both of you and shows you're not blocking - just de-risking.
When reviewing Liquid Glass implementation (your code or others'), check:
Wrong:
List(landmarks) { landmark in
LandmarkRow(landmark)
.glassEffect() // ❌
}
.glassEffect() // ❌
Correct:
NavigationView {
List(landmarks) { landmark in
LandmarkRow(landmark) // No glass
}
}
.toolbar {
ToolbarItem {
Button("Add") {}
.glassEffect() // ✅ Navigation layer only
}
}
Why: Content layer should defer to Liquid Glass navigation layer.
Wrong:
ZStack {
VideoPlayer(player: player)
PlayButton()
.glassEffect(.clear) // ❌ No dimming, poor legibility
}
Correct:
ZStack {
VideoPlayer(player: player)
.overlay(.black.opacity(0.4)) // Dimming layer
PlayButton()
.glassEffect(.clear) // ✅
}
Wrong: All buttons tinted different colors
Correct: Primary action tinted, others use standard appearance
Wrong: Assuming glass always looks the same (e.g., hardcoded shadows, fixed opacity)
Correct: Embrace adaptive behavior, test across light/dark modes and backgrounds
Issue: Glass appears too transparent or invisible
Check:
Issue: Glass appears opaque or has harsh edges
Check:
Issue: Glass doesn't flip to dark style on dark backgrounds
Check:
.preferredColorScheme() if unintended)Issue: Content on glass not legible in dark mode
Fix:
// Let SwiftUI handle contrast automatically
Text("Label")
.foregroundStyle(.primary) // ✅ Adapts automatically
// Don't hardcode colors
Text("Label")
.foregroundColor(.black) // ❌ Won't adapt to dark mode
Issue: Scrolling feels janky with Liquid Glass
Debug:
axiom-swiftui-performance skill)Issue: Animations stuttering
Check:
Before (UIKit):
let blurEffect = UIBlurEffect(style: .systemMaterial)
let blurView = UIVisualEffectView(effect: blurEffect)
view.addSubview(blurView)
After (SwiftUI with Liquid Glass):
ZStack {
// Content
}
.glassEffect()
Benefits:
If you've built custom translucent effects:
When to keep custom materials:
glassEffect(in:isInteractive:)Applies Liquid Glass effect to view.
func glassEffect<S: Shape>(
in shape: S = Capsule(),
isInteractive: Bool = false
) -> some View
Parameters:
shape: Shape defining glass bounds (default: Capsule())isInteractive: On iOS, enables interactive mode for custom controls (default: false)Returns: View with Liquid Glass effect applied
Availability: iOS 26+, iPadOS 26+, macOS Tahoe+, axiom-visionOS 3+
Example:
// Default capsule shape
Text("Hello").glassEffect()
// Custom shape
Text("Hello").glassEffect(in: RoundedRectangle(cornerRadius: 16))
// Interactive (iOS)
Button("Tap") {}.glassEffect(isInteractive: true)
glassEffect(_:in:isInteractive:)Applies specific Liquid Glass variant.
func glassEffect<S: Shape>(
_ variant: GlassVariant,
in shape: S = Capsule(),
isInteractive: Bool = false
) -> some View
Parameters:
variant: .regular or .clearshape: Shape defining glass boundsisInteractive: Interactive mode for custom controls (iOS)Example:
Text("Hello").glassEffect(.clear, in: Circle())
scrollEdgeEffect(_:)Configures scroll edge appearance with Liquid Glass.
func scrollEdgeEffect(_ style: ScrollEdgeStyle) -> some View
Parameters:
style: .automatic, .soft, or .hardExample:
ScrollView {
// Content
}
.scrollEdgeEffect(.hard) // For pinned accessories
scrollEdgeEffectStyle(_:for:) NEW in iOS 26Optimizes legibility for controls when content scrolls beneath them.
func scrollEdgeEffectStyle(_ style: ScrollEdgeStyle, for edges: Edge.Set) -> some View
Parameters:
style: .hard, .soft, or .automaticedges: Which edges to apply effect (.top, .bottom, .leading, .trailing)Use case: Custom bars with controls, text, or icons that have content scrolling beneath them. System bars (toolbars, navigation bars) adopt this automatically.
Example:
// Custom toolbar with controls
CustomToolbar()
.scrollEdgeEffectStyle(.hard, for: .top) // Maintain legibility
ScrollView {
LazyVStack {
ForEach(items) { item in
ItemRow(item)
}
}
}
Availability: iOS 26+, iPadOS 26+, macOS Tahoe+
glassBackgroundEffect() NEW in iOS 26Applies glass effect to custom views for reflecting surrounding content.
func glassBackgroundEffect() -> some View
Use case: Apply Liquid Glass appearance to custom views (not buttons/controls) that should beautifully reflect surrounding content like photos.
Example:
struct PhotoGalleryView: View {
var body: some View {
CustomPhotoGrid()
.glassBackgroundEffect() // Reflects surrounding photos
}
}
Availability: iOS 26+, iPadOS 26+, macOS Tahoe+, axiom-visionOS 3+
.toolbar with Spacer(.fixed)Separates toolbar button groups with fixed spacing.
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
Button("Up") { }
Button("Down") { }
Spacer(.fixed) // Fixed spacer separates groups
Button("Settings") { }
}
}
Why use .fixed: Creates logical visual separation between button groups. Default Spacer() is flexible and adjusts based on available space; .fixed maintains consistent separation.
Common pattern: Separate navigation buttons from action buttons, or primary actions from secondary actions.
Availability: iOS 26+, iPadOS 26+, macOS Tahoe+
.buttonStyle(.borderedProminent) + .tint() in ToolbarsMakes toolbar items more prominent with Liquid Glass tinting.
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Add Trip") {
addTrip()
}
.buttonStyle(.borderedProminent)
.tint(.blue) // Liquid Glass toolbars support tinting
}
}
Visual effect: Button appears with bordered prominent style and custom tint color, making it stand out against Liquid Glass toolbar background.
Best practice: Use for primary actions in toolbars. Don't over-tint - use for prominence, not decoration.
Availability: iOS 26+, iPadOS 26+, macOS Tahoe+
Search automatically appears bottom-aligned on iPhone (more ergonomic), top-trailing on iPad.
NavigationSplitView {
List { }
.searchable(text: $searchText)
}
// Placement on NavigationSplitView automatically:
// - Bottom-aligned on iPhone
// - Top trailing corner on iPad
No code changes required — existing .searchable() modifier automatically adopts platform-specific placement.
Why bottom-aligned: More ergonomic to reach on iPhone with thumb-based interaction.
Availability: iOS 26+, iPadOS 26+
Separates search tab from other tabs in tab bar, morphs into search field.
TabView {
SearchView()
.tabItem { Label("Search", systemImage: "magnifyingglass") }
.tabRole(.search) // Separated from other tabs, morphs into search
TripsView()
.tabItem { Label("Trips", systemImage: "map") }
}
Visual effect: Search tab appears separated from other tabs in the tab bar. When tapped, morphs into the search field.
Use case: Tab-based apps where search is a primary destination.
Availability: iOS 26+
containerRelativeShape()Aligns control shapes with container curvature for visual continuity.
func containerRelativeShape(_ shape: ContainerRelativeShape) -> some View
Parameters:
shape: Shape that aligns with container (e.g., .roundedRectangle)Use case: Create visual harmony by making controls concentric to their containers (sheets concentric to windows, controls concentric to sheets).
Example:
// Control shape aligns with container curvature
Button("Action") { }
.containerRelativeShape(.roundedRectangle)
.glassEffect()
Visual Effect Nested elements feel visually harmonious, with curvature matching container shape.
Availability: iOS 26+, iPadOS 26+, macOS Tahoe+
tabBarMinimizationBehavior(_:) NEW in iOS 26Configures tab bar to minimize when scrolling to elevate underlying content.
func tabBarMinimizationBehavior(_ behavior: TabBarMinimizationBehavior) -> some View
Parameters:
behavior: .onScrollDown, .onScrollUp, .automatic, or .neverUse case: Content-focused apps (reading, media) where tab bar should recede during scrolling.
Example:
TabView {
ContentView()
.tabItem { Label("Home", systemImage: "house") }
}
.tabBarMinimizationBehavior(.onScrollDown) // Minimize when scrolling down
Visual Effect Tab bar recedes when scrolling down, expands when scrolling up. Content gains more screen space.
Availability: iOS 26+
GlassVariantenum GlassVariant {
case regular // Default - full adaptive behavior
case clear // More transparent, no adaptation
}
ScrollEdgeStyleenum ScrollEdgeStyle {
case automatic // System determines style
case soft // Gradual fade
case hard // Uniform effect across toolbar height
}
GlassEffectContainer NEW in iOS 26Container for combining multiple Liquid Glass effects with optimized rendering performance.
struct GlassEffectContainer<Content: View>: View {
init(@ViewBuilder content: () -> Content)
}
Use case When applying Liquid Glass effects to multiple custom elements. Optimizes performance and enables fluid morphing between glass shapes.
// ✅ Combine effects in container for optimization
GlassEffectContainer {
HStack {
Button("Action 1") { }
.glassEffect()
Button("Action 2") { }
.glassEffect()
Button("Action 3") { }
.glassEffect()
}
}
Availability: iOS 26+, iPadOS 26+, macOS Tahoe+, axiom-visionOS 3+
To ship with latest SDKs while maintaining previous appearance:
<key>UIDesignRequiresCompatibility</key>
<true/>
UIDesignRequiresCompatibility enabledAvailability: iOS 26+, iPadOS 26+
WWDC: 2025-219, 2025-323, 2025-256
Docs: /technologyoverviews/adopting-liquid-glass, /swiftui/landmarks-building-an-app-with-liquid-glass, /swiftui/applying-liquid-glass-to-custom-views
Skills: axiom-liquid-glass-ref
Platforms: iOS 26+, iPadOS 26+, macOS Tahoe, axiom-visionOS 3 Xcode: 26+ History: See git log for changes
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.