Agent skill

iOS Design Guidelines

by ehmo in ehmo/platform-design-skills

Apple Human Interface Guidelines for iPhone. An agent skill from ehmo/platform-design-skills.

MITAuto-check passedMobile

Install iOS Design Guidelines

skills CLI
$ npx skills add ehmo/platform-design-skills --skill ios-design-guidelines -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install ehmo/platform-design-skills ios-design-guidelines --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/ehmo/platform-design-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/ios .claude/skills/ios-design-guidelines && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
ios-design-guidelines
GitHub stars
606
Token cost
~9k tokens
SKILL.md length
2,876 words
Files
4
Skills in repo
8
Repo updated
First seen
Licence
MIT

At a glance

Apple Human Interface Guidelines for iPhone. An agent skill from ehmo/platform-design-skills.

  • Works in 10 steps: Layout & Safe Areas → Navigation → Typography & Dynamic Type → …
  • Refactoring SwiftUI/UIKit interfaces for iOS
  • SKILL.md covers 1. Layout & Safe Areas, 2. Navigation, 3. Typography & Dynamic Type and 4. Color & Dark Mode
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

iOS Design Guidelines is an agent skill from ehmo/platform-design-skills. Apple Human Interface Guidelines for iPhone. Use when building, reviewing, or refactoring SwiftUI/UIKit interfaces for iOS. Triggers on tasks involving iPhone UI, iOS components, accessibility, Dynamic Type, Dark Mode, or HIG compliance.

Its SKILL.md is about 9k tokens, which your agent loads only when the skill is triggered. The skill folder holds 4 other files (for example `AGENTS.md`, `metadata.json` and `rules/_sections.md`).

It sits in Mobile, covering iOS development. It works with iOS and SwiftUI. The repository describes itself as: Platform design skill pack: 300+ rules for Apple HIG, Material Design 3, and WCAG 2.2 across iOS, iPadOS, macOS, watchOS, visionOS, tvOS, Android, and Web. The licence is MIT.

When your agent uses it

  • Refactoring SwiftUI/UIKit interfaces for iOS
  • Tasks involving iPhone UI

Example prompts

  • “/ios-design-guidelines”

Workflow steps

10 steps, taken from the step headings in SKILL.md.

  1. Layout & Safe Areas
  2. Navigation
  3. Typography & Dynamic Type
  4. Color & Dark Mode
  5. Accessibility
  6. Gestures & Input
  7. Components
  8. Patterns
  9. Privacy & Permissions
  10. System Integration

What it can do on your machine

Read from SKILL.md and the folder at commit dc2be82. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are swift).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

iOS Design Guidelines loads about 9k tokens when it runs. Until then it costs about 65 tokens; SKILL.md has 2,876 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~65
When it runs · the whole SKILL.md, loaded when a task matches
~9k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from ehmo/platform-design-skills at commit dc2be82, republished under its MIT licence (© ehmo). 2,876 words, ~8,991 tokens.

