$npx -y skills add ehmo/platform-design-skills --skill macosApple Human Interface Guidelines for Mac. Use when building macOS apps with SwiftUI or AppKit, implementing menu bars, toolbars, window management, or keyboard shortcuts. Triggers on tasks involving Mac UI, desktop apps, or Mac Catalyst.
| 1 | # macOS Human Interface Guidelines |
| 2 | |
| 3 | Mac apps serve power users who expect deep keyboard control, persistent menu bars, resizable multi-window layouts, and tight system integration. These guidelines codify Apple's HIG into actionable rules with SwiftUI and AppKit examples. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## 1. Menu Bar (CRITICAL) |
| 8 | |
| 9 | Every Mac app must have a menu bar. It is the primary discovery mechanism for commands. Users who cannot find a feature will look in the menu bar before anywhere else. |
| 10 | |
| 11 | ### Rule 1.1 — Provide Standard Menus |
| 12 | |
| 13 | Every app must include at minimum: **App**, **File**, **Edit**, **View**, **Window**, **Help**. Omit File only if the app is not document-based. Add app-specific menus between Edit and View or between View and Window. |
| 14 | |
| 15 | ```swift |
| 16 | // SwiftUI — Standard menu structure |
| 17 | @main |
| 18 | struct MyApp: App { |
| 19 | var body: some Scene { |
| 20 | WindowGroup { |
| 21 | ContentView() |
| 22 | } |
| 23 | .commands { |
| 24 | // Adds to existing standard menus |
| 25 | CommandGroup(after: .newItem) { |
| 26 | Button("New from Template...") { newFromTemplate() } |
| 27 | .keyboardShortcut("T", modifiers: [.command, .shift]) |
| 28 | } |
| 29 | CommandMenu("Canvas") { |
| 30 | Button("Zoom to Fit") { zoomToFit() } |
| 31 | .keyboardShortcut("0", modifiers: .command) |
| 32 | Divider() |
| 33 | Button("Add Artboard") { addArtboard() } |
| 34 | .keyboardShortcut("A", modifiers: [.command, .shift]) |
| 35 | } |
| 36 | } |
| 37 | } |
| 38 | } |
| 39 | ``` |
| 40 | |
| 41 | ```swift |
| 42 | // AppKit — Building menus programmatically |
| 43 | let editMenu = NSMenu(title: "Edit") |
| 44 | let undoItem = NSMenuItem(title: "Undo", action: #selector(UndoManager.undo), keyEquivalent: "z") |
| 45 | let redoItem = NSMenuItem(title: "Redo", action: #selector(UndoManager.redo), keyEquivalent: "Z") |
| 46 | editMenu.addItem(undoItem) |
| 47 | editMenu.addItem(redoItem) |
| 48 | editMenu.addItem(.separator()) |
| 49 | ``` |
| 50 | |
| 51 | ### Rule 1.2 — Keyboard Shortcuts for All Menu Items |
| 52 | |
| 53 | Every menu item that performs an action must have a keyboard shortcut. Use standard shortcuts for standard actions (Cmd+C, Cmd+V, Cmd+Z, etc.). Custom shortcuts should use Cmd plus a letter. Reserve Cmd+Shift, Cmd+Option, and Cmd+Ctrl combos for secondary actions. |
| 54 | |
| 55 | **Standard Shortcut Reference:** |
| 56 | |
| 57 | | Action | Shortcut | |
| 58 | |--------|----------| |
| 59 | | New | Cmd+N | |
| 60 | | Open | Cmd+O | |
| 61 | | Close | Cmd+W | |
| 62 | | Save | Cmd+S | |
| 63 | | Save As | Cmd+Shift+S | |
| 64 | | Print | Cmd+P | |
| 65 | | Undo | Cmd+Z | |
| 66 | | Redo | Cmd+Shift+Z | |
| 67 | | Cut | Cmd+X | |
| 68 | | Copy | Cmd+C | |
| 69 | | Paste | Cmd+V | |
| 70 | | Select All | Cmd+A | |
| 71 | | Find | Cmd+F | |
| 72 | | Find Next | Cmd+G | |
| 73 | | Preferences/Settings | Cmd+, | |
| 74 | | Hide App | Cmd+H | |
| 75 | | Quit | Cmd+Q | |
| 76 | | Minimize | Cmd+M | |
| 77 | | Fullscreen | Cmd+Ctrl+F | |
| 78 | |
| 79 | ### Rule 1.3 — Dynamic Menu Updates |
| 80 | |
| 81 | Menu items must reflect current state. Disable items that are not applicable. Update titles to match context (e.g., "Undo Typing" not just "Undo"). Toggle checkmarks for on/off states. |
| 82 | |
| 83 | ```swift |
| 84 | // SwiftUI — Add sidebar toggle alongside existing toolbar menu commands |
| 85 | CommandGroup(after: .toolbar) { |
| 86 | Button(showingSidebar ? "Hide Sidebar" : "Show Sidebar") { |
| 87 | showingSidebar.toggle() |
| 88 | } |
| 89 | .keyboardShortcut("S", modifiers: [.command, .control]) |
| 90 | } |
| 91 | ``` |
| 92 | |
| 93 | ```swift |
| 94 | // AppKit — Validate menu items |
| 95 | override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { |
| 96 | if menuItem.action == #selector(delete(_:)) { |
| 97 | menuItem.title = selectedItems.count > 1 ? "Delete \(selectedItems.count) Items" : "Delete" |
| 98 | return !selectedItems.isEmpty |
| 99 | } |
| 100 | return super.validateMenuItem(menuItem) |
| 101 | } |
| 102 | ``` |
| 103 | |
| 104 | ### Rule 1.4 — Contextual Menus |
| 105 | |
| 106 | Provide right-click context menus on all interactive elements. Context menus should contain the most relevant subset of menu bar actions for the clicked element, plus element-specific actions. |
| 107 | |
| 108 | ```swift |
| 109 | // SwiftUI |
| 110 | Text(item.name) |
| 111 | .contextMenu { |
| 112 | Button("Rename...") { rename(item) } |
| 113 | Button("Duplicate") { duplicate(item) } |
| 114 | Divider() |
| 115 | Button("Delete", role: .destructive) { delete(item) } |
| 116 | } |
| 117 | ``` |
| 118 | |
| 119 | ### Rule 1.5 — App Menu Structure |
| 120 | |
| 121 | The App menu (leftmost, bold app name) must contain: About, Preferences/Settings (Cmd+,), Services submenu, Hide App (Cmd+H), Hide Others (Cmd+Option+H), Show All, Quit (Cmd+Q). Never rename or remove these standard items. |
| 122 | |
| 123 | ```swift |
| 124 | // SwiftUI — Settings scene |
| 125 | @main |
| 126 | struct MyApp: App { |
| 127 | var body: some Scene { |
| 128 | WindowGroup { ContentView() } |
| 129 | Settings { SettingsView() } // Automatically wired to Cmd+, |
| 130 | } |
| 131 | } |
| 132 | ``` |
| 133 | |
| 134 | ### Rule 1.6 — Stable Command Names and Locations |
| 135 | |
| 136 | Treat the menu bar as the app's command memory. Keep common actions in consistent menus with stable names and shortcuts so user |