iOS 26/macOS 26 Liquid Glass design system with complete API coverage. Use when user asks about iOS 26 design, Liquid Glass, glassEffect modifier, GlassEffectContainer, morphing animations, HIG compliance, visual styling, or the new Apple design language.
/plugin marketplace add bluewaves-creations/bluewaves-skills/plugin install swift-apple-dev@bluewaves-skillsThis skill is limited to using the following tools:
Comprehensive guide to iOS 26 and macOS Tahoe's revolutionary Liquid Glass design system, including complete SwiftUI API coverage, Human Interface Guidelines, morphing animations, and implementation best practices.
Liquid Glass is Apple's new design language introduced at WWDC 2025. It creates a lightweight, dynamic material that:
Liquid Glass establishes a clear visual hierarchy:
Key Principle: Reserve glass for navigation and controls, NOT for content.
┌─────────────────────────────────────┐
│ Liquid Glass Material Properties │
├─────────────────────────────────────┤
│ • Translucency with depth │
│ • Real-time light refraction │
│ • Specular highlights on motion │
│ • Adaptive shadows │
│ • Color informed by backdrop │
│ • Light/dark environment aware │
└─────────────────────────────────────┘
The versatile, adaptive variant. Use for most UI elements.
Button("Action") {
performAction()
}
.buttonStyle(.glass)
Characteristics:
Permanently transparent variant with minimal visual impact.
Button("Subtle Action") {
performAction()
}
.buttonStyle(.glassClear)
Characteristics:
No glass effect applied. Use for comparisons or opt-out.
.glassEffect(.identity)
NEVER mix Regular and Clear glass variants in the same interface.
This creates visual inconsistency and violates HIG principles.
// WRONG - Mixed variants
HStack {
Button("Save") { }
.buttonStyle(.glass) // Regular
Button("Cancel") { }
.buttonStyle(.glassClear) // Clear - DON'T MIX
}
// CORRECT - Consistent variant
HStack {
Button("Save") { }
.buttonStyle(.glass)
Button("Cancel") { }
.buttonStyle(.glass)
}
// Simple glass effect with default shape
View()
.glassEffect()
// Glass with specific shape
View()
.glassEffect(in: RoundedRectangle(cornerRadius: 16))
// Glass with variant and shape
View()
.glassEffect(.regular, in: Capsule())
// Conditional glass
View()
.glassEffect(in: Circle(), isEnabled: showGlass)
func glassEffect(
_ glass: Glass = .regular,
in shape: some Shape = .rect,
isEnabled: Bool = true
) -> some View
enum Glass {
case regular // Adaptive, versatile (default)
case clear // High transparency
case identity // No effect
}
Add color tint to glass elements:
Button("Tinted") { }
.buttonStyle(.glass)
.tint(.blue)
// Or with glass directly
View()
.glassEffect(in: RoundedRectangle(cornerRadius: 12))
.tint(.green)
Enable interactive behaviors for glass:
View()
.glassEffect(.regular.interactive(), in: Capsule())
// Standard glass button
Button("Glass") { }
.buttonStyle(.glass)
// Prominent glass button (more opaque)
Button("Prominent") { }
.buttonStyle(.glassProminent)
// Borderless glass
Button("Borderless") { }
.buttonStyle(.glassBorderless)
GlassEffectContainer combines multiple glass shapes into a single morphable unit with shared visual properties.
GlassEffectContainer {
HStack {
Button("First") { }
.glassEffect(in: Capsule())
Button("Second") { }
.glassEffect(in: Capsule())
Button("Third") { }
.glassEffect(in: Capsule())
}
}
The spacing parameter controls the threshold distance for morphing:
// Buttons close together will merge
GlassEffectContainer(spacing: 8) {
HStack(spacing: 4) { // Less than container spacing
Button("A") { }
.glassEffect(in: Capsule())
Button("B") { }
.glassEffect(in: Capsule())
}
// These buttons will visually merge into one glass shape
}
// Buttons far apart stay separate
GlassEffectContainer(spacing: 8) {
HStack(spacing: 20) { // Greater than container spacing
Button("A") { }
.glassEffect(in: Capsule())
Button("B") { }
.glassEffect(in: Capsule())
}
// These buttons maintain individual glass shapes
}
When views are inside a GlassEffectContainer:
struct ExpandableMenu: View {
@State private var isExpanded = false
@Namespace private var animation
var body: some View {
GlassEffectContainer {
if isExpanded {
VStack {
Button("Option 1") { }
.glassEffect(in: Capsule())
.glassEffectID("menu", in: animation)
Button("Option 2") { }
.glassEffect(in: Capsule())
Button("Option 3") { }
.glassEffect(in: Capsule())
}
} else {
Button("Menu") {
withAnimation(.spring) {
isExpanded.toggle()
}
}
.glassEffect(in: Capsule())
.glassEffectID("menu", in: animation)
}
}
}
}
Link glass elements across states for fluid morphing:
@Namespace private var animation
// Source state
Button("Collapsed") { }
.glassEffect(in: Capsule())
.glassEffectID("button", in: animation)
// Expanded state
HStack {
Button("Edit") { }
.glassEffect(in: Capsule())
.glassEffectID("button", in: animation) // Same ID = morph
Button("Delete") { }
.glassEffect(in: Capsule())
}
Combine multiple glass shapes into one:
GlassEffectContainer {
ForEach(items) { item in
ItemView(item: item)
.glassEffect(in: RoundedRectangle(cornerRadius: 12))
.glassEffectUnion(id: "group", namespace: animation)
}
}
Control how glass appears/disappears:
View()
.glassEffect(in: Capsule())
.glassEffectTransition(.scale, isEnabled: true)
// Transition types
.glassEffectTransition(.opacity)
.glassEffectTransition(.scale)
.glassEffectTransition(.slide)
.glassEffectTransition(.identity) // No transition
struct MorphingToolbar: View {
@State private var mode: Mode = .browse
@Namespace private var morphing
enum Mode {
case browse, edit, select
}
var body: some View {
GlassEffectContainer {
switch mode {
case .browse:
HStack {
Button("Edit") {
withAnimation(.spring(duration: 0.4)) {
mode = .edit
}
}
.glassEffect(in: Capsule())
.glassEffectID("primary", in: morphing)
}
case .edit:
HStack {
Button("Done") {
withAnimation(.spring(duration: 0.4)) {
mode = .browse
}
}
.glassEffect(in: Capsule())
.glassEffectID("primary", in: morphing)
Button("Select All") { }
.glassEffect(in: Capsule())
.glassEffectTransition(.scale)
}
case .select:
HStack {
Button("Cancel") {
withAnimation(.spring(duration: 0.4)) {
mode = .browse
}
}
.glassEffect(in: Capsule())
.glassEffectID("primary", in: morphing)
Spacer()
Button("Delete") { }
.glassEffect(in: Capsule())
.tint(.red)
}
}
}
.padding()
}
}
Bug: Placing a
Menuinside aGlassEffectContainerbreaks morphing animations.
// AVOID in iOS 26.1
GlassEffectContainer {
Menu("Options") { // This breaks morphing
Button("Edit") { }
Button("Delete") { }
}
.glassEffect(in: Capsule())
}
// WORKAROUND: Move Menu outside container
VStack {
Menu("Options") {
Button("Edit") { }
Button("Delete") { }
}
.buttonStyle(.glass)
GlassEffectContainer {
// Other morphing content
}
}
Toolbars automatically adopt Liquid Glass in iOS 26:
struct ContentView: View {
var body: some View {
NavigationStack {
ContentList()
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("Add", systemImage: "plus") {
addItem()
}
}
ToolbarSpacer(.fixed) // NEW: Group related items
ToolbarItem(placement: .secondaryAction) {
Button("Edit") {
editMode.toggle()
}
}
}
}
}
}
New in iOS 26 for grouping toolbar items:
.toolbar {
// Group 1
ToolbarItem(placement: .primaryAction) {
Button("Save") { }
}
ToolbarSpacer(.fixed) // Creates visual separation
// Group 2
ToolbarItem(placement: .secondaryAction) {
Button("Share") { }
}
ToolbarItem(placement: .secondaryAction) {
Button("Delete") { }
}
}
New button role for dismiss actions with glass X styling:
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Close", role: .close) {
dismiss()
}
// Automatically renders as glass X button
}
}
Control glass background visibility:
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("Action") { }
}
}
.toolbarBackgroundVisibility(.visible, for: .navigationBar)
// Options: .automatic, .visible, .hidden
NavigationStack {
ContentView()
.navigationTitle("My App")
.navigationBarTitleDisplayMode(.large)
// Glass navigation bar is automatic in iOS 26
}
TabView {
HomeView()
.tabItem {
Label("Home", systemImage: "house")
}
SearchView()
.tabItem {
Label("Search", systemImage: "magnifyingglass")
}
.tab(role: .search) // NEW: Search morphs into field
}
// Glass tab bar is automatic
NavigationSplitView {
Sidebar()
} content: {
ContentList()
} detail: {
DetailView()
}
// Glass adapts to column visibility
Liquid Glass automatically respects accessibility settings:
When enabled:
// Check setting in code if needed
@Environment(\.accessibilityReduceTransparency) var reduceTransparency
var body: some View {
if reduceTransparency {
// Provide alternative styling if needed
}
}
When enabled:
@Environment(\.colorSchemeContrast) var contrast
var body: some View {
if contrast == .increased {
// Adjust colors for higher contrast
}
}
When enabled:
@Environment(\.accessibilityReduceMotion) var reduceMotion
var body: some View {
withAnimation(reduceMotion ? .none : .spring) {
// Animation
}
}
Bring SwiftUI glass into UIKit apps:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swiftUIView = GlassButtonView()
let hostingController = UIHostingController(rootView: swiftUIView)
addChild(hostingController)
view.addSubview(hostingController.view)
hostingController.didMove(toParent: self)
}
}
struct GlassButtonView: View {
var body: some View {
Button("SwiftUI Glass") { }
.buttonStyle(.glass)
}
}
Wrap UIKit in SwiftUI with glass:
struct LegacyViewWrapper: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> LegacyViewController {
LegacyViewController()
}
func updateUIViewController(_ vc: LegacyViewController, context: Context) {}
}
// Usage with glass overlay
struct ContentView: View {
var body: some View {
ZStack {
LegacyViewWrapper()
VStack {
Spacer()
HStack {
Button("Control") { }
.buttonStyle(.glass)
}
.padding()
}
}
}
}
iOS 26 introduces new icon aesthetics:
┌─────────────────────────────────┐
│ iOS 26 App Icon Grid │
│ │
│ ┌─────────────────────────┐ │
│ │ │ │
│ │ Safe Content Area │ │
│ │ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ │ │ │
│ │ │ Main Element │ │ │
│ │ │ │ │ │
│ │ └─────────────────┘ │ │
│ │ │ │
│ └─────────────────────────┘ │
│ │
│ 1024x1024 @ 1x │
└─────────────────────────────────┘
import SwiftUI
struct GlassInterfaceView: View {
@State private var isEditing = false
@State private var selectedTab = 0
@Namespace private var morphing
var body: some View {
TabView(selection: $selectedTab) {
// Home Tab
NavigationStack {
ScrollView {
ContentGrid()
}
.navigationTitle("Home")
.toolbar {
ToolbarItem(placement: .primaryAction) {
GlassEffectContainer {
if isEditing {
HStack {
Button("Done") {
withAnimation(.spring(duration: 0.35)) {
isEditing = false
}
}
.glassEffect(in: Capsule())
.glassEffectID("edit", in: morphing)
Button("Select All") { }
.glassEffect(in: Capsule())
.glassEffectTransition(.scale)
}
} else {
Button("Edit") {
withAnimation(.spring(duration: 0.35)) {
isEditing = true
}
}
.glassEffect(in: Capsule())
.glassEffectID("edit", in: morphing)
}
}
}
}
}
.tabItem {
Label("Home", systemImage: "house")
}
.tag(0)
// Search Tab
SearchView()
.tabItem {
Label("Search", systemImage: "magnifyingglass")
}
.tab(role: .search)
.tag(1)
// Settings Tab
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
.tag(2)
}
}
}
struct ContentGrid: View {
let items = (1...20).map { "Item \($0)" }
var body: some View {
LazyVGrid(columns: [
GridItem(.adaptive(minimum: 150))
], spacing: 16) {
ForEach(items, id: \.self) { item in
ContentCard(title: item)
}
}
.padding()
}
}
struct ContentCard: View {
let title: String
var body: some View {
VStack {
RoundedRectangle(cornerRadius: 12)
.fill(.secondary.opacity(0.2))
.frame(height: 100)
Text(title)
.font(.headline)
}
// NO glass on content cards - they're content, not navigation
.padding()
.background(.regularMaterial)
.clipShape(RoundedRectangle(cornerRadius: 16))
}
}
struct SearchView: View {
@State private var query = ""
var body: some View {
NavigationStack {
List {
ForEach(searchResults, id: \.self) { result in
Text(result)
}
}
.navigationTitle("Search")
.searchable(text: $query)
}
}
var searchResults: [String] {
// Filter results based on query
[]
}
}
struct SettingsView: View {
var body: some View {
NavigationStack {
List {
Section("Account") {
NavigationLink("Profile") { Text("Profile") }
NavigationLink("Privacy") { Text("Privacy") }
}
Section("App") {
NavigationLink("Appearance") { Text("Appearance") }
NavigationLink("Notifications") { Text("Notifications") }
}
}
.navigationTitle("Settings")
}
}
}
#Preview {
GlassInterfaceView()
}
// Temporarily add borders to see glass boundaries
View()
.glassEffect(in: RoundedRectangle(cornerRadius: 12))
.border(.red) // Debug: see actual frame
// Verify GlassEffectContainer is wrapping correctly
GlassEffectContainer {
VStack {
// All glass effects here share container
}
}
.border(.blue) // Debug: see container bounds
// Slow down animations for debugging
withAnimation(.spring(duration: 2.0)) { // Slower for inspection
state.toggle()
}
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 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 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.