Download SKILL.mdSave it as .claude/skills/ios-design-guidelines/SKILL.md (or your agent's skills folder). This skill also uses 3 other files; get the full folder from GitHub.
name
ios-design-guidelines
description
Apple Human Interface Guidelines for iPhone. Use when building, reviewing, or refactoring SwiftUI/UIKit interfaces for iOS. Triggers on tasks involving iPhone UI, iOS components, accessibility, Dynamic Type, Dark Mode, or HIG compliance.
license
MIT
metadata.author
platform-design-skills
metadata.version
1.0.0

iOS Design Guidelines for iPhone

Comprehensive rules derived from Apple's Human Interface Guidelines. Apply these when building, reviewing, or refactoring any iPhone app interface.


1. Layout & Safe Areas

Impact: CRITICAL

Rule 1.1: Minimum 44pt Touch Targets

All interactive elements must have a minimum tap target of 44x44 points. This includes buttons, links, toggles, and custom controls.

Correct:

swift
Button("Save") { save() }
    .frame(minWidth: 44, minHeight: 44)

Incorrect:

swift
// 20pt icon with no padding — too small to tap reliably
Button(action: save) {
    Image(systemName: "checkmark")
        .font(.system(size: 20))
}
// Missing .frame(minWidth: 44, minHeight: 44)
Rule 1.2: Respect Safe Areas

Never place interactive or essential content under the status bar, Dynamic Island, or home indicator. Use SwiftUI's automatic safe area handling or UIKit's safeAreaLayoutGuide.

Correct:

swift
struct ContentView: View {
    var body: some View {
        VStack {
            Text("Content")
        }
        // SwiftUI respects safe areas by default
    }
}

Incorrect:

swift
struct ContentView: View {
    var body: some View {
        VStack {
            Text("Content")
        }
        .ignoresSafeArea() // Content will be clipped under notch/Dynamic Island
    }
}

Use .ignoresSafeArea() only for background fills, images, or decorative elements — never for text or interactive controls.

Rule 1.3: Primary Actions in the Thumb Zone

Place primary actions at the bottom of the screen where the user's thumb naturally rests. Secondary actions and navigation belong at the top.

Correct:

swift
VStack {
    ScrollView { /* content */ }
    Button("Continue") { next() }
        .buttonStyle(.borderedProminent)
        .padding()
}

Incorrect:

swift
VStack {
    Button("Continue") { next() } // Top of screen — hard to reach one-handed
        .buttonStyle(.borderedProminent)
        .padding()
    ScrollView { /* content */ }
}
Rule 1.4: Support All iPhone Screen Sizes

Design for iPhone SE (375pt wide) through iPhone Pro Max (430pt wide). Use flexible layouts, avoid hardcoded widths.

Correct:

swift
HStack(spacing: 12) {
    ForEach(items) { item in
        CardView(item: item)
            .frame(maxWidth: .infinity) // Adapts to screen width
    }
}

Incorrect:

swift
HStack(spacing: 12) {
    ForEach(items) { item in
        CardView(item: item)
            .frame(width: 180) // Breaks on SE, wastes space on Pro Max
    }
}
Rule 1.5: 8pt Grid Alignment

Align spacing, padding, and element sizes to multiples of 8 points (8, 16, 24, 32, 40, 48). Use 4pt for fine adjustments.

Rule 1.6: Landscape Support

Support landscape orientation unless the app is task-specific (e.g., camera). Use ViewThatFits or GeometryReader for adaptive layouts.


2. Navigation

Impact: CRITICAL

Rule 2.1: Tab Bar for Top-Level Sections

Use a tab bar at the bottom of the screen for 3 to 5 top-level sections. Each tab should represent a distinct category of content or functionality.

Correct:

swift
TabView {
    HomeView()
        .tabItem {
            Label("Home", systemImage: "house")
        }
    SearchView()
        .tabItem {
            Label("Search", systemImage: "magnifyingglass")
        }
    ProfileView()
        .tabItem {
            Label("Profile", systemImage: "person")
        }
}

Incorrect:

swift
// Hamburger menu hidden behind three lines — discoverability is near zero
NavigationView {
    Button(action: { showMenu.toggle() }) {
        Image(systemName: "line.horizontal.3")
    }
}
Rule 2.2: Never Use Hamburger Menus

Hamburger (drawer) menus hide navigation, reduce discoverability, and violate iOS conventions. Use a tab bar instead. If you have more than 5 sections, consolidate or use a "More" tab.

Rule 2.3: Large Titles in Primary Views

Use .navigationBarTitleDisplayMode(.large) for top-level views. Titles transition to inline (.inline) when the user scrolls.

Correct:

swift
NavigationStack {
    List(items) { item in
        ItemRow(item: item)
    }
    .navigationTitle("Messages")
    .navigationBarTitleDisplayMode(.large)
}
Rule 2.4: Never Override Back Swipe

The swipe-from-left-edge gesture for back navigation is a system-level expectation. Never attach custom gesture recognizers that interfere with it.

Incorrect:

swift
.gesture(
    DragGesture()
        .onChanged { /* custom drawer */ } // Conflicts with system back swipe
)
Rule 2.5: Use NavigationStack for Hierarchical Content

Use NavigationStack (not the deprecated NavigationView) for drill-down content. Use NavigationPath for programmatic navigation.

Correct:

swift
NavigationStack(path: $path) {
    List(items) { item in
        NavigationLink(value: item) {
            ItemRow(item: item)
        }
    }
    .navigationDestination(for: Item.self) { item in
        ItemDetail(item: item)
    }
}
Rule 2.6: Preserve State Across Navigation

When users navigate back and then forward, or switch tabs, restore the previous scroll position and input state. Use @SceneStorage or @State to persist view state.

Rule 2.7: Prefer Recognition Over Recall

Keep current location, recent choices, and available destinations visible. Restore tab, scroll, filter, and selection state so users continue from recognition instead of reconstructing context from memory.


3. Typography & Dynamic Type

Impact: HIGH

Rule 3.1: Use Built-in Text Styles

Always use semantic text styles rather than hardcoded sizes. These scale automatically with Dynamic Type.

Correct:

swift
VStack(alignment: .leading, spacing: 4) {
    Text("Section Title")
        .font(.headline)
    Text("Body content that explains the section.")
        .font(.body)
    Text("Last updated 2 hours ago")
        .font(.caption)
        .foregroundStyle(.secondary)
}

Incorrect:

swift
VStack(alignment: .leading, spacing: 4) {
    Text("Section Title")
        .font(.system(size: 17, weight: .semibold)) // Won't scale with Dynamic Type
    Text("Body content")
        .font(.system(size: 15)) // Won't scale with Dynamic Type
}
Rule 3.2: Support Dynamic Type Including Accessibility Sizes

Dynamic Type can scale text up to approximately 200% at the largest accessibility sizes. Layouts must reflow — never truncate or clip essential text.

Correct:

swift
HStack {
    Image(systemName: "star")
    Text("Favorites")
        .font(.body)
}
// At accessibility sizes, consider using ViewThatFits or
// AnyLayout to switch from HStack to VStack

Use @Environment(\.dynamicTypeSize) to detect size category and adapt layouts:

swift
@Environment(\.dynamicTypeSize) var dynamicTypeSize

var body: some View {
    if dynamicTypeSize.isAccessibilitySize {
        VStack { content }
    } else {
        HStack { content }
    }
}
Rule 3.3: Custom Fonts Must Scale with Dynamic Type

If you use a custom typeface, scale it so it responds to Dynamic Type. The API differs by framework.

Correct (SwiftUI):

swift
extension Font {
    static func scaledCustom(size: CGFloat, relativeTo textStyle: Font.TextStyle) -> Font {
        .custom("CustomFont-Regular", size: size, relativeTo: textStyle)
    }
}

// Usage
Text("Hello")
    .font(.scaledCustom(size: 17, relativeTo: .body))

Correct (UIKit):

swift
let metrics = UIFontMetrics(forTextStyle: .body)
let customFont = UIFont(name: "CustomFont-Regular", size: 17)!
label.font = metrics.scaledFont(for: customFont)
label.adjustsFontForContentSizeCategory = true
Rule 3.4: SF Pro as System Font

Use the system font (SF Pro) unless brand requirements dictate otherwise. SF Pro is optimized for legibility on Apple displays.

Rule 3.5: Minimum 11pt Text

Never display text smaller than 11pt. Prefer 17pt for body text. Use the caption2 style (11pt) as the absolute minimum.

Rule 3.6: Hierarchy Through Weight and Size

Establish visual hierarchy through font weight and size. Do not rely solely on color to differentiate text levels.


4. Color & Dark Mode

Impact: HIGH

Rule 4.1: Use Semantic System Colors

Use system-provided semantic colors that automatically adapt to light and dark modes.

Correct:

swift
Text("Primary text")
    .foregroundStyle(.primary) // Adapts to light/dark

Text("Secondary info")
    .foregroundStyle(.secondary)

VStack { }
    .background(Color(.systemBackground)) // White in light, black in dark

Incorrect:

swift
Text("Primary text")
    .foregroundColor(.black) // Invisible on dark backgrounds

VStack { }
    .background(.white) // Blinding in Dark Mode
Rule 4.2: Provide Light and Dark Variants for Custom Colors

Define custom colors in the asset catalog with both Any Appearance and Dark Appearance variants.

swift
// In Assets.xcassets, define "BrandBlue" with:
// Any Appearance: #0066CC
// Dark Appearance: #4DA3FF

Text("Brand text")
    .foregroundStyle(Color("BrandBlue")) // Automatically switches
Rule 4.3: Never Rely on Color Alone

Always pair color with text, icons, or shapes to convey meaning. Approximately 8% of men have some form of color vision deficiency.

Correct:

swift
HStack {
    Image(systemName: "exclamationmark.triangle.fill")
        .foregroundStyle(.red)
    Text("Error: Invalid email address")
        .foregroundStyle(.red)
}

Incorrect:

swift
// Only color indicates the error — invisible to colorblind users
TextField("Email", text: $email)
    .border(isValid ? .green : .red)
Rule 4.4: 4.5:1 Contrast Ratio Minimum

All text must meet WCAG AA contrast ratios: 4.5:1 for normal text, 3:1 for large text (18pt+ or 14pt+ bold).

Rule 4.5: Support Display P3 Wide Gamut

Use Display P3 color space for vibrant, accurate colors on modern iPhones. Define colors in the asset catalog with the Display P3 gamut.

Rule 4.6: Background Hierarchy

Use the three-level background hierarchy for depth:

  • systemBackground — primary surface
  • secondarySystemBackground — grouped content, cards
  • tertiarySystemBackground — elements within grouped content
Rule 4.7: One Accent Color for Interactive Elements

Choose a single tint/accent color for all interactive elements (buttons, links, toggles). This creates a consistent, learnable visual language.

swift
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .tint(.indigo) // All interactive elements use indigo
        }
    }
}

