Files
gh-jamesrochabrun-skills-al…/skills/apple-hig-designer/SKILL.md
2025-11-29 18:48:58 +08:00

947 lines
20 KiB
Markdown

---
name: apple-hig-designer
description: Design iOS apps following Apple's Human Interface Guidelines. Generate native components, validate designs, and ensure accessibility compliance for iPhone, iPad, and Apple Watch.
---
# Apple HIG Designer
Design beautiful, native iOS apps following Apple's Human Interface Guidelines (HIG). Create accessible, intuitive interfaces with native components, proper typography, semantic colors, and Apple's design principles.
## What This Skill Does
Helps you design and build iOS apps that feel native and follow Apple's guidelines:
- **Generate iOS Components** - Create SwiftUI and UIKit components
- **Validate Designs** - Check compliance with Apple HIG
- **Ensure Accessibility** - VoiceOver, Dynamic Type, color contrast
- **Apply Design Principles** - Clarity, Deference, Depth
- **Use Semantic Colors** - Automatic dark mode support
- **Implement Typography** - San Francisco font system
- **Follow Spacing** - 8pt grid system and safe areas
## Apple's Design Principles
### 1. Clarity
**Make content clear and focused.**
Text is legible at every size, icons are precise and lucid, adornments are subtle and appropriate, and a focus on functionality drives the design.
```swift
// Clear, focused content
Text("Welcome back, Sarah")
.font(.title)
.foregroundColor(.primary)
// Unclear, cluttered
Text("Welcome back, Sarah!!!")
.font(.title)
.foregroundColor(.red)
.background(.yellow)
.overlay(Image(systemName: "star.fill"))
```
### 2. Deference
**UI helps people understand and interact with content, but never competes with it.**
The interface defers to content, using a light visual treatment that keeps focus on the content and gives the content room to breathe.
```swift
// Content-focused
VStack(alignment: .leading, spacing: 8) {
Text("Article Title")
.font(.headline)
Text("Article content goes here...")
.font(.body)
.foregroundColor(.secondary)
}
.padding()
// Distracting UI
VStack(spacing: 8) {
Text("Article Title")
.font(.headline)
.foregroundColor(.white)
.background(.blue)
.border(.red, width: 3)
}
```
### 3. Depth
**Visual layers and realistic motion convey hierarchy and help people understand relationships.**
Distinct visual layers and realistic motion impart vitality and facilitate understanding. Touch and discoverability heighten delight and enable access to functionality without losing context.
```swift
// Clear depth hierarchy
ZStack {
Color(.systemBackground)
VStack {
// Card with elevation
CardView()
.shadow(radius: 8)
}
}
// Using blur for depth
Text("Content")
.background(.ultraThinMaterial)
```
## iOS UI Components
### Navigation Patterns
#### 1. Navigation Bar
**Top bar for navigation and actions.**
```swift
NavigationStack {
List {
Text("Item 1")
Text("Item 2")
}
.navigationTitle("Title")
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Add") {
// Action
}
}
}
}
```
**Guidelines:**
- Use large titles for top-level views
- Use inline titles for detail views
- Keep actions relevant to current context
- Maximum 2-3 toolbar items
#### 2. Tab Bar
**Bottom navigation for top-level destinations.**
```swift
TabView {
HomeView()
.tabItem {
Label("Home", systemImage: "house")
}
SearchView()
.tabItem {
Label("Search", systemImage: "magnifyingglass")
}
ProfileView()
.tabItem {
Label("Profile", systemImage: "person")
}
}
```
**Guidelines:**
- 3-5 tabs maximum
- Use SF Symbols for icons
- Labels should be concise (one word)
- Never hide or disable tabs
- Don't use tab bar with toolbar in same view
#### 3. List
**Scrollable list of items.**
```swift
List {
Section("Today") {
ForEach(items) { item in
NavigationLink {
DetailView(item: item)
} label: {
HStack {
Image(systemName: item.icon)
.foregroundColor(.accentColor)
Text(item.title)
}
}
}
}
}
.listStyle(.insetGrouped)
```
**List Styles:**
- `.plain` - Edge-to-edge rows
- `.insetGrouped` - Rounded, inset sections (iOS default)
- `.sidebar` - For navigation sidebars
#### 4. Sheet (Modal)
**Present content modally.**
```swift
struct ContentView: View {
@State private var showSheet = false
var body: some View {
Button("Show Details") {
showSheet = true
}
.sheet(isPresented: $showSheet) {
DetailView()
.presentationDetents([.medium, .large])
}
}
}
```
**Sheet Detents:**
- `.medium` - Half screen
- `.large` - Full screen
- Custom heights available
### Form Controls
#### 1. Button
**Primary action control.**
```swift
// Filled button (primary action)
Button("Continue") {
// Action
}
.buttonStyle(.borderedProminent)
// Bordered button (secondary action)
Button("Cancel") {
// Action
}
.buttonStyle(.bordered)
// Plain button (tertiary action)
Button("Learn More") {
// Action
}
.buttonStyle(.plain)
```
**Button Hierarchy:**
1. **Prominent** - Primary action (one per screen)
2. **Bordered** - Secondary actions
3. **Plain** - Tertiary actions, links
**Guidelines:**
- Minimum tap target: 44x44 points
- Use verbs for button labels
- Make destructive actions require confirmation
#### 2. TextField
**Text input control.**
```swift
@State private var username = ""
@State private var password = ""
VStack(alignment: .leading, spacing: 16) {
// Standard text field
TextField("Username", text: $username)
.textFieldStyle(.roundedBorder)
.textContentType(.username)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
// Secure field
SecureField("Password", text: $password)
.textFieldStyle(.roundedBorder)
.textContentType(.password)
}
```
**Text Content Types:**
- `.username` - Username field
- `.password` - Password field
- `.emailAddress` - Email field
- `.telephoneNumber` - Phone number
- `.creditCardNumber` - Credit card
#### 3. Toggle
**Boolean control (switch).**
```swift
@State private var isEnabled = false
Toggle("Enable notifications", isOn: $isEnabled)
.toggleStyle(.switch)
```
**Guidelines:**
- Label describes what the toggle controls
- Effect should be immediate
- Use for binary choices only
#### 4. Picker
**Selection control.**
```swift
@State private var selectedSize = "Medium"
let sizes = ["Small", "Medium", "Large"]
// Menu style
Picker("Size", selection: $selectedSize) {
ForEach(sizes, id: \.self) { size in
Text(size).tag(size)
}
}
.pickerStyle(.menu)
// Segmented style (for 2-5 options)
Picker("Size", selection: $selectedSize) {
ForEach(sizes, id: \.self) { size in
Text(size).tag(size)
}
}
.pickerStyle(.segmented)
```
**Picker Styles:**
- `.menu` - Dropdown menu (default)
- `.segmented` - Segmented control (2-5 options)
- `.wheel` - Scrollable wheel
- `.inline` - Inline list (in forms)
### Cards and Containers
#### Card View
```swift
struct CardView: View {
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Title")
.font(.headline)
Text("Description goes here with some details about the content.")
.font(.subheadline)
.foregroundColor(.secondary)
.lineLimit(2)
Spacer()
Button("Action") {
// Action
}
.buttonStyle(.borderedProminent)
}
.padding()
.frame(width: 300, height: 200)
.background(Color(.systemBackground))
.cornerRadius(12)
.shadow(color: .black.opacity(0.1), radius: 8, x: 0, y: 4)
}
}
```
## Typography
### San Francisco Font System
Apple's system font designed for optimal legibility.
```swift
// Dynamic Type text styles
Text("Large Title").font(.largeTitle) // 34pt
Text("Title").font(.title) // 28pt
Text("Title 2").font(.title2) // 22pt
Text("Title 3").font(.title3) // 20pt
Text("Headline").font(.headline) // 17pt semibold
Text("Body").font(.body) // 17pt regular
Text("Callout").font(.callout) // 16pt
Text("Subheadline").font(.subheadline) // 15pt
Text("Footnote").font(.footnote) // 13pt
Text("Caption").font(.caption) // 12pt
Text("Caption 2").font(.caption2) // 11pt
```
### Custom Fonts with Dynamic Type
```swift
// Custom font that scales with Dynamic Type
Text("Custom Text")
.font(.custom("YourFont-Regular", size: 17, relativeTo: .body))
```
### Font Weights
```swift
Text("Light").fontWeight(.light)
Text("Regular").fontWeight(.regular)
Text("Medium").fontWeight(.medium)
Text("Semibold").fontWeight(.semibold)
Text("Bold").fontWeight(.bold)
Text("Heavy").fontWeight(.heavy)
```
### Typography Guidelines
**Do:**
- ✅ Use system font (San Francisco) for consistency
- ✅ Support Dynamic Type for accessibility
- ✅ Use semantic text styles (.headline, .body, etc.)
- ✅ Minimum body text: 17pt
- ✅ Line spacing: 120-145% of font size
**Don't:**
- ❌ Use too many font sizes (stick to system styles)
- ❌ Make text smaller than 11pt
- ❌ Use all caps for long text
- ❌ Disable Dynamic Type
## Colors
### Semantic Colors
**Colors that automatically adapt to light/dark mode.**
```swift
// UI Element Colors
Color(.label) // Primary text
Color(.secondaryLabel) // Secondary text
Color(.tertiaryLabel) // Tertiary text
Color(.quaternaryLabel) // Watermark text
Color(.systemBackground) // Primary background
Color(.secondarySystemBackground) // Secondary background
Color(.tertiarySystemBackground) // Tertiary background
Color(.systemFill) // Fill colors
Color(.secondarySystemFill)
Color(.tertiarySystemFill)
Color(.quaternarySystemFill)
Color(.separator) // Separator lines
Color(.opaqueSeparator) // Non-transparent separator
```
### System Colors
```swift
// Standard system colors (adapt to dark mode)
Color(.systemRed)
Color(.systemOrange)
Color(.systemYellow)
Color(.systemGreen)
Color(.systemMint)
Color(.systemTeal)
Color(.systemCyan)
Color(.systemBlue)
Color(.systemIndigo)
Color(.systemPurple)
Color(.systemPink)
Color(.systemBrown)
Color(.systemGray)
```
### Custom Colors with Dark Mode
```swift
// Define adaptive color
extension Color {
static let customBackground = Color("CustomBackground")
}
// In Assets.xcassets, create color set with:
// - Any Appearance: #FFFFFF
// - Dark Appearance: #000000
```
### Color Contrast Guidelines
**WCAG AA Compliance:**
- Normal text: 4.5:1 contrast ratio minimum
- Large text (24pt+): 3:1 contrast ratio minimum
- UI components: 3:1 contrast ratio
**Custom colors:**
- Test with Increase Contrast enabled
- Aim for 7:1 for critical text
- Provide sufficient contrast in both modes
## Spacing and Layout
### 8-Point Grid System
**All spacing should be multiples of 8.**
```swift
// Spacing values
.padding(8) // 8pt
.padding(16) // 16pt (standard)
.padding(24) // 24pt
.padding(32) // 32pt
.padding(40) // 40pt
.padding(48) // 48pt
// Edge-specific padding
.padding(.horizontal, 16)
.padding(.vertical, 24)
.padding(.top, 16)
.padding(.bottom, 16)
```
### Safe Areas
**Respect device safe areas.**
```swift
// Content within safe area (default)
VStack {
Text("Content")
}
// Extend beyond safe area
VStack {
Color.blue
}
.ignoresSafeArea()
// Extend top only
VStack {
Color.blue
}
.ignoresSafeArea(edges: .top)
```
### Touch Targets
**Minimum interactive size: 44x44 points.**
```swift
Button("Tap") {
// Action
}
.frame(minWidth: 44, minHeight: 44)
```
### Spacing Guidelines
```swift
// Component spacing
VStack(spacing: 8) { // Tight spacing
Text("Line 1")
Text("Line 2")
}
VStack(spacing: 16) { // Standard spacing
Text("Section 1")
Text("Section 2")
}
VStack(spacing: 24) { // Loose spacing
SectionView()
SectionView()
}
```
## Accessibility
### VoiceOver Support
**Screen reader for blind and low-vision users.**
```swift
// Accessible label
Image(systemName: "heart.fill")
.accessibilityLabel("Favorite")
// Accessible value
Slider(value: $volume)
.accessibilityLabel("Volume")
.accessibilityValue("\(Int(volume * 100))%")
// Accessible hint
Button("Share") {
share()
}
.accessibilityHint("Shares this item with others")
// Group elements
HStack {
Image(systemName: "person")
Text("John Doe")
}
.accessibilityElement(children: .combine)
// Hidden from VoiceOver
Image("decorative")
.accessibilityHidden(true)
```
### Dynamic Type
**Support user's preferred text size.**
```swift
// Automatically supported with system fonts
Text("This text scales")
.font(.body)
// Limit scaling (if necessary)
Text("This text has limits")
.font(.body)
.dynamicTypeSize(...DynamicTypeSize.xxxLarge)
// Custom font with Dynamic Type
Text("Custom font")
.font(.custom("YourFont", size: 17, relativeTo: .body))
```
### Color Blindness
**Design for color-blind users.**
```swift
// Don't rely on color alone
HStack {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
Text("Success")
}
// Not just color
Circle()
.fill(.green)
// Color only
// Better with shape/icon
HStack {
Image(systemName: "checkmark.circle.fill")
Circle().fill(.green)
}
// Color + shape
```
### Reduce Motion
**Respect user's motion preferences.**
```swift
@Environment(\.accessibilityReduceMotion) var reduceMotion
var animation: Animation {
reduceMotion ? .none : .spring()
}
Button("Animate") {
withAnimation(animation) {
// Animate
}
}
```
### Increase Contrast
**Support high contrast mode.**
```swift
@Environment(\.colorSchemeContrast) var contrast
var textColor: Color {
contrast == .increased ? .primary : .secondary
}
Text("Content")
.foregroundColor(textColor)
```
## Dark Mode
**Support both light and dark appearances.**
### Automatic Support
```swift
// Use semantic colors (automatic)
Color(.label) // Adapts automatically
Color(.systemBackground) // Adapts automatically
```
### Testing Dark Mode
```swift
// Preview both modes
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.preferredColorScheme(.light)
ContentView()
.preferredColorScheme(.dark)
}
}
```
### Dark Mode Guidelines
**Do:**
- ✅ Use semantic colors
- ✅ Test with Increase Contrast
- ✅ Test with Reduce Transparency
- ✅ Ensure sufficient contrast in both modes
**Don't:**
- ❌ Use pure black (#000000) - use systemBackground
- ❌ Invert colors automatically
- ❌ Assume user preference
## SF Symbols
**Apple's icon system (3000+ symbols).**
```swift
// Basic symbol
Image(systemName: "heart")
// Colored symbol
Image(systemName: "heart.fill")
.foregroundColor(.red)
// Sized symbol
Image(systemName: "heart")
.imageScale(.large)
// Font-based sizing
Image(systemName: "heart")
.font(.title)
// Multicolor symbols
Image(systemName: "person.crop.circle.fill.badge.checkmark")
.symbolRenderingMode(.multicolor)
// Hierarchical rendering
Image(systemName: "heart.fill")
.symbolRenderingMode(.hierarchical)
.foregroundColor(.red)
```
### SF Symbols Guidelines
- Use system symbols when available
- Maintain visual weight consistency
- Use multicolor for semantic meaning
- Size appropriately for context
## App Icons
### Icon Sizes
```
iOS:
- 1024x1024 (App Store)
- 180x180 (iPhone @3x)
- 120x120 (iPhone @2x)
- 167x167 (iPad Pro)
- 152x152 (iPad @2x)
watchOS:
- 1024x1024 (App Store)
- 196x196 (49mm)
- 216x216 (45mm)
```
### Icon Design Guidelines
**Do:**
- ✅ Use simple, recognizable shapes
- ✅ Fill entire icon space
- ✅ Test on device (not just mockups)
- ✅ Use consistent visual style
**Don't:**
- ❌ Include text (very small)
- ❌ Use photos
- ❌ Replicate Apple hardware
- ❌ Use translucency
## Animation and Motion
### Standard Animations
```swift
// Spring animation (natural, bouncy)
withAnimation(.spring()) {
offset = 100
}
// Linear animation
withAnimation(.linear(duration: 0.3)) {
opacity = 0
}
// Ease in/out
withAnimation(.easeInOut(duration: 0.3)) {
scale = 1.2
}
```
### Gesture-Driven
```swift
@State private var offset = CGSize.zero
var body: some View {
Circle()
.offset(offset)
.gesture(
DragGesture()
.onChanged { value in
offset = value.translation
}
.onEnded { _ in
withAnimation(.spring()) {
offset = .zero
}
}
)
}
```
### Motion Guidelines
- Keep animations under 0.3 seconds
- Use spring animations for interactive elements
- Respect Reduce Motion setting
- Provide visual feedback for all interactions
## Best Practices
### Navigation
- **Hierarchical** - Use NavigationStack for drilldown
- **Flat** - Use TabView for peer destinations
- **Content-Driven** - Use for media apps
### Feedback
- **Visual** - Highlight on tap
- **Haptic** - Use UIImpactFeedbackGenerator
- **Audio** - Use system sounds sparingly
### Loading States
```swift
struct LoadingView: View {
var body: some View {
VStack {
ProgressView()
.scaleEffect(1.5)
Text("Loading...")
.font(.caption)
.foregroundColor(.secondary)
.padding(.top)
}
}
}
```
### Error States
```swift
struct ErrorView: View {
let message: String
let retry: () -> Void
var body: some View {
VStack(spacing: 16) {
Image(systemName: "exclamationmark.triangle")
.font(.system(size: 48))
.foregroundColor(.orange)
Text("Something went wrong")
.font(.headline)
Text(message)
.font(.subheadline)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
Button("Try Again") {
retry()
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}
```
### Empty States
```swift
struct EmptyStateView: View {
var body: some View {
VStack(spacing: 16) {
Image(systemName: "tray")
.font(.system(size: 64))
.foregroundColor(.secondary)
Text("No Items")
.font(.title2)
Text("Your items will appear here")
.font(.subheadline)
.foregroundColor(.secondary)
Button("Add Item") {
// Action
}
.buttonStyle(.borderedProminent)
}
}
}
```
## Platform Considerations
### iPhone
- Design for various sizes (SE, Pro, Pro Max)
- Support portrait and landscape
- Use safe areas for notch/Dynamic Island
- Consider one-handed use
### iPad
- Support multitasking (Split View, Slide Over)
- Use sidebars for navigation
- Adapt to larger screen (don't just scale)
- Consider keyboard shortcuts
- Support external displays
### Apple Watch
- Glanceable information
- Large touch targets (>44pt)
- Minimal interaction required
- Use Digital Crown for scrolling
- Support Always-On display
## Resources
- [Apple HIG Official](https://developer.apple.com/design/human-interface-guidelines/)
- [SF Symbols App](https://developer.apple.com/sf-symbols/)
- [WWDC Videos](https://developer.apple.com/videos/)
- [Apple Design Resources](https://developer.apple.com/design/resources/)
---
**"Design is not just what it looks like and feels like. Design is how it works." - Steve Jobs**