@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,21 @@
1
+ [
2
+ {"query": "what's the minimum font size apple allows on iphone? design wants 9pt captions", "should_trigger": true},
3
+ {"query": "our custom font doesn't scale with dynamic type, we use Font.custom(name, size: 16) everywhere", "should_trigger": true},
4
+ {"query": "is #8E8E93 text on white ok for accessibility? it's a 14pt price label", "should_trigger": true},
5
+ {"query": "which font weights should I avoid in the app, designer loves Ultralight for headlines", "should_trigger": true},
6
+ {"query": "how much padding between rows and what margins does apple recommend, we keep guessing 7, 9, 13", "should_trigger": true},
7
+ {"query": "dark mode: our hardcoded Color.white backgrounds and gray dividers look awful, what colors should we use", "should_trigger": true},
8
+ {"query": "headline vs body vs callout - what sizes are these text styles on iOS and when do I make one bold", "should_trigger": true},
9
+ {"query": "tiny 20pt close button in the corner, testers keep missing it - what size should tap targets be", "should_trigger": true},
10
+ {"query": "status shown only with red/green dots in our order list, is that a problem per HIG", "should_trigger": true},
11
+ {"query": "make my glass toolbar buttons tinted with the brand color on iOS 26", "should_trigger": false},
12
+ {"query": "which SF Symbol rendering mode should I use for the battery icon with a level", "should_trigger": false},
13
+ {"query": "design the app icon dark and tinted variants in Icon Composer", "should_trigger": false},
14
+ {"query": "set up VoiceOver labels and rotors for our custom chart", "should_trigger": false},
15
+ {"query": "tab bar or sidebar for an iPad app with 9 sections?", "should_trigger": false},
16
+ {"query": "onboarding flow: should we ask for notification permission on first launch", "should_trigger": false},
17
+ {"query": "Material 3 typography scale for our Compose app", "should_trigger": false},
18
+ {"query": "take screenshots of the checkout screen at AX5 and dark mode and review them", "should_trigger": false},
19
+ {"query": "CSS rem vs px for our marketing site font sizes", "should_trigger": false},
20
+ {"query": "add a gentle haptic when a row is deleted", "should_trigger": false}
21
+ ]
@@ -2,6 +2,18 @@
2
2
 
3
3
  Companion to `../SKILL.md` for the `hig-foundations` skill.
4
4
 