5. Accessibility

Impact: CRITICAL

Rule 5.1: VoiceOver Labels on All Interactive Elements

Every button, control, and interactive element must have a meaningful accessibility label.

Correct:

swift
Button(action: addToCart) {
    Image(systemName: "cart.badge.plus")
}
.accessibilityLabel("Add to cart")

Incorrect:

swift
Button(action: addToCart) {
    Image(systemName: "cart.badge.plus")
}
// VoiceOver reads "cart.badge.plus" — meaningless to users
Rule 5.2: Logical VoiceOver Navigation Order

Ensure VoiceOver reads elements in a logical order. Use .accessibilitySortPriority() to adjust when the visual layout doesn't match the reading order.

swift
VStack {
    Text("Price: $29.99")
        .accessibilitySortPriority(1) // Read second (lower number = lower priority)
    Text("Product Name")
        .accessibilitySortPriority(2) // Read first (higher number = higher priority)
}
Rule 5.3: Support Bold Text

When the user enables Bold Text in Settings, custom-rendered text must adapt. SwiftUI text styles handle this automatically. For SwiftUI custom rendering, use @Environment(\.legibilityWeight) to apply heavier weights. UIKit code must check UIAccessibility.isBoldTextEnabled and re-query on UIAccessibility.boldTextStatusDidChangeNotification.

Correct:

swift
// SwiftUI — standard text styles adapt automatically
Text("Section Header")
    .font(.headline)

// SwiftUI — custom rendering respects legibilityWeight
@Environment(\.legibilityWeight) var legibilityWeight

var body: some View {
    Text("Custom Label")
        .fontWeight(legibilityWeight == .bold ? .bold : .regular)
}

Incorrect:

swift
// Hardcoded weight ignores Bold Text preference
label.font = UIFont.systemFont(ofSize: 17, weight: .regular)
// Missing: re-query font when UIAccessibility.boldTextStatusDidChangeNotification fires
Rule 5.4: Support Reduce Motion

Disable decorative animations and parallax when Reduce Motion is enabled. Use @Environment(\.accessibilityReduceMotion).

Correct:

swift
@Environment(\.accessibilityReduceMotion) var reduceMotion

var body: some View {
    CardView()
        .animation(reduceMotion ? nil : .spring(), value: isExpanded)
}
Rule 5.5: Support Increase Contrast

