skills/ios-liquid-glass/SKILL.md
API reference: Liquid Glass (iOS 26+). Query for glass effects, navigation patterns, GlassEffect modifiers, design principles.
npx skillsauth add vabole/apple-skills ios-liquid-glassInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create distinctive, Apple Design Award-worthy iOS applications using the Liquid Glass design system. This skill pushes beyond generic implementations toward memorable, polished interfaces that feel genuinely designed—not AI-generated.
Before writing code, commit to a distinctive aesthetic direction:
| Principle | Description | |-----------|-------------| | Hierarchy | Controls float above content. Glass frames, never obscures. Content is king. | | Harmony | Software design aligns with hardware. Concentric corners. Fluid gestures. | | Consistency | Adapt fluidly across iPhone, iPad, Mac. Same identity, contextual expression. |
These are hallmarks of generic AI-generated iOS design:
import SwiftUI
// Simple glass application
Text("Action")
.padding()
.glassEffect() // .regular variant, .capsule shape
// With explicit parameters
Button("Confirm") { }
.padding()
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: 12), isEnabled: true)
| Variant | Use Case |
|---------|----------|
| .regular | Toolbars, nav bars, tab bars, standard controls |
| .clear | Floating controls over media (photos, maps, video) |
| .identity | Conditional disable: glassEffect(isActive ? .regular : .identity) |
// Semantic tinting (for primary actions only)
.glassEffect(.regular.tint(.accentColor))
// Interactive behaviors (scaling, shimmer, touch illumination)
.glassEffect(.regular.interactive())
// Combined
.glassEffect(.regular.tint(.blue).interactive())
// Standard shapes
.glassEffect(.regular, in: .capsule)
.glassEffect(.regular, in: .circle)
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: 16))
// Container-concentric (matches device/container corners)
.glassEffect(.regular, in: .rect(cornerRadius: .containerConcentric))
Glass cannot sample other glass. Use containers for multiple glass elements:
GlassEffectContainer(spacing: 30) {
HStack(spacing: 20) {
ForEach(actions) { action in
Button(action.title, systemImage: action.icon) { }
.frame(width: 44, height: 44)
.glassEffect(.regular.interactive())
}
}
}
struct ExpandableActions: View {
@State private var isExpanded = false
@Namespace private var namespace
var body: some View {
GlassEffectContainer(spacing: 30) {
VStack(spacing: 30) {
if isExpanded {
ActionButton(icon: "rotate.right")
.glassEffectID("rotate", in: namespace)
}
HStack(spacing: 30) {
if isExpanded {
ActionButton(icon: "slider.horizontal.3")
.glassEffectID("adjust", in: namespace)
}
Button {
withAnimation(.bouncy) { isExpanded.toggle() }
} label: {
Image(systemName: isExpanded ? "xmark" : "plus")
.frame(width: 56, height: 56)
}
.glassEffect(.regular.tint(.accentColor).interactive())
.glassEffectID("toggle", in: namespace)
if isExpanded {
ActionButton(icon: "crop")
.glassEffectID("crop", in: namespace)
}
}
if isExpanded {
ActionButton(icon: "wand.and.stars")
.glassEffectID("enhance", in: namespace)
}
}
}
}
}
TabView {
Tab("Home", systemImage: "house") {
HomeView()
}
Tab("Search", systemImage: "magnifyingglass") {
SearchView()
}
Tab("Profile", systemImage: "person") {
ProfileView()
}
}
// Tab bar now floats, reacts to background, collapses on scroll
.toolbar {
ToolbarItemGroup(placement: .topBarTrailing) {
Button("Edit", systemImage: "pencil") { }
Button("Share", systemImage: "square.and.arrow.up") { }
}
ToolbarSpacer(.flexible, placement: .topBarTrailing)
ToolbarItem(placement: .topBarTrailing) {
Button("Done", systemImage: "checkmark") { }
.tint(.accentColor)
}
}
struct ContentView: View {
@State private var showSettings = false
@Namespace private var namespace
var body: some View {
NavigationStack {
ContentView()
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Settings", systemImage: "gear") {
showSettings = true
}
.matchedTransitionSource(id: "settings", in: namespace)
}
}
.sheet(isPresented: $showSettings) {
SettingsView()
.navigationTransition(.zoom(sourceID: "settings", in: namespace))
.presentationDetents([.medium, .large])
}
}
}
}
Don't default to system fonts everywhere. Create typographic hierarchy:
// Display fonts for headers
Text("Dashboard")
.font(.largeTitle.bold())
.foregroundStyle(.primary)
// Secondary information
Text("Last updated 5 min ago")
.font(.subheadline)
.foregroundStyle(.secondary)
// Consider custom fonts for brand identity
Text("Premium")
.font(.custom("PlayfairDisplay-Bold", size: 32))
| Use Case | Options | |----------|---------| | Display | SF Pro Display, New York, custom serif | | Body | SF Pro Text (system), custom sans | | Technical | SF Mono, custom monospace | | Editorial | New York, custom serif with character |
// Semantic colors that adapt
.foregroundStyle(.primary)
.foregroundStyle(.secondary)
.background(.background)
// Accent with purpose
.tint(.accentColor)
Don't distribute color evenly. Choose:
Dark mode often produces more distinctive results. Design dark, then adapt to light.
withAnimation(.bouncy) {
isExpanded.toggle()
}
// With parameters
withAnimation(.bouncy(duration: 0.5, extraBounce: 0.2)) {
state = newState
}
| Moment | Treatment | |--------|-----------| | Button press | Scale to 0.95, spring back | | State change | Morph, don't swap | | List appear | Staggered fade-in | | Sheet present | Zoom from source | | Error | Shake with haptic |
Image(systemName: "checkmark.circle")
.symbolEffect(.drawOn, value: isComplete)
Image(systemName: "heart.fill")
.symbolEffect(.bounce, value: isFavorite)
import UIKit
// Impact feedback
let generator = UIImpactFeedbackGenerator(style: .medium)
generator.impactOccurred()
// Selection feedback
let selection = UISelectionFeedbackGenerator()
selection.selectionChanged()
// Success/error
let notification = UINotificationFeedbackGenerator()
notification.notificationOccurred(.success)
Glass automatically adapts to:
@Environment(\.accessibilityReduceTransparency) var reduceTransparency
@Environment(\.accessibilityReduceMotion) var reduceMotion
var animation: Animation? {
reduceMotion ? nil : .bouncy
}
Before considering UI complete:
From 2025 Apple Design Award winners:
For official Apple documentation links, WWDC session IDs, and API quick reference tables, see reference.md.
Remember: The goal is an app worthy of an Apple Design Award—an app that feels genuinely designed, not generated. Every interface should have:
Don't settle for "working." Push for memorable.
development
API reference: XCUITest. Query for element queries, waiting patterns, Swift 6 @MainActor, assertions, screenshots, launch arguments.
development
API reference: TipKit. Tip protocol, TipView, PopoverTipView, Tips.configure, inline and popover tips.
development
API reference: MapKit for SwiftUI. Map view, Marker, Annotation, camera positions, map features.
development
API reference: Apple Human Interface Guidelines. Query for design patterns, UI components, accessibility, color, typography, layout, haptics.