@mmerterden/multi-agent-pipeline 20.17.1 → 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 (73) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/docs/facts.json +6 -6
  3. package/install/catalog-history.json +1 -1
  4. package/manifest.json +75 -37
  5. package/package.json +1 -1
  6. package/pipeline/skills/.skill-manifest.json +33 -13
  7. package/pipeline/skills/.skills-index.json +66 -11
  8. package/pipeline/skills/shared/README.md +18 -13
  9. package/pipeline/skills/shared/external/NOTICE-avdlee-swiftui-agent-skill.md +3 -1
  10. package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +2 -1
  11. package/pipeline/skills/shared/external/hig-app-icons/SKILL.md +159 -0
  12. package/pipeline/skills/shared/external/hig-app-icons/evals/evals.json +48 -0
  13. package/pipeline/skills/shared/external/hig-app-icons/evals/trigger-eval.json +22 -0
  14. package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +115 -0
  15. package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +32 -4
  16. package/pipeline/skills/shared/external/hig-components-layout/evals/evals.json +47 -0
  17. package/pipeline/skills/shared/external/hig-components-layout/evals/trigger-eval.json +22 -0
  18. package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +231 -0
  19. package/pipeline/skills/shared/external/hig-components-layout/references/containers.md +5 -30
  20. package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
  21. package/pipeline/skills/shared/external/hig-foundations/SKILL.md +23 -4
  22. package/pipeline/skills/shared/external/hig-foundations/evals/evals.json +47 -0
  23. package/pipeline/skills/shared/external/hig-foundations/evals/trigger-eval.json +21 -0
  24. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +34 -26
  25. package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +219 -0
  26. package/pipeline/skills/shared/external/hig-inputs/SKILL.md +5 -4
  27. package/pipeline/skills/shared/external/hig-patterns/SKILL.md +19 -3
  28. package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +188 -0
  29. package/pipeline/skills/shared/external/hig-patterns/references/patterns.md +5 -11
  30. package/pipeline/skills/shared/external/hig-platforms/SKILL.md +17 -5
  31. package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +218 -0
  32. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +4 -1
  33. package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +135 -0
  34. package/pipeline/skills/shared/external/ios-design-review/SKILL.md +146 -0
  35. package/pipeline/skills/shared/external/ios-design-review/evals/evals.json +48 -0
  36. package/pipeline/skills/shared/external/ios-design-review/evals/trigger-eval.json +22 -0
  37. package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +135 -0
  38. package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +193 -0
  39. package/pipeline/skills/shared/external/ios-haptics/SKILL.md +191 -0
  40. package/pipeline/skills/shared/external/ios-haptics/evals/evals.json +49 -0
  41. package/pipeline/skills/shared/external/ios-haptics/evals/trigger-eval.json +22 -0
  42. package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +215 -0
  43. package/pipeline/skills/shared/external/ios-iphone-duo/NOTICE.md +53 -0
  44. package/pipeline/skills/shared/external/ios-iphone-duo/SKILL.md +170 -0
  45. package/pipeline/skills/shared/external/ios-iphone-duo/evals/evals.json +50 -0
  46. package/pipeline/skills/shared/external/ios-iphone-duo/evals/trigger-eval.json +22 -0
  47. package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +212 -0
  48. package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +145 -0
  49. package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +122 -0
  50. package/pipeline/skills/shared/external/ios-sf-symbols/SKILL.md +177 -0
  51. package/pipeline/skills/shared/external/ios-sf-symbols/evals/evals.json +49 -0
  52. package/pipeline/skills/shared/external/ios-sf-symbols/evals/trigger-eval.json +22 -0
  53. package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +135 -0
  54. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +38 -50
  55. package/pipeline/skills/shared/external/swiftui-animation/evals/evals.json +47 -0
  56. package/pipeline/skills/shared/external/swiftui-animation/evals/trigger-eval.json +22 -0
  57. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +7 -73
  58. package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +185 -0
  59. package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +6 -6
  60. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +3 -253
  61. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +4 -47
  62. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +3 -111
  63. package/pipeline/skills/shared/external/swiftui-expert-skill/references/liquid-glass.md +11 -381
  64. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +184 -224
  65. package/pipeline/skills/shared/external/swiftui-liquid-glass/evals/evals.json +47 -0
  66. package/pipeline/skills/shared/external/swiftui-liquid-glass/evals/trigger-eval.json +22 -0
  67. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +246 -33
  68. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +227 -0
  69. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +15 -18
  70. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/components-index.md +1 -1
  71. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/haptics.md +6 -68
  72. package/pipeline/skills/skills-index.md +16 -11
  73. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +0 -786