When the user enables Increase Contrast, ensure custom colors have higher-contrast variants. Use @Environment(\.colorSchemeContrast) to detect.

Rule 5.6: Don't Convey Info Only by Color, Shape, or Position

Information must be available through multiple channels. Pair visual indicators with text or accessibility descriptions.

Rule 5.7: Alternative Interactions for All Gestures

Every custom gesture must have an equivalent tap-based or menu-based alternative for users who cannot perform complex gestures.

Rule 5.8: Support Switch Control and Full Keyboard Access

Ensure all interactions work with Switch Control (external switches) and Full Keyboard Access (Bluetooth keyboards). Test navigation order and focus behavior.


6. Gestures & Input

Impact: HIGH

Rule 6.1: Use Standard Gestures

Use the standard iOS gesture vocabulary: tap, long press, swipe, pinch, rotate. Users already understand these.

GestureStandard Use
TapPrimary action, selection
Long pressContext menu, preview
Swipe horizontalDelete, archive, navigate back
Swipe verticalScroll, dismiss sheet
PinchZoom in/out
Two-finger rotateRotate content
Rule 6.2: Never Override System Gestures

These gestures are reserved by the system and must not be intercepted:

  • Swipe from left edge (back navigation)
  • Swipe down from top-left (Notification Center)
  • Swipe down from top-right (Control Center)
  • Swipe up from bottom (home / app switcher)
Rule 6.3: Custom Gestures Must Be Discoverable

If you add a custom gesture, provide visual hints (e.g., a grabber handle) and ensure the action is also available through a visible button or menu item.

Rule 6.4: Support All Input Methods

Design for touch first, but also support:

  • Hardware keyboards (iPad keyboard accessories, Bluetooth keyboards)
  • Assistive devices (Switch Control, head tracking)
  • Pointer input (assistive touch)

7. Components

Impact: HIGH

Rule 7.1: Button Styles

Use the built-in button styles appropriately:

  • .borderedProminent — primary call-to-action
  • .bordered — secondary actions
  • .borderless — tertiary or inline actions
  • .destructive role — red tint for delete/remove

Correct:

swift
VStack(spacing: 16) {
    Button("Purchase") { buy() }
        .buttonStyle(.borderedProminent)

    Button("Add to Wishlist") { wishlist() }
        .buttonStyle(.bordered)

    Button("Delete", role: .destructive) { delete() }
}
Rule 7.2: Alerts — Critical Info Only

Use alerts sparingly for critical information that requires a decision. Prefer 2 buttons; maximum 3. The destructive option should use .destructive role.

Correct:

swift
.alert("Delete Photo?", isPresented: $showAlert) {
    Button("Delete", role: .destructive) { deletePhoto() }
    Button("Cancel", role: .cancel) { }
} message: {
    Text("This photo will be permanently removed.")
}

Incorrect:

swift
// Alert for non-critical info — should be a banner or toast
.alert("Tip", isPresented: $showTip) {
    Button("OK") { }
} message: {
    Text("Swipe left to delete items.")
}
Rule 7.3: Sheets for Scoped Tasks

Present sheets for self-contained tasks. Always provide a way to dismiss (close button or swipe down). Use .presentationDetents() for half-height sheets.

swift
.sheet(isPresented: $showCompose) {
    NavigationStack {
        ComposeView()
            .navigationTitle("New Message")
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") { showCompose = false }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button("Send") { send() }
                }
            }
    }
    .presentationDetents([.medium, .large])
}
Rule 7.4: Lists — Inset Grouped Default

Use the .insetGrouped list style as the default. Support swipe actions for common operations. Minimum row height is 44pt.

Correct:

swift
List {
    Section("Recent") {
        ForEach(recentItems) { item in
            ItemRow(item: item)
                .swipeActions(edge: .trailing) {
                    Button(role: .destructive) { delete(item) } label: {
                        Label("Delete", systemImage: "trash")
                    }
                    Button { archive(item) } label: {
                        Label("Archive", systemImage: "archivebox")
                    }
                    .tint(.blue)
                }
        }
    }
}
.listStyle(.insetGrouped)
Rule 7.5: Tab Bar Behavior
  • Use SF Symbols for tab icons — filled variant for the selected tab, outline for unselected
  • Never hide the tab bar when navigating deeper within a tab
  • Badge important counts with .badge()
swift
TabView {
    MessagesView()
        .tabItem {
            Label("Messages", systemImage: "message")
        }
        .badge(unreadCount)
}

Place search using .searchable(). Provide search suggestions and support recent searches.

swift
NavigationStack {
    List(filteredItems) { item in
        ItemRow(item: item)
    }
    .searchable(text: $searchText, prompt: "Search items")
    .searchSuggestions {
        ForEach(suggestions) { suggestion in
            Text(suggestion.title)
                .searchCompletion(suggestion.title)
        }
    }
}
Rule 7.7: Context Menus

Use context menus (long press) for secondary actions. Never use a context menu as the only way to access an action.

swift
PhotoView(photo: photo)
    .contextMenu {
        Button { share(photo) } label: {
            Label("Share", systemImage: "square.and.arrow.up")
        }
        Button { favorite(photo) } label: {
            Label("Favorite", systemImage: "heart")
        }
        Button(role: .destructive) { delete(photo) } label: {
            Label("Delete", systemImage: "trash")
        }
    }
