Apple Human Interface Guidelines for iOS and iPadOS. Load for iOS or iPadOS design tasks.
Scanned 9/6/2026
Install to Claude Code
npx -y skills add Dev-Toolbelt/dev-team-agents --skill ios-hig --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ios Hig?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/dev-toolbelt-ios-hig)More formats (shields.io, HTML) on the badges page.
---
name: ios-hig
description: Apple Human Interface Guidelines for iOS and iPadOS. Load for iOS or iPadOS design tasks.
---
## Detection Signals
Load this skill when **any** of the following are present:
| Signal | Location |
|--------|---------|
| iOS target in cross-platform project | React Native, Flutter, or `pubspec.yaml` with iOS support |
| Native iOS/iPadOS project | `.xcodeproj`, `.xcworkspace`, `Info.plist`, Swift source files |
| SwiftUI or UIKit usage | `import SwiftUI`, `import UIKit` in source files |
| Design task for iOS | User mentions "iOS design", "HIG", "Apple guidelines", or "SwiftUI design" |
---
## Core Principle — Feel Native
iOS users expect apps to feel like a natural extension of the operating system. Deviation from platform patterns — even aesthetically — creates friction. When in doubt, prefer the native pattern over a custom one.
---
## Navigation Patterns
Choose the navigation model that matches the information architecture. Never mix models without a clear reason.
### Tab Bar
- Use for **3–5 top-level destinations** that are peers (no hierarchy between them)
- Tabs are always visible and always accessible — never hide or disable individual tabs
- Icons: use SF Symbols; active tab uses filled variant + `tint` color; inactive uses outlined variant + `secondaryLabel` color
- Tab bar height: 49pt (83pt with home indicator safe area on notched devices)
- Labels: short (1–2 words max); truncate with `…` only as a last resort
### Navigation Stack (Push)
- Use for **hierarchical content** — each level drills deeper into detail
- Back button appears automatically; never replace it with a custom "Back" label unless the destination title is ambiguous
- Navigation bar title: use `.large` style at the root level, `.inline` for deeper levels
- Never push more than 3–4 levels without a shortcut back to root (e.g., long-press back button)
### Modal Presentation
| Style | Usage |
|-------|-------|
| **Sheet** (`.sheet`) | Lightweight tasks that don't require full context switch |
| **Full-screen cover** (`.fullScreenCover`) | Immersive tasks (camera, onboarding, video); user cannot swipe to dismiss |
| **Popover** (iPad) | Contextual information anchored to a control; auto-adapts to sheet on iPhone |
- Sheets support **pull-to-dismiss** by default — preserve this behavior; only disable when unsaved data is at risk (show a confirmation action sheet instead)
- Never present a sheet from within a sheet — use navigation push instead
- Full-screen covers must always provide an explicit dismiss control (button or gesture)
### Split View (iPad)
- Use `NavigationSplitView` for two- or three-column layouts on iPad
- Sidebar (first column): persistent navigation; list of sections or categories
- Content (second column): list or index within a section
- Detail (third column): full detail view
- Collapse gracefully to a single-column navigation stack on iPhone and compact-width iPad
---
## Controls
### SF Symbols
- Always prefer SF Symbols over custom icons — they scale with Dynamic Type, support weight variants, and adapt to Dark Mode automatically
- Match symbol weight to the surrounding text weight: `regular` text → `regular` symbol
- Use **filled** variants for selected/active states; **outlined** for inactive
- Symbols used as interactive controls must have an accessibility label
- Custom symbols must follow the SF Symbols grid and optical alignment — use the SF Symbols app to verify
### Buttons
| Style | Usage |
|-------|-------|
| **Filled** (`.borderedProminent`) | Primary action; one per screen |
| **Tinted** (`.bordered` with tint) | Secondary action; safe to use multiple times |
| **Gray** (`.bordered`) | Tertiary or neutral actions |
| **Plain** (`.plain`) | Low-emphasis, inline, or destructive actions |
- Minimum touch target: **44×44pt** — use `.contentShape(Rectangle())` to expand hit area without changing visual size
- Destructive actions must use `.destructive` role — renders in red and triggers a confirmation if needed
- Button labels: verb phrases ("Save Changes", "Delete Account") — never noun-only labels ("OK")
### Toggles and Pickers
- `Toggle` (Switch): for immediate binary settings — changes apply instantly without a "Save" button
- `Picker`: use `.segmented` style for 2–4 mutually exclusive short options; use `.menu` style for longer lists
- `Stepper`: for incrementing/decrementing numeric values within a known range
- `Slider`: for continuous values where precision is less critical than range exploration
- `DatePicker`: always use the system date picker — never build a custom one
### Text Input
- `TextField`: single-line; set `keyboardType`, `textContentType`, and `autocorrectionDisabled` appropriately
- `SecureField`: for passwords — never use `TextField` for passwords
- `TextEditor`: multi-line; add a character count label when there is a limit
- Always dismiss the keyboard when the user taps outside the field (`onTapGesture` on the background)
- Return key label should match the action: "Done", "Search", "Send", "Next"
---
## Typography — SF Pro
Apple's system font family is **SF Pro** (iOS/macOS) and **SF Pro Rounded** (friendly contexts). Use it via Dynamic Type — never specify a fixed font size.
### Dynamic Type Scales
| Style | Default Size | Usage |
|-------|-------------|-------|
| `largeTitle` | 34pt | Hero page titles (used sparingly) |
| `title1` | 28pt | Primary screen headings |
| `title2` | 22pt | Secondary headings |
| `title3` | 20pt | Tertiary headings |
| `headline` | 17pt **semibold** | List row primaries, emphasized labels |
| `body` | 17pt | Primary body text |
| `callout` | 16pt | Secondary body, sidebars |
| `subheadline` | 15pt | Supporting labels |
| `footnote` | 13pt | Secondary descriptions, timestamps |
| `caption1` | 12pt | Image captions, form helper text |
| `caption2` | 11pt | Smallest visible labels |
**Rules:**
- Always use `.font(.body)`, `.font(.headline)`, etc. — never `Font.system(size: 17)`
- Support **Accessibility text sizes** (up to ~310% of default) — test with "Larger Text" in Accessibility settings
- When Accessibility sizes are enabled, consider moving labels to a vertical stack using `@ScaledMetric` or `ViewThatFits`
- Minimum: `caption2` (11pt) — never use sizes below this
---
## Layout
### Safe Area
- **Never place interactive content or critical text outside the safe area** — it will be obscured by notch, Dynamic Island, or home indicator
- Use `.safeAreaInset()` (SwiftUI) or `safeAreaLayoutGuide` (UIKit) — never hard-code pixel offsets
- Bottom content (buttons, tab bars): account for the home indicator (`34pt` on notched devices)
### Spacing and Margins
- Standard content margin: **16pt** from screen edges (20pt on Plus/Max models)
- Group related content with consistent spacing; use the 8pt grid as the base unit
- List rows: minimum **44pt** height for tappable rows
- Section headers: `listRowInsets` preserves the standard left inset unless deliberately removed
### Grids and Collections
- Use `LazyVGrid` / `LazyHGrid` (SwiftUI) or `UICollectionViewCompositionalLayout` (UIKit) for grid layouts — never manual frame placement
- Item spacing: 8pt default; increase to 16pt for card-style items
- Always account for variable row heights — never assume fixed item height in a collection
---
## Dark Mode
iOS automatically switches between light and dark appearances. Designs must support both.
### Color Usage
- Always use **semantic system colors** — they adapt automatically: `.primary`, `.secondary`, `.background`, `.secondaryBackground`, `.grouped Background`, `.separator`, `.label`, `.secondaryLabel`
- Custom colors must define both light and dark variants in the Asset Catalog (Appearances: Any, Dark)
- Never hard-code `Color.white` for backgrounds or `Color.black` for text — use semantic equivalents
### Images and Icons
- SF Symbols adapt automatically — no extra work needed
- Custom images: provide light and dark variants in the Asset Catalog when they differ
- Avoid images with embedded text — they do not adapt to Dark Mode
### Elevation and Depth
- iOS does not use colored tonal elevation like Material — use shadows, blur (`.background(.ultraThinMaterial)`), or grouping to separate layers
- Materials (`ultraThinMaterial`, `thinMaterial`, `regularMaterial`, `thickMaterial`, `ultraThickMaterial`) adapt to both appearances automatically
---
## Accessibility
### VoiceOver
- Every interactive element must have an `accessibilityLabel` (what it is) and, when needed, an `accessibilityHint` (what happens when activated)
- Decorative images: mark as `.accessibilityHidden(true)`
- Custom controls: implement `accessibilityValue` for current state and `accessibilityAdjustableAction` for sliders/pickers
- Test all key flows with VoiceOver enabled before release
### Reduce Motion
- Respect the **Reduce Motion** accessibility setting — replace all non-essential animations with simple crossfades or instant transitions
- In SwiftUI: `@Environment(\.accessibilityReduceMotion) var reduceMotion`
- Never use motion to convey meaning that cannot be understood without it
### Increase Contrast
- Respect the **Increase Contrast** setting — borders and separators become more prominent automatically with system colors; custom colors must provide high-contrast variants
- In SwiftUI: `@Environment(\.accessibilityDifferentiateWithoutColor)` and `@Environment(\.colorSchemeContrast)`
### Touch Targets
- Minimum: **44×44pt** for all tappable elements
- For small visual elements (icons, toggles), expand the hit area with padding or `.contentShape(Rectangle())`
---
## iPhone vs iPad Adaptations
The same app must adapt its layout based on the available space — not serve a different design.
| Aspect | iPhone (Compact) | iPad (Regular) |
|--------|-----------------|----------------|
| Navigation | Tab Bar + Navigation Stack | Split View or Tab Bar |
| Modals | Full-width sheets | Popovers, centered sheets |
| Content columns | Single column | Two or three columns |
| Keyboard | Full-screen, pushes content | Floating or docked; content shifts |
| Pointer (iPadOS) | N/A | Support hover effects (`.hoverEffect()`) |
- Use `@Environment(\.horizontalSizeClass)` to branch layout — `compact` = iPhone / iPad portrait in some cases; `regular` = iPad landscape and most iPad orientations
- Never detect device model (`UIDevice.current.userInterfaceIdiom`) to branch layout — use size class; it handles Split View and Stage Manager correctly
- Test on iPad with Stage Manager enabled — the app may appear in various window sizes
---
## Platform Conventions to Never Break
These behaviors are deeply ingrained in iOS users. Deviating causes immediate friction:
- **Pull-to-refresh**: always supported in scrollable lists that show server data
- **Swipe-to-delete**: always supported in editable lists (`onDelete` in SwiftUI)
- **Long-press context menu**: offer contextual actions on tappable items (`.contextMenu`)
- **Back swipe**: never disable edge-swipe-to-go-back; it is a core navigation gesture
- **Scroll-to-top**: tapping the status bar scrolls the active scroll view to the top — do not break this
- **Share sheet**: use `UIActivityViewController` / `ShareLink` — never build a custom share UI
- **System alerts**: use `UIAlertController` / `.alert()` for OS-level confirmations — never custom modal dialogs for destructive actions
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!