@mmerterden/multi-agent-pipeline 20.17.1 → 20.18.0
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.
- package/CHANGELOG.md +24 -0
- package/docs/facts.json +6 -6
- package/install/_mcp-register.mjs +1 -1
- package/install/_xcode-skills.mjs +263 -0
- package/install/catalog-history.json +1 -1
- package/install/claude.mjs +23 -0
- package/install/codex.mjs +11 -0
- package/install/copilot.mjs +9 -0
- package/manifest.json +85 -45
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/stack/SKILL.md +2 -0
- package/pipeline/lib/xcode-plugin.mjs +131 -0
- package/pipeline/multi-agent-refs/features/doctor.md +10 -0
- package/pipeline/scripts/doctor.mjs +38 -0
- package/pipeline/scripts/uninstall.mjs +57 -1
- package/pipeline/skills/.skill-manifest.json +33 -13
- package/pipeline/skills/.skills-index.json +66 -11
- package/pipeline/skills/shared/README.md +18 -13
- package/pipeline/skills/shared/external/NOTICE-avdlee-swiftui-agent-skill.md +3 -1
- package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +2 -1
- package/pipeline/skills/shared/external/hig-app-icons/SKILL.md +159 -0
- package/pipeline/skills/shared/external/hig-app-icons/evals/evals.json +48 -0
- package/pipeline/skills/shared/external/hig-app-icons/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +115 -0
- package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +32 -4
- package/pipeline/skills/shared/external/hig-components-layout/evals/evals.json +47 -0
- package/pipeline/skills/shared/external/hig-components-layout/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +231 -0
- package/pipeline/skills/shared/external/hig-components-layout/references/containers.md +5 -30
- package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-foundations/SKILL.md +23 -4
- package/pipeline/skills/shared/external/hig-foundations/evals/evals.json +47 -0
- package/pipeline/skills/shared/external/hig-foundations/evals/trigger-eval.json +21 -0
- package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +34 -26
- package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +219 -0
- package/pipeline/skills/shared/external/hig-inputs/SKILL.md +5 -4
- package/pipeline/skills/shared/external/hig-patterns/SKILL.md +19 -3
- package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +188 -0
- package/pipeline/skills/shared/external/hig-patterns/references/patterns.md +5 -11
- package/pipeline/skills/shared/external/hig-platforms/SKILL.md +17 -5
- package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +218 -0
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +4 -1
- package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +135 -0
- package/pipeline/skills/shared/external/ios-design-review/SKILL.md +146 -0
- package/pipeline/skills/shared/external/ios-design-review/evals/evals.json +48 -0
- package/pipeline/skills/shared/external/ios-design-review/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +135 -0
- package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +193 -0
- package/pipeline/skills/shared/external/ios-haptics/SKILL.md +191 -0
- package/pipeline/skills/shared/external/ios-haptics/evals/evals.json +49 -0
- package/pipeline/skills/shared/external/ios-haptics/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +215 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/NOTICE.md +53 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/SKILL.md +179 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/evals/evals.json +65 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +212 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +202 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +122 -0
- package/pipeline/skills/shared/external/ios-sf-symbols/SKILL.md +177 -0
- package/pipeline/skills/shared/external/ios-sf-symbols/evals/evals.json +49 -0
- package/pipeline/skills/shared/external/ios-sf-symbols/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +135 -0
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +38 -50
- package/pipeline/skills/shared/external/swiftui-animation/evals/evals.json +47 -0
- package/pipeline/skills/shared/external/swiftui-animation/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +7 -73
- package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +185 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +6 -6
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +3 -253
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +4 -47
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +3 -111
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/liquid-glass.md +11 -381
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +184 -224
- package/pipeline/skills/shared/external/swiftui-liquid-glass/evals/evals.json +47 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/evals/trigger-eval.json +22 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +246 -33
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +227 -0
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +15 -18
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/components-index.md +1 -1
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/haptics.md +6 -68
- package/pipeline/skills/skills-index.md +16 -11
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +0 -786
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Bars, tabs and toolbars on iOS 26 and 27
|
|
2
|
+
|
|
3
|
+
Companion to `../SKILL.md` for the `hig-components-layout` skill. Each API
|
|
4
|
+
carries the iOS release it first shipped in; gate anything newer than the
|
|
5
|
+
deployment target with `#available`. The glass material itself (variants,
|
|
6
|
+
custom glass, fallbacks) is in `swiftui-liquid-glass`.
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
1. [Content layer and navigation layer](#1-content-layer-and-navigation-layer)
|
|
11
|
+
2. [Tab bars](#2-tab-bars)
|
|
12
|
+
3. [Search in a tab bar or toolbar](#3-search-in-a-tab-bar-or-toolbar)
|
|
13
|
+
4. [Toolbars and navigation bars](#4-toolbars-and-navigation-bars)
|
|
14
|
+
5. [Overflow, priority and pinning (iOS 27)](#5-overflow-priority-and-pinning-ios-27)
|
|
15
|
+
6. [Minimizing bars on scroll](#6-minimizing-bars-on-scroll)
|
|
16
|
+
7. [Review checklist](#7-review-checklist)
|
|
17
|
+
|
|
18
|
+
## 1. Content layer and navigation layer
|
|
19
|
+
|
|
20
|
+
From iOS 26, tab bars, toolbars, navigation bars and sidebars float in a
|
|
21
|
+
Liquid Glass layer above the content. Three consequences:
|
|
22
|
+
|
|
23
|
+
- **Keep navigation and content apart.** Destinations live in tab bars and
|
|
24
|
+
sidebars, actions in toolbars, and content scrolls underneath. Do not put
|
|
25
|
+
navigation inside content cards, or content inside bars.
|
|
26
|
+
- **No custom bar backgrounds.** Background images, colors and appearance
|
|
27
|
+
fills on `TabView`, toolbars and navigation bars sit on top of the glass and
|
|
28
|
+
break the scroll edge effect. Remove them and let content color the bar. Where
|
|
29
|
+
the bar must separate more from content, adjust
|
|
30
|
+
`scrollEdgeEffectStyle(_:for:)` rather than painting.
|
|
31
|
+
- **Tint sparingly.** Bar symbols are monochrome by default and follow the
|
|
32
|
+
content beneath. Over colorful content keep them monochrome, or pick an
|
|
33
|
+
accent that differs clearly from the content.
|
|
34
|
+
|
|
35
|
+
## 2. Tab bars
|
|
36
|
+
|
|
37
|
+
| API | Since | What it does |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `Tab(_:systemImage:value:content:)` | iOS 18 | A tab; keep five or fewer by default |
|
|
40
|
+
| `.tabViewStyle(.sidebarAdaptable)` | iOS 18 | Tab bar on iPhone, sidebar on iPad and Mac |
|
|
41
|
+
| `.tabBarMinimizeBehavior(_:)` | iOS 26 | `.automatic`, `.never`, `.onScrollDown`, `.onScrollUp` |
|
|
42
|
+
| `.tabViewBottomAccessory { }` | iOS 26 | A persistent accessory, such as a mini player, above the tab bar |
|
|
43
|
+
| `.tabViewBottomAccessory(isEnabled:content:)` | iOS 26.1 | The same, shown and hidden dynamically |
|
|
44
|
+
| `@Environment(\.tabViewBottomAccessoryPlacement)` | iOS 26 | `.expanded` above the bar or `.inline` beside a minimized bar |
|
|
45
|
+
| `Tab(role: .search)` | iOS 18 | The search tab; iOS 26 sets it apart at the trailing end |
|
|
46
|
+
| `TabRole.prominent` | iOS 27 | Prominent treatment for one tab; without one, the search tab may get it |
|
|
47
|
+
|
|
48
|
+
```swift
|
|
49
|
+
TabView(selection: $selection) {
|
|
50
|
+
Tab("Trips", systemImage: "airplane", value: .trips) { TripsView() }
|
|
51
|
+
Tab("Wallet", systemImage: "wallet.pass", value: .wallet) { WalletView() }
|
|
52
|
+
.badge(pendingPasses)
|
|
53
|
+
Tab("Account", systemImage: "person.crop.circle", value: .account) { AccountView() }
|
|
54
|
+
Tab(value: .search, role: .search) { SearchView() }
|
|
55
|
+
}
|
|
56
|
+
.tabViewStyle(.sidebarAdaptable)
|
|
57
|
+
.tabBarMinimizeBehavior(.onScrollDown)
|
|
58
|
+
.tabViewBottomAccessory {
|
|
59
|
+
BoardingStatusBar()
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Accessory content adapts to its placement:
|
|
64
|
+
|
|
65
|
+
```swift
|
|
66
|
+
struct BoardingStatusBar: View {
|
|
67
|
+
@Environment(\.tabViewBottomAccessoryPlacement) private var placement
|
|
68
|
+
|
|
69
|
+
var body: some View {
|
|
70
|
+
if placement == .inline {
|
|
71
|
+
Label("Gate B12", systemImage: "airplane.departure")
|
|
72
|
+
} else {
|
|
73
|
+
HStack {
|
|
74
|
+
Label("Gate B12", systemImage: "airplane.departure")
|
|
75
|
+
Spacer()
|
|
76
|
+
Text("Boarding 09:40")
|
|
77
|
+
}
|
|
78
|
+
.padding(.horizontal)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
On iOS 27, mark the one tab that deserves emphasis. With an older deployment
|
|
85
|
+
target, build the iOS 27 `TabView` in its own `@available(iOS 27, *)` view and
|
|
86
|
+
choose between the two with `if #available`.
|
|
87
|
+
|
|
88
|
+
```swift
|
|
89
|
+
Tab("Book", systemImage: "plus", value: .book, role: .prominent) {
|
|
90
|
+
BookingFlow()
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Rules:
|
|
95
|
+
|
|
96
|
+
- A tab switches context; never use a tab as an action ("New trip"). Only one
|
|
97
|
+
tab can be prominent.
|
|
98
|
+
- Do not hide or disable tabs because of state; explain inside the tab.
|
|
99
|
+
- Minimize the tab bar only when reading content benefits; people restore it
|
|
100
|
+
by tapping a tab or scrolling the other way.
|
|
101
|
+
- The accessory is for something persistent and app-wide (playback, an active
|
|
102
|
+
trip), not for a screen's primary action.
|
|
103
|
+
|
|
104
|
+
## 3. Search in a tab bar or toolbar
|
|
105
|
+
|
|
106
|
+
- `Tab(role: .search)` gives search its own tab; the first tab with that role
|
|
107
|
+
implements search. `tabViewSearchActivation(.searchTabSelection)` (iOS 26)
|
|
108
|
+
activates search when the tab is selected and returns to the previous tab
|
|
109
|
+
when search is dismissed.
|
|
110
|
+
- `.searchable(text:)` on a `NavigationStack` places the field where the
|
|
111
|
+
platform expects; `searchToolbarBehavior(.minimize)` (iOS 26) shows the
|
|
112
|
+
bottom-toolbar field on iPhone as a compact button until used.
|
|
113
|
+
- Scopes and suggestions for large catalogs: `.searchScopes(_:scopes:)` (iOS
|
|
114
|
+
16), the `activation:` form iOS 16.4, and `.searchSuggestions`.
|
|
115
|
+
|
|
116
|
+
```swift
|
|
117
|
+
NavigationStack {
|
|
118
|
+
LibraryList(query: query, scope: scope)
|
|
119
|
+
.searchable(text: $query)
|
|
120
|
+
.searchScopes($scope) {
|
|
121
|
+
ForEach(LibraryScope.allCases, id: \.self) { Text($0.label).tag($0) }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 4. Toolbars and navigation bars
|
|
127
|
+
|
|
128
|
+
| API | Since | What it does |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| Semantic placements (`.primaryAction`, `.confirmationAction`, `.cancellationAction`, `.topBarLeading`, `.topBarTrailing`, `.bottomBar`) | iOS 14 / 17 SDK | Let the system position items per platform |
|
|
131
|
+
| `ToolbarSpacer(.fixed)` / `ToolbarSpacer(.flexible)` | iOS 26 | Splits items into separate glass groups, or pushes groups apart |
|
|
132
|
+
| `.sharedBackgroundVisibility(.hidden)` | iOS 26 | Takes an item out of the shared glass group |
|
|
133
|
+
| `Button(role: .confirm)` / `Button(role: .close)` | iOS 26 | System confirm and close buttons in a toolbar |
|
|
134
|
+
|
|
135
|
+
```swift
|
|
136
|
+
.toolbar {
|
|
137
|
+
ToolbarItem(placement: .cancellationAction) {
|
|
138
|
+
Button(role: .close) { dismiss() }
|
|
139
|
+
}
|
|
140
|
+
ToolbarItem { Button("Share", systemImage: "square.and.arrow.up", action: share) }
|
|
141
|
+
ToolbarItem { Button("Duplicate", systemImage: "plus.square.on.square", action: duplicate) }
|
|
142
|
+
ToolbarSpacer(.fixed)
|
|
143
|
+
ToolbarItem(placement: .confirmationAction) {
|
|
144
|
+
Button(role: .confirm) { save() }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
HIG guidance for toolbars:
|
|
150
|
+
|
|
151
|
+
- Prefer system symbols without borders to text; do not mix text and icons in
|
|
152
|
+
one group. Every icon still gets an accessibility label.
|
|
153
|
+
- One primary action, on the trailing side, with the prominent treatment.
|
|
154
|
+
- Aim for at most three groups; group items that act on the same thing.
|
|
155
|
+
- Use the standard Back and Close buttons, not custom "Back" text.
|
|
156
|
+
- Hide a toolbar item itself rather than the view inside it, or an empty glass
|
|
157
|
+
slot remains.
|
|
158
|
+
- Custom components inside a bar keep corner radii concentric with the bar.
|
|
159
|
+
|
|
160
|
+
## 5. Overflow, priority and pinning (iOS 27)
|
|
161
|
+
|
|
162
|
+
| API | What it does |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `ToolbarOverflowMenu { }` | Actions that always live in the toolbar's overflow menu, whatever the width; on iOS it is the navigation bar's overflow menu |
|
|
165
|
+
| `.toolbarOverflowMenu { }` (view modifier) | Configures that overflow menu from a view |
|
|
166
|
+
| `.visibilityPriority(.high)` / `.low` | When space runs out, lower-priority items move to overflow first; `ToolbarItemVisibilityPriority(higherThan:)` and `(lowerThan:)` give relative ranks |
|
|
167
|
+
| `ToolbarItemPlacement.topBarPinnedTrailing` | Pins an item to the trailing edge of the top bar; it moves to overflow only when search is active and space is short |
|
|
168
|
+
|
|
169
|
+
```swift
|
|
170
|
+
.toolbar {
|
|
171
|
+
ToolbarItem(placement: .topBarPinnedTrailing) {
|
|
172
|
+
Button("Add", systemImage: "plus", action: add)
|
|
173
|
+
}
|
|
174
|
+
ToolbarItem { Button("Filter", systemImage: "line.3.horizontal.decrease", action: filter) }
|
|
175
|
+
.visibilityPriority(.high)
|
|
176
|
+
ToolbarItem { Button("Sort", systemImage: "arrow.up.arrow.down", action: sort) }
|
|
177
|
+
ToolbarOverflowMenu {
|
|
178
|
+
Button("Export", action: export)
|
|
179
|
+
Button("Print", action: printList)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
With an older deployment target, keep this toolbar in an
|
|
185
|
+
`@available(iOS 27, *)` view modifier and apply it behind `if #available`,
|
|
186
|
+
with a plain toolbar for earlier releases. HIG says the system adds an overflow menu by itself on
|
|
187
|
+
iPad and Mac when items do not fit: do not hand-build a "more" `Menu` for that,
|
|
188
|
+
and choose priorities so the important items stay.
|
|
189
|
+
|
|
190
|
+
## 6. Minimizing bars on scroll
|
|
191
|
+
|
|
192
|
+
| API | Since | What it does |
|
|
193
|
+
|---|---|---|
|
|
194
|
+
| `.tabBarMinimizeBehavior(.onScrollDown)` | iOS 26 | Tab bar recedes while scrolling |
|
|
195
|
+
| `.toolbarMinimizationBehavior(_:for:)` | iOS 27 | Navigation bar minimizes on scroll; an integrated top tab bar minimizes with it. Supported placement: `.navigationBar` |
|
|
196
|
+
| `.toolbarMinimizationRestoration(.atScrollEdge, for:)` | iOS 27 | Restore only at the scroll edge (with `.onScrollDown`, navigation bar only) |
|
|
197
|
+
| `.toolbarMinimizationSafeAreaAdjustment(_:for:)` | iOS 27 | Whether the safe area follows the minimizing bar |
|
|
198
|
+
|
|
199
|
+
```swift
|
|
200
|
+
NavigationStack {
|
|
201
|
+
ArticleView()
|
|
202
|
+
.navigationTitle("Reading")
|
|
203
|
+
.toolbarMinimizationBehavior(.onScrollDown, for: .navigationBar)
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Use minimization for long reading or browsing surfaces, not for forms or
|
|
208
|
+
screens whose bar holds the main actions.
|
|
209
|
+
|
|
210
|
+
## 7. Review checklist
|
|
211
|
+
|
|
212
|
+
- [ ] No custom backgrounds on tab bars, toolbars or navigation bars
|
|
213
|
+
- [ ] Navigation in bars and sidebars, content in the content layer
|
|
214
|
+
- [ ] Tabs are destinations, five or fewer by default, at most one `.prominent` (iOS 27)
|
|
215
|
+
- [ ] Search uses `Tab(role: .search)` or `.searchable`, not a custom field in a bar
|
|
216
|
+
- [ ] Toolbar items use semantic placements, symbols with labels, one prominent action, groups split with `ToolbarSpacer`
|
|
217
|
+
- [ ] iOS 27 overflow, priority, pinning and minimization APIs are gated with `#available`
|
|
218
|
+
- [ ] Minimizing bars only on reading surfaces; accessory content adapts to `.inline`
|
|
219
|
+
|
|
220
|
+
Sources:
|
|
221
|
+
[HIG: Tab bars](https://developer.apple.com/design/human-interface-guidelines/tab-bars),
|
|
222
|
+
[HIG: Toolbars](https://developer.apple.com/design/human-interface-guidelines/toolbars),
|
|
223
|
+
[Adopting Liquid Glass](https://developer.apple.com/documentation/technologyoverviews/adopting-liquid-glass),
|
|
224
|
+
[tabBarMinimizeBehavior(_:)](https://developer.apple.com/documentation/swiftui/view/tabbarminimizebehavior(_:)),
|
|
225
|
+
[tabViewBottomAccessory(content:)](https://developer.apple.com/documentation/swiftui/view/tabviewbottomaccessory(content:)),
|
|
226
|
+
[TabRole.prominent](https://developer.apple.com/documentation/swiftui/tabrole/prominent),
|
|
227
|
+
[ToolbarOverflowMenu](https://developer.apple.com/documentation/swiftui/toolbaroverflowmenu),
|
|
228
|
+
[visibilityPriority(_:)](https://developer.apple.com/documentation/swiftui/toolbarcontent/visibilitypriority(_:)),
|
|
229
|
+
[topBarPinnedTrailing](https://developer.apple.com/documentation/swiftui/toolbaritemplacement/topbarpinnedtrailing),
|
|
230
|
+
[toolbarMinimizationBehavior(_:for:)](https://developer.apple.com/documentation/swiftui/view/toolbarminimizationbehavior(_:for:)),
|
|
231
|
+
[ToolbarSpacer](https://developer.apple.com/documentation/swiftui/toolbarspacer).
|
|
@@ -8,31 +8,9 @@ Companion to `../SKILL.md` for the `hig-components-layout` skill.
|
|
|
8
8
|
back returns people to where they left off.
|
|
9
9
|
- Never hide or disable a tab because of state. Show the tab and explain
|
|
10
10
|
inside it (an empty state or sign-in prompt).
|
|
11
|
-
- On iOS 18 and later, `Tab(role: .search)` places search as a dedicated tab;
|
|
12
|
-
on iOS 26 it is set apart at the trailing end of the floating glass tab bar.
|
|
13
|
-
- iOS 26 renders the tab bar as floating Liquid Glass above content. Let
|
|
14
|
-
content scroll underneath it; do not paint an opaque background behind it.
|
|
15
|
-
- `.tabBarMinimizeBehavior(.onScrollDown)` (iOS 26) shrinks the bar while
|
|
16
|
-
reading; `.tabViewBottomAccessory` places a persistent control such as a
|
|
17
|
-
playback bar above it.
|
|
18
11
|
- Badges (`.badge(3)`) are for counts people act on, not marketing.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
TabView(selection: $selection) {
|
|
22
|
-
Tab("Orders", systemImage: "shippingbox", value: .orders) {
|
|
23
|
-
OrdersView()
|
|
24
|
-
}
|
|
25
|
-
.badge(pendingCount)
|
|
26
|
-
Tab("Products", systemImage: "square.grid.2x2", value: .products) {
|
|
27
|
-
ProductsView()
|
|
28
|
-
}
|
|
29
|
-
Tab(value: .search, role: .search) {
|
|
30
|
-
SearchView()
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
.tabViewStyle(.sidebarAdaptable)
|
|
34
|
-
.tabBarMinimizeBehavior(.onScrollDown)
|
|
35
|
-
```
|
|
12
|
+
- Minimize behavior, the bottom accessory, the search tab and the iOS 27
|
|
13
|
+
prominent tab are in [bars-and-tabs.md](bars-and-tabs.md).
|
|
36
14
|
|
|
37
15
|
## Sidebars and split views
|
|
38
16
|
|
|
@@ -60,12 +38,9 @@ TabView(selection: $selection) {
|
|
|
60
38
|
|
|
61
39
|
## Toolbars
|
|
62
40
|
|
|
63
|
-
- Declare actions with `.toolbar` and placements
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
- On iOS 26 toolbar items sit on glass and are grouped automatically;
|
|
67
|
-
`ToolbarSpacer` separates groups. Prefer SF Symbols over text for bar items
|
|
68
|
-
and let the system apply glass; do not add your own backgrounds.
|
|
41
|
+
- Declare actions with `.toolbar` and semantic placements so the system
|
|
42
|
+
positions them per platform. Grouping, `ToolbarSpacer`, overflow, priority
|
|
43
|
+
and pinning are in [bars-and-tabs.md](bars-and-tabs.md).
|
|
69
44
|
- Use `.toolbarRole(.editor)` on document-style screens and let people
|
|
70
45
|
customise Mac and iPad toolbars with `id`-based toolbar items.
|
|
71
46
|
|
|
@@ -111,7 +111,7 @@ struct UploadRow: View {
|
|
|
111
111
|
## Feedback after an action
|
|
112
112
|
|
|
113
113
|
- Confirm success quietly and in place: a checkmark in the button, a row that
|
|
114
|
-
updates, a short haptic
|
|
114
|
+
updates, a short haptic (see `ios-haptics`).
|
|
115
115
|
- Reserve alerts for failures that need a decision. For everything else,
|
|
116
116
|
explain inline next to the thing that failed.
|
|
117
117
|
- Network and sync status belongs near the affected content (a banner above
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hig-foundations
|
|
3
|
-
description: "Apple HIG foundations mapped to SwiftUI: Dynamic Type, semantic color, contrast, safe areas,
|
|
3
|
+
description: "Apple HIG foundations mapped to SwiftUI: typography and Dynamic Type, semantic color, contrast, spacing, safe areas, touch targets, motion and UX writing. Use when a design decision turns on type, color, contrast or spacing."
|
|
4
4
|
metadata:
|
|
5
5
|
risk: safe
|
|
6
6
|
source: multi-agent-pipeline
|
|
@@ -27,13 +27,28 @@ hide problems in the steps after it.
|
|
|
27
27
|
1. Text scales and stays readable at every size.
|
|
28
28
|
2. Colors come from semantic sources and hold up in both appearances.
|
|
29
29
|
3. Content respects safe areas and layout margins.
|
|
30
|
-
4. Icons are symbols with consistent weight and scale.
|
|
30
|
+
4. Icons are symbols with consistent weight and scale (`ios-sf-symbols`).
|
|
31
31
|
5. Materials and motion support the content instead of competing with it.
|
|
32
32
|
6. Copy is short, specific and written in the product's voice.
|
|
33
33
|
|
|
34
|
+
## The numbers that come up most
|
|
35
|
+
|
|
36
|
+
| Question | Answer (iOS and iPadOS) |
|
|
37
|
+
|---|---|
|
|
38
|
+
| Default and minimum text size | 17 pt and 11 pt, for system and custom fonts |
|
|
39
|
+
| Weights | Prefer Regular, Medium, Semibold, Bold; avoid Ultralight, Thin, Light |
|
|
40
|
+
| Second level of hierarchy inside a style | The emphasized weight via `.bold()` |
|
|
41
|
+
| Custom font that scales | `Font.custom(_:size:relativeTo:)`, plus `@ScaledMetric` for sizes beside text |
|
|
42
|
+
| Contrast | 4.5:1 for text up to 17 pt, 3:1 from 18 pt or for bold text |
|
|
43
|
+
| Touch target | 44 x 44 pt default, 28 x 28 pt minimum |
|
|
44
|
+
|
|
34
45
|
## Details on demand
|
|
35
46
|
|
|
36
|
-
|
|
47
|
+
- `references/foundations-detail.md`: each review step above with the
|
|
48
|
+
question to ask, the SwiftUI tools and what a reviewer looks for.
|
|
49
|
+
- `references/type-color-spacing.md`: the text style table with emphasized
|
|
50
|
+
weights, custom-font scaling, color roles, contrast table, color on Liquid
|
|
51
|
+
Glass, spacing scale, margins and touch targets, with code.
|
|
37
52
|
|
|
38
53
|
## Reporting findings
|
|
39
54
|
|
|
@@ -49,7 +64,11 @@ dynamicTypeSize.isAccessibilitySize.
|
|
|
49
64
|
|
|
50
65
|
- `hig-platforms` for how these foundations shift per device.
|
|
51
66
|
- `hig-components-layout`, `hig-components-content` for specific containers.
|
|
52
|
-
- `swiftui-liquid-glass` for
|
|
67
|
+
- `swiftui-liquid-glass` for materials and Liquid Glass (the single owner).
|
|
68
|
+
- `ios-sf-symbols` for symbol rendering modes, variable values and effects.
|
|
69
|
+
- `hig-app-icons` for app icons, Icon Composer and appearance variants.
|
|
70
|
+
- `ios-accessibility` for implementing Dynamic Type, contrast and motion
|
|
71
|
+
settings; `ios-design-review` for checking them with screenshots.
|
|
53
72
|
|
|
54
73
|
## References
|
|
55
74
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "hig-foundations",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 0,
|
|
6
|
+
"name": "brand-typography-scale",
|
|
7
|
+
"prompt": "Our brand font is 'Moderat' and the design file uses Moderat Light 10 pt for row metadata, Moderat Thin 15 pt for body copy and hard-coded .font(.custom(\"Moderat-Bold\", size: 24)) for titles. Accessibility audit says text doesn't grow with the system setting. Rewrite our Font extension for SwiftUI and tell me what to push back on with design.",
|
|
8
|
+
"expected_output": "Pushes back on 10 pt (below the 11 pt iOS minimum) and on Light and Thin weights for interface text, recommending Regular or heavier and sizes at or above the defaults. Rewrites fonts with Font.custom(_:size:relativeTo:) tied to text styles (body 17 pt relative to .body, titles relative to .title or .largeTitle, metadata to .caption or .footnote), uses @ScaledMetric for adjacent dimensions, and suggests the emphasized weight via .bold() for hierarchy. Mentions checking accessibility sizes and adapting layout.",
|
|
9
|
+
"files": [],
|
|
10
|
+
"expectations": [
|
|
11
|
+
"Flags 10 pt as below the 11 pt iOS minimum text size",
|
|
12
|
+
"Advises against Light and Thin weights in favor of Regular, Medium, Semibold or Bold",
|
|
13
|
+
"Uses Font.custom(_:size:relativeTo:) mapped to text styles so the custom font scales with Dynamic Type",
|
|
14
|
+
"Mentions 17 pt as the iOS default body size",
|
|
15
|
+
"Mentions @ScaledMetric or adaptive layout at accessibility sizes"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": 1,
|
|
20
|
+
"name": "contrast-and-color-roles",
|
|
21
|
+
"prompt": "Product page in SwiftUI: price in Color(hex: \"#9AA0A6\") on white at 15 pt, 'Out of stock' shown only by turning the row red, background Color.white, and a custom gray for dividers. Dark mode looks broken. What do the HIG foundations say and what do we change?",
|
|
22
|
+
"expected_output": "Explains the HIG minimum contrast (4.5:1 for text up to 17 pt, 3:1 for 18 pt and larger or bold), that the light gray price likely fails, and replaces hard-coded colors with semantic ones (.secondary or secondaryLabel only if contrast holds, systemBackground, separator) or asset catalog colors with light, dark and increased-contrast variants. Says out-of-stock must not be conveyed by color alone and adds a label or symbol. Mentions testing with Increase Contrast and both appearances.",
|
|
23
|
+
"files": [],
|
|
24
|
+
"expectations": [
|
|
25
|
+
"States the 4.5:1 minimum for text up to 17 pt (and 3:1 for larger or bold text)",
|
|
26
|
+
"Replaces hard-coded white and gray with semantic system colors such as systemBackground and separator, or asset colors with dark variants",
|
|
27
|
+
"Says color alone must not convey out-of-stock and adds text or a symbol",
|
|
28
|
+
"Mentions an increased-contrast variant or testing with Increase Contrast",
|
|
29
|
+
"Does not recommend hard-coding the documented system color values"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": 2,
|
|
34
|
+
"name": "spacing-and-targets-review",
|
|
35
|
+
"prompt": "Review this SwiftUI row: HStack(spacing: 3) { Image(systemName: \"trash\").font(.system(size: 12)).onTapGesture(perform: delete); Text(title).font(.system(size: 13)).lineLimit(1) }.padding(.horizontal, 7). It sits in a ScrollView edge to edge on iPad too. Give findings in the foundations format.",
|
|
36
|
+
"expected_output": "Reports findings per foundation: the trash target is far below 44x44 pt and should become a Button with a 44 pt hit area (contentShape/frame) or use swipe actions; spacing values 3 and 7 are off any grid and should be omitted or taken from tokens or a 4 pt scale; fixed system sizes should be text styles so Dynamic Type works, and lineLimit(1) truncates at large sizes; on iPad use contentMargins or readable width instead of edge to edge. Uses the [Foundation] issue plus Fix format.",
|
|
37
|
+
"files": [],
|
|
38
|
+
"expectations": [
|
|
39
|
+
"Flags the tap target as below 44 x 44 pt and makes it a Button with a larger hit area",
|
|
40
|
+
"Replaces .system(size:) with text styles for Dynamic Type and questions lineLimit(1)",
|
|
41
|
+
"Flags the arbitrary 3 and 7 point values and suggests default spacing, tokens or a 4 pt scale",
|
|
42
|
+
"Suggests contentMargins or a readable width for iPad instead of edge-to-edge text",
|
|
43
|
+
"Reports each finding with the foundation name and a concrete fix"
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
@@ -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
|
-
-
|
|
41
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
114
|
-
|
|
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
|
|