Rule 7.8: Progress Indicators
  • Determinate (ProgressView(value:total:)) for operations with known duration
  • Indeterminate (ProgressView()) for unknown duration
  • Never block the entire screen with a spinner
Rule 7.9: SF Symbols — Rendering Modes

Use the appropriate rendering mode for each symbol. Monochrome is the default; hierarchical, palette, and multicolor provide richer expression where appropriate. Always prefer the symbol rendering mode that best communicates meaning — do not default to monochrome when multicolor conveys critical state.

Correct:

swift
// Hierarchical: single color with automatic opacity layers
Image(systemName: "person.crop.circle.fill")
    .symbolRenderingMode(.hierarchical)
    .foregroundStyle(.blue)

// Multicolor: system-defined color per layer (e.g., battery, weather)
Image(systemName: "battery.100percent.bolt")
    .symbolRenderingMode(.multicolor)

// Palette: explicit per-layer colors
Image(systemName: "folder.badge.plus")
    .symbolRenderingMode(.palette)
    .foregroundStyle(.white, .blue)

Incorrect:

swift
// Monochrome on a symbol that has meaningful multicolor layers
Image(systemName: "battery.100percent.bolt")
    .foregroundColor(.gray) // loses the contextual color meaning
Rule 7.10: SF Symbols — Weight and Scale

Match the symbol weight to adjacent text weight. Use scale variants (.small, .medium, .large) rather than resizing. The symbol weight should never appear heavier than adjacent text.

Correct:

swift
Label("Download", systemImage: "arrow.down.circle.fill")
    .font(.body.weight(.semibold))
    // Symbol inherits .semibold weight automatically via Label

Incorrect:

swift
HStack {
    Image(systemName: "arrow.down.circle.fill")
        .font(.system(size: 32)) // explicit size ignores type scale
    Text("Download")
        .font(.body)
}
Rule 7.11: SF Symbols — Animations (iOS 17+)

Use symbolEffect for symbol state transitions. Prefer discrete effects (.bounce, .pulse) for actions and indefinite effects (.variableColor) for ongoing state. Do not use manual cross-fade between symbol names when contentTransition(.symbolEffect) is available.

Correct:

swift
Image(systemName: isLoading ? "arrow.2.circlepath" : "checkmark.circle")
    .contentTransition(.symbolEffect(.replace))
    .symbolEffect(.pulse, isActive: isLoading)

Incorrect:

swift
// Manual opacity cross-fade between symbol names
if isLoading {
    Image(systemName: "arrow.2.circlepath")
} else {
    Image(systemName: "checkmark.circle")
}

8. Patterns

Impact: MEDIUM

Rule 8.1: Onboarding — Max 3 Pages, Skippable

Keep onboarding to 3 or fewer pages. Always provide a skip option. Defer sign-in until the user needs authenticated features.

swift
TabView {
    OnboardingPage(
        image: "wand.and.stars",
        title: "Smart Suggestions",
        subtitle: "Get personalized recommendations based on your preferences."
    )
    OnboardingPage(
        image: "bell.badge",
        title: "Stay Updated",
        subtitle: "Receive notifications for things that matter to you."
    )
    OnboardingPage(
        image: "checkmark.shield",
        title: "Private & Secure",
        subtitle: "Your data stays on your device."
    )
}
.tabViewStyle(.page)
.overlay(alignment: .topTrailing) {
    Button("Skip") { completeOnboarding() }
        .padding()
}
Rule 8.2: Loading — Skeleton Views, No Blocking Spinners

Use skeleton/placeholder views that match the layout of the content being loaded. Never show a full-screen blocking spinner.

Correct:

swift
if isLoading {
    ForEach(0..<5) { _ in
        SkeletonRow() // Placeholder matching final row layout
            .redacted(reason: .placeholder)
    }
} else {
    ForEach(items) { item in
        ItemRow(item: item)
    }
}

Incorrect:

swift
if isLoading {
    ProgressView("Loading...") // Blocks the entire view
} else {
    List(items) { item in ItemRow(item: item) }
}
Rule 8.3: Launch Screen — Match First Screen

The launch storyboard must visually match the initial screen of the app. No splash logos, no branding screens. This creates the perception of instant launch.

Rule 8.4: Modality — Use Sparingly

Present modal views only when the user must complete or abandon a focused task. Always provide a clear dismiss action. Never stack modals on top of modals.

Rule 8.5: Notifications — High Value Only

Only send notifications for content the user genuinely cares about. Support actionable notifications. Categorize notifications so users can control them granularly.

Rule 8.6: Settings Placement
  • Frequent settings: In-app settings screen accessible from a profile or gear icon
  • Privacy/permission settings: Defer to the system Settings app via URL scheme
  • Never duplicate system-level controls in-app
Rule 8.7: Feedback — Visual + Haptic

Provide immediate feedback for every user action:

  • Visual state change (button highlight, animation)
  • Haptic feedback for significant actions using UIImpactFeedbackGenerator, UINotificationFeedbackGenerator, or UISelectionFeedbackGenerator
swift
Button("Complete") {
    let generator = UINotificationFeedbackGenerator()
    generator.notificationOccurred(.success)
    completeTask()
}
Show full SKILL.md (1,156 more words)Show less
Rule 8.8: Show Waiting States Immediately

If an action cannot complete immediately, acknowledge the tap at once, then show inline progress, skeletons, or partial results. Never leave the interface visually unchanged while work continues.


9. Privacy & Permissions

Impact: HIGH

