mcpField
L
liquid-glass

liquid-glass

github.com/casper-studiosDesign & UI15322 installsUpdated 8w ago
Repository

About liquid-glass

A design skill for building native macOS and iOS apps with Apple’s Liquid Glass design and Human Interface Guidelines. It provides rules and SwiftUI patterns for materials, layout, typography, motion, and accessibility.

Key features

  1. 01Glass and content layer rules
  2. 02Liquid Glass and material examples
  3. 03SwiftUI cards, rows, and badges
  4. 04Spacing, typography, and shape guidance
  5. 05Accessibility adaptations and hit targets

Use cases

  1. 01Designing navigation and controls that float above app content
  2. 02Building consistent cards, rows, and status badges
  3. 03Checking touch targets and Dynamic Type support

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/casper-studios/casper-marketplace --skill liquid-glass

After installing, type/liquid-glassin Claude Code to confirm it loaded.

Bundled filesreferences/apple-hig.mdreferences/components.md

Details

From the project README

Liquid Glass Design

Build native macOS/iOS applications with Apple's Liquid Glass design and HIG.

Core Rules

Three-Layer Model
┌─────────────────────────────────────────┐
│     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.

Five Principles
  1. Content First - Glass floats above, content shines through
  2. Depth Through Light - Lensing creates hierarchy, not opacity
  3. Adaptive Tinting - Colors respond to background dynamically
  4. Semantic Emphasis - Tint primary actions only
  5. Accessibility Built-In - System handles adaptations automatically

Materials (macOS 14+ / Pre-iOS 26)

// Card with material
.padding(24)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16, style: .continuous))

// Material options (lightest → heaviest):
// .ultraThinMaterial, .thinMaterial, .regularMaterial (default), .thickMaterial, .ultraThickMaterial

Liquid Glass API (iOS 26+ / macOS Tahoe)

// 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

Essential Patterns

Card
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))
Row with Hover
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 } }
Badge
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())
Icon with Tinted Background
ZStack {
    Circle().fill(color.opacity(0.15)).frame(width: 32, height: 32)
    Image(systemName: icon).font(.system(size: 14, weight: .semibold)).foregroundStyle(color)
}

Shapes

// 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

Colors

// 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))

Typography

.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.

Spacing (8pt Grid)

// 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

SF Symbols

// 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))

Hit Targets

// MINIMUM: 44pt × 44pt
Button { } label: { Image(systemName: "gear") }
    .frame(minWidth: 44, minHeight: 44)

Animations

// 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's and Don'ts

DO:

  • Use .regularMaterial for cards/toolbars
  • Use .continuous on ALL rounded rectangles
  • Use 15% opacity for icon backgrounds
  • Wrap multiple glass in GlassEffectContainer
  • Use 44pt minimum hit targets
  • Use 8pt grid spacing
  • Use SF Symbols with .hierarchical

DON'T:

  • Apply glass to content (lists, tables)
  • Stack glass on glass without container
  • Use sharp corners
  • Go below 11pt text
  • Create touch targets < 44pt
  • Use full-color images on glass

Accessibility

System handles automatically:

  • Reduce Transparency → opaquer glass
  • Increase Contrast → visible borders
  • Reduce Motion → no bouncy effects
  • Dynamic Type → text scales

Manual override if needed:

@Environment(\.accessibilityReduceTransparency) var reduceTransparency
.glassEffect(reduceTransparency ? .identity : .regular)

References

For detailed patterns and examples, see:

Comments

Markdown supported