| name | components |
| description | visionOS UI components: glass backgrounds, hover effects, ornaments, 3D content views. Use when working on visionOS component patterns, spatial UI, or Vision Pro interactions. Triggers: Button, glass, hover, ornament, RealityView, Model3D, component. |
Component Patterns (visionOS)
CRITICAL — Glass-First, Minimal Color
visionOS windows use system glass material by default. The glass auto-adapts brightness based on the physical environment. Custom opaque backgrounds destroy this adaptive behavior.
BANNED on visionOS:
- Opaque/solid background colors on windows or main containers (
.background(AppTheme.Colors.background))
- Colored text (
.foregroundStyle(AppTheme.Colors.primary) on body text) — use white/system text on glass
- Heavy color saturation — colors may clash with the user's physical environment
- Dark theme backgrounds — there is no dark mode on visionOS, the glass handles it
.glassBackgroundEffect() on list row backgrounds — creates glass-on-glass, text becomes invisible
.scrollContentBackground(.hidden) on Lists — removes contrast needed for text visibility
REQUIRED on visionOS:
- Use
.glassBackgroundEffect() for custom container backgrounds (sidebars, cards, panels)
- Use white or system-default text on glass for readability
- Limit accent color to primary action buttons and key interactive elements only
- Use system vibrancy for text hierarchy (
.primary, .secondary, .tertiary)
VStack {
Text("Settings")
.font(AppTheme.Fonts.title)
.foregroundStyle(.primary)
Text("Configure your preferences")
.font(AppTheme.Fonts.body)
.foregroundStyle(.secondary)
}
.padding(AppTheme.Spacing.lg)
.glassBackgroundEffect()
VStack { content }
.background(AppTheme.Colors.background)
.foregroundStyle(AppTheme.Colors.primary)
AppTheme Color Usage on visionOS
AppTheme colors are ONLY for:
.borderedProminent button tint (accent color)
- Badge backgrounds (status indicators)
- Small decorative accents (icons, dividers)
- Chart/data visualization elements
AppTheme colors must NEVER be used for:
- Window or view backgrounds
- Card/container backgrounds (use
.glassBackgroundEffect())
- Body text color (use
.foregroundStyle(.primary) / .secondary / .tertiary)
Hover Effects
ALL interactive elements MUST have .hoverEffect() for eye tracking feedback:
Button("Play") {
play()
}
.hoverEffect()
Button { } label: {
Image(systemName: "star")
.font(AppTheme.Fonts.title)
}
.hoverEffect(.highlight)
Button Styles
Button("Start", systemImage: "play.fill") {
start()
}
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
.hoverEffect()
Button("Settings", systemImage: "gear") {
openSettings()
}
.buttonStyle(.bordered)
.buttonBorderShape(.roundedRectangle)
.hoverEffect()
BUTTON HIERARCHY:
| Level | Style | Use Case |
|---|
| Primary action | .borderedProminent | Start, Play, Confirm |
| Secondary | .bordered | Settings, More Info |
| Tertiary | .borderless | Dismiss, Cancel |
Ornaments
Ornaments attach supplementary controls to windows:
.ornament(attachmentAnchor: .scene(.bottom)) {
HStack(spacing: 20) {
Button("Previous", systemImage: "backward.fill") {
previous()
}
Button("Play", systemImage: "play.fill") {
play()
}
Button("Next", systemImage: "forward.fill") {
next()
}
}
.padding()
.glassBackgroundEffect()
}
3D Content — RealityView
For interactive 3D content:
import RealityKit
RealityView { content in
if let entity = try? await Entity(named: "Scene", in: realityKitContentBundle) {
content.add(entity)
}
}
3D Content — Model3D
For simple 3D model display (no interaction):
import RealityKit
Model3D(named: "Globe") { model in
model
.resizable()
.scaledToFit()
} placeholder: {
ProgressView()
}
Lists
visionOS List views have built-in glass-compatible styling. Do NOT add custom glass backgrounds to list rows — this creates glass-on-glass rendering where text becomes invisible.
List {
Section("General") {
NavigationLink("Profile") { ProfileView() }
Toggle("Notifications", isOn: $notifications)
}
}
.listStyle(.insetGrouped)
List {
ForEach(items) { item in
ItemRow(item: item)
.listRowBackground(
RoundedRectangle(cornerRadius: 12)
.fill(.clear)
.glassBackgroundEffect()
)
}
}
.scrollContentBackground(.hidden)
Key rules for Lists on visionOS:
- Use
.listStyle(.insetGrouped) — it provides proper visionOS styling
- NEVER use
.scrollContentBackground(.hidden) — it removes the contrast needed for text visibility
- NEVER apply
.glassBackgroundEffect() to .listRowBackground() — glass-on-glass makes text invisible
- NEVER use
.listRowBackground() with custom glass shapes — the system handles row backgrounds
Empty States
ContentUnavailableView(
"No Results",
systemImage: "magnifyingglass",
description: Text("Try a different search term")
)
NOT Available on visionOS
- No
UIScreen.main.bounds — use GeometryReader or window sizing
- No UIKit views directly — use SwiftUI
- No haptic feedback (CoreHaptics)
- No camera access (enterprise only)
- No HealthKit
- No dark mode — glass auto-adapts to environment
Rules
- NEVER use opaque background colors on windows or main containers — use glass
- Use
.glassBackgroundEffect() for custom container backgrounds (sidebars, cards, panels) — but NEVER on list rows
- NEVER use
.scrollContentBackground(.hidden) or .listRowBackground() with glass — creates invisible text
- Use
.listStyle(.insetGrouped) for Lists — system handles row styling
- ALL interactive elements MUST have
.hoverEffect() for eye tracking
- Use system vibrancy (
.primary, .secondary, .tertiary) for text — not AppTheme color tokens
- Limit AppTheme accent colors to buttons and small decorative elements only
- Use
.buttonBorderShape() for spatial-appropriate button shapes
- Use RealityView for interactive 3D, Model3D for display-only 3D
- Ornaments for supplementary controls attached to windows
- ONE
.borderedProminent per visible area
- Minimum touch target: 60x60 points for comfortable spatial interaction