@@ -0,0 +1,219 @@
1
+ # Typography, color and spacing: numbers and code
2
+
3
+ Companion to `../SKILL.md` for the `hig-foundations` skill. The numbers below
4
+ come from Apple's Human Interface Guidelines (Typography, Accessibility,
5
+ Color, Layout); the spacing scale is a design convention, marked as such.
6
+
7
+ ## Contents
8
+
9
+ 1. [Text sizes and weights](#1-text-sizes-and-weights)
10
+ 2. [iOS text styles at the default size](#2-ios-text-styles-at-the-default-size)
11
+ 3. [Custom fonts that still scale](#3-custom-fonts-that-still-scale)
12
+ 4. [Adapting layout at large sizes](#4-adapting-layout-at-large-sizes)
13
+ 5. [Color roles](#5-color-roles)
14
+ 6. [Contrast minimums](#6-contrast-minimums)
15
+ 7. [Color on Liquid Glass](#7-color-on-liquid-glass)
16
+ 8. [Spacing and margins](#8-spacing-and-margins)
17
+ 9. [Touch targets](#9-touch-targets)
18
+
19
+ ## 1. Text sizes and weights
20
+
21
+ | Platform | Default size | Minimum size |
22
+ |---|---|---|
23
+ | iOS, iPadOS | 17 pt | 11 pt |
24
+ | macOS | 13 pt | 10 pt |
25
+ | tvOS | 29 pt | 23 pt |
26
+ | visionOS | 17 pt | 12 pt |
27
+ | watchOS | 16 pt | 12 pt |
28
+
29
+ - The minimum applies to custom fonts too. A thin custom face needs to sit
30
+ above these sizes to stay legible.
31
+ - Prefer Regular, Medium, Semibold or Bold. Avoid Ultralight, Thin and Light,
32
+ which are hard to read, especially at small sizes.
33
+ - Keep the number of typefaces small; build hierarchy with weight, size and
34
+ color inside one family.
35
+
36
+ ## 2. iOS text styles at the default size
37
+
38
+ Values at the Large (default) content size. Emphasized weight is what the
39
+ style turns into with `.bold()` in SwiftUI or the bold symbolic trait in UIKit.
40
+
41
+ | SwiftUI style | Weight | Size (pt) | Leading (pt) | Emphasized weight |
42
+ |---|---|---|---|---|
43
+ | `.largeTitle` | Regular | 34 | 41 | Bold |
44
+ | `.title` | Regular | 28 | 34 | Bold |
45
+ | `.title2` | Regular | 22 | 28 | Bold |
46
+ | `.title3` | Regular | 20 | 25 | Semibold |
47
+ | `.headline` | Semibold | 17 | 22 | Semibold |
48
+ | `.body` | Regular | 17 | 22 | Semibold |
49
+ | `.callout` | Regular | 16 | 21 | Semibold |
50
+ | `.subheadline` | Regular | 15 | 20 | Semibold |
51
+ | `.footnote` | Regular | 13 | 18 | Semibold |
52
+ | `.caption` | Regular | 12 | 16 | Semibold |
53
+ | `.caption2` | Regular | 11 | 13 | Semibold |
54
+
55
+ ```swift
56
+ Text(order.total, format: .currency(code: order.currencyCode))
57
+ .font(.body)
58
+ .bold()
59
+ ```
60
+
61
+ Use the emphasized variant for a second level of hierarchy inside a style
62
+ instead of jumping to a bigger style. Bold Text (an accessibility setting) is
63
+ honoured automatically by system fonts; read `legibilityWeight` when a custom
64
+ drawing needs to follow it.
65
+
66
+ ## 3. Custom fonts that still scale
67
+
68
+ A custom face keeps Dynamic Type only when it is tied to a text style:
69
+
70
+ ```swift
71
+ extension Font {
72
+ static func brand(_ style: Font.TextStyle, size: CGFloat) -> Font {
73
+ .custom("BrandSans-Regular", size: size, relativeTo: style)
74
+ }
75
+ }
76
+
77
+ Text("Your trips")
78
+ .font(.brand(.title, size: 28))
79
+ ```
80
+
81
+ `Font.custom(_:size:relativeTo:)` (iOS 14) scales the given size the way the
82
+ named text style scales. `Font.custom(_:size:)` alone also scales relative to
83
+ body; `Font.custom(_:fixedSize:)` does not scale and is only for content that
84
+ must not grow. Numeric layout values next to text use `@ScaledMetric`:
85
+
86
+ ```swift
87
+ @ScaledMetric(relativeTo: .body) private var iconSide: CGFloat = 24
88
+ ```
89
+
90
+ In UIKit, `UIFontMetrics(forTextStyle:).scaledFont(for:)` with
91
+ `adjustsFontForContentSizeCategory = true` does the same.
92
+
93
+ ## 4. Adapting layout at large sizes
94
+
95
+ - Keep truncation to a minimum: aim to show as much useful text at the largest
96
+ accessibility size as at the largest standard size.
97
+ - Switch side-by-side arrangements to stacked ones in horizontally constrained
98
+ places, and reduce the number of columns.
99
+ - Keep primary elements toward the top even at very large sizes.
100
+ - Not every word must grow: tab titles, for example, stay put while content
101
+ grows. Small fixed controls can offer the Large Content Viewer instead
102
+ (see `ios-accessibility`).
103
+
104
+ ```swift
105
+ @Environment(\.dynamicTypeSize) private var typeSize
106
+
107
+ var body: some View {
108
+ let layout = typeSize.isAccessibilitySize
109
+ ? AnyLayout(VStackLayout(alignment: .leading, spacing: 8))
110
+ : AnyLayout(HStackLayout(spacing: 12))
111
+ layout {
112
+ Text(item.title).font(.headline)
113
+ Text(item.subtitle).font(.subheadline).foregroundStyle(.secondary)
114
+ }
115
+ }
116
+ ```
117
+
118
+ ## 5. Color roles
119
+
120
+ | Role | SwiftUI |
121
+ |---|---|
122
+ | Labels | `.primary`, `.secondary`, `Color(uiColor: .tertiaryLabel)`, `Color(uiColor: .quaternaryLabel)` |
123
+ | Backgrounds | `Color(uiColor: .systemBackground)`, `.secondarySystemBackground`, `.tertiarySystemBackground` |
124
+ | Grouped backgrounds | `Color(uiColor: .systemGroupedBackground)` and its secondary and tertiary levels |
125
+ | Fills and separators | `Color(uiColor: .systemFill)` and its levels, `Color(uiColor: .separator)` |
126
+ | Accent | `.tint(...)` once near the root; controls pick it up |
127
+ | Destructive | `Button(role: .destructive)` rather than painting text red |
128
+
129
+ - Do not redefine a semantic color's meaning (no `separator` as text color, no
130
+ `secondaryLabel` as a background).
131
+ - Do not hard-code the documented system values; they change between releases.
132
+ - Custom colors live in the asset catalog with light, dark and
133
+ increased-contrast variants.
134
+ - Use one color for one meaning across the app; do not reuse the brand color
135
+ that marks interactive text for non-interactive text.
136
+ - Never carry meaning with color alone: add a symbol or a word.
137
+
138
+ ## 6. Contrast minimums
139
+
140
+ From the HIG accessibility page:
141
+
142
+ | Text size | Weight | Minimum contrast ratio |
143
+ |---|---|---|
144
+ | Up to 17 pt | All | 4.5:1 |
145
+ | 18 pt and up | All | 3:1 |
146
+ | All | Bold | 3:1 |
147
+
148
+ If the default palette cannot meet these, the app must at least provide a
149
+ higher-contrast scheme when Increase Contrast is on
150
+ (`colorSchemeContrast == .increased`). Check both light and dark appearances.
151
+
152
+ ## 7. Color on Liquid Glass
153
+
154
+ Glass takes its color from the content behind it. Toolbars and tab bars keep
155
+ symbols and text monochrome by default, flipping dark or light with what is
156
+ underneath. Add color sparingly: for a primary action, tint the background of
157
+ that one control (as prominent buttons do) rather than coloring symbols on many
158
+ controls. Over colorful content, keep bar items monochrome or pick an accent
159
+ that differs clearly from the content. Implementation is in
160
+ `swiftui-liquid-glass`.
161
+
162
+ ## 8. Spacing and margins
163
+
164
+ Start by leaving `spacing:` off stacks; the adaptive default follows the
165
+ platform. When a value is chosen on purpose, a 4 point grid is a widespread
166
+ design convention (Apple does not mandate one). Do not invent values between
167
+ its stops, and prefer the project's spacing tokens when they exist.
168
+
169
+ | Points | Typical use |
170
+ |---|---|
171
+ | 4 | Icon to label, inline badge offset |
172
+ | 8 | Related items in one group |
173
+ | 12 | Row inner padding, label to secondary text |
174
+ | 16 | Standard margin, gap between sections |
175
+ | 24 | Card inner padding, section separators |
176
+ | 32 | Major groups, header to content |
177
+ | 48 | Hero and onboarding layouts |
178
+
179
+ - Use system margins (`.padding()` with no value, layout margins, readable
180
+ content guides) before fixed numbers.
181
+ - Keep content inside the safe area; let only backgrounds bleed with
182
+ `.ignoresSafeArea()` or `.background(_:ignoresSafeAreaEdges:)`.
183
+ - Pin persistent actions with `.safeAreaInset(edge: .bottom)`, or
184
+ `safeAreaBar` on iOS 26 when the bar should get the scroll edge effect.
185
+ - `.contentMargins(_:_:for:)` (iOS 17) keeps reading width comfortable in
186
+ scroll views on iPad and in landscape.
187
+
188
+ ```swift
189
+ ScrollView {
190
+ LazyVStack { ForEach(lines) { OrderLineRow(line: $0) } }
191
+ }
192
+ .contentMargins(.horizontal, 20, for: .scrollContent)
193
+ .safeAreaInset(edge: .bottom) {
194
+ Button("Place order", action: placeOrder)
195
+ .buttonStyle(.borderedProminent)
196
+ .padding()
197
+ }
198
+ ```
199
+
200
+ ## 9. Touch targets
201
+
202
+ | Platform | Default | Minimum |
203
+ |---|---|---|
204
+ | iOS, iPadOS | 44 x 44 pt | 28 x 28 pt |
205
+ | macOS | 28 x 28 pt | 20 x 20 pt |
206
+ | visionOS | 60 x 60 pt | 28 x 28 pt |
207
+ | watchOS | 44 x 44 pt | 28 x 28 pt |
208
+
209
+ Spacing matters as much as size: about 12 points of padding around bezeled
210
+ elements and about 24 around elements without a bezel. Grow the hit area of a
211
+ small visual with `.contentShape(Rectangle())` and a frame rather than
212
+ enlarging the glyph.
213
+
214
+ Sources:
215
+ [Typography](https://developer.apple.com/design/human-interface-guidelines/typography),
216
+ [Accessibility](https://developer.apple.com/design/human-interface-guidelines/accessibility),
217
+ [Color](https://developer.apple.com/design/human-interface-guidelines/color),
218
+ [Layout](https://developer.apple.com/design/human-interface-guidelines/layout),
219
+ [Font.custom(_:size:relativeTo:)](https://developer.apple.com/documentation/swiftui/font/custom(_:size:relativeto:)).
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hig-inputs
3
- description: "Apple HIG for input mapped to SwiftUI: touch gestures, keyboards, pointer, Apple Pencil, focus and game controllers, Digital Crown, eyes and hands, haptics. Use when designing or reviewing input on an Apple platform."
3
+ description: "Apple HIG for input mapped to SwiftUI: gestures, keyboards, pointer, Apple Pencil, focus, game controllers and Digital Crown. Use when designing or reviewing input on an Apple platform."
4
4
  metadata:
5
5
  risk: safe
6
6
  source: multi-agent-pipeline
@@ -116,9 +116,9 @@ Hardware keyboard (iPad, Mac, visionOS):
116
116
 
117
117
  ## Haptics and motion input
118
118
 
119
- - Use haptics to confirm meaningful events, not every tap:
120
- `.sensoryFeedback(.selection, trigger: selectedSize)` in a picker,
121
- `.success` when an order is placed.
119
+ - Haptics (when to play them, `sensoryFeedback`, feedback generators, Core
120
+ Haptics) are covered in `ios-haptics`. As an input rule: haptics confirm
121
+ meaningful events, never replace a visible change.
122
122
  - Shake is reserved for undo; avoid other shake gestures.
123
123
  - Motion-based input (tilt) needs a non-motion alternative.
124
124
 
@@ -136,6 +136,7 @@ accessibilityAction(named: "Archive").
136
136
  - `hig-components-system` for menus, controls and text fields.
137
137
  - `swiftui-gestures` for gesture implementation detail.
138
138
  - `pencilkit-drawing` for Apple Pencil canvases.
139
+ - `ios-haptics` for haptic feedback.
139
140
 
140
141
  ## References
141
142
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hig-patterns
3
- description: "Apple HIG interaction patterns mapped to SwiftUI: modality, loading, empty and error states, feedback, undo, search, data entry, settings, sharing, accounts. Use when designing a UX flow on an Apple platform."
3
+ description: "Apple HIG interaction patterns mapped to SwiftUI: onboarding, permission requests, loading, empty and error states, modality, search, settings. Use when designing a UX flow on an Apple platform."
4
4
  metadata:
5
5
  risk: safe
6
6
  source: multi-agent-pipeline
@@ -29,7 +29,22 @@ Each maps to a pattern in `references/patterns.md`.
29
29
 
30
30
  ## Details on demand
31
31
 
32
- Load `references/patterns.md` for launch, loading, empty and error states, feedback, undo, search, data entry, settings, sharing, accounts and notifications.
32
+ - `references/onboarding-and-states.md`: onboarding rules, TipKit versus an
33
+ up-front flow, when and how to ask for permission, the HIG rules for a
34
+ pre-permission screen (one button that opens the alert), and empty, error
35
+ and loading states with `ContentUnavailableView`.
36
+ - `references/patterns.md`: feedback, undo, search, data entry, settings,
37
+ sharing, accounts and notifications.
38
+
39
+ ## First launch and access in one glance
40
+
41
+ - Onboarding is optional, short and interactive; context tips beat a carousel.
42
+ - Ask for access when the feature is first used, not at launch, unless the app
43
+ cannot work without it.
44
+ - A pre-permission screen has one button ("Continue", never "Allow") that
45
+ opens the system alert, and no way around it.
46
+ - Empty states say what would be here and offer the next step; errors say
47
+ what happened and offer Retry while keeping input.
33
48
 
34
49
  ## Choosing how to present a task
35
50
 
@@ -64,7 +79,8 @@ Expected: sheet with Cancel and Save; discard confirmation when edited.
64
79
 
65
80
  - `hig-components-content`, `hig-components-system` for the parts of each pattern.
66
81
  - `swiftui-navigation` for push, sheet and deep link implementation.
67
- - `tipkit` for in-context onboarding.
82
+ - `tipkit` for in-context tips that replace up-front onboarding pages.
83
+ - `ios-haptics` for confirming success or failure with haptics.
68
84
 
69
85
  ## References
70
86
 
@@ -0,0 +1,188 @@
1
+ # Onboarding, permission requests, empty and error states
2
+
3
+ Companion to `../SKILL.md` for the `hig-patterns` skill. Guidance follows
4
+ Apple's HIG pages on onboarding, launching, privacy and loading; the SwiftUI
5
+ code is ours.
6
+
7
+ ## Contents
8
+
9
+ 1. [Onboarding](#1-onboarding)
10
+ 2. [Teaching in context with TipKit](#2-teaching-in-context-with-tipkit)
11
+ 3. [Asking for permission](#3-asking-for-permission)
12
+ 4. [Pre-permission screens](#4-pre-permission-screens)
13
+ 5. [Empty states](#5-empty-states)
14
+ 6. [Error states](#6-error-states)
15
+ 7. [Loading before the empty state](#7-loading-before-the-empty-state)
16
+
17
+ ## 1. Onboarding
18
+
19
+ Ideally people understand the app by using it. When onboarding is needed:
20
+
21
+ - It comes after launch is complete; it is not part of the launch screen.
22
+ - Teach by doing: let people try the action safely rather than read about it.
23
+ - Prefer several context-specific tips over one long up-front flow.
24
+ - Keep any required flow brief; do not ask people to memorize steps.
25
+ - Make a tutorial optional. If someone skips it, do not show it again on the
26
+ next launch, but keep it reachable from help, account or settings.
27
+ - Teach the app, not the device or the system.
28
+ - Postpone nonessential setup; ship defaults good enough to start right away.
29
+ - Let people use the app before asking for ratings, purchases or an account
30
+ where possible.
31
+ - A splash screen, if any, is brief; do not hold people on downloads, and
32
+ leave licensing text to the App Store page.
33
+
34
+ Remember completion with `@AppStorage` and present the flow from the root, not
35
+ from inside a feature screen:
36
+
37
+ ```swift
38
+ struct RootView: View {
39
+ @AppStorage("onboarding.completed") private var onboardingCompleted = false
40
+
41
+ var body: some View {
42
+ let showsOnboarding = Binding(
43
+ get: { !onboardingCompleted },
44
+ set: { isPresented in onboardingCompleted = !isPresented }
45
+ )
46
+ MainTabs()
47
+ .fullScreenCover(isPresented: showsOnboarding) {
48
+ OnboardingFlow { onboardingCompleted = true }
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ Every onboarding page has a visible way to skip or finish; the last page ends
55
+ on the app's first useful screen.
56
+
57
+ ## 2. Teaching in context with TipKit
58
+
59
+ For a feature people discover later (filters, a gesture, a new toolbar item),
60
+ a TipKit tip shown next to the control beats an onboarding page. Rules,
61
+ display frequency, invalidation and testing are in the `tipkit` skill.
62
+
63
+ ## 3. Asking for permission
64
+
65
+ - Ask only for what the feature needs, and as late as possible: when the
66
+ person first uses the feature that depends on it.
67
+ - Ask at launch only when the app cannot work without it (a navigation app and
68
+ location), where the reason is obvious.
69
+ - If the app needs access before it can function at all, the request can be
70
+ part of onboarding, with a sentence on why and what the person gains.
71
+ - The purpose string (usage description) is a short, complete, specific
72
+ sentence in sentence case, active voice, ending with a period:
73
+ "Your location is used to show nearby gates and walking times."
74
+ - Prefer controls that avoid a prompt altogether when they fit: the location
75
+ button, the photos picker, contact and calendar pickers.
76
+ - After a denial, keep the feature visible with an explanation and a link to
77
+ Settings; never re-prompt in a loop.
78
+
79
+ ## 4. Pre-permission screens
80
+
81
+ A custom screen before the system alert is allowed when the context alone does
82
+ not explain the request. HIG rules for it:
83
+
84
+ - **Exactly one button, and it opens the system alert.** Title it "Continue"
85
+ or "Next", never "Allow", which mimics the alert's own button.
86
+ - **No other actions**, such as Close, Cancel or "Not now", unless a legal
87
+ consent requires one. The screen must lead to the alert.
88
+ - **No manipulation.** No incentives, no imitation of the alert, no image of
89
+ the alert, no annotations pointing at the alert's buttons. App Review
90
+ rejects these, and tracking requests are watched especially closely.
91
+
92
+ ```swift
93
+ struct CameraPrimer: View {
94
+ let onContinue: () -> Void
95
+
96
+ var body: some View {
97
+ VStack(spacing: 24) {
98
+ Image(systemName: "camera.viewfinder")
99
+ .font(.system(.largeTitle))
100
+ .foregroundStyle(.tint)
101
+ .accessibilityHidden(true)
102
+ Text("Scan boarding passes")
103
+ .font(.title2.bold())
104
+ Text("The camera reads the barcode on a paper pass so you can add it to your trip.")
105
+ .font(.body)
106
+ .multilineTextAlignment(.center)
107
+ .foregroundStyle(.secondary)
108
+ Button("Continue", action: onContinue)
109
+ .buttonStyle(.borderedProminent)
110
+ .controlSize(.large)
111
+ }
112
+ .padding()
113
+ }
114
+ }
115
+ ```
116
+
117
+ `onContinue` triggers the real request, for example
118
+ `AVCaptureDevice.requestAccess(for: .video)`.
119
+
120
+ ## 5. Empty states
121
+
122
+ `ContentUnavailableView` (iOS 17) is the standard empty and error view. An
123
+ empty state says what would appear here and offers the next step.
124
+
125
+ ```swift
126
+ ContentUnavailableView {
127
+ Label("No trips yet", systemImage: "airplane")
128
+ } description: {
129
+ Text("Trips you book or add with a confirmation code appear here.")
130
+ } actions: {
131
+ Button("Add a trip") { isAddingTrip = true }
132
+ .buttonStyle(.borderedProminent)
133
+ }
134
+ ```
135
+
136
+ - For search with no results use `ContentUnavailableView.search` or
137
+ `ContentUnavailableView.search(text:)`, which word the message for you.
138
+ - Distinguish first use (nothing created yet: invite creation), filtered to
139
+ nothing (offer to clear filters) and cleared by the person (a quiet
140
+ confirmation is enough).
141
+ - Never hide a tab or section because it is empty; show the empty state in it.
142
+
143
+ ## 6. Error states
144
+
145
+ - Say what happened in plain words and what the person can do: "Couldn't
146
+ refresh your trips. Check your connection and try again."
147
+ - Offer Retry for transient failures, and keep everything the person entered.
148
+ - Show errors in place, next to the content that failed; reserve alerts for
149
+ failures that block the task.
150
+ - Keep blame out of the copy and avoid codes unless support needs them; when
151
+ they do, show them in a secondary line.
152
+
153
+ ```swift
154
+ ContentUnavailableView {
155
+ Label("Couldn't load trips", systemImage: "wifi.exclamationmark")
156
+ } description: {
157
+ Text("Check your connection and try again.")
158
+ } actions: {
159
+ Button("Try Again") { Task { await reload() } }
160
+ }
161
+ ```
162
+
163
+ ## 7. Loading before the empty state
164
+
165
+ Order the states so the screen never flashes "empty" while loading:
166
+
167
+ ```swift
168
+ switch state {
169
+ case .loading:
170
+ TripsList(trips: Trip.placeholders)
171
+ .redacted(reason: .placeholder)
172
+ case .loaded(let trips) where trips.isEmpty:
173
+ ContentUnavailableView("No trips yet", systemImage: "airplane")
174
+ case .loaded(let trips):
175
+ TripsList(trips: trips)
176
+ case .failed:
177
+ LoadErrorView(retry: reload)
178
+ }
179
+ ```
180
+
181
+ Placeholders keep the layout in place; a spinner suits short, global waits.
182
+ Screen-level loading recipes are in `swiftui-ui-patterns`.
183
+
184
+ Sources:
185
+ [HIG: Onboarding](https://developer.apple.com/design/human-interface-guidelines/onboarding),
186
+ [HIG: Privacy](https://developer.apple.com/design/human-interface-guidelines/privacy),
187
+ [HIG: Loading](https://developer.apple.com/design/human-interface-guidelines/loading),
188
+ [ContentUnavailableView](https://developer.apple.com/documentation/swiftui/contentunavailableview).
@@ -7,10 +7,8 @@ Companion to `../SKILL.md` for the `hig-patterns` skill.
7
7
  - Launch straight into useful content. The launch screen should look like the
8
8
  first frame of the app, not a splash advert.
9
9
  - Restore where the person left off (`@SceneStorage` for per-window state).
10
- - Keep onboarding optional and short. Teach in context instead, for example a
11
- TipKit tip the first time someone opens the filters on a product list.
12
- - Ask for permissions at the moment they are needed, with a sentence of
13
- context before the system prompt. Never ask for everything at launch.
10
+ - Onboarding, permission timing and pre-permission screens:
11
+ [onboarding-and-states.md](onboarding-and-states.md).
14
12
  - Let people try the app before creating an account whenever possible.
15
13
 
16
14
  ## Waiting: loading and progress
@@ -25,17 +23,13 @@ Companion to `../SKILL.md` for the `hig-patterns` skill.
25
23
 
26
24
  ## Nothing to show, or something went wrong
27
25
 
28
- - Empty states explain what would appear and offer the next step:
29
- `ContentUnavailableView("No notes yet", systemImage: "note.text", description: ...)`
30
- with a button to create one.
31
- - For search with no results use `ContentUnavailableView.search(text:)`.
32
- - Errors say what happened in plain language and what to do. Offer retry for
33
- transient failures; keep entered data intact.
26
+ Empty, no-results and error states with `ContentUnavailableView`:
27
+ [onboarding-and-states.md](onboarding-and-states.md).
34
28
 
35
29
  ## Feedback and confirmation
36
30
 
37
31
  - Confirm success quietly and in place: a checkmark symbol effect, an updated
38
- row, a brief haptic (`.sensoryFeedback(.success, trigger:)`).
32
+ row, a brief haptic (see `ios-haptics`).
39
33
  - Reserve alerts for failures that block progress.
40
34
  - Make destructive actions recoverable (undo, trash) rather than piling on
41
35
  confirmation dialogs. Where there is no undo, confirm with a destructive-role
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hig-platforms
3
- description: "Apple HIG per platform: iPhone, iPad, Mac, Watch, TV and Vision Pro conventions, and adapting one SwiftUI codebase across them. Use when a design has to fit a specific Apple platform."
3
+ description: "Apple HIG per platform: iPhone, iPad, Mac, Watch, TV and Vision Pro, iPad size classes and windowing. Use when a design has to fit a specific Apple platform or adapt across them."
4
4
  metadata:
5
5
  risk: safe
6
6
  source: multi-agent-pipeline
@@ -36,9 +36,10 @@ The answers usually decide navigation structure before any visual question.
36
36
  `NavigationStack`.
37
37
  - Portrait first, but layouts must not break in landscape or at accessibility
38
38
  text sizes.
39
- - On iOS 26, standard tab bars and toolbars render in Liquid Glass and can
40
- minimize on scroll (`.tabBarMinimizeBehavior(.onScrollDown)`); a search tab
41
- can use `Tab(role: .search)`.
39
+ - On iOS 26, standard tab bars and toolbars render in Liquid Glass; bar
40
+ behaviour (minimizing, accessories, the search tab) is in
41
+ `hig-components-layout`.
42
+ - Hinged iPhone hardware (iPhone Duo, iOS 27.1): `ios-iphone-duo`.
42
43
 
43
44
  ### iPad
44
45
 
@@ -52,6 +53,9 @@ The answers usually decide navigation structure before any visual question.
52
53
  Pencil where drawing or markup fits.
53
54
  - Support multiple scenes when documents or records can be open side by side.
54
55
  - iPadOS 26 adds a menu bar; populate it through `.commands` as on the Mac.
56
+ - Size classes, full screen versus windowed mode, minimum window sizes,
57
+ `ViewThatFits`, `AnyLayout` and custom `Layout` types:
58
+ `references/ipad-layout.md`.
55
59
 
56
60
  ### Mac
57
61
 
@@ -103,7 +107,7 @@ The answers usually decide navigation structure before any visual question.
103
107
  |------|--------------|
104
108
  | List and detail on large screens, stack on small | `NavigationSplitView` (collapses automatically) |
105
109
  | Tabs that become a sidebar | `.tabViewStyle(.sidebarAdaptable)` |
106
- | Layout switches on width | `@Environment(\.horizontalSizeClass)`, `ViewThatFits` |
110
+ | Layout switches on width | `@Environment(\.horizontalSizeClass)`, `ViewThatFits`, `AnyLayout` |
107
111
  | Platform-only modifiers | `#if os(macOS)` / `#if os(visionOS)` kept at the edge of a view |
108
112
  | Commands shared by menu, shortcut and toolbar | `.commands`, `CommandMenu`, `.keyboardShortcut` |
109
113
  | Several windows of the same content | `WindowGroup(for: Order.ID.self)` plus `openWindow` |
@@ -139,6 +143,14 @@ is usually two views that should share a model instead.
139
143
  - `hig-inputs` for input-specific behavior.
140
144
  - `hig-components-layout` for split views, sidebars and tab bars in detail.
141
145
  - `swiftui-navigation` for navigation implementation.
146
+ - `ios-iphone-duo` for hinged iPhone layouts (iPhone Duo).
147
+
148
+ ## Details on demand
149
+
150
+ - `references/ipad-layout.md`: iPadOS 26 windows, size classes, choosing
151
+ between `NavigationSplitView`, `ViewThatFits`, `AnyLayout`,
152
+ `containerRelativeFrame` and a custom `Layout`, multiple windows and a test
153
+ matrix.
142
154
 
143
155
  ## References
144
156