@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.
- package/CHANGELOG.md +11 -0
- package/docs/facts.json +6 -6
- package/install/catalog-history.json +1 -1
- package/manifest.json +75 -37
- package/package.json +1 -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 +170 -0
- package/pipeline/skills/shared/external/ios-iphone-duo/evals/evals.json +50 -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 +145 -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,218 @@
|
|
|
1
|
+
# iPad layout and windowing
|
|
2
|
+
|
|
3
|
+
Companion to `../SKILL.md` for the `hig-platforms` skill: how one SwiftUI
|
|
4
|
+
layout adapts from a narrow iPad window to a full external display. Hinged
|
|
5
|
+
iPhone hardware (iPhone Duo) has its own skill, `ios-iphone-duo`.
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
1. [Windows on iPadOS 26](#1-windows-on-ipados-26)
|
|
10
|
+
2. [Size classes, not devices](#2-size-classes-not-devices)
|
|
11
|
+
3. [Choosing a layout tool](#3-choosing-a-layout-tool)
|
|
12
|
+
4. [ViewThatFits](#4-viewthatfits)
|
|
13
|
+
5. [AnyLayout](#5-anylayout)
|
|
14
|
+
6. [A custom Layout](#6-a-custom-layout)
|
|
15
|
+
7. [Scenes, multiple windows and the menu bar](#7-scenes-multiple-windows-and-the-menu-bar)
|
|
16
|
+
8. [Test matrix](#8-test-matrix)
|
|
17
|
+
|
|
18
|
+
## 1. Windows on iPadOS 26
|
|
19
|
+
|
|
20
|
+
People choose one of two presentations in Multitasking settings:
|
|
21
|
+
|
|
22
|
+
- **Full screen**: app windows fill the screen; people switch with the app
|
|
23
|
+
switcher.
|
|
24
|
+
- **Windowed**: people resize windows freely, keep several on screen,
|
|
25
|
+
reposition them and bring them to front. The system remembers size and
|
|
26
|
+
placement.
|
|
27
|
+
|
|
28
|
+
What this means for layout:
|
|
29
|
+
|
|
30
|
+
- Support arbitrary sizes. Windows resize fluidly down to a minimum rather
|
|
31
|
+
than snapping between preset sizes, so a layout cannot assume a short list of
|
|
32
|
+
widths. Stage Manager (iPadOS 16 and later on supported iPads) already
|
|
33
|
+
required this.
|
|
34
|
+
- `UIRequiresFullScreen` is deprecated in iPadOS 26. In windowed mode it no
|
|
35
|
+
longer gives a full-screen app; the system keeps a fixed scene size and
|
|
36
|
+
scales the app when people resize. Apple asks iPad apps to support
|
|
37
|
+
multitasking and dynamic resizing instead.
|
|
38
|
+
- Keep leading toolbar items clear of the window controls that appear at the
|
|
39
|
+
leading edge of the toolbar in windowed mode; move such buttons inward.
|
|
40
|
+
- Set a sensible minimum (and, where useful, maximum) size so the layout never
|
|
41
|
+
overlaps: `UISceneSizeRestrictions.minimumSize` in UIKit,
|
|
42
|
+
`.windowResizability(_:)` and `.defaultSize(_:)` on scenes in SwiftUI
|
|
43
|
+
(iOS 17).
|
|
44
|
+
- Use standard split views: `NavigationSplitView` reflows its columns for every
|
|
45
|
+
width with system transitions.
|
|
46
|
+
|
|
47
|
+
## 2. Size classes, not devices
|
|
48
|
+
|
|
49
|
+
| Context | Horizontal | Vertical |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| iPad full screen, either orientation | regular | regular |
|
|
52
|
+
| iPad window narrowed to a phone-like width | compact | regular |
|
|
53
|
+
| iPhone portrait | compact | regular |
|
|
54
|
+
| iPhone landscape (most models) | compact | compact |
|
|
55
|
+
|
|
56
|
+
Read `@Environment(\.horizontalSizeClass)` and `verticalSizeClass`; never
|
|
57
|
+
branch on the device model or on `UIDevice.current.userInterfaceIdiom` for
|
|
58
|
+
layout. A size class says whether there is room for more columns; the
|
|
59
|
+
container's actual size decides the details.
|
|
60
|
+
|
|
61
|
+
```swift
|
|
62
|
+
@Environment(\.horizontalSizeClass) private var widthClass
|
|
63
|
+
|
|
64
|
+
var body: some View {
|
|
65
|
+
if widthClass == .compact {
|
|
66
|
+
JobList(jobs: jobs)
|
|
67
|
+
} else {
|
|
68
|
+
NavigationSplitView {
|
|
69
|
+
JobList(jobs: jobs)
|
|
70
|
+
} detail: {
|
|
71
|
+
JobDetailPlaceholder()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`NavigationSplitView` already collapses to a stack in compact width, so the
|
|
78
|
+
branch above is only needed when the compact design differs.
|
|
79
|
+
|
|
80
|
+
## 3. Choosing a layout tool
|
|
81
|
+
|
|
82
|
+
| Question | Tool (since) |
|
|
83
|
+
|---|---|
|
|
84
|
+
| Does a list-detail hierarchy need columns? | `NavigationSplitView` (iOS 16) |
|
|
85
|
+
| Which of a few fixed arrangements fits the space? | `ViewThatFits` (iOS 16) |
|
|
86
|
+
| Switch between stack axes while keeping state and animating? | `AnyLayout` (iOS 16) |
|
|
87
|
+
| Size a view relative to its container? | `containerRelativeFrame(_:alignment:)` (iOS 17) |
|
|
88
|
+
| A layout no stack or grid expresses? | A type conforming to `Layout` (iOS 16) |
|
|
89
|
+
| Readable width for long text in a wide window? | `.contentMargins` (iOS 17) or a max width |
|
|
90
|
+
|
|
91
|
+
Reach for `GeometryReader` last; it takes all offered space and couples the
|
|
92
|
+
view to raw sizes.
|
|
93
|
+
|
|
94
|
+
## 4. ViewThatFits
|
|
95
|
+
|
|
96
|
+
Picks the first child that fits the proposed size along the given axes.
|
|
97
|
+
|
|
98
|
+
```swift
|
|
99
|
+
ViewThatFits(in: .horizontal) {
|
|
100
|
+
HStack(spacing: 12) {
|
|
101
|
+
JobSummary(job: job)
|
|
102
|
+
Spacer()
|
|
103
|
+
JobActions(job: job)
|
|
104
|
+
}
|
|
105
|
+
VStack(alignment: .leading, spacing: 8) {
|
|
106
|
+
JobSummary(job: job)
|
|
107
|
+
JobActions(job: job)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Each candidate is a separate subtree: state inside one is lost when the other
|
|
113
|
+
wins. Keep state above `ViewThatFits`.
|
|
114
|
+
|
|
115
|
+
## 5. AnyLayout
|
|
116
|
+
|
|
117
|
+
Wraps a layout type so the arrangement can change while view identity, and so
|
|
118
|
+
state, survives, and the change can animate.
|
|
119
|
+
|
|
120
|
+
```swift
|
|
121
|
+
@Environment(\.horizontalSizeClass) private var widthClass
|
|
122
|
+
@Environment(\.dynamicTypeSize) private var typeSize
|
|
123
|
+
|
|
124
|
+
var body: some View {
|
|
125
|
+
let isNarrow = widthClass == .compact || typeSize.isAccessibilitySize
|
|
126
|
+
let layout = isNarrow
|
|
127
|
+
? AnyLayout(VStackLayout(alignment: .leading, spacing: 12))
|
|
128
|
+
: AnyLayout(HStackLayout(alignment: .top, spacing: 20))
|
|
129
|
+
|
|
130
|
+
layout {
|
|
131
|
+
PartsList(parts: job.parts)
|
|
132
|
+
PhotoStrip(photos: job.photos)
|
|
133
|
+
}
|
|
134
|
+
.animation(.smooth, value: isNarrow)
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`HStackLayout`, `VStackLayout`, `ZStackLayout` and `GridLayout` are the
|
|
139
|
+
built-in layout types to swap between.
|
|
140
|
+
|
|
141
|
+
## 6. A custom Layout
|
|
142
|
+
|
|
143
|
+
Conform to `Layout` and implement `sizeThatFits(proposal:subviews:cache:)` and
|
|
144
|
+
`placeSubviews(in:proposal:subviews:cache:)`. A flow layout that wraps chips
|
|
145
|
+
onto new lines is the usual case:
|
|
146
|
+
|
|
147
|
+
```swift
|
|
148
|
+
struct FlowLayout: Layout {
|
|
149
|
+
var spacing: CGFloat = 8
|
|
150
|
+
|
|
151
|
+
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
|
|
152
|
+
let maxWidth = proposal.width ?? .infinity
|
|
153
|
+
var origin = CGPoint.zero
|
|
154
|
+
var lineHeight: CGFloat = 0
|
|
155
|
+
var usedWidth: CGFloat = 0
|
|
156
|
+
for subview in subviews {
|
|
157
|
+
let size = subview.sizeThatFits(.unspecified)
|
|
158
|
+
if origin.x > 0, origin.x + size.width > maxWidth {
|
|
159
|
+
origin.x = 0
|
|
160
|
+
origin.y += lineHeight + spacing
|
|
161
|
+
lineHeight = 0
|
|
162
|
+
}
|
|
163
|
+
origin.x += size.width + spacing
|
|
164
|
+
usedWidth = max(usedWidth, origin.x - spacing)
|
|
165
|
+
lineHeight = max(lineHeight, size.height)
|
|
166
|
+
}
|
|
167
|
+
return CGSize(width: usedWidth, height: origin.y + lineHeight)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
|
|
171
|
+
var origin = bounds.origin
|
|
172
|
+
var lineHeight: CGFloat = 0
|
|
173
|
+
for subview in subviews {
|
|
174
|
+
let size = subview.sizeThatFits(.unspecified)
|
|
175
|
+
if origin.x > bounds.minX, origin.x + size.width > bounds.maxX {
|
|
176
|
+
origin.x = bounds.minX
|
|
177
|
+
origin.y += lineHeight + spacing
|
|
178
|
+
lineHeight = 0
|
|
179
|
+
}
|
|
180
|
+
subview.place(at: origin, proposal: ProposedViewSize(size))
|
|
181
|
+
origin.x += size.width + spacing
|
|
182
|
+
lineHeight = max(lineHeight, size.height)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
A custom layout works with `AnyLayout` too, so it can be swapped in for wide
|
|
189
|
+
windows only.
|
|
190
|
+
|
|
191
|
+
## 7. Scenes, multiple windows and the menu bar
|
|
192
|
+
|
|
193
|
+
- Support multiple windows when people might compare two records
|
|
194
|
+
(`UIApplicationSupportsMultipleScenes`, `WindowGroup(for:)` with a value,
|
|
195
|
+
`openWindow(value:)`). Offer "Open in New Window" in a context menu rather
|
|
196
|
+
than opening windows by default.
|
|
197
|
+
- iPadOS 26 adds a menu bar; populate it with `.commands` on the scene, as on
|
|
198
|
+
the Mac, and give frequent commands keyboard shortcuts.
|
|
199
|
+
- Use the word "window" in user-facing text, never "scene".
|
|
200
|
+
- Do not build custom window frames or controls.
|
|
201
|
+
|
|
202
|
+
## 8. Test matrix
|
|
203
|
+
|
|
204
|
+
1. Full screen, portrait and landscape.
|
|
205
|
+
2. Windowed at the minimum size, a phone-like width and a wide width; resize
|
|
206
|
+
slowly and watch for jumps.
|
|
207
|
+
3. External display.
|
|
208
|
+
4. Largest Dynamic Type size in a narrow window.
|
|
209
|
+
5. Right-to-left: sidebars, swipe edges and back gestures mirror.
|
|
210
|
+
|
|
211
|
+
Sources:
|
|
212
|
+
[HIG: Windows](https://developer.apple.com/design/human-interface-guidelines/windows),
|
|
213
|
+
[HIG: Layout](https://developer.apple.com/design/human-interface-guidelines/layout),
|
|
214
|
+
[Adopting Liquid Glass: windows and modals](https://developer.apple.com/documentation/technologyoverviews/adopting-liquid-glass),
|
|
215
|
+
[UIRequiresFullScreen](https://developer.apple.com/documentation/bundleresources/information-property-list/uirequiresfullscreen),
|
|
216
|
+
[ViewThatFits](https://developer.apple.com/documentation/swiftui/viewthatfits),
|
|
217
|
+
[AnyLayout](https://developer.apple.com/documentation/swiftui/anylayout),
|
|
218
|
+
[Layout](https://developer.apple.com/documentation/swiftui/layout).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ios-accessibility
|
|
3
|
-
description: "VoiceOver, Voice Control, Switch Control and Full Keyboard Access in SwiftUI, UIKit and AppKit
|
|
3
|
+
description: "VoiceOver, Voice Control, Switch Control and Full Keyboard Access in SwiftUI, UIKit and AppKit: labels, traits, grouping, focus restore, Dynamic Type, Large Content Viewer, rotors, custom actions, accessibility checks and Nutrition Labels. Use when implementing, reviewing or auditing accessibility (a11y). Not for keyboard or directional focus mechanics: use hig-inputs."
|
|
4
4
|
metadata:
|
|
5
5
|
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
@@ -23,6 +23,9 @@ References:
|
|
|
23
23
|
label categories and what passes or fails each one.
|
|
24
24
|
- [Media](references/media-accessibility.md): captions, SDH, audio
|
|
25
25
|
descriptions and `AVMediaCharacteristic`.
|
|
26
|
+
- [Large content and appearance](references/large-content-and-appearance.md):
|
|
27
|
+
the Large Content Viewer for controls that cannot grow, and how Liquid
|
|
28
|
+
Glass reacts to Reduce Transparency, Increase Contrast and Reduce Motion.
|
|
26
29
|
|
|
27
30
|
## Ground rules
|
|
28
31
|
|
package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Large Content Viewer, and Liquid Glass with appearance settings
|
|
2
|
+
|
|
3
|
+
Companion to `../SKILL.md` for the `ios-accessibility` skill.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
1. [Large Content Viewer](#1-large-content-viewer)
|
|
8
|
+
2. [SwiftUI](#2-swiftui)
|
|
9
|
+
3. [UIKit](#3-uikit)
|
|
10
|
+
4. [Liquid Glass and appearance settings](#4-liquid-glass-and-appearance-settings)
|
|
11
|
+
5. [Checks](#5-checks)
|
|
12
|
+
|
|
13
|
+
## 1. Large Content Viewer
|
|
14
|
+
|
|
15
|
+
When text is set to one of the large accessibility sizes, some controls cannot
|
|
16
|
+
grow without breaking the screen: tab bar items, toolbar buttons, segmented
|
|
17
|
+
controls, a compact player bar. For those, the Large Content Viewer shows an
|
|
18
|
+
enlarged label and image in the middle of the screen while the person presses
|
|
19
|
+
and holds the control. System tab bars and toolbars do this already.
|
|
20
|
+
|
|
21
|
+
Rules from Apple's documentation:
|
|
22
|
+
|
|
23
|
+
- It is not a replacement for Dynamic Type. Lists, rows and body text must
|
|
24
|
+
grow; use the viewer only where an element must stay small for a real
|
|
25
|
+
design reason.
|
|
26
|
+
- Views whose Dynamic Type size is capped with `dynamicTypeSize(_:)` are the
|
|
27
|
+
typical candidates.
|
|
28
|
+
- If the control also has a long-press action, lengthen that press when the
|
|
29
|
+
viewer is enabled so people can read the enlarged content first.
|
|
30
|
+
|
|
31
|
+
## 2. SwiftUI
|
|
32
|
+
|
|
33
|
+
| API | Since | Use |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `accessibilityShowsLargeContentViewer()` | iOS 15 | Default large content built from the view's label and image |
|
|
36
|
+
| `accessibilityShowsLargeContentViewer(_:)` | iOS 15 | Custom large content, for example a `Label` for an icon-only button |
|
|
37
|
+
| `@Environment(\.accessibilityLargeContentViewerEnabled)` | iOS 15 | Whether the viewer is on, to adjust coexisting gestures |
|
|
38
|
+
|
|
39
|
+
```swift
|
|
40
|
+
struct PlayerBar: View {
|
|
41
|
+
@Environment(\.accessibilityLargeContentViewerEnabled) private var largeContentViewer
|
|
42
|
+
|
|
43
|
+
var body: some View {
|
|
44
|
+
HStack(spacing: 24) {
|
|
45
|
+
Button(action: skipBack) {
|
|
46
|
+
Image(systemName: "gobackward.15")
|
|
47
|
+
}
|
|
48
|
+
.accessibilityLabel("Skip back 15 seconds")
|
|
49
|
+
.accessibilityShowsLargeContentViewer {
|
|
50
|
+
Label("Skip back 15 seconds", systemImage: "gobackward.15")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Button("Play", systemImage: "play.fill", action: play)
|
|
54
|
+
.labelStyle(.iconOnly)
|
|
55
|
+
.accessibilityShowsLargeContentViewer()
|
|
56
|
+
}
|
|
57
|
+
.dynamicTypeSize(...DynamicTypeSize.xxxLarge)
|
|
58
|
+
.onLongPressGesture(minimumDuration: largeContentViewer ? 1.2 : 0.5) {
|
|
59
|
+
showQueue()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The cap keeps the bar's layout fixed; the viewer gives the enlarged reading
|
|
66
|
+
back.
|
|
67
|
+
|
|
68
|
+
## 3. UIKit
|
|
69
|
+
|
|
70
|
+
| API | Since | Use |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `UILargeContentViewerInteraction` | iOS 13 | Adds the press-and-hold gesture to a container view |
|
|
73
|
+
| `UIView.showsLargeContentViewer` | iOS 13 | Opts a view in |
|
|
74
|
+
| `UIView.largeContentTitle`, `largeContentImage` | iOS 13 | What the viewer shows |
|
|
75
|
+
| `UIView.scalesLargeContentImage` | iOS 13 | Scale the image up (useful for template symbols) |
|
|
76
|
+
| `UILargeContentViewerInteraction.isEnabled` | iOS 13 | Whether the viewer is on |
|
|
77
|
+
|
|
78
|
+
Bar items in system bars are covered by the system. For custom controls, set
|
|
79
|
+
the properties on the view and add the interaction to its container:
|
|
80
|
+
|
|
81
|
+
```swift
|
|
82
|
+
final class CompactPlayerBar: UIView {
|
|
83
|
+
private let skipBackButton = UIButton(type: .system)
|
|
84
|
+
|
|
85
|
+
override init(frame: CGRect) {
|
|
86
|
+
super.init(frame: frame)
|
|
87
|
+
skipBackButton.setImage(UIImage(systemName: "gobackward.15"), for: .normal)
|
|
88
|
+
skipBackButton.accessibilityLabel = String(localized: "Skip back 15 seconds")
|
|
89
|
+
skipBackButton.showsLargeContentViewer = true
|
|
90
|
+
skipBackButton.largeContentTitle = String(localized: "Skip back 15 seconds")
|
|
91
|
+
skipBackButton.largeContentImage = UIImage(systemName: "gobackward.15")
|
|
92
|
+
skipBackButton.scalesLargeContentImage = true
|
|
93
|
+
addSubview(skipBackButton)
|
|
94
|
+
addInteraction(UILargeContentViewerInteraction())
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
required init?(coder: NSCoder) {
|
|
98
|
+
fatalError("init(coder:) is not supported")
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 4. Liquid Glass and appearance settings
|
|
104
|
+
|
|
105
|
+
On Apple's 26 releases, people can pick a preferred look for Liquid Glass in
|
|
106
|
+
Settings and can turn on Reduce Transparency, Increase Contrast or Reduce
|
|
107
|
+
Motion. Each can remove or modify glass effects. System bars, sheets and
|
|
108
|
+
controls adapt by themselves; custom glass and custom colors do not always.
|
|
109
|
+
|
|
110
|
+
| Setting | SwiftUI | UIKit | What custom UI must do |
|
|
111
|
+
|---|---|---|---|
|
|
112
|
+
| Reduce Transparency | `accessibilityReduceTransparency` | `UIAccessibility.isReduceTransparencyEnabled` | Text over custom glass or blur stays readable; switch to an opaque fill where it does not |
|
|
113
|
+
| Increase Contrast | `colorSchemeContrast == .increased` | `UIAccessibility.isDarkerSystemColorsEnabled` | Custom colors provide increased-contrast variants; contrast minimums hold |
|
|
114
|
+
| Reduce Motion | `accessibilityReduceMotion` | `UIAccessibility.isReduceMotionEnabled` | Glass morphing and custom transitions become simple fades or instant changes |
|
|
115
|
+
| Bold Text | `legibilityWeight == .bold` | `UIAccessibility.isBoldTextEnabled` | Custom-drawn text follows the heavier weight |
|
|
116
|
+
|
|
117
|
+
Glass-specific checks (clear glass and its dimming layer, tint contrast, no
|
|
118
|
+
opaque fills behind system bars) and the material rules (where glass belongs,
|
|
119
|
+
regular versus clear, fallbacks) are in `swiftui-liquid-glass`.
|
|
120
|
+
|
|
121
|
+
## 5. Checks
|
|
122
|
+
|
|
123
|
+
- Set an accessibility text size, then press and hold every small fixed
|
|
124
|
+
control (tab items, toolbar buttons, player controls): the viewer appears
|
|
125
|
+
with a meaningful title and image.
|
|
126
|
+
- Rows and body text grow instead of relying on the viewer.
|
|
127
|
+
- Turn on Reduce Transparency, Increase Contrast and Reduce Motion one at a
|
|
128
|
+
time and walk every screen with custom glass.
|
|
129
|
+
- The screenshot matrix for a design review lives in `ios-design-review`.
|
|
130
|
+
|
|
131
|
+
Sources:
|
|
132
|
+
[accessibilityShowsLargeContentViewer()](https://developer.apple.com/documentation/swiftui/view/accessibilityshowslargecontentviewer()),
|
|
133
|
+
[UILargeContentViewerInteraction](https://developer.apple.com/documentation/uikit/uilargecontentviewerinteraction),
|
|
134
|
+
[HIG: Materials](https://developer.apple.com/design/human-interface-guidelines/materials),
|
|
135
|
+
[Adopting Liquid Glass](https://developer.apple.com/documentation/technologyoverviews/adopting-liquid-glass).
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ios-design-review
|
|
3
|
+
description: "Design critique and screenshot-based visual QA of iOS screens against the HIG, checked across light and dark, Dynamic Type, RTL, contrast and transparency settings with simulator tools. Use when asking whether a screen looks right, wanting a design pass on screenshots or a build, or visual QA before release, not a Swift code review."
|
|
4
|
+
metadata:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
|
+
date_added: "2026-10-08"
|
|
7
|
+
not-for: swiftui-pro, ios-accessibility
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# iOS design review
|
|
11
|
+
|
|
12
|
+
A design review looks at what people see and touch: the rendered screen, in
|
|
13
|
+
every appearance it can take. It answers "does this screen meet the platform's
|
|
14
|
+
design bar, and where does it fall short", with evidence a designer or
|
|
15
|
+
developer can act on. It is not a code review; reading Swift for API misuse is
|
|
16
|
+
`swiftui-pro`'s job, and building accessibility support is `ios-accessibility`'s.
|
|
17
|
+
|
|
18
|
+
The reason to keep the two apart: code that looks correct can still render
|
|
19
|
+
badly (a fixed frame that clips at the largest text size, a tint that fails
|
|
20
|
+
contrast in dark mode), and odd-looking code can render well. Only the screen
|
|
21
|
+
settles a design question.
|
|
22
|
+
|
|
23
|
+
## Where this sits
|
|
24
|
+
|
|
25
|
+
| Need | Go to |
|
|
26
|
+
|---|---|
|
|
27
|
+
| Critique a screen against HIG, any appearance | this skill |
|
|
28
|
+
| Pixel and spacing conformance to a Figma frame, every state, mock mode | `/multi-agent:design-check` |
|
|
29
|
+
| One-axis sweep of a whole app | `/multi-agent:test-dark-mode`, `/multi-agent:test-dynamic-type`, `/multi-agent:test-accessibility` |
|
|
30
|
+
| Store screenshot set | `/multi-agent:test-screenshots` |
|
|
31
|
+
| Review the SwiftUI code | `swiftui-pro` |
|
|
32
|
+
| Implement VoiceOver, traits, focus | `ios-accessibility` |
|
|
33
|
+
| Implement or fix Liquid Glass | `swiftui-liquid-glass` |
|
|
34
|
+
| Icons, symbols, haptics, foldable layout | `hig-app-icons`, `ios-sf-symbols`, `ios-haptics`, `ios-iphone-duo` |
|
|
35
|
+
|
|
36
|
+
Use the commands for breadth (every screen, one axis) and this skill for depth
|
|
37
|
+
(one flow, every axis, judged against the guidelines). When a command already
|
|
38
|
+
produced captures, review those instead of recapturing.
|
|
39
|
+
|
|
40
|
+
## Inputs and honesty
|
|
41
|
+
|
|
42
|
+
Accept whatever exists: screenshots, a running simulator, a design render on
|
|
43
|
+
disk, or the source. Say up front which you have, because it bounds what can be
|
|
44
|
+
proven:
|
|
45
|
+
|
|
46
|
+
- Screenshots only: layout, hierarchy, copy and visible contrast can be judged;
|
|
47
|
+
touch target sizes, accessibility labels and behaviour cannot.
|
|
48
|
+
- A running simulator: add the accessibility tree (`ios_get_ui_tree`) and
|
|
49
|
+
element geometry (`design_ui_geometry`) to measure targets and spacing.
|
|
50
|
+
- Source as well: findings can be pinned to `file:line`.
|
|
51
|
+
- A design render: `design_visual_compare` measures insets, gaps and colour
|
|
52
|
+
against it. Without one, judge against HIG alone and say so.
|
|
53
|
+
|
|
54
|
+
## The review
|
|
55
|
+
|
|
56
|
+
Walk the layers in `references/checklist.md`, in order. Each layer has the
|
|
57
|
+
questions to ask and the HIG page that backs them. In short:
|
|
58
|
+
|
|
59
|
+
1. **Layout**: content inside safe areas, backgrounds may extend past them;
|
|
60
|
+
consistent margins; alignment; nothing tappable under bars; adapts to width
|
|
61
|
+
instead of assuming a device size.
|
|
62
|
+
2. **Typography**: text styles rather than fixed sizes, iOS default 17 pt and
|
|
63
|
+
minimum 11 pt, hierarchy through weight and size, no truncation that hides
|
|
64
|
+
meaning at large sizes.
|
|
65
|
+
3. **Colour and contrast**: semantic colours, at least 4.5:1 for text up to
|
|
66
|
+
17 pt and 3:1 for 18 pt or bold, meaning never carried by colour alone.
|
|
67
|
+
4. **Materials and Liquid Glass**: glass on the navigation and control layer,
|
|
68
|
+
standard materials in the content layer, clear glass only over rich media
|
|
69
|
+
with a dimming layer when the media is bright.
|
|
70
|
+
5. **Iconography**: SF Symbols weight and scale matched to adjacent text,
|
|
71
|
+
consistent variants, labels on icon-only controls.
|
|
72
|
+
6. **Navigation and bars**: system bars and tab bars, standard placements,
|
|
73
|
+
back and close where people expect them.
|
|
74
|
+
7. **Controls and touch**: 44x44 pt default target, 28x28 pt minimum, spacing
|
|
75
|
+
between adjacent targets, standard controls where they exist.
|
|
76
|
+
8. **Feedback and motion**: every action acknowledged, motion that explains
|
|
77
|
+
rather than decorates, a reduced alternative under Reduce Motion.
|
|
78
|
+
9. **States**: empty, loading, error, offline, permission denied, long content.
|
|
79
|
+
10. **Content and copy**: short, specific, consistent terms, localized.
|
|
80
|
+
|
|
81
|
+
## The appearance matrix
|
|
82
|
+
|
|
83
|
+
A screen is reviewed in each appearance it can take, because each one breaks
|
|
84
|
+
different things. `references/appearance-matrix.md` has, for every row, how to
|
|
85
|
+
set it (toolkit tool or manual setting) and what typically goes wrong.
|
|
86
|
+
|
|
87
|
+
| Appearance | Set with | Typical failures |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| Light, dark | `ios_set_appearance` | hard-coded colours, invisible separators, images without a dark variant |
|
|
90
|
+
| Dynamic Type, default to AX5 | `ios_set_content_size` | clipping, truncation, overlapping, fixed-height rows, layouts that never switch to vertical |
|
|
91
|
+
| Right-to-left | `ios_set_locale` with `ar` or `he` (relaunch) | unflipped back chevrons and progress, mirrored artwork, left-aligned text |
|
|
92
|
+
| Increase Contrast | `ios_set_increase_contrast` | custom colours with no high-contrast variant |
|
|
93
|
+
| Reduce Transparency | Settings app, set by hand | glass or blur that does not fall back to an opaque surface |
|
|
94
|
+
| Reduce Motion | Settings app, set by hand | parallax, zooms and repeating symbol effects still playing |
|
|
95
|
+
| Liquid Glass look (Clear or Tinted) | Settings > Display & Brightness, by hand | custom glass that ignores the setting, legibility on Clear |
|
|
96
|
+
|
|
97
|
+
Not every screen needs every cell. Pick the cells the screen can break (a
|
|
98
|
+
text-heavy form needs every Dynamic Type step; a media player needs Reduce
|
|
99
|
+
Motion and Clear glass), list the ones you skipped and why, and record which
|
|
100
|
+
were set by hand.
|
|
101
|
+
|
|
102
|
+
## Procedure on a simulator
|
|
103
|
+
|
|
104
|
+
1. Clean the status bar: `ios_status_bar` with preset `clean`, so captures
|
|
105
|
+
compare across runs.
|
|
106
|
+
2. Reach the screen with `ui_locate` and `ios_tap_element` rather than
|
|
107
|
+
coordinates. A locator that resolves to exactly one element survives layout
|
|
108
|
+
changes; a coordinate does not.
|
|
109
|
+
3. Baseline capture: `ios_screenshot`, `ios_get_ui_tree`, `design_ui_geometry`.
|
|
110
|
+
4. For each chosen matrix cell: set it, wait for the UI to settle, capture
|
|
111
|
+
again. Batch the sweep with `agent_run_steps` and add `expect_text` or
|
|
112
|
+
`expect_element` steps so a navigation slip fails the batch instead of
|
|
113
|
+
producing captures of the wrong screen.
|
|
114
|
+
5. Measure: target sizes and gaps from geometry, contrast from the capture,
|
|
115
|
+
insets and spacing against the design with `design_visual_compare` when a
|
|
116
|
+
render exists.
|
|
117
|
+
6. Reset what you changed (appearance, content size, contrast, locale) so the
|
|
118
|
+
next run starts clean.
|
|
119
|
+
|
|
120
|
+
The full step list with arguments is in `references/appearance-matrix.md`.
|
|
121
|
+
|
|
122
|
+
## Reporting
|
|
123
|
+
|
|
124
|
+
One finding per problem, each with:
|
|
125
|
+
|
|
126
|
+
- **Layer** from the checklist and **severity**: blocking (unusable, illegible,
|
|
127
|
+
inaccessible, or a guideline the store enforces), important (clearly below
|
|
128
|
+
the HIG bar), suggestion (polish).
|
|
129
|
+
- **Where**: `file:line` when the source is known, otherwise screen, element
|
|
130
|
+
and the matrix cell it appears in.
|
|
131
|
+
- **Evidence**: capture path, UI tree node, measured value against the
|
|
132
|
+
threshold (for example "close button 32x32 pt, minimum 44x44 pt").
|
|
133
|
+
- **Why it matters** in one sentence, and the **fix direction**.
|
|
134
|
+
|
|
135
|
+
End with a **Not proven** section: what could not be checked and why (no
|
|
136
|
+
device, setting not available, screenshot-only input, behaviour that needs a
|
|
137
|
+
real device such as haptics). A short honest list is worth more than a long
|
|
138
|
+
confident one. `references/checklist.md` has a report template.
|
|
139
|
+
|
|
140
|
+
## References
|
|
141
|
+
|
|
142
|
+
- `references/checklist.md`: the layer-by-layer checklist, thresholds, report template.
|
|
143
|
+
- `references/appearance-matrix.md`: setting each appearance, toolkit procedure, failure catalogue.
|
|
144
|
+
- HIG: https://developer.apple.com/design/human-interface-guidelines/
|
|
145
|
+
- Accessibility thresholds: https://developer.apple.com/design/human-interface-guidelines/accessibility
|
|
146
|
+
- Materials: https://developer.apple.com/design/human-interface-guidelines/materials
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "ios-design-review",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 0,
|
|
6
|
+
"name": "settings-screen-matrix-review",
|
|
7
|
+
"prompt": "Our redesigned Settings screen ships next week. I have the app running on an iPhone 17 simulator with the multi-agent-toolkit MCP connected. Give it a proper design pass, not a code review: tell me what to check, how you would capture it, and what the report should look like.",
|
|
8
|
+
"expected_output": "Treats the task as a visual design review rather than code review. Chooses relevant appearance-matrix cells (light and dark, Dynamic Type including an accessibility size such as AX5, right-to-left, Increase Contrast, Reduce Transparency, Reduce Motion, Liquid Glass look) and says which can be set with toolkit tools (ios_set_appearance, ios_set_content_size, ios_set_increase_contrast, ios_set_locale) and which must be set by hand in Settings. Navigates with ui_locate and ios_tap_element, captures with ios_screenshot, ios_get_ui_tree and design_ui_geometry, and guards a batch with expect_element or expect_text. Applies HIG thresholds (44x44 pt targets, 4.5:1 text contrast, 17 pt default text). Reports findings with layer, severity, location, evidence and fix, and ends with a Not proven section.",
|
|
9
|
+
"files": [],
|
|
10
|
+
"expectations": [
|
|
11
|
+
"Frames the work as a rendered-screen design review, not a Swift code review",
|
|
12
|
+
"Covers dark mode and at least one accessibility Dynamic Type size",
|
|
13
|
+
"Names toolkit tools for setting appearances (ios_set_appearance, ios_set_content_size or ios_set_increase_contrast)",
|
|
14
|
+
"States that Reduce Transparency, Reduce Motion or the Liquid Glass look have no toolkit switch and are set by hand",
|
|
15
|
+
"Uses at least one concrete HIG threshold such as 44x44 pt or 4.5:1",
|
|
16
|
+
"Report format includes evidence per finding and a section for what could not be proven"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": 1,
|
|
21
|
+
"name": "screenshot-only-critique",
|
|
22
|
+
"prompt": "Here are three screenshots of our checkout flow (light mode only, default text size). The designer says it is ready. Can you critique it against Apple's guidelines and tell me if it is shippable?",
|
|
23
|
+
"expected_output": "Reviews what screenshots can show (layout, hierarchy, contrast, copy, glass use, symbol consistency) and explicitly states the limits: touch target hit areas, accessibility labels, dark mode, large text, right-to-left and behaviour cannot be judged from three light-mode captures. Does not declare it shippable; lists the missing matrix cells as Not proven and recommends capturing them or running the test-dark-mode and test-dynamic-type commands.",
|
|
24
|
+
"files": [],
|
|
25
|
+
"expectations": [
|
|
26
|
+
"States what cannot be proven from light-mode, default-size screenshots",
|
|
27
|
+
"Does not declare the flow shippable without the missing appearances",
|
|
28
|
+
"Mentions dark mode and large Dynamic Type as unchecked",
|
|
29
|
+
"Points to the dark-mode or dynamic-type test commands, or to capturing those cells",
|
|
30
|
+
"Keeps findings tied to visible evidence rather than speculation about the code"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": 2,
|
|
35
|
+
"name": "glass-and-contrast-judgement",
|
|
36
|
+
"prompt": "Design review question: our photo viewer puts a toolbar with clear Liquid Glass over full-screen photos, and the product cards on the home feed also use glassEffect as their background. Text on the toolbar is sometimes hard to read on bright photos. What does Apple's guidance say, and how should the review report it?",
|
|
37
|
+
"expected_output": "Says Liquid Glass belongs to the navigation and control layer and that content-layer cards should use standard materials, so the card backgrounds are a finding. Accepts clear glass over rich media for the photo toolbar but recommends a dark dimming layer of about 35% when the photo is bright. Suggests checking with the Liquid Glass look set to Clear and Tinted and with Reduce Transparency, and routes the implementation fix to swiftui-liquid-glass. Reports with layer, severity, evidence and fix direction.",
|
|
38
|
+
"files": [],
|
|
39
|
+
"expectations": [
|
|
40
|
+
"Flags glass on content cards as a content-layer misuse",
|
|
41
|
+
"Recommends standard materials for the content layer",
|
|
42
|
+
"Recommends a dimming layer of about 35% behind clear glass over bright media",
|
|
43
|
+
"Mentions checking under Reduce Transparency or the Clear and Tinted look setting",
|
|
44
|
+
"Routes implementation to swiftui-liquid-glass rather than writing the fix as the review"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "query": "Can you do a design review of these screenshots of our onboarding?", "should_trigger": true },
|
|
3
|
+
{ "query": "Check whether the profile screen looks right in dark mode and at the largest text size on the simulator", "should_trigger": true },
|
|
4
|
+
{ "query": "Visual QA pass on the checkout flow before release, against the HIG", "should_trigger": true },
|
|
5
|
+
{ "query": "Is this settings screen up to Apple's design bar? Tell me what looks off", "should_trigger": true },
|
|
6
|
+
{ "query": "Critique the spacing, typography and contrast of this iOS screen", "should_trigger": true },
|
|
7
|
+
{ "query": "Run through the appearance matrix for the home screen: RTL, Increase Contrast, Reduce Transparency", "should_trigger": true },
|
|
8
|
+
{ "query": "Does our use of Liquid Glass on the feed look right, design-wise?", "should_trigger": true },
|
|
9
|
+
{ "query": "Take screenshots of the order screen in light and dark and tell me what fails the guidelines", "should_trigger": true },
|
|
10
|
+
{ "query": "Our designer wants a second opinion on the new tab bar layout, here is a capture", "should_trigger": true },
|
|
11
|
+
{ "query": "Review the rendered login screen for touch target sizes and text contrast", "should_trigger": true },
|
|
12
|
+
{ "query": "Review this SwiftUI view's code for deprecated APIs and performance issues", "should_trigger": false },
|
|
13
|
+
{ "query": "Add VoiceOver labels and accessibility traits to our custom slider", "should_trigger": false },
|
|
14
|
+
{ "query": "Compare every state of the payment module against the Figma frames in mock mode and export the report", "should_trigger": false },
|
|
15
|
+
{ "query": "Generate the App Store screenshot set in German", "should_trigger": false },
|
|
16
|
+
{ "query": "Implement glassEffect on our floating action button", "should_trigger": false },
|
|
17
|
+
{ "query": "Which SF Symbol rendering mode should I use for a battery indicator?", "should_trigger": false },
|
|
18
|
+
{ "query": "Design our app icon's dark and tinted variants in Icon Composer", "should_trigger": false },
|
|
19
|
+
{ "query": "Review my pull request diff for bugs", "should_trigger": false },
|
|
20
|
+
{ "query": "Walk every screen of the app at accessibility-extra-large and list truncation bugs", "should_trigger": false },
|
|
21
|
+
{ "query": "Make our app ready for the foldable iPhone's hinge and reserved regions", "should_trigger": false }
|
|
22
|
+
]
|