swift-code-reviewer-skill 1.1.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +44 -162
  2. package/README.md +91 -21
  3. package/SKILL.md +107 -725
  4. package/bin/install.js +87 -22
  5. package/package.json +16 -2
  6. package/references/companion-skills.md +70 -0
  7. package/skills/README.md +43 -0
  8. package/skills/swift-concurrency/NOTICE.md +18 -0
  9. package/skills/swift-concurrency/SKILL.md +235 -0
  10. package/skills/swift-concurrency/references/actors.md +640 -0
  11. package/skills/swift-concurrency/references/async-await-basics.md +249 -0
  12. package/skills/swift-concurrency/references/async-sequences.md +635 -0
  13. package/skills/swift-concurrency/references/core-data.md +533 -0
  14. package/skills/swift-concurrency/references/glossary.md +96 -0
  15. package/skills/swift-concurrency/references/linting.md +38 -0
  16. package/skills/swift-concurrency/references/memory-management.md +542 -0
  17. package/skills/swift-concurrency/references/migration.md +721 -0
  18. package/skills/swift-concurrency/references/performance.md +574 -0
  19. package/skills/swift-concurrency/references/sendable.md +578 -0
  20. package/skills/swift-concurrency/references/tasks.md +604 -0
  21. package/skills/swift-concurrency/references/testing.md +565 -0
  22. package/skills/swift-concurrency/references/threading.md +452 -0
  23. package/skills/swift-expert/NOTICE.md +18 -0
  24. package/skills/swift-expert/SKILL.md +226 -0
  25. package/skills/swift-expert/references/async-concurrency.md +363 -0
  26. package/skills/swift-expert/references/memory-performance.md +380 -0
  27. package/skills/swift-expert/references/protocol-oriented.md +357 -0
  28. package/skills/swift-expert/references/swiftui-patterns.md +294 -0
  29. package/skills/swift-expert/references/testing-patterns.md +402 -0
  30. package/skills/swift-testing/NOTICE.md +18 -0
  31. package/skills/swift-testing/SKILL.md +295 -0
  32. package/skills/swift-testing/references/async-testing.md +245 -0
  33. package/skills/swift-testing/references/dump-snapshot-testing.md +265 -0
  34. package/skills/swift-testing/references/fixtures.md +193 -0
  35. package/skills/swift-testing/references/integration-testing.md +189 -0
  36. package/skills/swift-testing/references/migration-xctest.md +301 -0
  37. package/skills/swift-testing/references/parameterized-tests.md +171 -0
  38. package/skills/swift-testing/references/snapshot-testing.md +201 -0
  39. package/skills/swift-testing/references/test-doubles.md +243 -0
  40. package/skills/swift-testing/references/test-organization.md +231 -0
  41. package/skills/swiftui-expert-skill/NOTICE.md +18 -0
  42. package/skills/swiftui-expert-skill/SKILL.md +281 -0
  43. package/skills/swiftui-expert-skill/references/accessibility-patterns.md +151 -0
  44. package/skills/swiftui-expert-skill/references/animation-advanced.md +403 -0
  45. package/skills/swiftui-expert-skill/references/animation-basics.md +284 -0
  46. package/skills/swiftui-expert-skill/references/animation-transitions.md +326 -0
  47. package/skills/swiftui-expert-skill/references/charts-accessibility.md +135 -0
  48. package/skills/swiftui-expert-skill/references/charts.md +602 -0
  49. package/skills/swiftui-expert-skill/references/image-optimization.md +203 -0
  50. package/skills/swiftui-expert-skill/references/latest-apis.md +464 -0
  51. package/skills/swiftui-expert-skill/references/layout-best-practices.md +266 -0
  52. package/skills/swiftui-expert-skill/references/liquid-glass.md +414 -0
  53. package/skills/swiftui-expert-skill/references/list-patterns.md +394 -0
  54. package/skills/swiftui-expert-skill/references/macos-scenes.md +318 -0
  55. package/skills/swiftui-expert-skill/references/macos-views.md +357 -0
  56. package/skills/swiftui-expert-skill/references/macos-window-styling.md +303 -0
  57. package/skills/swiftui-expert-skill/references/performance-patterns.md +403 -0
  58. package/skills/swiftui-expert-skill/references/scroll-patterns.md +293 -0
  59. package/skills/swiftui-expert-skill/references/sheet-navigation-patterns.md +363 -0
  60. package/skills/swiftui-expert-skill/references/state-management.md +417 -0
  61. package/skills/swiftui-expert-skill/references/view-structure.md +389 -0
  62. package/skills/swiftui-ui-patterns/NOTICE.md +18 -0
  63. package/skills/swiftui-ui-patterns/SKILL.md +95 -0
  64. package/skills/swiftui-ui-patterns/references/app-wiring.md +201 -0
  65. package/skills/swiftui-ui-patterns/references/async-state.md +96 -0
  66. package/skills/swiftui-ui-patterns/references/components-index.md +50 -0
  67. package/skills/swiftui-ui-patterns/references/controls.md +57 -0
  68. package/skills/swiftui-ui-patterns/references/deeplinks.md +66 -0
  69. package/skills/swiftui-ui-patterns/references/focus.md +90 -0
  70. package/skills/swiftui-ui-patterns/references/form.md +97 -0
  71. package/skills/swiftui-ui-patterns/references/grids.md +71 -0
  72. package/skills/swiftui-ui-patterns/references/haptics.md +71 -0
  73. package/skills/swiftui-ui-patterns/references/input-toolbar.md +51 -0
  74. package/skills/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
  75. package/skills/swiftui-ui-patterns/references/list.md +86 -0
  76. package/skills/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
  77. package/skills/swiftui-ui-patterns/references/macos-settings.md +71 -0
  78. package/skills/swiftui-ui-patterns/references/matched-transitions.md +59 -0
  79. package/skills/swiftui-ui-patterns/references/media.md +73 -0
  80. package/skills/swiftui-ui-patterns/references/menu-bar.md +101 -0
  81. package/skills/swiftui-ui-patterns/references/navigationstack.md +159 -0
  82. package/skills/swiftui-ui-patterns/references/overlay.md +45 -0
  83. package/skills/swiftui-ui-patterns/references/performance.md +62 -0
  84. package/skills/swiftui-ui-patterns/references/previews.md +48 -0
  85. package/skills/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
  86. package/skills/swiftui-ui-patterns/references/scrollview.md +87 -0
  87. package/skills/swiftui-ui-patterns/references/searchable.md +71 -0
  88. package/skills/swiftui-ui-patterns/references/sheets.md +155 -0
  89. package/skills/swiftui-ui-patterns/references/split-views.md +72 -0
  90. package/skills/swiftui-ui-patterns/references/tabview.md +114 -0
  91. package/skills/swiftui-ui-patterns/references/theming.md +71 -0
  92. package/skills/swiftui-ui-patterns/references/title-menus.md +93 -0
  93. package/skills/swiftui-ui-patterns/references/top-bar.md +49 -0
  94. package/templates/agents/swift-code-reviewer.md +78 -0
  95. package/templates/commands/review.md +56 -0
