@mmerterden/multi-agent-pipeline 20.17.0 → 20.17.2

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 (83) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/docs/facts.json +6 -6
  3. package/docs/features.md +1 -1
  4. package/install/catalog-history.json +1 -1
  5. package/manifest.json +85 -47
  6. package/package.json +1 -1
  7. package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
  8. package/pipeline/commands/multi-agent/update/SKILL.md +2 -2
  9. package/pipeline/multi-agent-refs/features/usage-reporting.md +106 -67
  10. package/pipeline/schemas/prefs.schema.json +23 -2
  11. package/pipeline/scripts/usage-identity.mjs +312 -53
  12. package/pipeline/scripts/usage-register.mjs +204 -85
  13. package/pipeline/scripts/usage-report.mjs +7 -5
  14. package/pipeline/skills/.skill-manifest.json +35 -15
  15. package/pipeline/skills/.skills-index.json +66 -11
  16. package/pipeline/skills/shared/README.md +18 -13
  17. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +2 -2
  18. package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +2 -2
  19. package/pipeline/skills/shared/external/NOTICE-avdlee-swiftui-agent-skill.md +3 -1
  20. package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +2 -1
  21. package/pipeline/skills/shared/external/hig-app-icons/SKILL.md +159 -0
  22. package/pipeline/skills/shared/external/hig-app-icons/evals/evals.json +48 -0
  23. package/pipeline/skills/shared/external/hig-app-icons/evals/trigger-eval.json +22 -0
  24. package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +115 -0
  25. package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +32 -4
  26. package/pipeline/skills/shared/external/hig-components-layout/evals/evals.json +47 -0
  27. package/pipeline/skills/shared/external/hig-components-layout/evals/trigger-eval.json +22 -0
  28. package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +231 -0
  29. package/pipeline/skills/shared/external/hig-components-layout/references/containers.md +5 -30
  30. package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
  31. package/pipeline/skills/shared/external/hig-foundations/SKILL.md +23 -4
  32. package/pipeline/skills/shared/external/hig-foundations/evals/evals.json +47 -0
  33. package/pipeline/skills/shared/external/hig-foundations/evals/trigger-eval.json +21 -0
  34. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +34 -26
  35. package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +219 -0
  36. package/pipeline/skills/shared/external/hig-inputs/SKILL.md +5 -4
  37. package/pipeline/skills/shared/external/hig-patterns/SKILL.md +19 -3
  38. package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +188 -0
  39. package/pipeline/skills/shared/external/hig-patterns/references/patterns.md +5 -11
  40. package/pipeline/skills/shared/external/hig-platforms/SKILL.md +17 -5
  41. package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +218 -0
  42. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +4 -1
  43. package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +135 -0
  44. package/pipeline/skills/shared/external/ios-design-review/SKILL.md +146 -0
  45. package/pipeline/skills/shared/external/ios-design-review/evals/evals.json +48 -0
  46. package/pipeline/skills/shared/external/ios-design-review/evals/trigger-eval.json +22 -0
  47. package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +135 -0
  48. package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +193 -0
  49. package/pipeline/skills/shared/external/ios-haptics/SKILL.md +191 -0
  50. package/pipeline/skills/shared/external/ios-haptics/evals/evals.json +49 -0
  51. package/pipeline/skills/shared/external/ios-haptics/evals/trigger-eval.json +22 -0
  52. package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +215 -0
  53. package/pipeline/skills/shared/external/ios-iphone-duo/NOTICE.md +53 -0
  54. package/pipeline/skills/shared/external/ios-iphone-duo/SKILL.md +170 -0
  55. package/pipeline/skills/shared/external/ios-iphone-duo/evals/evals.json +50 -0
  56. package/pipeline/skills/shared/external/ios-iphone-duo/evals/trigger-eval.json +22 -0
  57. package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +212 -0
  58. package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +145 -0
  59. package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +122 -0
  60. package/pipeline/skills/shared/external/ios-sf-symbols/SKILL.md +177 -0
  61. package/pipeline/skills/shared/external/ios-sf-symbols/evals/evals.json +49 -0
  62. package/pipeline/skills/shared/external/ios-sf-symbols/evals/trigger-eval.json +22 -0
  63. package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +135 -0
  64. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +38 -50
  65. package/pipeline/skills/shared/external/swiftui-animation/evals/evals.json +47 -0
  66. package/pipeline/skills/shared/external/swiftui-animation/evals/trigger-eval.json +22 -0
  67. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +7 -73
  68. package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +185 -0
  69. package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +6 -6
  70. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +3 -253
  71. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +4 -47
  72. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +3 -111
  73. package/pipeline/skills/shared/external/swiftui-expert-skill/references/liquid-glass.md +11 -381
  74. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +184 -224
  75. package/pipeline/skills/shared/external/swiftui-liquid-glass/evals/evals.json +47 -0
  76. package/pipeline/skills/shared/external/swiftui-liquid-glass/evals/trigger-eval.json +22 -0
  77. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +246 -33
  78. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +227 -0
  79. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +15 -18
  80. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/components-index.md +1 -1
  81. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/haptics.md +6 -68
  82. package/pipeline/skills/skills-index.md +16 -11
  83. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +0 -786