Rule 9.1: Request Permissions in Context

Request a permission at the moment the user takes an action that needs it — never at app launch.

Correct:

swift
Button("Take Photo") {
    // Request camera permission only when the user taps this button
    AVCaptureDevice.requestAccess(for: .video) { granted in
        if granted { showCamera = true }
    }
}

Incorrect:

swift
// In AppDelegate.didFinishLaunching — too early, no context
func application(_ application: UIApplication, didFinishLaunchingWithOptions ...) {
    AVCaptureDevice.requestAccess(for: .video) { _ in }
    CLLocationManager().requestWhenInUseAuthorization()
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { _, _ in }
}
Rule 9.2: Explain Before System Prompt

Show a custom explanation screen before triggering the system permission dialog. The system dialog only appears once — if the user denies, the app must direct them to Settings.

swift
struct LocationExplanation: View {
    var body: some View {
        VStack(spacing: 16) {
            Image(systemName: "location.fill")
                .font(.largeTitle)
            Text("Find Nearby Stores")
                .font(.headline)
            Text("We use your location to show stores within walking distance. Your location is never shared or stored.")
                .font(.body)
                .multilineTextAlignment(.center)
            Button("Enable Location") {
                locationManager.requestWhenInUseAuthorization()
            }
            .buttonStyle(.borderedProminent)
            Button("Not Now") { dismiss() }
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}
Rule 9.3: Support Sign in with Apple

If the app offers any third-party sign-in (Google, Facebook), it must also offer Sign in with Apple. Present it as the first option.

Rule 9.4: Don't Require Accounts Unless Necessary

Let users explore the app before requiring sign-in. Gate only features that genuinely need authentication (purchases, sync, social features).

Rule 9.5: App Tracking Transparency

If you track users across apps or websites, display the ATT prompt. Respect denial — do not degrade the experience for users who opt out.

Rule 9.6: Location Button for One-Time Access

Use LocationButton for actions that need location once without requesting ongoing permission.

swift
import CoreLocationUI

LocationButton(.currentLocation) {
    fetchNearbyStores()
}
.labelStyle(.titleAndIcon)

10. System Integration

Impact: MEDIUM

Rule 10.1: Widgets for Glanceable Data

Provide widgets using WidgetKit for information users check frequently. Show the most useful snapshot. Since iOS 17, widgets support interactive controls: use Button and Toggle backed by App Intents for actions users perform directly from the widget without opening the app.

swift
// iOS 17+ interactive widget with a Button
struct TimerWidgetView: View {
    let entry: TimerEntry

    var body: some View {
        VStack {
            Text(entry.remaining, style: .timer)
                .font(.title2.bold())
            Button(intent: ToggleTimerIntent()) {
                Label(entry.isRunning ? "Pause" : "Start",
                      systemImage: entry.isRunning ? "pause.fill" : "play.fill")
            }
            .buttonStyle(.borderedProminent)
        }
    }
}
Rule 10.2: App Shortcuts for Key Actions

Define App Shortcuts so users can trigger key actions from Siri, Spotlight, and the Shortcuts app.

swift
struct MyAppShortcuts: AppShortcutsProvider {
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: StartWorkoutIntent(),
            phrases: ["Start a workout in \(.applicationName)"],
            shortTitle: "Start Workout",
            systemImageName: "figure.run"
        )
    }
}
Rule 10.3: Spotlight Indexing

Index app content with CSSearchableItem so users can find it from Spotlight search.

Rule 10.4: Share Sheet Integration

Support the system share sheet for content that users might want to send elsewhere. Implement UIActivityItemSource or use ShareLink in SwiftUI.

swift
ShareLink(item: article.url) {
    Label("Share", systemImage: "square.and.arrow.up")
}
Rule 10.5: Live Activities

Use Live Activities and the Dynamic Island for real-time, time-bound events (delivery tracking, sports scores, workouts).

Rule 10.6: Handle Interruptions Gracefully

Save state and pause gracefully when interrupted by:

  • Phone calls
  • Siri invocations
  • Notifications
  • App switcher
  • FaceTime SharePlay

Use scenePhase to detect transitions:

swift
@Environment(\.scenePhase) var scenePhase

.onChange(of: scenePhase) { _, newPhase in
    switch newPhase {
    case .active: resumeActivity()
    case .inactive: pauseActivity()
    case .background: saveState()
    @unknown default: break
    }
}

Quick Reference

NeedComponentNotes
Top-level sections (3-5)TabView with .tabItemBottom tab bar, SF Symbols
Hierarchical drill-downNavigationStackLarge title on root, inline on children
Self-contained task.sheetSwipe to dismiss, cancel/done buttons
Critical decision.alert2 buttons preferred, max 3
Secondary actions.contextMenuLong press; must also be accessible elsewhere
Scrolling contentList with .insetGrouped44pt min row, swipe actions
Text inputTextField / TextEditorLabel above, validation below
Selection (few options)PickerSegmented for 2-5, wheel for many
Selection (on/off)ToggleAligned right in a list row
Search.searchableSuggestions, recent searches
Progress (known)ProgressView(value:total:)Show percentage or time remaining
Progress (unknown)ProgressView()Inline, never full-screen blocking
One-time locationLocationButtonNo persistent permission needed
Sharing contentShareLinkSystem share sheet
Haptic feedbackUIImpactFeedbackGenerator.light, .medium, .heavy
Destructive actionButton(role: .destructive)Red tint, confirm via alert