@@ -0,0 +1,266 @@
1
+ # SwiftUI Layout Best Practices Reference
2
+
3
+ ## Table of Contents
4
+
5
+ - [Relative Layout Over Constants](#relative-layout-over-constants)
6
+ - [Context-Agnostic Views](#context-agnostic-views)
7
+ - [Own Your Container](#own-your-container)
8
+ - [Layout Performance](#layout-performance)
9
+ - [View Logic and Testability](#view-logic-and-testability)
10
+ - [Full-Width Views](#full-width-views)
11
+ - [Action Handlers](#action-handlers)
12
+ - [Summary Checklist](#summary-checklist)
13
+
14
+ ## Relative Layout Over Constants
15
+
16
+ **Use dynamic layout calculations instead of hard-coded values.**
17
+
18
+ ```swift
19
+ // Good - relative to actual layout
20
+ GeometryReader { geometry in
21
+ VStack {
22
+ HeaderView()
23
+ .frame(height: geometry.size.height * 0.2)
24
+ ContentView()
25
+ }
26
+ }
27
+
28
+ // Avoid - magic numbers that don't adapt
29
+ VStack {
30
+ HeaderView()
31
+ .frame(height: 150) // Doesn't adapt to different screens
32
+ ContentView()
33
+ }
34
+ ```
35
+
36
+ **Why**: Hard-coded values don't account for different screen sizes, orientations, or dynamic content (like status bars during phone calls).
37
+
38
+ ## Context-Agnostic Views
39
+
40
+ **Views should work in any context.** Never assume presentation style or screen size.
41
+
42
+ ```swift
43
+ // Good - adapts to given space
44
+ struct ProfileCard: View {
45
+ let user: User
46
+
47
+ var body: some View {
48
+ VStack {
49
+ Image(user.avatar)
50
+ .resizable()
51
+ .aspectRatio(contentMode: .fit)
52
+ Text(user.name)
53
+ Spacer()
54
+ }
55
+ .padding()
56
+ }
57
+ }
58
+
59
+ // Avoid - assumes full screen
60
+ struct ProfileCard: View {
61
+ let user: User
62
+
63
+ var body: some View {
64
+ VStack {
65
+ Image(user.avatar)
66
+ .frame(width: UIScreen.main.bounds.width) // Wrong!
67
+ Text(user.name)
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ **Why**: Views should work as full screens, modals, sheets, popovers, or embedded content.
74
+
75
+ ## Own Your Container
76
+
77
+ **Custom views should own static containers but not lazy/repeatable ones.**
78
+
79
+ ```swift
80
+ // Good - owns static container
81
+ struct HeaderView: View {
82
+ var body: some View {
83
+ HStack {
84
+ Image(systemName: "star")
85
+ Text("Title")
86
+ Spacer()
87
+ }
88
+ }
89
+ }
90
+
91
+ // Avoid - missing container
92
+ struct HeaderView: View {
93
+ var body: some View {
94
+ Image(systemName: "star")
95
+ Text("Title")
96
+ // Caller must wrap in HStack
97
+ }
98
+ }
99
+
100
+ // Good - caller owns lazy container
101
+ struct FeedView: View {
102
+ let items: [Item]
103
+
104
+ var body: some View {
105
+ LazyVStack {
106
+ ForEach(items) { item in
107
+ ItemRow(item: item)
108
+ }
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ ## Layout Performance
115
+
116
+ ### Avoid Layout Thrash
117
+
118
+ **Minimize deep view hierarchies and excessive layout dependencies.**
119
+
120
+ ```swift
121
+ // Bad - deep nesting, excessive layout passes
122
+ VStack {
123
+ HStack {
124
+ VStack {
125
+ HStack {
126
+ VStack {
127
+ Text("Deep")
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ // Good - flatter hierarchy
135
+ VStack {
136
+ Text("Shallow")
137
+ Text("Structure")
138
+ }
139
+ ```
140
+
141
+ **Avoid excessive `GeometryReader` and preference chains:**
142
+
143
+ ```swift
144
+ // Bad - multiple geometry readers cause layout thrash
145
+ GeometryReader { outerGeometry in
146
+ VStack {
147
+ GeometryReader { innerGeometry in
148
+ // Layout recalculates multiple times
149
+ }
150
+ }
151
+ }
152
+
153
+ // Good - single geometry reader or use alternatives (iOS 17+)
154
+ containerRelativeFrame(.horizontal) { width, _ in
155
+ width * 0.8
156
+ }
157
+ ```
158
+
159
+ **Gate frequent geometry updates:**
160
+
161
+ ```swift
162
+ // Bad - updates on every pixel change
163
+ .onPreferenceChange(ViewSizeKey.self) { size in
164
+ currentSize = size
165
+ }
166
+
167
+ // Good - gate by threshold
168
+ .onPreferenceChange(ViewSizeKey.self) { size in
169
+ let difference = abs(size.width - currentSize.width)
170
+ if difference > 10 { // Only update if significant change
171
+ currentSize = size
172
+ }
173
+ }
174
+ ```
175
+
176
+ ## View Logic and Testability
177
+
178
+ ### Keep Business Logic in Services and Models
179
+
180
+ **Business logic belongs in services and models, not in views.** Views should stay simple and declarative — orchestrating UI state, not implementing business rules. This makes logic independently testable without requiring view instantiation.
181
+
182
+ > **iOS 17+**: Use `@Observable` with `@State`.
183
+
184
+ ```swift
185
+ @Observable
186
+ final class AuthService {
187
+ var email = ""
188
+ var password = ""
189
+ var isValid: Bool {
190
+ !email.isEmpty && password.count >= 8
191
+ }
192
+
193
+ func login() async throws {
194
+ // Business logic here — testable without the view
195
+ }
196
+ }
197
+
198
+ struct LoginView: View {
199
+ @State private var authService = AuthService()
200
+
201
+ var body: some View {
202
+ Form {
203
+ TextField("Email", text: $authService.email)
204
+ SecureField("Password", text: $authService.password)
205
+ Button("Login") {
206
+ Task {
207
+ try? await authService.login()
208
+ }
209
+ }
210
+ .disabled(!authService.isValid)
211
+ }
212
+ }
213
+ }
214
+ ```
215
+
216
+ For iOS 16 and earlier, use `ObservableObject` with `@StateObject` -- see `state-management.md` for the legacy pattern.
217
+
218
+ Avoid embedding business logic directly in view closures (e.g., validation checks inside a `Button` action). This makes logic untestable without view instantiation.
219
+
220
+ **Note**: This is about making business logic testable, not about enforcing a specific architecture. The key is that logic lives outside views where it can be tested independently.
221
+
222
+ ## Full-Width Views
223
+
224
+ **When a single view needs to fill the available width, use `.frame(maxWidth: .infinity, alignment:)` instead of wrapping it in a stack with a `Spacer`.**
225
+
226
+ ```swift
227
+ // Good - frame modifier
228
+ Text("Hello")
229
+ .frame(maxWidth: .infinity, alignment: .leading)
230
+
231
+ // Avoid - unnecessary stack and spacer
232
+ HStack {
233
+ Text("Hello")
234
+ Spacer()
235
+ }
236
+ ```
237
+
238
+ **Why**: `.frame(maxWidth:alignment:)` is a single modifier that clearly communicates intent. Wrapping in an `HStack` with a `Spacer` adds an extra container to the view hierarchy for no benefit.
239
+
240
+ ## Action Handlers
241
+
242
+ **Separate layout from logic.** View body should reference action methods, not contain inline logic.
243
+
244
+ ```swift
245
+ // Good - action references method
246
+ Button("Publish Project", action: publishService.handlePublish)
247
+
248
+ // Avoid - multi-line logic in closure
249
+ Button("Publish Project") {
250
+ isLoading = true
251
+ apiService.publish(project) { result in /* ... */ }
252
+ }
253
+ ```
254
+
255
+ ## Summary Checklist
256
+
257
+ - [ ] Use relative layout over hard-coded constants
258
+ - [ ] Views work in any context (don't assume screen size)
259
+ - [ ] Custom views own static containers
260
+ - [ ] Avoid deep view hierarchies (layout thrash)
261
+ - [ ] Gate frequent geometry updates by thresholds
262
+ - [ ] Business logic kept in services and models (not in views)
263
+ - [ ] Action handlers reference methods, not inline logic
264
+ - [ ] Use `.frame(maxWidth: .infinity, alignment:)` for full-width views (not `HStack` + `Spacer`)
265
+ - [ ] Avoid excessive `GeometryReader` usage
266
+ - [ ] Use `containerRelativeFrame()` when appropriate
@@ -0,0 +1,414 @@
1
+ # SwiftUI Liquid Glass Reference (iOS 26+)
2
+
3
+ ## Table of Contents
4
+
5
+ - [Overview](#overview)
6
+ - [Availability](#availability)
7
+ - [Core APIs](#core-apis)
8
+ - [GlassEffectContainer](#glasseffectcontainer)
9
+ - [Glass Button Styles](#glass-button-styles)
10
+ - [Morphing Transitions](#morphing-transitions)
11
+ - [Modifier Order](#modifier-order)
12
+ - [Complete Examples](#complete-examples)
13
+ - [Fallback Strategies](#fallback-strategies)
14
+ - [Design System Notes](#design-system-notes)
15
+ - [Best Practices](#best-practices)
16
+ - [Checklist](#checklist)
17
+
18
+ ## Overview
19
+
20
+ Liquid Glass is Apple's new design language introduced in iOS 26. It provides translucent, dynamic surfaces that respond to content and user interaction. This reference covers the native SwiftUI APIs for implementing Liquid Glass effects.
21
+
22
+ ## Availability
23
+
24
+ All Liquid Glass APIs require iOS 26 or later. Always provide fallbacks:
25
+
26
+ ```swift
27
+ if #available(iOS 26, *) {
28
+ // Liquid Glass implementation
29
+ } else {
30
+ // Fallback using materials
31
+ }
32
+ ```
33
+
34
+ ## Core APIs
35
+
36
+ ### glassEffect Modifier
37
+
38
+ The primary modifier for applying glass effects to views:
39
+
40
+ ```swift
41
+ .glassEffect(_ style: GlassEffectStyle = .regular, in shape: some Shape = .rect)
42
+ ```
43
+
44
+ #### Basic Usage
45
+
46
+ ```swift
47
+ Text("Hello")
48
+ .padding()
49
+ .glassEffect() // Default regular style, rect shape
50
+ ```
51
+
52
+ #### With Shape
53
+
54
+ ```swift
55
+ Text("Rounded Glass")
56
+ .padding()
57
+ .glassEffect(in: .rect(cornerRadius: 16))
58
+
59
+ Image(systemName: "star")
60
+ .padding()
61
+ .glassEffect(in: .circle)
62
+
63
+ Text("Capsule")
64
+ .padding(.horizontal, 20)
65
+ .padding(.vertical, 10)
66
+ .glassEffect(in: .capsule)
67
+ ```
68
+
69
+ ### GlassEffectStyle
70
+
71
+ #### Prominence Levels
72
+
73
+ ```swift
74
+ .glassEffect(.regular) // Standard glass appearance
75
+ .glassEffect(.prominent) // More visible, higher contrast
76
+ ```
77
+
78
+ #### Tinting
79
+
80
+ Add color tint to the glass:
81
+
82
+ ```swift
83
+ .glassEffect(.regular.tint(.blue))
84
+ .glassEffect(.prominent.tint(.red.opacity(0.3)))
85
+ ```
86
+
87
+ #### Interactivity
88
+
89
+ Make glass respond to touch/pointer hover:
90
+
91
+ ```swift
92
+ // Interactive glass - responds to user interaction
93
+ .glassEffect(.regular.interactive())
94
+
95
+ // Combined with tint
96
+ .glassEffect(.regular.tint(.blue).interactive())
97
+ ```
98
+
99
+ **Important**: Only use `.interactive()` on elements that actually respond to user input (buttons, tappable views, focusable elements).
100
+
101
+ ## GlassEffectContainer
102
+
103
+ Wraps multiple glass elements for proper visual grouping and spacing.
104
+
105
+ **Glass cannot sample other glass.** The glass material reflects and refracts light by sampling content from an area larger than itself. Nearby glass elements in different containers will produce inconsistent visual results because they cannot sample each other. `GlassEffectContainer` gives grouped elements a shared sampling region, ensuring a consistent appearance.
106
+
107
+ ```swift
108
+ GlassEffectContainer {
109
+ HStack {
110
+ Button("One") { }
111
+ .glassEffect()
112
+ Button("Two") { }
113
+ .glassEffect()
114
+ }
115
+ }
116
+ ```
117
+
118
+ ### With Spacing
119
+
120
+ Control the visual spacing between glass elements:
121
+
122
+ ```swift
123
+ GlassEffectContainer(spacing: 24) {
124
+ HStack(spacing: 24) {
125
+ GlassChip(icon: "pencil")
126
+ GlassChip(icon: "eraser")
127
+ GlassChip(icon: "trash")
128
+ }
129
+ }
130
+ ```
131
+
132
+ **Note**: The container's `spacing` parameter should match the actual spacing in your layout for proper glass effect rendering.
133
+
134
+ > Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)
135
+
136
+ ## Glass Button Styles
137
+
138
+ Built-in button styles for glass appearance:
139
+
140
+ ```swift
141
+ // Standard glass button
142
+ Button("Action") { }
143
+ .buttonStyle(.glass)
144
+
145
+ // Prominent glass button (higher visibility)
146
+ Button("Primary Action") { }
147
+ .buttonStyle(.glassProminent)
148
+ ```
149
+
150
+ ### Custom Glass Buttons
151
+
152
+ For more control, apply glass effect manually:
153
+
154
+ ```swift
155
+ Button(action: { }) {
156
+ Label("Settings", systemImage: "gear")
157
+ .padding()
158
+ }
159
+ .glassEffect(.regular.interactive(), in: .capsule)
160
+ ```
161
+
162
+ ## Morphing Transitions
163
+
164
+ Create smooth transitions between glass elements using `glassEffectID` and `@Namespace`:
165
+
166
+ ```swift
167
+ struct MorphingExample: View {
168
+ @Namespace private var animation
169
+ @State private var isExpanded = false
170
+
171
+ var body: some View {
172
+ GlassEffectContainer {
173
+ if isExpanded {
174
+ ExpandedCard()
175
+ .glassEffect()
176
+ .glassEffectID("card", in: animation)
177
+ } else {
178
+ CompactCard()
179
+ .glassEffect()
180
+ .glassEffectID("card", in: animation)
181
+ }
182
+ }
183
+ .animation(.smooth, value: isExpanded)
184
+ }
185
+ }
186
+ ```
187
+
188
+ ### Requirements for Morphing
189
+
190
+ 1. Both views must have the same `glassEffectID`
191
+ 2. Use the same `@Namespace`
192
+ 3. Wrap in `GlassEffectContainer`
193
+ 4. Apply animation to the container or parent
194
+
195
+ ## Modifier Order
196
+
197
+ **Critical**: Apply `glassEffect` after layout and visual modifiers:
198
+
199
+ ```swift
200
+ // CORRECT order
201
+ Text("Label")
202
+ .font(.headline) // 1. Typography
203
+ .foregroundStyle(.primary) // 2. Color
204
+ .padding() // 3. Layout
205
+ .glassEffect() // 4. Glass effect LAST
206
+
207
+ // WRONG order - glass applied too early
208
+ Text("Label")
209
+ .glassEffect() // Wrong position
210
+ .padding()
211
+ .font(.headline)
212
+ ```
213
+
214
+ ## Complete Examples
215
+
216
+ ### Toolbar with Glass Buttons
217
+
218
+ ```swift
219
+ struct GlassToolbar: View {
220
+ var body: some View {
221
+ if #available(iOS 26, *) {
222
+ GlassEffectContainer(spacing: 16) {
223
+ HStack(spacing: 16) {
224
+ ToolbarButton(icon: "pencil", action: { })
225
+ ToolbarButton(icon: "eraser", action: { })
226
+ ToolbarButton(icon: "scissors", action: { })
227
+ Spacer()
228
+ ToolbarButton(icon: "square.and.arrow.up", action: { })
229
+ }
230
+ .padding(.horizontal)
231
+ }
232
+ } else {
233
+ // Fallback toolbar
234
+ HStack(spacing: 16) {
235
+ // ... fallback implementation
236
+ }
237
+ }
238
+ }
239
+ }
240
+
241
+ struct ToolbarButton: View {
242
+ let icon: String
243
+ let action: () -> Void
244
+
245
+ var body: some View {
246
+ Button(action: action) {
247
+ Image(systemName: icon)
248
+ .font(.title2)
249
+ .frame(width: 44, height: 44)
250
+ }
251
+ .glassEffect(.regular.interactive(), in: .circle)
252
+ }
253
+ }
254
+ ```
255
+
256
+ ### Card with Glass Effect
257
+
258
+ ```swift
259
+ struct GlassCard: View {
260
+ let title: String
261
+ let subtitle: String
262
+
263
+ var body: some View {
264
+ if #available(iOS 26, *) {
265
+ cardContent
266
+ .glassEffect(.regular, in: .rect(cornerRadius: 20))
267
+ } else {
268
+ cardContent
269
+ .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 20))
270
+ }
271
+ }
272
+
273
+ private var cardContent: some View {
274
+ VStack(alignment: .leading, spacing: 8) {
275
+ Text(title)
276
+ .font(.headline)
277
+ Text(subtitle)
278
+ .font(.subheadline)
279
+ .foregroundStyle(.secondary)
280
+ }
281
+ .padding()
282
+ .frame(maxWidth: .infinity, alignment: .leading)
283
+ }
284
+ }
285
+ ```
286
+
287
+ ### Segmented Control
288
+
289
+ ```swift
290
+ struct GlassSegmentedControl: View {
291
+ @Binding var selection: Int
292
+ let options: [String]
293
+ @Namespace private var animation
294
+
295
+ var body: some View {
296
+ if #available(iOS 26, *) {
297
+ GlassEffectContainer(spacing: 4) {
298
+ HStack(spacing: 4) {
299
+ ForEach(options.indices, id: \.self) { index in
300
+ Button(options[index]) {
301
+ withAnimation(.smooth) {
302
+ selection = index
303
+ }
304
+ }
305
+ .padding(.horizontal, 16)
306
+ .padding(.vertical, 8)
307
+ .glassEffect(
308
+ selection == index ? .prominent.interactive() : .regular.interactive(),
309
+ in: .capsule
310
+ )
311
+ .glassEffectID(selection == index ? "selected" : "option\(index)", in: animation)
312
+ }
313
+ }
314
+ .padding(4)
315
+ }
316
+ } else {
317
+ Picker("Options", selection: $selection) {
318
+ ForEach(options.indices, id: \.self) { index in
319
+ Text(options[index]).tag(index)
320
+ }
321
+ }
322
+ .pickerStyle(.segmented)
323
+ }
324
+ }
325
+ }
326
+ ```
327
+
328
+ ## Fallback Strategies
329
+
330
+ ### Using Materials
331
+
332
+ ```swift
333
+ if #available(iOS 26, *) {
334
+ content.glassEffect()
335
+ } else {
336
+ content.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
337
+ }
338
+ ```
339
+
340
+ ### Available Materials for Fallback
341
+
342
+ - `.ultraThinMaterial` - Closest to glass appearance
343
+ - `.thinMaterial` - Slightly more opaque
344
+ - `.regularMaterial` - Standard blur
345
+ - `.thickMaterial` - More opaque
346
+ - `.ultraThickMaterial` - Most opaque
347
+
348
+ ### Conditional Modifier Extension
349
+
350
+ ```swift
351
+ extension View {
352
+ @ViewBuilder
353
+ func glassEffectWithFallback(
354
+ _ style: GlassEffectStyle = .regular,
355
+ in shape: some Shape = .rect,
356
+ fallbackMaterial: Material = .ultraThinMaterial
357
+ ) -> some View {
358
+ if #available(iOS 26, *) {
359
+ self.glassEffect(style, in: shape)
360
+ } else {
361
+ self.background(fallbackMaterial, in: shape)
362
+ }
363
+ }
364
+ }
365
+ ```
366
+
367
+ ## Design System Notes
368
+
369
+ ### Toolbar Icons
370
+
371
+ In the new design, toolbar icons use **monochrome rendering** by default. The monochrome palette reduces visual noise and maintains legibility. Use `tint(_:)` only to convey meaning (e.g., a call to action), not for visual effect.
372
+
373
+ ### Sheet Presentations
374
+
375
+ Partial-height sheets use a Liquid Glass background by default. If you previously used `presentationBackground(_:)` with a custom background, consider removing it to let the new material shine. Sheets can morph out of the glass controls that present them using `navigationZoomTransition`.
376
+
377
+ ### Scroll Edge Effects
378
+
379
+ An automatic scroll edge effect blurs and fades content under system toolbars to keep controls legible. Remove any custom background-darkening effects behind bar items, as they will interfere.
380
+
381
+ > Source: "Build a SwiftUI app with the new design" (WWDC25, session 323)
382
+
383
+ ## Best Practices
384
+
385
+ ### Do
386
+
387
+ - Use `GlassEffectContainer` for grouped glass elements (glass cannot sample other glass)
388
+ - Apply glass after layout modifiers
389
+ - Use `.interactive()` only on tappable elements
390
+ - Match container spacing with layout spacing
391
+ - Provide material-based fallbacks for older iOS
392
+ - Keep glass shapes consistent within a feature
393
+ - Remove custom `presentationBackground(_:)` on sheets to use the default glass material
394
+
395
+ ### Don't
396
+
397
+ - Apply glass to every element (use sparingly)
398
+ - Use `.interactive()` on static content
399
+ - Mix different corner radii arbitrarily
400
+ - Forget iOS version checks
401
+ - Apply glass before padding/frame modifiers
402
+ - Nest `GlassEffectContainer` unnecessarily
403
+ - Add custom darkening backgrounds behind toolbars (conflicts with scroll edge effect)
404
+
405
+ ## Checklist
406
+
407
+ - [ ] `#available(iOS 26, *)` with fallback
408
+ - [ ] `GlassEffectContainer` wraps grouped elements
409
+ - [ ] `.glassEffect()` applied after layout modifiers
410
+ - [ ] `.interactive()` only on user-interactable elements
411
+ - [ ] `glassEffectID` with `@Namespace` for morphing
412
+ - [ ] Consistent shapes and spacing across feature
413
+ - [ ] Container spacing matches layout spacing
414
+ - [ ] Appropriate prominence levels used