Ios design
iOS UX design guidance for building premium SwiftUI interfaces. Triggers on: iOS UI/UX, layout, components, screen design, Liquid Glass, SwiftUI design, app design review, UI audit, view hierarchy, iOS design patterns.From its SKILL.md
npx -y skills add skullninja/skills --skill ios-designAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
11.9 KB, ~2.8k tokens by cl100k_base, as published. Nobody here has run it
iOS UX Design — SwiftUI + Liquid Glass
Opinionated design guidance for building premium iOS apps with SwiftUI and iOS 26 Liquid Glass.
1. Design Thinking
Before writing any code, answer these questions:
- Purpose: What is this screen's single job?
- Audience: Who uses this and what do they expect?
- Tone: Playful, professional, minimal, editorial?
- Constraints: Offline support? Accessibility requirements? iPad?
Commit to a clear aesthetic direction. The bottleneck is the experience, not the look. A screen that does one thing well beats a screen that does five things adequately.
2. Core Principles
These are non-negotiable:
- Simplicity is architecture. Every element must justify its existence. If you can't explain why something is on screen, remove it.
- One primary action per screen. Visual hierarchy drives everything. The user should never wonder "what do I do here?"
- Whitespace is a feature. Generous padding and spacing create calm, readable interfaces. Cramped layouts signal amateur work.
- Precision over vagueness. Specify exact padding values, exact font weights, exact colors. "Make it look nice" is not a design decision.
- The experience matters more than the decoration. Motion, feedback, loading states, and error handling ARE the design. A beautiful screen that feels broken is broken.
3. Workflow Decision Tree
Choose your track:
Track A: Review Existing UI
- Gather input: Ask for a screenshot if not provided. Locate the SwiftUI source files for the screen (ask user or search for
*View.swiftfiles). - Read the screenshot to identify visual issues (hierarchy, spacing, color, alignment).
- Read the source files to understand current implementation.
- Audit against the 12 dimensions (Section 8), cross-referencing visual findings with code.
- Produce phased improvement plan (Section 9) with fixes tied to specific files and lines.
- Do NOT change code until the user approves the plan
Conversational alternative: Use /design-review for a guided dialogue about information architecture, content priority, and CTAs before the technical audit runs. Best when you want to discuss and align on design direction first.
Track B: Improve Existing UI
- Identify specific targets (user-provided or from audit)
- Refactor with glass materials, improved layout, proper hierarchy
- Apply one change at a time — verify each before proceeding
Track C: Build New UI
- Run Design Thinking (Section 1)
- Define state ownership and data flow
- Scaffold view hierarchy with placeholder subviews
- Implement subviews bottom-up
- Add loading/empty/error states
- Validate accessibility and Dark Mode
3a. Input Gathering for Existing UI
When auditing or improving an existing screen, gather both visual and code context:
Screenshot (Visual Context)
- Read the screenshot image file to see the current UI
- Identify: visual hierarchy, spacing issues, color problems, alignment, missing states
- Note what looks wrong before reading any code
Source Files (Code Context)
- Ask the user which files implement this screen, or search:
- Glob for
**/*View.swift,**/*Screen.swift,**/*Page.swift - Grep for view names visible in the screenshot
- Glob for
- Read all relevant view files + any shared components they reference
- Map visual issues to specific modifiers, layouts, and view structures
Cross-Reference
- For each visual issue found in the screenshot, locate the responsible code
- For each code smell found in the source, verify it's visible in the screenshot
- This produces the strongest audit: visual problem → exact code fix
4. Liquid Glass API Reference
Basic Glass Effect
// Simple glass surface
.glassEffect()
// Configured glass effect
.glassEffect(
.regular
.tint(.blue)
.interactive(),
in: .rect(cornerRadius: 16)
)
Glass Effect Container
// Groups glass elements so they merge visually when overlapping
GlassEffectContainer(spacing: 8) {
// child views with .glassEffect()
}
Glass Unions (Merging Surfaces)
// Multiple elements that share one glass surface
@Namespace var glassNS
view1.glassEffectUnion(id: "toolbar", namespace: glassNS)
view2.glassEffectUnion(id: "toolbar", namespace: glassNS)
Glass Morphing (Animated Transitions)
@Namespace var morphNS
// Source
icon.glassEffectID(item.id, namespace: morphNS)
// Destination (same id = morph animation)
expandedView.glassEffectID(item.id, namespace: morphNS)
Glass Button Styles
Button("Action") { }
.buttonStyle(.glass) // Subtle glass button
Button("Primary") { }
.buttonStyle(.glassProminent) // Emphasized glass button
Critical Rules
- Apply
.glassEffect()AFTER layout and appearance modifiers (padding, foregroundStyle, etc.) - Use
#available(iOS 26, *)checks with.ultraThinMaterialfallback for older OS - Glass tinting: use
.tint()parameter, not.background()behind glass - Don't stack glass on glass — it creates visual mud
5. Component → SwiftUI Mapping
| Component | SwiftUI Implementation |
|---|---|
| Card | VStack + .glassEffect(in: .rect(cornerRadius: 16)) |
| Button (primary) | Button + .buttonStyle(.glassProminent) |
| Button (secondary) | Button + .buttonStyle(.glass) |
| Text field | TextField + .textFieldStyle(.roundedBorder) |
| List | List + .listStyle(.insetGrouped) |
| Empty state | ContentUnavailableView |
| Loading skeleton | .redacted(reason: .placeholder) |
| Avatar | AsyncImage + .clipShape(.circle) |
| Badge | Text + .font(.caption2).padding(.horizontal, 6).background(.red, in: .capsule) |
| Toggle row | Toggle inside LabeledContent |
| Segmented control | Picker + .pickerStyle(.segmented) |
| Menu | Menu with Button actions |
| Progress | ProgressView or Gauge |
| Alert | .alert(title:isPresented:actions:message:) |
| Confirmation | .confirmationDialog |
| Toolbar | .toolbar { ToolbarItem(.primaryAction) { } } |
| Search | .searchable(text:placement:) |
| Pull to refresh | .refreshable { } |
6. New View Workflow
When building a new view, follow this order:
Step 1: State Ownership
Determine what data this view needs and who owns it.
- View-local:
@State - Passed in:
letproperties orBinding - Shared:
@Environmentor@Observablemodel
Step 2: Environment Dependencies
Identify needed environment values:
@Environment(\.dismiss) private var dismiss
@Environment(\.horizontalSizeClass) private var sizeClass
@Environment(\.dynamicTypeSize) private var typeSize
Step 3: View Hierarchy
Break the body into composed subviews. Each subview should be:
- Under ~40 lines
- Named for what it shows, not how it looks
- A computed property or extracted
Viewstruct
Step 4: Async Data
.task { await loadData() }
.task(id: filterValue) { await reload() }
Never use Task { } in onAppear. Use .task — it handles cancellation.
Step 5: Accessibility
- Every interactive element needs a label
- Decorative images:
.accessibilityHidden(true) - Group related content:
.accessibilityElement(children: .combine) - Test with Dynamic Type at largest size
Step 6: Validate
- Dark Mode: does it look correct?
- Glass materials: does text remain readable?
- Empty state: what shows with no data?
- Error state: what shows on failure?
- Loading state: skeleton or spinner?
7. Anti-Slop: iOS AI Tells to Avoid
These patterns scream "AI-generated." Avoid them:
- Generic gray cards — Use
.glassEffect()instead ofRoundedRectangle+.fill(.gray.opacity(0.1)) - Default unstyled List rows — Customize with proper padding, typography hierarchy, and SF Symbol alignment
- Stock SF Symbols at default size — Always specify
.font()and.fontWeight()on symbols. Match the visual weight of surrounding text. - Missing states — Every data-driven view needs empty, loading, and error states.
ContentUnavailableViewexists — use it. - Ignoring safe areas — Don't blindly
.ignoresSafeArea(). Understand which edges you're extending into and why. - Hardcoded colors — Use
Color(.label),Color(.systemBackground),.foregroundStyle(.secondary). NeverColor.grayorColor(hex:)for UI chrome. - Monolithic view bodies — If
bodyexceeds 60 lines, extract subviews. Deeply nested closures are unreadable. - Fake custom tab bars — Use the system
TabView. iOS 26 gives it glass styling automatically. - ZStack for overlays — Use
.overlay()and.background()modifiers instead of manual ZStack layering. - Magic number padding — Use consistent spacing: 4, 8, 12, 16, 20, 24, 32. Define a spacing scale and stick to it.
8. Audit Dimensions
When reviewing an existing screen, evaluate across these 12 dimensions:
- Visual Hierarchy — Is the primary action obvious? Can you tell what's most important in < 2 seconds?
- Spacing & Rhythm — Consistent padding? Related items grouped? Adequate breathing room?
- Typography — Clear size/weight hierarchy? No more than 2–3 text styles per screen?
- Color — Semantic colors used? Accent color applied purposefully? No random grays?
- Alignment — Leading-aligned text? Consistent insets? Grid-aware layout?
- Components — Using system components where they exist? Not reinventing wheels?
- Iconography — SF Symbols with correct weight/size? Consistent symbol rendering mode?
- Motion & Animation — Meaningful transitions?
.animation(.default, value:)not blanket.animation()? - Empty / Loading / Error — All three states handled? Appropriate UI for each?
- Dark Mode + Glass — Tested in both modes? Glass surfaces readable in both?
- Adaptive Layout — Size classes respected? iPad layout considered? Dynamic Type tested?
- Accessibility — VoiceOver labels present? Touch targets ≥ 44pt? Sufficient contrast?
9. Audit Output Format
Structure findings as a phased plan:
Phase 1: Critical (Functional / Usability Issues)
[ViewName]: Missing loading state → Add .redacted(reason: .placeholder) skeleton
→ Apply to List content, show ProgressView in overlay during initial load
[ViewName]: No error handling → Add .alert for failures
→ .task { do { try await load() } catch { self.error = error } }
→ .alert("Error", isPresented: $showError) { } message: { Text(error.localizedDescription) }
Phase 2: Refinement (Visual Quality)
[ViewName]: Cards use flat gray background → Convert to .glassEffect()
→ Remove .background(Color.gray.opacity(0.1))
→ Add .glassEffect(in: .rect(cornerRadius: 12))
→ Wrap parent in GlassEffectContainer(spacing: 8)
[ViewName]: Typography lacks hierarchy → Apply .font(.headline) to titles, .font(.subheadline) + .foregroundStyle(.secondary) to subtitles
Phase 3: Polish (Delight)
[ViewName]: No transition animations → Add .animation(.smooth, value: items)
[ViewName]: Pull-to-refresh missing → Add .refreshable { await reload() }
Each item must specify:
- The exact view or component
- The specific issue
- The concrete fix with SwiftUI code
10. Scope Discipline
This skill covers visual design only:
- Layout, styling, materials, typography, color, spacing, hierarchy
- Component selection and SwiftUI API usage
- Accessibility as it relates to visual presentation
It does NOT cover:
- Data modeling, networking, persistence
- Business logic, state machines
- Architecture patterns (MVVM, TCA, etc.)
If a design improvement requires functional changes (new data model, different API call), flag it clearly: "This visual change requires a functional change: [description]. Approve before proceeding."
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.