Help us improve
Share bugs, ideas, or general feedback.
From casper
Implements Apple's Liquid Glass design and HIG for native macOS/iOS SwiftUI apps using materials, glass effects, cards, rows, badges, SF Symbols, and system spacing/typography/colors.
npx claudepluginhub casper-studios/casper-marketplace --plugin casperHow this skill is triggered — by the user, by Claude, or both
Slash command
/casper:liquid-glassThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Build native macOS/iOS applications with Apple's Liquid Glass design and HIG.
Build and migrate iOS, macOS, iPadOS, watchOS, tvOS, and visionOS apps with Apple's Liquid Glass design system (iOS 26+, macOS 26 Tahoe+). Use when creating new SwiftUI apps targeting Apple's 2025+ platforms, migrating existing apps to Liquid Glass, applying .glassEffect(), .backgroundExtensionEffect(), .buttonStyle(.glass), GlassEffectContainer, glass toolbars, glass tab bars, glass sheets, or any Liquid Glass UI work. Also use when the user asks about modern Apple design, navigation patterns with glass, or SwiftUI best practices for the latest OS versions.
Provides API reference and design guidelines for Liquid Glass effects in iOS 26+ SwiftUI/UIKit apps, covering GlassEffect modifiers, navigation patterns, principles, and anti-patterns.
Implements Apple's Liquid Glass design system for iOS 26+ using SwiftUI glassEffect APIs and UIKit NSGlassEffectView integration. Covers fallbacks, accessibility, performance, morphing transitions, and version checks.
Share bugs, ideas, or general feedback.
Build native macOS/iOS applications with Apple's Liquid Glass design and HIG.
┌─────────────────────────────────────────┐
│ GLASS LAYER (Navigation/Controls) │ ← Glass here ONLY
├─────────────────────────────────────────┤
│ CONTENT LAYER (Your App) │ ← Never glass here
└─────────────────────────────────────────┘
Glass is ONLY for navigation floating above content. Never on content itself.
// Card with material
.padding(24)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16, style: .continuous))
// Material options (lightest → heaviest):
// .ultraThinMaterial, .thinMaterial, .regularMaterial (default), .thickMaterial, .ultraThickMaterial
// Basic
Button("Action") { }.glassEffect()
// Variants
.glassEffect(.regular) // Standard UI
.glassEffect(.clear) // Media backgrounds only
.glassEffect(.identity) // Disabled
// Interactive (adds bounce/shimmer)
Button("Tap") { }.glassEffect(.regular.interactive())
// Multiple glass elements - MUST wrap in container
GlassEffectContainer(spacing: 30) {
HStack {
Button("A") { }.glassEffect()
Button("B") { }.glassEffect()
}
}
// Button styles
Button("Cancel") { }.buttonStyle(.glass) // Secondary
Button("Save") { }.buttonStyle(.glassProminent).tint(.blue) // Primary
VStack(alignment: .leading, spacing: 12) {
HStack {
ZStack {
Circle().fill(color.opacity(0.15)).frame(width: 36, height: 36)
Image(systemName: icon).foregroundStyle(color)
}
Spacer()
}
Text(value).font(.system(size: 28, weight: .bold, design: .rounded))
Text(label).font(.caption).foregroundStyle(.secondary)
}
.padding(20)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16, style: .continuous))
HStack { content }
.padding(16)
.background(isHovering ? Color.primary.opacity(0.04) : .clear)
.background(.quaternary.opacity(0.5), in: RoundedRectangle(cornerRadius: 12, style: .continuous))
.onHover { withAnimation(.easeInOut(duration: 0.15)) { isHovering = $0 } }
HStack(spacing: 8) {
Circle().fill(isActive ? .green : .orange).frame(width: 8, height: 8)
Text(status).font(.caption).fontWeight(.medium)
}
.padding(.horizontal, 12).padding(.vertical, 8)
.background(.regularMaterial, in: Capsule())
ZStack {
Circle().fill(color.opacity(0.15)).frame(width: 32, height: 32)
Image(systemName: icon).font(.system(size: 14, weight: .semibold)).foregroundStyle(color)
}
// Always use .continuous
RoundedRectangle(cornerRadius: 16, style: .continuous) // Cards
RoundedRectangle(cornerRadius: 12, style: .continuous) // Rows
RoundedRectangle(cornerRadius: 8, style: .continuous) // Small elements
Capsule() // Pills, badges
Circle() // Icons
// Semantic foreground
.foregroundStyle(.primary) // Main content
.foregroundStyle(.secondary) // Subtitles
.foregroundStyle(.tertiary) // Timestamps
// Backgrounds
.background(.quaternary)
.background(.quaternary.opacity(0.5))
// Accent meanings
Color.blue // Primary actions, selection
Color.green // Success, active
Color.orange // Warning, loading
Color.red // Destructive, error
Color.purple // Premium, AI
Color.cyan // Security
// Tinted backgrounds: always 15% opacity
.fill(color.opacity(0.15))
.font(.largeTitle).fontWeight(.bold) // Page titles
.font(.headline).fontWeight(.semibold) // Section headers
.font(.subheadline).fontWeight(.medium) // Row titles
.font(.body) // Content (17pt default)
.font(.caption) // Metadata
.font(.caption2) // Timestamps
.font(.system(size: 28, weight: .bold, design: .rounded)) // Stats
Rules: Min 11pt. Avoid Ultralight/Thin/Light. Use system fonts for Dynamic Type.
// Standard values: 4, 8, 12, 16, 20, 24, 32, 40, 48
.padding(24) // Cards
.padding(16) // Rows
.padding(.horizontal, 12).padding(.vertical, 8) // Badges
// Rule: external spacing ≥ internal spacing
VStack(spacing: 24) { CardView().padding(20) } // Correct
// Preferred: hierarchical for depth
Image(systemName: icon).symbolRenderingMode(.hierarchical).foregroundStyle(color)
// Match weight to nearby text
Image(systemName: "gear").font(.system(size: 14, weight: .semibold))
// MINIMUM: 44pt × 44pt
Button { } label: { Image(systemName: "gear") }
.frame(minWidth: 44, minHeight: 44)
// Hover
.onHover { withAnimation(.easeInOut(duration: 0.15)) { isHovering = $0 } }
// Spring
withAnimation(.spring(response: 0.3)) { }
withAnimation(.bouncy) { }
// Entry
.opacity(appeared ? 1 : 0).offset(y: appeared ? 0 : 10)
.onAppear { withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) { appeared = true } }
DO:
.regularMaterial for cards/toolbars.continuous on ALL rounded rectanglesGlassEffectContainer.hierarchicalDON'T:
System handles automatically:
Manual override if needed:
@Environment(\.accessibilityReduceTransparency) var reduceTransparency
.glassEffect(reduceTransparency ? .identity : .regular)
For detailed patterns and examples, see: