Installs into .claude/skills of the current project.
Are you the author of Ios Hig?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/dev-toolbelt-ios-hig)
---
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