5
+ ## Contents
6
+
7
+ - [1. Typography](#1-typography)
8
+ - [2. Color](#2-color)
9
+ - [3. Layout, margins and safe areas](#3-layout-margins-and-safe-areas)
10
+ - [4. Icons and SF Symbols](#4-icons-and-sf-symbols)
11
+ - [5. Materials, Liquid Glass and depth](#5-materials-liquid-glass-and-depth)
12
+ - [6. Motion](#6-motion)
13
+ - [7. App icon](#7-app-icon)
14
+ - [8. Writing](#8-writing)
15
+ - [9. Accessibility, as a foundation](#9-accessibility-as-a-foundation)
16
+
5
17
  ## 1. Typography
6
18
 
7
19
  Ask: if the user sets the largest accessibility text size, does every string
@@ -19,7 +31,13 @@ still fit, wrap or scroll?
19
31
  `@Environment(\.dynamicTypeSize)` and check `.isAccessibilitySize`, or use
20
32
  `ViewThatFits` to let the layout choose.
21
33
  - Keep a clear hierarchy with weight and style, not with many sizes. Two or
22
- three styles per screen is usually enough.
34
+ three styles per screen is usually enough; use a style's emphasized weight
35
+ (`.bold()`) before reaching for a bigger style.
36
+ - Respect the floor: 17 pt default and 11 pt minimum on iOS, for custom fonts
37
+ too. Avoid Ultralight, Thin and Light weights for interface text.
38
+
39
+ Numbers, the full text style table and custom-font code:
40
+ [type-color-spacing.md](type-color-spacing.md).
23
41
 
24
42
  Review signal: truncated order totals, overlapping labels, or buttons whose
25
43
  titles are clipped at `accessibility3` and above.
@@ -37,8 +55,9 @@ with increased contrast, and to someone who cannot distinguish red from green?
37
55
  - Set the app accent once with `.tint(...)` near the root; controls pick it up.
38
56
  - Never carry meaning by color alone. A failed payment shows an icon and a
39
57
  word, not only a red row.
40
- - Aim for at least 4.5:1 contrast for body text and 3:1 for large text and
41
- essential glyphs.
58
+ - Meet at least 4.5:1 contrast for text up to 17 pt and 3:1 for 18 pt and
59
+ larger or bold text; if the default palette cannot, provide a
60
+ higher-contrast scheme under Increase Contrast.
42
61
 
43
62
  Review signal: hard-coded hex values, `Color.white` text on a brand color that
44
63
  fails contrast in dark mode, status conveyed only by hue.
@@ -66,34 +85,25 @@ rounded corners and the keyboard, while backgrounds still reach the edges?
66
85
  Ask: is there a system symbol for this, and does it match the weight of the
67
86
  text beside it?
68
87
 
69
- - Use `Image(systemName:)` and `Label` so symbols align with text baselines and
70
- scale with Dynamic Type.
71
- - Pick a rendering mode on purpose: `.symbolRenderingMode(.hierarchical)` for
72
- depth, `.palette` for brand pairs, `.multicolor` where the symbol ships it.
73
- - Use `.symbolVariant(.fill)` for selected states, as tab bars do.
74
- - Animate state with `.symbolEffect` (bounce on add-to-cart, pulse while
75
- syncing) and `.contentTransition(.symbolEffect(.replace))` between glyphs.
76
- - Custom symbols belong in the asset catalog as symbol templates so they gain
77
- weights and scales like system ones.
88
+ Use `Image(systemName:)` and `Label` so symbols align with text and scale with
89
+ Dynamic Type. Rendering modes, variable values, symbol effects and custom
90
+ symbols are covered in `ios-sf-symbols`.
78
91
 
79
92
  ## 5. Materials, Liquid Glass and depth
80
93
 
81
94
  Ask: does this layer float above content, and does the content stay legible
82
95
  under it?
83
96
 
84
- - On iOS 26 and later, system bars, tab bars, sheets and toolbars adopt Liquid
85
- Glass automatically when built with standard SwiftUI containers. Avoid
86
- painting opaque backgrounds on them; that removes the effect.
87
- - For custom floating controls, apply `.glassEffect()` and group neighbors in a
88
- `GlassEffectContainer` so shapes blend and morph together. Keep glass for the
89
- navigation and control layer, not for content cards.
90
- - Use `.buttonStyle(.glass)` or `.glassProminent` for floating actions that
91
- should match system controls.
92
- - Before iOS 26, use materials (`.regularMaterial`, `.thinMaterial`) for
93
- overlays; gate newer APIs with `if #available(iOS 26, *)`.
97
+ - Liquid Glass is for the navigation and control layer; content uses standard
98
+ materials (`.ultraThinMaterial` to `.thickMaterial`) or solid fills.
99
+ - Vibrant label and fill colors keep text legible on materials; avoid the
100
+ quaternary label level on thin and ultra-thin materials.
94
101
  - Respect Reduce Transparency: system materials handle it, custom blurs must
95
102
  check `accessibilityReduceTransparency`.
96
103
 
104
+ Everything about glass itself (variants, containers, fallbacks, UIKit) lives
105
+ in `swiftui-liquid-glass`.
106
+
97
107
  ## 6. Motion
98
108
 
99
109
  Ask: does this animation explain a change, and what happens when Reduce Motion
@@ -110,10 +120,8 @@ is on?
110
120
 
111
121
  ## 7. App icon
112
122
 
113
- - Provide a single 1024 by 1024 source and let Xcode generate sizes; on iOS 18
114
- and later supply dark and tinted variants. From iOS 26, author layered icons
115
- in Icon Composer so the system can render the glass appearance.
116
- - Keep the icon a simple, recognizable shape without text or screenshots.
123
+ Layered icons, Icon Composer, the default, dark, clear and tinted appearances
124
+ and the asset catalog setup are covered in `hig-app-icons`.
117
125
 
118
126
  ## 8. Writing
119
127
 
@@ -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