Evaluation Checklist

Use this checklist to audit an iPhone app for HIG compliance:

Layout & Safe Areas
  • All touch targets are at least 44x44pt
  • No content is clipped under status bar, Dynamic Island, or home indicator
  • Primary actions are in the bottom half of the screen (thumb zone)
  • Layout adapts from iPhone SE to Pro Max without breaking
  • Spacing aligns to the 8pt grid
Navigation
  • Tab bar is used for 3-5 top-level sections
  • No hamburger/drawer menus
  • Primary views use large titles
  • Swipe-from-left-edge back navigation works throughout
  • State is preserved when switching tabs
Typography
  • All text uses built-in text styles or custom fonts scaled with Dynamic Type (Font.custom(_:size:relativeTo:) in SwiftUI or UIFontMetrics in UIKit)
  • Dynamic Type is supported up to accessibility sizes
  • Layouts reflow at large text sizes (no truncation of essential text)
  • Minimum text size is 11pt
Color & Dark Mode
  • App uses semantic system colors or provides light/dark asset variants
  • Dark Mode looks intentional (not just inverted)
  • No information conveyed by color alone
  • Text contrast meets 4.5:1 (normal) or 3:1 (large)
  • Single accent color for interactive elements
Accessibility
  • VoiceOver reads all screens logically with meaningful labels
  • Bold Text preference is respected
  • Reduce Motion disables decorative animations
  • Increase Contrast variant exists for custom colors
  • All gestures have alternative access paths
Components
  • Alerts are used only for critical decisions
  • Sheets have a dismiss path (button and/or swipe)
  • List rows are at least 44pt tall
  • Tab bar is never hidden during navigation
  • Destructive buttons use the .destructive role
Privacy
  • Permissions are requested in context, not at launch
  • Custom explanation shown before each system permission dialog
  • Sign in with Apple offered alongside other providers
  • App is usable without an account for basic features
  • ATT prompt is shown if tracking, and denial is respected
System Integration
  • Widgets show glanceable, up-to-date information
  • App content is indexed for Spotlight
  • Share Sheet is available for shareable content
  • App handles interruptions (calls, background, Siri) gracefully

Anti-Patterns

These are common mistakes that violate the iOS Human Interface Guidelines. Never do these:

  1. Hamburger menus — Use a tab bar. Hamburger menus hide navigation and reduce feature discoverability by up to 50%.

  2. Custom back buttons that break swipe-back — If you replace the back button, ensure the swipe-from-left-edge gesture still works via NavigationStack.

  3. Full-screen blocking spinners — Use skeleton views or inline progress indicators. Blocking spinners make the app feel frozen.

  4. Splash screens with logos — The launch screen must mirror the first screen of the app. Branding delays feel artificial.

  5. Requesting all permissions at launch — Asking for camera, location, notifications, and contacts on first launch guarantees most will be denied.

  6. Hardcoded font sizes — Use text styles. Hardcoded sizes ignore Dynamic Type and accessibility preferences, breaking the app for millions of users.

  7. Using only color to indicate state — Red/green for valid/invalid excludes colorblind users. Always pair with icons or text.

  8. Alerts for non-critical information — Alerts interrupt flow and require dismissal. Use banners, toasts, or inline messages for tips and non-critical information.

  9. Hiding the tab bar on push — Tab bars should remain visible throughout navigation within a tab. Hiding them disorients users.

  10. Ignoring safe areas — Using .ignoresSafeArea() on content views causes text and buttons to disappear under the notch, Dynamic Island, or home indicator.

  11. Non-dismissable modals — Every modal must have a clear dismiss path (close button, cancel, swipe down). Trapping users in a modal is hostile.

  12. Custom gestures without alternatives — A three-finger swipe for undo is unusable for many people. Provide a visible button or menu item as well.

  13. Tiny touch targets — Buttons and links smaller than 44pt cause mis-taps, especially in lists and toolbars.

  14. Stacked modals — Presenting a sheet on top of a sheet on top of a sheet creates navigation confusion. Use navigation within a single modal instead.

  15. Dark Mode as an afterthought — Using hardcoded colors means the app is either broken in Dark Mode or light mode. Always use semantic colors.

© ehmo, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 3 other files in skills/ios of ehmo/platform-design-skills.

  • SKILL.md
  • AGENTS.md
  • metadata.json
  • rules/_sections.md

Open the folder on GitHubat commit dc2be82

Compare with similar skills

iOS Design Guidelines next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

iOS Design Guidelines compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
iOS Design Guidelines this skillehmo/platform-design-skills606—~9kAutomated safety check: PassMIT
Hig Project Contextraintree-technology/hig-doctor1435 repos~1.2kAutomated safety check: PassMIT
Hig Components Contentraintree-technology/hig-doctor1435 repos~1.3kAutomated safety check: PassMIT
Aipay Frontend ThemeOpenAIPay/openaipay129—~386Automated safety check: PassNone
iOS Accessibilitydpearson2699/swift-ios-skills1.2k—~4.6kAutomated safety check: PassCustom licence
iOS Application DevHHU3637kr/skills1452 repos~1.8kAutomated safety check: PassMIT