@@ -0,0 +1,212 @@
1
+ # iPhone Duo API map
2
+
3
+ Every API this skill relies on, grouped by job, with the iOS version that
4
+ introduced it. *(27.1 beta)* marks symbols documented as beta in the iOS 27.1
5
+ SDK; their names and behaviour may change before release.
6
+
7
+ ## Contents
8
+
9
+ 1. Size, screen and shape
10
+ 2. Reserved regions
11
+ 3. Arrangements
12
+ 4. Vertical bars
13
+ 5. Sheets
14
+ 6. Hinge
15
+ 7. Scenes and accessories
16
+
17
+ ## 1. Size, screen and shape
18
+
19
+ | Job | SwiftUI | UIKit | iOS |
20
+ |---|---|---|---|
21
+ | Size class | `horizontalSizeClass`, `verticalSizeClass` environment | trait collection size classes | 13 |
22
+ | Space actually available | `GeometryReader`, `onGeometryChange(for:of:action:)` | `view.bounds`, `viewDidLayoutSubviews`, `layoutSubviews` | 13 / 16 |
23
+ | Display scale | `displayScale` environment | `traitCollection.displayScale` | 13 / 8 |
24
+ | A window for a scene | | `UIWindow(windowScene:)` | 13 |
25
+ | The scene's screen, if truly needed | | `view.window?.windowScene?.screen` | 13 |
26
+ | Choose among layouts | `ViewThatFits`, `AnyLayout` | | 16 |
27
+ | Size relative to container | `containerRelativeFrame(_:alignment:)` | Auto Layout | 17 |
28
+ | Corners concentric with the device | `ConcentricRectangle` | `UICornerConfiguration`, `UIView.cornerConfiguration` | 26 |
29
+
30
+ `UIScreen.main` is deprecated as of iOS 26. Apple's guidance is to base layout
31
+ on the scene or containing view's bounds and on size classes, and not to use
32
+ `userInterfaceIdiom` or `UIInterfaceOrientation` for layout decisions.
33
+
34
+ ## 2. Reserved regions
35
+
36
+ | Job | SwiftUI | UIKit |
37
+ |---|---|---|
38
+ | Region type | `ReservedRegion` *(27.1 beta)* | `UIView.ReservedRegion` *(27.1 beta)* |
39
+ | Query | `GeometryProxy.reservedRegions(kind:options:layoutDirectionBehavior:)` *(27.1 beta)* | `UIView.reservedRegions(kind:options:)` *(27.1 beta)* |
40
+ | Kinds | `.division` (fold), `.occlusion` (cameras) | same |
41
+ | Options | `.includeInactive` | same |
42
+ | Properties | `frame` (includes margins), `margins`, `isActive`, `kind`, `id` | `frame`, `margins`, `isActive`, `kind` (no `id`) |
43
+
44
+ ```swift
45
+ struct FoldAwareGrid<Content: View>: View {
46
+ @ViewBuilder var content: (_ columns: Int) -> Content
47
+
48
+ var body: some View {
49
+ GeometryReader { proxy in
50
+ let hasFold = !proxy.reservedRegions(kind: .division, options: .includeInactive).isEmpty
51
+ let base = proxy.size.width > proxy.size.height ? 3 : 2
52
+ content(hasFold && base.isMultiple(of: 2) == false ? base + 1 : base)
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ Notes:
59
+
60
+ - The query returns regions that intersect the view whether active or not.
61
+ Filter `isActive` when positioning for the current pose.
62
+ - `frame` already contains `margins`, the clearance kept for interactive
63
+ content. Avoiding `frame` is enough for controls; inset `frame` by
64
+ `margins` when full-bleed media may run up to the bare hardware area.
65
+ - SwiftUI mirrors region frames for right-to-left by default
66
+ (`layoutDirectionBehavior: .mirrors`), so a custom `Layout` can compare them
67
+ with subview frames directly. Pass `.fixed` for the physical frames.
68
+ - System containers (split views, lists, scroll views, sheets, alerts, menus)
69
+ already avoid reserved regions; custom views and custom layouts are the
70
+ ones that need this API.
71
+
72
+ ## 3. Arrangements
73
+
74
+ | Job | SwiftUI | UIKit |
75
+ |---|---|---|
76
+ | Container | `ArrangementView(primary:secondary:)` *(27.1 beta)* | `UIArrangementViewController` *(27.1 beta)* |
77
+ | Choose style | `.arrangementViewStyle(.split)` / `.overlay` / `.automatic` | `updateArrangement(_:animated:)` with `UISplitArrangement` / `UIOverlayArrangement` |
78
+ | Limit axes | `.split.axes(.vertical)`, `.overlay.axes(...)` | `axes(_:)` on the arrangement |
79
+ | Assign content | the two view builders | `setViewController(_:for:animated:)` with `.primary` / `.secondary` |
80
+ | Split sizing | `splitArrangementLayoutRatio(_:)`, `splitArrangementLayoutSize(...)`, `splitArrangementFixedLayoutSize(...)` | `UISplitArrangement.Dimension` |
81
+ | Overlay details | `overlayArrangementEdge(_:)`, `overlayArrangementZIndex` environment | `state(for:)` |
82
+
83
+ ```swift
84
+ ArrangementView {
85
+ NowPlayingView()
86
+ } secondary: {
87
+ LyricsView()
88
+ }
89
+ .arrangementViewStyle(.split)
90
+
91
+ ArrangementView {
92
+ PlayerControls()
93
+ } secondary: {
94
+ VideoSurface()
95
+ }
96
+ .arrangementViewStyle(.overlay)
97
+ ```
98
+
99
+ The automatic style resolves to split. Split goes side by side when wider than
100
+ tall and stacks when taller than wide; overlay layers primary over secondary
101
+ when closed or fully open and places them on either side of the fold when
102
+ partially open (primary trailing or bottom, secondary leading or top).
103
+
104
+ Placement rules: navigation containers go around an arrangement, never inside
105
+ it; an arrangement does not go inside `NavigationSplitView`, `List`,
106
+ `ScrollView` or another container that could make part of it unreachable.
107
+
108
+ ## 4. Vertical bars
109
+
110
+ | Job | SwiftUI | UIKit | iOS |
111
+ |---|---|---|---|
112
+ | Bars that can go vertical | `.toolbar` on `NavigationStack` / `NavigationSplitView` | items on a view controller in a navigation controller | |
113
+ | Custom Back or Close | `ToolbarItem(placement: .cancellationAction)` | `leadingItemGroups` | 14 / 16 |
114
+ | Prominent action | `topBarPinnedTrailing` | `pinnedTrailingGroup` | 27 / 16 |
115
+ | Group items | `ToolbarItemGroup` | `UIBarButtonItemGroup` | 14 |
116
+ | Title plus symbol | `Label` | `UIBarButtonItem` title and image | |
117
+ | Overflow order | `visibilityPriority(_:)`, `ToolbarItemVisibilityPriority` (`.automatic`, `.low`, `.high`, `init(higherThan:)`, `init(lowerThan:)`) | `visibilityPriority`, `UIBarButtonItemVisibilityPriority` | 27 |
118
+ | Put items in overflow | `ToolbarOverflowMenu` | `additionalOverflowItems` | 27 / 16 |
119
+ | Keep an item horizontal | `axisBehavior(_:)` with `ToolbarItemAxisBehavior` (`.automatic`, `.horizontalOnly`, `.verticalPreferred`) | `axisBehavior`, `UIBarButtonItem.AxisBehavior` | *(27.1 beta)* |
120
+ | Tab bar versus toolbar when tight | `toolbarVerticalCompressionBehavior(_:)` (`.automatic`, `.prefersTabBar`, `.prefersToolbarItems`) | `UIVerticalBarCompressionBehavior` | *(27.1 beta)* |
121
+ | Opt a screen out | `toolbarVerticalBehavior(.disabled)` | `preferredVerticalBarBehavior`, `setNeedsUpdateOfVerticalBarConfiguration()` | *(27.1 beta)* |
122
+ | Which edge the bar prefers | `toolbarVerticalEdge` environment (`HorizontalEdge?`) | `verticalBarEdge` trait (`UIVerticalBarEdge`) | *(27.1 beta)* |
123
+ | Image under the bar | `backgroundExtensionEffect()` | `UIBackgroundExtensionView` | 26 |
124
+
125
+ ```swift
126
+ NavigationStack {
127
+ EditorView()
128
+ .toolbar {
129
+ ToolbarItem(placement: .cancellationAction) {
130
+ Button("Close", systemImage: "xmark") { dismiss() }
131
+ }
132
+ ToolbarItem(placement: .topBarPinnedTrailing) {
133
+ Button("Done", systemImage: "checkmark") { save() }
134
+ }
135
+ ToolbarItemGroup {
136
+ Button("Bold", systemImage: "bold") { toggleBold() }
137
+ Button("Italic", systemImage: "italic") { toggleItalic() }
138
+ }
139
+ .visibilityPriority(.low)
140
+ }
141
+ }
142
+ ```
143
+
144
+ How items are shown: vertically with their icon; horizontally with icon or
145
+ title (icon preferred); in overflow with both. An item with only a title or a
146
+ custom view is never placed vertically.
147
+
148
+ Contexts that choose differently: inspectors keep horizontal bars; a split
149
+ view uses horizontal bars for sidebar and content columns and vertical bars
150
+ for the detail column; sheets on the outer display go vertical by default.
151
+
152
+ `toolbarVerticalBehavior` is resolved per container: the top view of a
153
+ `NavigationStack`, the selected tab of a `TabView`, the trailing column of a
154
+ `NavigationSplitView`. To hide bars rather than change their axis, use
155
+ `toolbarVisibility(_:for:)`.
156
+
157
+ `toolbarVerticalEdge` reports the preferred edge even when no vertical bar is
158
+ currently visible, and nil where the system never uses one:
159
+
160
+ ```swift
161
+ @Environment(\.toolbarVerticalEdge) private var barEdge
162
+
163
+ FloatingPalette()
164
+ .frame(maxWidth: .infinity, alignment: barEdge == .leading ? .leading : .trailing)
165
+ ```
166
+
167
+ ## 5. Sheets
168
+
169
+ On the inner display, sheets use horizontal toolbars when centred or leading
170
+ and vertical ones when trailing. Choose placement with
171
+ `presentationPlacement(_:)` (iOS 27) or `UISheetPresentationController.preferredPlacement`
172
+ (iOS 27). On the outer display, a sheet with a single action can opt out of the
173
+ vertical bar with `toolbarVerticalBehavior(.disabled)`.
174
+
175
+ ## 6. Hinge
176
+
177
+ | | SwiftUI *(27.1 beta)* | UIKit *(27.1 beta)* |
178
+ |---|---|---|
179
+ | Observe | `onHingeChange(isEnabled:_:)`, old and new `DeviceHingeContext` | `UIHingeInteraction(updateHandler:)`, `(interaction, update)` |
180
+ | Hinge value | `DeviceHingeContext.hinge: DeviceHinge?` | `Update.hinge: UIHinge?` |
181
+ | Angle | `angle: Angle` | `angle: CGFloat`, radians |
182
+ | Status | `DeviceHinge.Status` struct: `.closed`, `.partiallyOpen`, `.fullyOpen` | `UIHinge.Status` enum: same plus `.unknown` |
183
+
184
+ ```swift
185
+ @State private var hingeStatus: DeviceHinge.Status?
186
+
187
+ var body: some View {
188
+ Canvas3DView(tilted: hingeStatus == .partiallyOpen)
189
+ .onHingeChange { _, new in
190
+ hingeStatus = new.hinge?.status
191
+ }
192
+ }
193
+ ```
194
+
195
+ - `hinge` is nil where there is no hinge; treat that as the normal phone case.
196
+ - The UIKit handler runs with the initial state, on changes, and when the
197
+ interaction moves between hierarchies. It escapes, so capture weakly.
198
+ - Status comes from angle and orientation together; do not rebuild it from
199
+ angle thresholds. Update rate and precision are system policy.
200
+ - Use the hinge for interactions and effects. Layout belongs to reserved
201
+ regions, arrangements and size classes.
202
+
203
+ ## 7. Scenes and accessories
204
+
205
+ - `sceneAccessory(content:)` (iOS 27) and UIKit's scene accessory
206
+ registration (`registerSceneAccessory(_:)`) attach content to another
207
+ display, for example a camera capture accessory on the outer display while
208
+ the app runs on the inner one. See Apple's article on registering a camera
209
+ capture accessory on iPhone Duo.
210
+ - New windows depend on what the current display allows. Prefer
211
+ `UIWindowScene.ActivationAction` for "open in new window" menu items, and
212
+ handle a failed scene request instead of assuming success.
@@ -0,0 +1,145 @@
1
+ # Fixed assumptions: grep list and replacements
2
+
3
+ Run these from the repository root. Every hit is a candidate, not a verdict:
4
+ read the surrounding code, decide whether it affects layout, and confirm on
5
+ the iPhone Duo simulator. Exclude vendored and generated code from the count.
6
+
7
+ ## Contents
8
+
9
+ 1. The search
10
+ 2. Orientation
11
+ 3. The screen
12
+ 4. Cached sizes
13
+ 5. Idiom and size class
14
+ 6. Device sizes and thresholds
15
+ 7. Insets
16
+ 8. Bars
17
+ 9. Media
18
+ 10. Recording the result
19
+
20
+ ## 1. The search
21
+
22
+ ```bash
23
+ rg -n --type swift \
24
+ -e 'UIScreen\.main' \
25
+ -e 'UIScreen\.screens' \
26
+ -e 'UIDevice\.current\.orientation' \
27
+ -e 'interfaceOrientation' \
28
+ -e 'statusBarOrientation' \
29
+ -e 'userInterfaceIdiom' \
30
+ -e 'horizontalSizeClass\s*==\s*\.regular' \
31
+ -e 'UIWindow\(frame:' \
32
+ -e 'safeAreaInsets\.(left|right|top|bottom)\s*\*\s*2' \
33
+ -e '(width|height)\s*(==|>=|<=|>|<)\s*(320|375|390|393|402|414|428|430|440)\b' \
34
+ -e 'UIToolbar\(|UINavigationBar\(|UITabBar\(' \
35
+ -e 'scaleAspectFill|contentMode:\s*\.fill' \
36
+ .
37
+ ```
38
+
39
+ Add Objective-C (`--type objc`) and storyboards (`-g '*.storyboard'` for
40
+ fixed-size constraints) when the project has them.
41
+
42
+ ## 2. Orientation
43
+
44
+ Hits: `UIDevice.current.orientation`, `interfaceOrientation` (including
45
+ `windowScene.effectiveGeometry.interfaceOrientation`), `statusBarOrientation`.
46
+
47
+ Why: on a foldable device orientation no longer says whether the app is wide
48
+ or tall, and Apple's guidance is not to use `UIInterfaceOrientation` for
49
+ layout decisions.
50
+
51
+ Replace: compare the width and height of the space you have, `view.bounds` in
52
+ a view controller, `bounds` of the superview in a view, the size from
53
+ `GeometryReader` or `onGeometryChange` in SwiftUI. Keep orientation only for
54
+ things that are about the device's physical attitude (camera capture
55
+ rotation), not about layout.
56
+
57
+ ## 3. The screen
58
+
59
+ Hits: `UIScreen.main` (deprecated in iOS 26), `UIScreen.screens`,
60
+ `UIWindow(frame: UIScreen.main.bounds)`.
61
+
62
+ | Instead of | Use |
63
+ |---|---|
64
+ | `UIScreen.main.bounds` | `view.bounds`, `window.bounds`, `GeometryReader`, `onGeometryChange` |
65
+ | `UIScreen.main.scale` | `traitCollection.displayScale`, `@Environment(\.displayScale)` |
66
+ | `UIWindow(frame: UIScreen.main.bounds)` | `UIWindow(windowScene:)` |
67
+ | A stored `UIScreen` | `view.window?.windowScene?.screen`, read when needed |
68
+
69
+ ## 4. Cached sizes
70
+
71
+ Hard to grep; look for sizes stored in properties at `viewDidLoad`,
72
+ `viewIsAppearing`, `init`, `onAppear`, or a `GeometryReader` value copied into
73
+ `@State` once.
74
+
75
+ Why: folding and unfolding resizes the app during a session. A size read at
76
+ launch is wrong after the first fold.
77
+
78
+ Replace: do size-dependent layout in `viewDidLayoutSubviews` or
79
+ `layoutSubviews`; use `viewWillTransition(to:with:)` for work tied to the
80
+ change itself; in SwiftUI derive layout from the current geometry every time,
81
+ or keep it current with `onGeometryChange`.
82
+
83
+ ## 5. Idiom and size class
84
+
85
+ Hits: `userInterfaceIdiom == .pad`, `horizontalSizeClass == .regular` used to
86
+ mean "iPad", and code that assumes iPhone is always compact.
87
+
88
+ Why: the inner display is regular width on an iPhone. An iPad-only layout
89
+ branch now runs on a phone, and a "phone means compact" branch never expects
90
+ regular.
91
+
92
+ Replace: branch on size class and available space only. Check both
93
+ directions: code that fires on regular, and code that assumes compact.
94
+
95
+ ## 6. Device sizes and thresholds
96
+
97
+ Hits: comparisons with known iPhone widths or heights, device lookup tables,
98
+ "if width > 390" breakpoints.
99
+
100
+ Why: no table contains this device, and Split View produces sizes no device
101
+ has.
102
+
103
+ Replace: size classes (compact for the outer display, regular for the inner),
104
+ `ViewThatFits`, `containerRelativeFrame`, proportional and intrinsic sizing.
105
+
106
+ ## 7. Insets
107
+
108
+ Hits: `safeAreaInsets.left * 2`, `width - 2 * inset`, `insets.left` used for
109
+ both sides, padding that mirrors one edge onto the other.
110
+
111
+ Why: side-mounted bars make safe areas and layout margins asymmetric, and the
112
+ heavy side depends on pose and Split View position.
113
+
114
+ Replace: inset each edge by its own value, or constrain to the safe area
115
+ layout guide and layout margins and let the system resolve each edge.
116
+
117
+ ## 8. Bars
118
+
119
+ Hits: `UIToolbar(`, `UINavigationBar(`, `UITabBar(` created by hand, or a
120
+ SwiftUI `HStack` styled as a bar pinned to the top or bottom.
121
+
122
+ Why: only bars provided by navigation containers take part in vertical
123
+ placement. A custom bar stays where it is and collides with the system's
124
+ vertical controls.
125
+
126
+ Replace: toolbar items on `NavigationStack` / `NavigationSplitView` with
127
+ `.toolbar`, or on a view controller inside a navigation controller; `TabView`
128
+ or `UITabBarController` for tabs.
129
+
130
+ ## 9. Media
131
+
132
+ Hits: `scaleAspectFill`, `.aspectRatio(contentMode: .fill)` on hero images
133
+ and video.
134
+
135
+ Why: wider displays crop more of the image and can lose the subject.
136
+
137
+ Replace: choose fill or fit from the current size or aspect ratio, crop around
138
+ a focal point, and extend backgrounds under bars with
139
+ `backgroundExtensionEffect()` rather than stretching the image.
140
+
141
+ ## 10. Recording the result
142
+
143
+ For each hit, record `file:line`, the category, keep or fix, and for fixes the
144
+ replacement. Separate "confirmed on the simulator" from "found in code only".
145
+ Pose-dependent behaviour is not proven until it has been seen running.
@@ -0,0 +1,122 @@
1
+ # Poses, panes and the design review
2
+
3
+ Design guidance for iPhone Duo. The rules marked HIG come from Apple's
4
+ Designing for iPhone Duo page; the pattern table is a recommendation built on
5
+ those rules and on size classes, and should be adapted to the app's content.
6
+
7
+ ## Contents
8
+
9
+ 1. Poses
10
+ 2. Pose to pattern
11
+ 3. Using the inner display
12
+ 4. Dual-pane rules
13
+ 5. Controls on the side
14
+ 6. The fold
15
+ 7. Review checklist
16
+
17
+ ## 1. Poses
18
+
19
+ People hold and set down iPhone Duo in several ways (HIG): closed, using the
20
+ outer display; open flat on the inner display; partially folded like a book;
21
+ placed on a surface; standing on its edges. Every pose can also be rotated,
22
+ and Split View can put two apps side by side on the inner display.
23
+
24
+ Supporting poses does not mean a custom layout per pose (HIG). A compact
25
+ width layout for the outer display and a regular width layout for the inner
26
+ display cover the fundamentals; the existing layout expands into the space.
27
+
28
+ ## 2. Pose to pattern
29
+
30
+ | Pose | Size class | What usually works |
31
+ |---|---|---|
32
+ | Closed, outer display | compact width | The phone layout. Bars on the side free vertical space; content offset from the bar side |
33
+ | Open, inner display, landscape | regular width | One more level of hierarchy: list and detail side by side, or content reflowed into columns. Bars stay on the side |
34
+ | Open, inner display, portrait | regular width | Same hierarchy as landscape; horizontal bars return because vertical space is plentiful |
35
+ | Partially folded (book) | regular width | A split that rebalances around the fold; even column counts in grids; nothing tappable in the curve |
36
+ | On a surface, partly folded | regular width | Viewing on the upper part, touch on the lower, stable part; suits media and hands-free use |
37
+ | Standing on edges | depends on orientation | Glanceable, hands-free content with few controls |
38
+ | Split View | either, per half | Each app keeps its controls on its outer edge; the app must work at half width |
39
+
40
+ The on-surface layout is optional and still has to offer every control the
41
+ other poses offer.
42
+
43
+ ## 3. Using the inner display
44
+
45
+ A stretched phone layout is the failure to avoid. Options, in rough order of
46
+ cost:
47
+
48
+ - **Split view**: `NavigationSplitView` shows list and detail together when
49
+ open and collapses to a stack when closed. The hierarchy is the same; the
50
+ inner display shows one more level of it (HIG). Mail shows the list or a
51
+ message when closed and both side by side when open.
52
+ - **Reflow**: a single column becomes two columns when width allows
53
+ (`ViewThatFits`, `AnyLayout`, adaptive grids).
54
+ - **Arrangement**: two related views (now playing and lyrics, controls and
55
+ video) in an `ArrangementView` that adapts to the fold.
56
+ - **Sidebar tabs**: a sidebar-adaptable tab view for information-dense apps.
57
+
58
+ ## 4. Dual-pane rules
59
+
60
+ - Same functions and state on both displays (HIG). Opening or closing the
61
+ device mid-task must not lose a selection, a scroll position or a draft.
62
+ - Never tie a function to a pose (HIG). Everything reachable in one pose is
63
+ reachable in all of them, even if it moves into overflow.
64
+ - The secondary pane depends on the primary: selecting in the list drives the
65
+ detail. When the device closes, the detail the person was reading should be
66
+ what they see, not the list.
67
+ - Keep controls with the content they act on (HIG). Actions for the list sit
68
+ above the list pane, not in the side bar beside the detail.
69
+ - Each pane keeps its own safe area and margins; do not let one pane's insets
70
+ bleed into the other.
71
+ - Prefer containers that rebalance themselves around the fold over manual
72
+ positioning (HIG), and in grids prefer an even number of columns so content
73
+ divides cleanly.
74
+
75
+ ## 5. Controls on the side
76
+
77
+ - Follow the system's vertical placement and do not override it without a
78
+ strong reason (HIG); it is one of the device's core patterns.
79
+ - Top of the vertical axis: Back or Close, then prominent actions such as
80
+ Done; remaining items keep their original groupings (HIG).
81
+ - Give every item a symbol and a title; keep text-only buttons to a minimum,
82
+ because text items stay in a horizontal bar (HIG).
83
+ - Assign visibility priority so frequent actions and items with badges stay
84
+ visible longest (HIG).
85
+ - Use the system overflow menu instead of a custom one; reserve the ellipsis
86
+ for overflow (HIG).
87
+ - Content next to side controls is asymmetric. Offset content by the safe
88
+ area by default; centre on the full display only for immersive,
89
+ non-scrolling screens with nothing interactive under the controls; or let a
90
+ background span the full width while the scrollable, interactive content
91
+ stays inset (HIG).
92
+
93
+ ## 6. The fold
94
+
95
+ - Keep interactive elements out of the folding region (HIG). System controls
96
+ are moved aside automatically; custom ones use reserved regions.
97
+ - Move as little as possible as the device folds (HIG). Small adjustments
98
+ that keep elements visible beat controls that vanish or jump.
99
+ - Scrolling content may pass through the fold; fixed text and controls
100
+ should not rest in it.
101
+
102
+ ## 7. Review checklist
103
+
104
+ Use with `ios-design-review` for the general visual pass.
105
+
106
+ 1. Is anything interactive in the fold or under the side controls?
107
+ 2. Is any function or piece of state available in one pose but not another?
108
+ 3. Does state survive opening, closing and folding mid-task?
109
+ 4. Are there fixed widths or device breakpoints instead of size classes?
110
+ 5. Do toolbar items have symbols and titles, sensible priorities, and does the
111
+ overflow hold the right things?
112
+ 6. Does the inner display do better than stretching (split, reflow,
113
+ arrangement)?
114
+ 7. Is content offset from the side controls, deliberately centred, or mixed
115
+ with every control inside the inset area?
116
+ 8. Does the screen hold up in Split View on both sides, at half width?
117
+ 9. Do custom components reimplement fold avoidance the system components
118
+ would have provided?
119
+ 10. Are controls next to the content they affect?
120
+ 11. Do grids divide evenly around the fold?
121
+ 12. Which of the above were seen on the simulator, and which only reasoned
122
+ from design files or code?
@@ -0,0 +1,177 @@
1
+ ---
2
+ name: ios-sf-symbols
3
+ description: "SF Symbols in SwiftUI and UIKit: rendering modes, variable values, symbol effects including Draw On and Off, matching weight and scale to text, custom symbols and accessibility labels. Use when picking, styling, animating or reviewing symbols, or building a custom symbol."
4
+ metadata:
5
+ source: multi-agent-pipeline
6
+ date_added: "2026-10-08"
7
+ ---
8
+
9
+ # SF Symbols
10
+
11
+ SF Symbols are vector glyphs drawn to sit with San Francisco text: nine
12
+ weights and three scales that line up with the font, layers that take colour,
13
+ and built-in animations. Using them well is mostly about letting them follow
14
+ the text and the container instead of sizing them by hand.
15
+
16
+ ## Versions and availability
17
+
18
+ Symbols and symbol features introduced in a given year are not available on
19
+ earlier systems, so check both the symbol's availability in the SF Symbols app
20
+ and the API's introduction version.
21
+
22
+ | Capability | API | iOS |
23
+ |---|---|---|
24
+ | Rendering modes | `symbolRenderingMode(_:)`, `SymbolRenderingMode` | 15 |
25
+ | Variants | `symbolVariant(_:)`, `SymbolVariants` | 15 |
26
+ | Variable value | `Image(systemName:variableValue:)` | 16 |
27
+ | Symbol effects | `symbolEffect(_:options:value:)`, `symbolEffect(_:options:isActive:)`, `contentTransition(.symbolEffect(...))` | 17 |
28
+ | Wiggle, Rotate, Breathe | `WiggleSymbolEffect`, `RotateSymbolEffect`, `BreatheSymbolEffect` | 18 |
29
+ | Draw On, Draw Off | `DrawOnSymbolEffect`, `DrawOffSymbolEffect` (`.drawOn`, `.drawOff`) | 26 |
30
+ | Variable Draw | `symbolVariableValueMode(_:)` with `.draw` | 26 |
31
+ | Gradient rendering | `symbolColorRenderingMode(_:)` with `.gradient` | 26 |
32
+
33
+ SF Symbols 7 introduced Draw, Variable Draw and gradients. The current SF
34
+ Symbols app (27 releases) adds semantic search and new symbols. Check
35
+ developer.apple.com/sf-symbols for the current app before relying on a symbol
36
+ that may be new.
37
+
38
+ ## Sizing with text
39
+
40
+ Symbols take their size and weight from the font, which is what keeps them
41
+ aligned and scaling with Dynamic Type.
42
+
43
+ ```swift
44
+ Label("Downloads", systemImage: "arrow.down.circle")
45
+ .font(.headline)
46
+
47
+ Image(systemName: "bell")
48
+ .font(.body)
49
+ .imageScale(.large)
50
+ ```
51
+
52
+ - Set size with `.font(...)` and a text style, not `.resizable().frame(...)`.
53
+ A resized image stops scaling with Dynamic Type and loses weight matching.
54
+ - Use `imageScale(_:)` (small, medium, large) to change emphasis relative to
55
+ the text without breaking the weight match.
56
+ - Match weight to adjacent text (`.fontWeight(.semibold)` on both).
57
+ - In UIKit, `UIImage.SymbolConfiguration(textStyle:)` or
58
+ `init(font:scale:)` does the same job.
59
+
60
+ ## Rendering modes
61
+
62
+ | Mode | What it does | Good for |
63
+ |---|---|---|
64
+ | Monochrome | One colour for every layer | Toolbars, lists, most UI |
65
+ | Hierarchical | One colour, layers at decreasing opacity | Depth without extra colours |
66
+ | Palette | One explicit colour per layer | Brand or status colour pairs |
67
+ | Multicolor | The symbol's intrinsic colours | Weather, files, recognisable objects |
68
+
69
+ ```swift
70
+ Image(systemName: "cloud.sun.rain.fill")
71
+ .symbolRenderingMode(.hierarchical)
72
+ .foregroundStyle(.tint)
73
+
74
+ Image(systemName: "person.crop.circle.badge.checkmark")
75
+ .symbolRenderingMode(.palette)
76
+ .foregroundStyle(.green, .secondary)
77
+ ```
78
+
79
+ Gradient rendering (iOS 26) adds a light linear gradient from a single colour
80
+ in any mode: `.symbolColorRenderingMode(.gradient)`. It looks best at larger
81
+ sizes. Prefer system colours in every mode so symbols adapt to dark mode,
82
+ vibrancy and Increase Contrast. Check the automatic choice at small sizes and
83
+ on busy backgrounds, where a different mode may read better.
84
+
85
+ ## Variants
86
+
87
+ Outline is the default and sits best next to text. Fill gives emphasis and is
88
+ what tab bars prefer; slash marks something unavailable; enclosed shapes help
89
+ legibility at small sizes. Containers often pick the variant (a tab bar
90
+ chooses fill), so set `symbolVariant(_:)` only when the context does not.
91
+
92
+ ## Variable values
93
+
94
+ A variable value (0 to 1) shows a level: signal, volume, progress.
95
+
96
+ ```swift
97
+ Image(systemName: "wifi", variableValue: strength)
98
+
99
+ Image(systemName: "thermometer.high", variableValue: level)
100
+ .symbolVariableValueMode(.draw)
101
+ ```
102
+
103
+ Variable Color changes layer opacity at thresholds; Variable Draw (iOS 26)
104
+ draws the path to the value, which reads more precisely. Use variable values
105
+ to show change, not depth; hierarchical rendering is for depth.
106
+
107
+ ## Effects
108
+
109
+ Effects tell people that something happened or is happening. Each has a
110
+ meaning; pick by meaning, not by look.
111
+
112
+ | Effect | Use | Trigger |
113
+ |---|---|---|
114
+ | Bounce | An action happened | `value:` changes |
115
+ | Pulse, Breathe | Ongoing activity | `isActive:` |
116
+ | Variable Color | Progress or connecting | `isActive:` |
117
+ | Wiggle | Draw attention to something easy to miss | `value:` |
118
+ | Rotate | Work in progress, rotating parts | `isActive:` |
119
+ | Scale | Persistent emphasis on a selection | `isActive:` |
120
+ | Appear, Disappear | Show or hide a symbol | `isActive:` or transition |
121
+ | Draw On, Draw Off | Draw in or out along the path (iOS 26) | `isActive:` or transition |
122
+ | Replace, Magic Replace (18) | Swap one symbol for a related one | `contentTransition` |
123
+
124
+ ```swift
125
+ Image(systemName: "bell")
126
+ .symbolEffect(.bounce, value: unreadCount)
127
+
128
+ Image(systemName: isMuted ? "speaker.slash" : "speaker.wave.2")
129
+ .contentTransition(.symbolEffect(.replace))
130
+
131
+ Image(systemName: "checkmark.circle")
132
+ .symbolEffect(.drawOff.byLayer, isActive: !isSaved)
133
+ ```
134
+
135
+ Use effects sparingly; several animated symbols on one screen compete. Repeat
136
+ indefinitely only while the condition holds. Respect Reduce Motion: read
137
+ `accessibilityReduceMotion` and drop decorative repeating effects when it is
138
+ on, keeping the state change itself visible. Full effect options and the
139
+ UIKit equivalents are in `references/effects-and-custom-symbols.md`.
140
+
141
+ ## Custom symbols
142
+
143
+ When no system symbol fits, build one from the SF Symbols template rather than
144
+ drawing an icon from scratch, so it gets weights, scales, rendering modes and
145
+ animations. In short: export the closest system symbol's template from the SF
146
+ Symbols app, edit it in a vector tool, import it back, annotate layers (and
147
+ Draw guide points if it animates), test every effect, export, and add it to an
148
+ asset catalog as a symbol image. Load it with `Image("name")` or
149
+ `UIImage(named:)`. The full process is in the reference.
150
+
151
+ Symbols that depict Apple products or features cannot be customised; the SF
152
+ Symbols app marks them with an Info badge.
153
+
154
+ ## Accessibility
155
+
156
+ - Icon-only controls need a label. `Label("Share", systemImage: ...)` gives
157
+ VoiceOver and overflow menus a title even when only the icon shows.
158
+ - Use `.accessibilityLabel(...)` when the label must differ from visible text,
159
+ and hide purely decorative symbols with `.accessibilityHidden(true)`.
160
+ - Custom symbols need an accessibility description; VoiceOver cannot infer one.
161
+ - Directional symbols have right-to-left variants; custom symbols need their
162
+ directionality set when exported.
163
+
164
+ ## Not in app icons
165
+
166
+ The SF Symbols licence prohibits using symbols, or confusingly similar images,
167
+ in app icons, logos or any trademarked use. Draw the icon's artwork
168
+ separately; see `hig-app-icons`.
169
+
170
+ ## References
171
+
172
+ - `references/effects-and-custom-symbols.md`: effect options, UIKit API, custom symbol workflow, review checklist.
173
+ - HIG SF Symbols: https://developer.apple.com/design/human-interface-guidelines/sf-symbols
174
+ - SF Symbols app: https://developer.apple.com/sf-symbols/
175
+ - Symbols framework: https://developer.apple.com/documentation/symbols
176
+ - Configuring and displaying symbol images: https://developer.apple.com/documentation/uikit/configuring-and-displaying-symbol-images-in-your-ui
177
+ - Creating custom symbols: https://developer.apple.com/documentation/technologyoverviews/custom-sf-symbols