macOS Menu-Bar App Design
Parent: Apple Platform Publishing (Mac App Store) · Published reference · snapshot 2026-10-02 · skill frontend-ui/references/macos-menu-bar-app-design.md
Also known as: macOS menu-bar extra app design for a sandboxed network monitor (Mac App Store, macOS 26-27, Oct 2026): status items, HIG, notifications, accessibility, appearance, Python/PyObjC stack — verify against Apple HIG and developer docs
↓ Facts as markdownall context files
Source-anchored research for macOS Menu-Bar App Design. Run status: COMPLETED.
These notes link each claim to its source. Some citations identify archived research reports that are no longer hosted on this site. A published reference means the content is available; it does not certify independent review or accuracy.Read the editorial policy and follow the sources before relying on a claim.
Definitions
- rumps is a thin wrapper over NSStatusItem/NSMenu via PyObjC; its latest PyPI release is 0.4.0 (15 Oct 2022), so it has had no release for about four years and is effectively unmaintained. (high confidence) (verified as of 2026-10-02) [source]
- UNUserNotificationCenter is the replacement. It is available on macOS 10.14 and later, so any app targeting macOS 26-27 can use it without availability checks. (high confidence) [source]
Structure and components
- SF Symbols have four rendering modes (monochrome, hierarchical, palette, multicolor); monochrome applies one colour to all layers, which is the mode that suits an adaptive menu bar glyph. (medium confidence) [source]
- On macOS 26, iOS 26 and watchOS 26 app icons are layered (one background plus foreground layers) and gain Liquid Glass effects (specular highlight, refraction, translucency) applied by the system; authors import square, unmasked layers into Icon Composer, which also defines default/dark/mono variants, previews across system versions and exports to Xcode. HIG icon page last changed 'Refined guidance for Liquid Glass' on June 8, 2026. (medium confidence) (verified as of 2026-06-08) [source]
- Do not bake glass effects (highlights, inter-layer shadows, bevels, blur, glow) or masking into layers; prefer vector layers with crisp edges, vary layer opacity for depth, use solid/gradient backgrounds from Icon Composer or a full-bleed opaque image, and keep core features consistent across default, dark, clear and tinted appearances. Template is 1024x1024 px, Display P3. (low confidence) [source]
How it works
- Apple's criteria require that the VoiceOver activation (VO+Space) trigger the same behavior as a click, that all mouse-operable controls work under VoiceOver, that state and values be exposed through traits and value, and that non-default actions (context menus, long press) be offered as custom actions. (medium confidence) [source]
- NSImage.SymbolConfiguration(paletteColors:) yields a non-template, permanently coloured glyph. One app used it for a green active dot because vibrancy-disabling did not stop the colour shifting to blue on the dimmed menu bar of a non-key screen. (low confidence) [source]
- HIG: when people click a menu bar extra, display a menu, not a popover; use a popover only if the functionality is too complex for a menu. (medium confidence) [source]
- HIG: let the user, not the app, decide whether the menu bar extra is shown (typically a Settings toggle; consider offering it during setup). Do not rely on its presence, because the system hides extras to make room for app menus or when too many exist; also expose functionality through a Dock menu. (low confidence) [source]
- HIG notification actions: up to four buttons, short title-case labels, no app name, prefer nondestructive actions, and never an action that merely opens the app. Provide generic body text for when users hide previews. (low confidence) [source]
- Popover-based status item UIs are common despite HIG's menu preference, and macOS 26 introduced a positioning bug: SwiftUI popover content opens roughly 110 pt below the menu bar because AppKit sizes for the initial taller content; fix by laying out content and setting contentSize before showing. (low confidence) (verified as of 2026-09-18) [source]
- MenuBarExtra has no first-party API to read or set presentation state or to reach the NSStatusItem or popup NSWindow; third-party shims such as MenuBarExtraAccess reverse-engineer the window hierarchy, and direct NSStatusItem edits may be overwritten when SwiftUI refreshes. (medium confidence) [source]
- LSUIElement=YES in Info.plist is only the launch state: it starts the app with the accessory activation policy (no Dock icon, no app menu bar); NSApp.setActivationPolicy can move the process between .accessory and .regular at runtime. (medium confidence) [source]
How-to and procedures
- To claim VoiceOver support, users must complete all common tasks using only VoiceOver, without sighted help. Every control needs a concise, accurate label; label text must not include the control type or state. (high confidence) [source]
- Set a distinct autosaveName on every NSStatusItem: macOS stores 'NSStatusItem Preferred Position <name>' in the app's defaults and restores the Command-dragged position at launch; without it only the first item's position is remembered. (medium confidence) [source]
Measurements and reference values
- Bjango advises a working area of at most 22 pt tall, about 16x16 pt for a circular glyph to match system item weight, with formats SVG, PDF, or 1x/2x PNG; and notes Reduce Transparency turns the menu bar solid grey, so test the icon there. (low confidence) [source]
- A MenuBarExtra whose label/content views create models or new Date() values during body evaluation can loop on macOS 26 and pin the main thread at 100% CPU at launch, even with isInserted false; fix is eager model construction, TimelineView for time, and skipping unchanged writes. (low confidence) (verified as of 2026-01-01) [source]
Problems, failure modes and limitations
- HIG: use an alert, not a notification, to display an error message. Notifications do not appear when the app is frontmost (the app still receives the data and should surface it unobtrusively). (low confidence) [source]
- When there are more menu bar icons than fit right of the notch, macOS hides the overflow items with no notification and no user-facing indicator; apps cannot control their own position to avoid it. (high confidence) [source]
- NSStatusItem (AppKit) supports dynamic width, multiple click targets and non-template colours; the old view property is deprecated since macOS 10.14, so custom content goes through the button property and manual frame sizing. (medium confidence) [source]
- Accessory-policy apps cannot reliably bring a Settings window to the front or make it key; the workaround is switch to .regular, call NSApp.activate, open settings, then restore .accessory when the window closes. SettingsLink is unreliable inside MenuBarExtra, and the hidden helper window must be declared before the Settings scene. (medium confidence) [source]
- For a sandboxed live network monitor needing a dynamic status readout, right-click menu or detachable panel, NSStatusItem plus NSPopover/NSMenu is the lower-risk choice; MenuBarExtra fits simple menu or fixed-window UIs. (medium confidence) [source]
- CPython's standard library contains code that trips Mac App Store automated validation (reported as false positives); CPython ships a patch applied by configuring with --with-app-store-compliance. A python.org or Homebrew interpreter bundled unpatched may fail validation. (high confidence) [source]
- Signing a py2app bundle with sandbox entitlements can produce 'Killed: 9' with 'Requirements for restricted entitlements failed to validate, error -67050' and 'AMFI: code signature validation failed'; py2app issue 415 (Mar 2022, py2app 0.27) remains open with no maintainer resolution shown. (medium confidence) [source]
- Very old py2app versions (before 0.8.1) create invalid Python.framework bundles that fail Mac App Store checks; the older guides require recursive manual codesigning of nested frameworks and helpers with the sandbox entitlement. (medium confidence) [source]
- Apple's documentation marks NSUserNotification as deprecated at macOS 11.0 (introduced 10.8), with the message that all NSUserNotifications API should be replaced with UserNotifications.framework API. (high confidence) (verified as of 2026-10-02) [source]
- Python processes that are not an app bundle, such as Homebrew or conda Python, cannot use UNUserNotificationCenter. desktop-notifier falls back to a dummy backend that silently drops notifications. Its author says the API works only from a signed framework Python build or a bundled app, and that notifications appear to come from 'Python' unless the app is bundled. This matters for a PyObjC menu-bar app: ship it as a signed .app with its own bundle identifier. (medium confidence) [source]
- Calling requestAuthorization from a nested helper bundle (Notifier.app inside MyApp.app) was reported to fail with NSCocoaErrorDomain Code=4099, 'connection to service named com.apple.usernotifications.usernotificationservice was invalidated'. The thread has no accepted answer. Post notifications from the main app bundle. (low confidence) [source]
Comparisons and alternatives
- Using .accessory set in code (instead of LSUIElement) may flash the Dock icon at launch; one reported pattern is .prohibited in applicationWillFinishLaunching then .accessory in applicationDidFinishLaunching. Use LSUIElement=YES for a clean launch. (low confidence) [source]
- The user must authorize notifications. The first requestAuthorization call prompts and records the answer. Later calls do not prompt. Apple says to call getNotificationSettings and check authorization before scheduling, because the user can change settings at any time. It also advises requesting in context rather than blindly at first launch. Provisional authorization delivers quietly to Notification Center history with no prompt. (high confidence) [source]
Facts and statements
- Accessibility Nutrition Labels are optional. A developer may answer 'No' for support, and no mandatory date is stated in Apple's pages. (high confidence) (verified as of 2025-09-15) [source]
- The label covers nine features: VoiceOver, Voice Control, Larger Text, Dark Interface, Differentiate Without Color Alone, Sufficient Contrast, Reduced Motion, Captions, Audio Descriptions. (high confidence) [source]
- Support is declared per device in App Store Connect (App Accessibility sidebar), so a Mac app declares separately from iPhone or iPad. Answers save as drafts, publish only for devices with a live version, can change without a new submission, and may take up to 24 hours to show. (medium confidence) (verified as of 2026-10-02) [source]
- A status item whose button shows a bare SF Symbol or unlabeled template image is announced as the symbol's built-in name (e.g. 'Increase Brightness') or just 'image'. Set the button accessibilityLabel/accessibilityValue (and image accessibilityDescription) to the app name plus live status. (medium confidence) (verified as of 2026-05-07) [source]
- With a SwiftUI MenuBarExtra window-style item, the status button had nil target and action, so VO+Space (AXPress) and Voice Control did not open the popover, while real mouse clicks did. The reporter's fix was an AppKit NSStatusItem whose button has a target/action that toggles the popover. (low confidence) (verified as of 2026-10-02) [source]
- Popover content rows, progress bars and charts need explicit accessibility: group related text with accessibilityElement(children: .combine), expose percentages as accessibilityValue, and expose selection state on switcher buttons. (low confidence) (verified as of 2026-05-07) [source]
- The label is self-declared; App Review reportedly acts only on intentionally misleading labels, so accuracy is the developer's responsibility and drifts if not retested each release. (medium confidence) (verified as of 2025-06-30) [source]
- A template NSImage (isTemplate = true) uses only its alpha channel; the system fills it with the correct colour for light/dark menu bar and highlighted state. (medium confidence) [source]
- NSButton only recolours a template image with contentTintColor. Setting isTemplate = false before contentTintColor silently discards the tint: states rendered black (invisible on a dark menu bar) and SF Symbols fell back to default multicolor. (low confidence) [source]
- In SwiftUI MenuBarExtra, Image(systemName:).renderingMode(.original).foregroundStyle(.red) still renders grey in the menu bar. Workaround reported in an Apple forum thread: build an NSImage with NSImage.SymbolConfiguration().applying(.preferringMulticolor()) and wrap it in Image(nsImage:). (low confidence) [source]
- On macOS 26 Tahoe and 27 the menu bar is transparent by default, painted from the wallpaper; system text/icon colour is chosen automatically (white or black) from the brightness behind the bar. Users can enable 'Show menu bar background', Reduce transparency, and the Liquid Glass Clear/Tinted toggle (26.1) or slider (27). Fixed-colour icons therefore need contrast checks on bright and dark wallpapers. (low confidence) (verified as of 2026-10-02) [source]
- HIG recommends a symbol or interface icon for the extra; both are black-and-clear shapes that the system recolors for light/dark menu bars and the selected state. HIG states menu bar height is 24 pt. (low confidence) (verified as of 2026-10-02) [source]
- Menu bar extra images should be template (alpha-only) artwork; macOS ignores colour and uses only alpha, giving automatic light/dark adaptation. On the translucent macOS 26 menu bar, a shipped app fixed low-contrast status icons by preparing them as template images. (medium confidence) [source]
- HIG notifications: be concise and informative; do not send multiple notifications for the same thing; do not tell people to perform tasks in-app (use notification actions instead); do not include sensitive or personal info; do not include the app name or icon (system shows it). (low confidence) [source]
- HIG badges: use a badge only for the count of unread notifications, never for other numeric data (e.g. throughput numbers), do not make it the only channel for essential info, and do not mimic a badge with a custom image. The notifications page lists no additional macOS-specific considerations. (low confidence) [source]
- Build pipeline: the .icon bundle (a folder) compiles to Assets.car via xcrun actool, which needs Xcode 26 and the macOS 26 SDK; Info.plist needs CFBundleIconName naming the icon; keep the legacy .icns in Resources for pre-26 macOS; a .icon with more than 4 groups reportedly fails to compile. Apps shipping only .icns are shown shrunk inside a rounded-square container ('icon jail') on Tahoe. (medium confidence) (verified as of 2025-09-26) [source]
- An app can detect that its status item is hidden behind the notch by observing occlusionState (window occlusion notifications) on the status item's window; Tailscale used this to show a dialog telling users to rearrange icons. (low confidence) [source]
- Do not make the menu bar icon the only way to reach the app: Tailscale shipped a windowed interface (1.96.2+) reachable from the Dock to remove dependence on a possibly hidden icon. (low confidence) (verified as of 2026-03-27) [source]
- macOS 26 adds System Settings > Menu Bar > Allow in the Menu Bar, letting the user disable an individual app's status item. Apple DTS states there is no API to read that state; the alternative is to set NSStatusItem removalAllowed (NSStatusItemBehaviorRemovalAllowed) so the user can remove the item directly, and observe removal via KVO on isVisible. Feedback FB18341478 requests exposing the preference. (medium confidence) (verified as of 2025-06-30) [source]
- Position loss: on macOS 26, removing a status item (removeStatusItem) or setting isVisible = false while the app runs clears its saved Preferred Position, so the re-created item appears at the far left. Documentation says visibility persists via autosaveName, but a 2021 Feedback report (FB9052637, still open) showed isVisible=false then true loses the Command-dragged position. Workaround: capture the saved defaults before removal and restore after. (medium confidence) (verified as of 2026-09-18) [source]
- Reportedly macOS 27 (released 2026-09-14) adds a native expand/collapse chevron at the edge of the icon area that reveals notch-hidden icons to the left of the notch; the OS chooses which fold by width, nearest the notch first. The same sources say the render rework broke Bartender, Ice and other third-party menu bar managers in beta 1. (low confidence) (verified as of 2026-10-02) [source]
- Each SwiftUI MenuBarExtra creates one NSStatusItem under the hood; the status-item button is limited to image, text, or image+text, with no custom view or colour styling through the public API. (high confidence) [source]
- On macOS Tahoe (26) the menu bar grew from 24pt to 30pt and right-clicks at the very top edge no longer fire an NSStatusItem action (left-click still does); reported January 2026 with no Apple reply. (low confidence) (verified as of 2026-01-01) [source]
- The rumps README says it fits notification utilities, daemon controllers and periodic API displays, and is a poor fit for GUI-first apps; it recommends py2app for standalone builds (LSUIElement True) and warns against virtualenv. (high confidence) [source]
- py2app is maintained: 0.28.10 (13 Feb 2026) fixed setuptools 82 breakage; 0.28.9 added Python 3.14 support. It requires Python >=3.10, builds only on macOS and does not support free-threaded Python. (low confidence) (verified as of 2026-02-13) [source]
- A sandboxed py2app app fails at launch if the bundled Python runtime is missing or mis-linked: the app falls back to a system or home-directory Python the sandbox blocks, giving errors like 'ImportError: No module named os'. The fix is to bundle Python.framework properly and remove home-directory paths such as PythonExecutable from Info.plist. (medium confidence) [source]
- An Apple DTS engineer's suggested remedy in a sandboxed-Python forum thread was to rewrite the app in Swift, and the thread notes temporary-exception entitlements are not acceptable for App Store releases. A sandboxed network monitor should plan on only standard entitlements (e.g. network.client). (low confidence) [source]
- The most recent public end-to-end Python-to-Mac-App-Store guide found uses PyInstaller+PySide6 (Oct 2022, Python 3.9, tested only on M1) and requires a provisioning profile, two 3rd-party Mac Developer certificates and a hand-built entitlements plist; it is fragile to spec-file settings. No 2024-2026 py2app/rumps success story was found. (low confidence) (verified as of 2022-10-01) [source]
- Native-shell option: keep the status item, sandbox entitlements, signing and App Store packaging in a small Swift/Xcode app and run Python only as an embedded or helper engine. This follows from the unmaintained rumps, the interpreter-compliance patch and the fragile signing path; it is an inference, not an Apple-documented pattern. (low confidence) [source]
- UNUserNotificationCenter needs a valid main bundle. From an unbundled LaunchAgent, currentNotificationCenter asserts 'bundleProxyForCurrentProcess is nil'. The forum workaround is a bundled menu-bar app that receives requests over XPC and posts them. The old NSUserNotificationCenter worked without a bundle, which is why it was the quick fix. (medium confidence) [source]
- Apple's authorization guide says a notification delivered while the app is in the foreground reaches the delegate's willPresent method. Without a UNUserNotificationCenterDelegate implementing it, the system does not show a banner for the active app. A menu-bar app can count as foreground, so set the delegate before the first post. (low confidence) [source]
- On macOS 26, a Swift @MainActor class that passes a closure to requestAuthorization crashes at launch with EXC_BREAKPOINT/SIGTRAP. UserNotifications calls the closure on its own queue (UNUserNotificationServiceConnection.call-out) and the runtime traps on the executor check. macOS 15 tolerated it. The fix is to mark the callbacks @Sendable. In an LSUIElement app the crash shows no dialog or Dock icon, so check ~/Library/Logs/DiagnosticReports/. (low confidence) (verified as of 2026-10-02) [source]
Children
- Accessibility for menu bar apps VoiceOver and App Store Accessibility Nutrition Labels
- Briefcase macOS App Store packaging (frontier)
- Contrast testing on transparent menu bar (frontier)
- Dark mode semantic colours and SF Symbols in status items
- HIG for menu bar extras windows and notifications and Liquid Glass icons Icon Composer
- Icon Composer .icon to Assets.car build from a non-Xcode Python/py2app stack (frontier)
- Keyboard-only and Voice Control access to status items (frontier)
- Menu bar overflow notch hiding and user-disabled menu bar items
- Menu versus popover versus window trade-offs for a network monitor (frontier)
- NSAccessibility.post announcements for status changes (frontier)
- NSDockTile badge for regular-policy menu bar apps (frontier)
- NSStatusItem removalAllowed KVO (frontier)
- NSStatusItem versus SwiftUI MenuBarExtra and activation policy LSUIElement and Dock tile
- Notification authorization-denied UX in a menu-bar app (frontier)
- Notification categories and actions on macOS (frontier)
- PyObjC 12 and macOS 26 SDK bindings (frontier)
- Python PyObjC rumps py2app as a Mac App Store UI stack limits and native-shell options
- Reduced Motion and Increase Contrast handling in menu bar icons (frontier)
- Settings scene workaround for accessory apps (frontier)
- Swift shell with embedded Python helper (XPC) (frontier)
- UNUserNotificationCenterDelegate willPresent for menu-bar apps (frontier)
- UserNotifications UNUserNotificationCenter migration from NSUserNotification
- autosaveName position persistence (frontier)
- contentTintColor and badge/alert state icons (frontier)
- occlusionState detection of hidden status item (frontier)
Frontier under this node: Briefcase macOS App Store packaging, Contrast testing on transparent menu bar, Icon Composer .icon to Assets.car build from a non-Xcode Python/py2app stack, Keyboard-only and Voice Control access to status items, Menu versus popover versus window trade-offs for a network monitor, NSAccessibility.post announcements for status changes, NSDockTile badge for regular-policy menu bar apps, NSStatusItem removalAllowed KVO, Notification authorization-denied UX in a menu-bar app, Notification categories and actions on macOS, PyObjC 12 and macOS 26 SDK bindings, Reduced Motion and Increase Contrast handling in menu bar icons, Settings scene workaround for accessory apps, Swift shell with embedded Python helper (XPC), UNUserNotificationCenterDelegate willPresent for menu-bar apps, autosaveName position persistence, contentTintColor and badge/alert state icons, occlusionState detection of hidden status item