Similar skills

  • Hig Project Context

    raintree-technology/hig-doctor

    Create or update a shared Apple design context document that other HIG skills use to tailor guidance.

    143 GitHub starsUsed in 5 repos~1.2k tokens
    MobileAuto-check passed
  • Hig Components Content

    raintree-technology/hig-doctor

    Apple Human Interface Guidelines for content display components.

    143 GitHub starsUsed in 5 repos~1.3k tokens
    MobileAuto-check passed
  • Aipay Frontend Theme

    OpenAIPay/openaipay

    A skill your agent uses when changing AiPay iOS frontend color style, replacing blue UI to purple, updating screenshot-based Reference pages, or enforcing component theme consistency across parallel…

    129 GitHub stars~386 tokensUpdated 5 mo ago
    MobileAuto-check passed
  • iOS Accessibility

    dpearson2699/swift-ios-skills

    Build and audit SwiftUI, UIKit, and AppKit accessibility for VoiceOver, Voice Control, Switch Control, Full Keyboard Access, Dynamic Type, focus restoration, labels/traits/actions, traversal, custom…

    1.2k GitHub stars~4.6k tokensUpdated 2 mo ago
    MobileAuto-check passed
  • iOS Application Dev

    HHU3637kr/skills

    iOS application development guide covering UIKit, SnapKit, and SwiftUI.

    145 GitHub starsUsed in 2 repos~1.8k tokens
    MobileAuto-check passed
  • iOS Hig Design

    wondelai/skills

    Design native iOS interfaces following Apple Human Interface Guidelines.

    2.4k GitHub stars~5.1k tokensUpdated 28 days ago
    MobileAuto-check passed

More from ehmo/platform-design-skills

All 8 skills in this repo
  • Tvos Design Guidelines

    ehmo/platform-design-skills

    Apple Human Interface Guidelines for Apple TV. An agent skill from ehmo/platform-design-skills.

    606 GitHub stars~4.8k tokensUpdated 6 mo ago
    Auto-check passed
  • macOS Design Guidelines

    ehmo/platform-design-skills

    Apple Human Interface Guidelines for Mac. An agent skill from ehmo/platform-design-skills.

    606 GitHub starsUsed in 1 repo~9.4k tokens
    Auto-check passed
  • Watchos Design Guidelines

    ehmo/platform-design-skills

    Apple Human Interface Guidelines for Apple Watch. An agent skill from ehmo/platform-design-skills.

    606 GitHub stars~4.3k tokensUpdated 6 mo ago
    Auto-check passed
  • Android Design Guidelines

    ehmo/platform-design-skills

    Material Design 3 and Android platform guidelines. An agent skill from ehmo/platform-design-skills.

    606 GitHub stars~9.7k tokensUpdated 6 mo ago
    Auto-check passed
  • Ipados Design Guidelines

    ehmo/platform-design-skills

    Apple Human Interface Guidelines for iPad. An agent skill from ehmo/platform-design-skills.

    606 GitHub stars~6.4k tokensUpdated 6 mo ago
    Auto-check passed
  • Visionos Design Guidelines

    ehmo/platform-design-skills

    Apple Human Interface Guidelines for Apple Vision Pro. An agent skill from ehmo/platform-design-skills.

    606 GitHub stars~5.7k tokensUpdated 6 mo ago
    Auto-check passed

Works with

Questions about iOS Design Guidelines

What does iOS Design Guidelines do?

Apple Human Interface Guidelines for iPhone. An agent skill from ehmo/platform-design-skills. iOS Design Guidelines is an agent skill from ehmo/platform-design-skills. Apple Human Interface Guidelines for iPhone.

When should I use iOS Design Guidelines?

iOS Design Guidelines fits situations like: refactoring SwiftUI/UIKit interfaces for iOS; tasks involving iPhone UI.

How do I install iOS Design Guidelines in Claude Code?

Run `npx skills add ehmo/platform-design-skills --skill ios-design-guidelines -a claude-code`. Or copy the skill folder (skills/ios in ehmo/platform-design-skills) into .claude/skills/ios-design-guidelines in your project. Claude Code loads it when a task matches its description.

How do I install iOS Design Guidelines in Codex?

Run `npx skills add ehmo/platform-design-skills --skill ios-design-guidelines -a codex`. Or copy the skill folder (skills/ios in ehmo/platform-design-skills) into .agents/skills/ios-design-guidelines in your project. Codex loads it when a task matches its description.

Can I use iOS Design Guidelines in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add ehmo/platform-design-skills --skill ios-design-guidelines -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/ios-design-guidelines, .gemini/skills/ios-design-guidelines, .github/skills/ios-design-guidelines and .opencode/skills/ios-design-guidelines in your project.

What does iOS Design Guidelines need to run?

SKILL.md names no scripts, command-line tools or credentials: iOS Design Guidelines is instructions for the agent only.

Does iOS Design Guidelines access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is iOS Design Guidelines safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does iOS Design Guidelines use?

iOS Design Guidelines is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does iOS Design Guidelines use?

About 9k tokens (SKILL.md is roughly 36k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to iOS Design Guidelines?

Skills that share tags, products or a category with iOS Design Guidelines: Hig Project Context (raintree-technology/hig-doctor, 143 stars), Hig Components Content (raintree-technology/hig-doctor, 143 stars), Aipay Frontend Theme (OpenAIPay/openaipay, 129 stars) and iOS Accessibility (dpearson2699/swift-ios-skills, 1.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains iOS Design Guidelines?

ehmo (a GitHub user) maintains it in ehmo/platform-design-skills, which has 606 GitHub stars. The repository holds 8 skills in this directory. The repository was last updated on March 19, 2026.

Source: ehmo/platform-design-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.