DOMAIN:IOS_DEVELOPMENT:HIG_AND_LIQUID_GLASS¶
OWNER: martijn
ALSO_USED_BY: valentin, alexander (design), floris/floor (web-ios consistency)
UPDATED: 2026-03-24
SOURCE: Apple Human Interface Guidelines (2025-2026), WWDC 2025 sessions, Liquid Glass design documentation
HIG:LIQUID_GLASS¶
What Is Liquid Glass¶
STANDARD: Apple Human Interface Guidelines — Liquid Glass (introduced WWDC 2025, iOS 26/macOS 26)
NOTE: Liquid Glass is Apple's new design language replacing the flat/frosted aesthetic used since iOS 7
DEFINITION: translucent, light-refracting material that responds to content beneath it
EFFECT: UI elements appear as physical glass surfaces — they refract, tint, and blur the background dynamically
APPLIES_TO: navigation bars, tab bars, toolbars, sidebars, alerts, sheets, controls
SCOPE: system-wide across iOS 26, iPadOS 26, macOS 26, watchOS 26, tvOS 26, visionOS 3
Key Visual Properties¶
- Translucency: content behind glass elements is visible (blurred and tinted)
- Refraction: glass bends the light of underlying content (parallax-like depth)
- Specular highlights: subtle light reflections that respond to device orientation
- Color tinting: glass picks up colors from content beneath it
- Dynamic: appearance changes as content scrolls underneath
Impact on App Development¶
RULE: apps using standard UIKit/SwiftUI components get Liquid Glass automatically when compiled for iOS 26+
RULE: custom chrome (navigation bars, tab bars) MUST be updated to use Liquid Glass materials
RULE: do NOT fight the system — if the system provides Liquid Glass, adopt it
RULE: heavily customized toolbars/navbars from pre-iOS 26 may look broken — audit and update
CHECK: does the app use custom navigation bar appearance?
IF: yes
THEN: test on iOS 26+ — custom backgrounds may conflict with Liquid Glass
FIX: use .toolbarBackgroundVisibility(.automatic) to let system manage glass
CHECK: does the app overlay custom UI on system bars?
IF: yes
THEN: remove overlays — they break glass refraction effect
Liquid Glass Controls¶
NEW_IN_IOS_26:
- Glass-style buttons: Button("Action").buttonStyle(.glass) or .glassEffect()
- Glass tab bar: automatic for TabView (standard)
- Glass navigation bar: automatic for NavigationStack (standard)
- Glass sheets: .presentationBackground(.glass) for modal sheets
- Glass sidebar: automatic for NavigationSplitView
- Floating tab bar: tabs can now appear as floating glass pill at bottom
ANTI_PATTERN: applying .glass style to every UI element
FIX: Liquid Glass is for chrome and controls, NOT for content areas. Content should remain solid/clear.
ANTI_PATTERN: using opaque backgrounds on bars that should be glass
FIX: remove .toolbarBackground(.visible, for: .navigationBar) hardcoding — let system decide
HIG:CORE_PRINCIPLES¶
1. Clarity¶
RULE: text must be legible at every size
RULE: icons must be precise and recognizable
RULE: adornments are subtle — never compete with content
RULE: negative space gives focus
2. Deference¶
RULE: UI recedes, content is primary
RULE: translucent/glass elements let content show through
RULE: full-screen content where appropriate (photos, video, maps)
3. Depth¶
RULE: visual layers communicate hierarchy
RULE: gesture-based transitions reveal depth (swipe back, pull down)
RULE: Liquid Glass adds physical depth to chrome elements
HIG:NAVIGATION_PATTERNS¶
Tab Bar (Primary)¶
STANDARD: HIG — Tab Bars
USE_WHEN: 3-5 top-level sections of equal importance
RULE: max 5 tabs on iPhone (More tab for additional items)
RULE: iPad can show more tabs
RULE: tab bar is persistent — visible on all screens
RULE: each tab maintains its own navigation state
RULE: Liquid Glass tab bar is now default in iOS 26 — floats as translucent pill
IF: fewer than 3 sections
THEN: do not use tab bar — use direct navigation
IF: more than 5 sections
THEN: prioritize top 4-5, put rest in "More" or redesign information architecture
NavigationStack (Drill-Down)¶
STANDARD: HIG — Navigation
USE_WHEN: hierarchical content (list → detail → sub-detail)
RULE: always provide back navigation (system handles via swipe-back gesture)
RULE: navigation title describes current location
RULE: large title collapses to inline title on scroll
RULE: in iOS 26, navigation bar uses Liquid Glass
NavigationSplitView (iPad/Mac)¶
STANDARD: HIG — Sidebars
USE_WHEN: multi-column layout on iPad or Mac (sidebar → content → detail)
RULE: collapses to single column on iPhone automatically
RULE: sidebar uses Liquid Glass material
RULE: 2-column or 3-column depending on content depth
Modal Sheets¶
STANDARD: HIG — Sheets
USE_WHEN: focused task that user completes or cancels (create, edit, compose)
RULE: sheet dismissal via swipe-down gesture
RULE: use .presentationDetents for half-sheet patterns
RULE: confirmation before dismissing sheets with unsaved changes
RULE: in iOS 26, sheets can use Liquid Glass background
HIG:TYPOGRAPHY¶
STANDARD: HIG — Typography
FONT: SF Pro (system default, do NOT bundle — it's a system font)
FONT: SF Mono (code/monospace)
FONT: SF Pro Rounded (playful contexts)
FONT: New York (serif, editorial contexts)
Text Styles (Use These, Not Fixed Sizes)¶
| Style | Default Size | Use For |
|---|---|---|
| .largeTitle | 34pt | Screen titles (rare) |
| .title | 28pt | Section titles |
| .title2 | 22pt | Sub-section titles |
| .title3 | 20pt | Card titles |
| .headline | 17pt semibold | Row titles, emphasis |
| .body | 17pt | Primary content |
| .callout | 16pt | Explanatory text |
| .subheadline | 15pt | Secondary text |
| .footnote | 13pt | Tertiary text, timestamps |
| .caption | 12pt | Labels, annotations |
| .caption2 | 11pt | Smallest readable text |
Dynamic Type¶
RULE: ALL text must support Dynamic Type — no fixed font sizes
RULE: use Text("Hello").font(.body) — NEVER Text("Hello").font(.system(size: 17))
RULE: test at ALL accessibility sizes (Settings → Accessibility → Larger Text)
RULE: layout must not break at the largest Dynamic Type size
RULE: images that contain text alternatives must also scale
TOOL: Xcode → Environment Overrides → Dynamic Type slider
CHECK: does UI remain usable at the 5 largest accessibility sizes?
CHECK: does text truncate or overlap at large sizes?
CHECK: do touch targets remain tappable at large sizes?
ANTI_PATTERN: using fixed font sizes (UIFont.systemFont(ofSize: 17))
FIX: use UIFont.preferredFont(forTextStyle: .body) or SwiftUI .font(.body)
ANTI_PATTERN: constraining view heights with fixed values
FIX: use content-hugging priorities and let text dictate height
HIG:COLOR_SYSTEM¶
Semantic Colors (Always Use These)¶
| Color | Purpose | Light Mode | Dark Mode |
|---|---|---|---|
.label |
Primary text | Black | White |
.secondaryLabel |
Secondary text | Gray | Light gray |
.tertiaryLabel |
Tertiary text | Lighter gray | Darker light gray |
.systemBackground |
Primary background | White | Black |
.secondarySystemBackground |
Grouped/card bg | Light gray | Dark gray |
.tertiarySystemBackground |
Third level bg | White | Darker gray |
.separator |
Divider lines | Adapted | Adapted |
.systemFill |
Thin overlay | Adapted | Adapted |
.tintColor |
Interactive elements | Blue (default) | Blue (default) |
RULE: ALWAYS use semantic colors — NEVER hardcode hex values for UI chrome
RULE: custom brand colors are OK for branding elements but must have dark mode variants
RULE: ALWAYS define both light and dark mode colors in asset catalog
RULE: ALWAYS test in dark mode
RULE: ALWAYS test with Increase Contrast accessibility setting
ANTI_PATTERN: using Color.white for backgrounds
FIX: use Color(.systemBackground) — adapts to dark mode
ANTI_PATTERN: using Color.black for text
FIX: use Color(.label) — adapts to dark mode and accessibility
High Contrast¶
RULE: test with Settings → Accessibility → Increase Contrast
CHECK: all text has minimum 4.5:1 contrast ratio against background (WCAG AA)
CHECK: interactive elements are distinguishable from decorative elements
CHECK: focus indicators are visible
HIG:ACCESSIBILITY¶
VoiceOver¶
RULE: ALL interactive elements must have accessibility labels
RULE: images must be decorative (.accessibilityHidden(true)) or have descriptions
RULE: custom controls must declare traits (.accessibilityAddTraits(.isButton))
RULE: grouping: related elements should be combined (.accessibilityElement(children: .combine))
RULE: order: reading order must be logical (.accessibilitySortPriority())
RULE: actions: custom swipe actions (.accessibilityAction())
CHECK: navigate entire app with VoiceOver enabled
CHECK: every screen is usable without seeing the screen
CHECK: form fields have proper labels and error descriptions
CHECK: state changes are announced (.accessibilityValue())
TOOL: Xcode → Accessibility Inspector
TOOL: iPhone → Settings → Accessibility → VoiceOver
Dynamic Type¶
SEE: typography section above
RULE: support ALL Dynamic Type sizes including accessibility sizes (up to ~310% of default)
Reduce Motion¶
RULE: check UIAccessibility.isReduceMotionEnabled or @Environment(\.accessibilityReduceMotion)
IF: reduce motion is ON
THEN: replace animations with cross-dissolves
THEN: disable parallax effects
THEN: disable auto-playing video
Reduce Transparency¶
RULE: check UIAccessibility.isReduceTransparencyEnabled
IF: reduce transparency is ON
THEN: system replaces translucent materials with opaque backgrounds
THEN: Liquid Glass effects are replaced with solid backgrounds
NOTE: the system handles this for standard components — only custom glass needs manual handling
Color Blindness¶
RULE: never convey information through color alone
RULE: pair color indicators with icons, labels, or patterns
CHECK: status indicators (red/green) must have secondary indicator (icon, text)
Minimum Touch Targets¶
RULE: 44x44 points minimum for ALL tappable elements
CHECK: buttons, links, toggles, icons all meet minimum size
ANTI_PATTERN: small icon buttons without adequate hit area
FIX: use .frame(minWidth: 44, minHeight: 44) or .contentShape(Rectangle())
HIG:IOS_VS_WEB_DIFFERENCES¶
NOTE: critical for agents transitioning from web development mindset
| Aspect | Web | iOS Native |
|---|---|---|
| Navigation | URL-based, browser back button | Stack-based, swipe-back gesture |
| Layout | CSS flexbox/grid, responsive breakpoints | Auto Layout / SwiftUI layout, trait collections |
| Typography | CSS font-size (px/rem), custom fonts | Text Styles, Dynamic Type, system fonts |
| Colors | CSS variables, media query prefers-color-scheme | Semantic colors, automatic dark mode |
| Animations | CSS transitions, requestAnimationFrame | Core Animation, SwiftUI .animation(), 60fps GPU |
| Touch | click events, hover states | tap gestures, long press, swipe, no hover (except iPad pointer) |
| Scrolling | overflow: scroll, virtual scrolling | UIScrollView/List, rubber-banding, momentum |
| Modals | CSS overlay, z-index | UISheetPresentationController, detents, swipe dismiss |
| Icons | SVG, icon fonts | SF Symbols (2000+ system icons, weight/scale adaptive) |
| State persistence | localStorage, cookies | UserDefaults, Keychain, SwiftData, file system |
| Loading states | Skeleton screens, spinners | ProgressView, pull-to-refresh (.refreshable) |
RULE: do NOT replicate web patterns on iOS — use platform-native patterns
RULE: iOS users expect: swipe-back, pull-to-refresh, swipe actions on rows, haptic feedback
ANTI_PATTERN: implementing a hamburger menu on iOS
FIX: use tab bar (bottom) for top-level nav, NavigationStack for hierarchy