Why 22 Vertical Pixels Beat a Full Window for Companion Apps
On non-notched displays, the classic macOS menu bar gives an application a strict 22 points of vertical height to justify its existence. Applying the system's NSStatusBar.squareLength constant assigns an icon-only item a width equal to that thickness, leaving a roughly 22-by-22-point square where status item artwork—drawn as a 16-to-18-point template image—sits with breathing room.
Building a native Mac companion for a web service like Basecamp, Highrise, or Campfire inside that tiny square forces three architectural decisions in a fixed order: defining the single core job, solving background state polling, and earning interruption rights. It demands these answers before engineering time disappears into custom window chrome, split views, or complex layout managers. Before any interface sketch begins on a companion project, a recurring review question settles the scope: what would the user do with this app if it could only show one icon and one small panel? If nobody can answer in a single sentence, the product is a browser bookmark masquerading as a Mac executable.
Full-window wrappers around web dashboards duplicate what modern browsers already handle well. Safari and Chrome provide tabs, back-and-forward history, find-in-page, zoom controls, and an active session cookie store. While authoring sessions in the main web application stretch across tens of minutes, ambient companion tasks—monitoring a project room, capturing a quick note, or glancing at team activity—are measured in seconds per interaction. A menu bar utility respects this boundary by living in the peripheral vision of the operating system.
This 22-point architecture fits ambient tools that watch a remote service or capture rapid input; software meant to be the primary place where long messages, documents, or project to-dos get written should start from a standard window instead, because the menu bar will cramp sustained authoring.
Stripping a Web Product Down to One Native Action
A popover or NSMenu attached to the system status bar refuses to hold an entire web dashboard. Its geometry forces interface design down to one recurring micro-interaction that completes in under two seconds from click to dismissal: one click opens the panel, one action completes the job, and pressing Escape or clicking elsewhere dismisses it.
During Mac development cycles, teams can trim feature bloat using a strict card test. Every candidate feature is written on its own line, and the popover is mocked at a fixed width between 280 and 360 points—wider than that, and the panel reads like a detached window anchored awkwardly to the top of the screen. Lines are added top-down until the card would require scrolling. Whatever falls below that cutoff gets routed to an "open in browser" link rather than rebuilt natively. On a time-tracking companion, only the start/stop toggle and current project name survive this cut.
The Scroll-View Cutoff: The moment a menu bar companion's popover needs a scroll view, its own toolbar, or a segmented control with three or more tabs, that feature moves to an "open in browser" link and does not get a native home until users demonstrate a need for a full window.
Purpose-built utilities differ from "mini-browser" desktop wrappers across classic web-app companions by isolating the highest-frequency action:
- Project management companions (Basecamp style): Dropping a single file or a one-line note into an active project without losing focus on the current desktop workspace.
- CRM utilities (Highrise style): Logging a quick call note or toggling a task timer between client accounts in two clicks.
- Team chat monitors (Campfire style): Checking whether a project room has unread lines since the last visit before committing to opening the full transcript.
What NSStatusItem Teaches You About Background State and Sync
How does a companion utility communicate status when its interface remains closed around 99 percent of the workday? A standard windowed application can afford to fetch data lazily when brought to the foreground, whereas a menu bar icon is perpetually visible. Your background polling, webhook listener, or local cache invalidation must be solid from day one.
The sync layer has to be designed before the popover, starting from the icon's visible states. In Apple's NSStatusItem documentation, the system-wide status bar requires careful resource citizenry, and since OS X 10.10, the item's button property is the supported surface for setting images and target-action handlers. Marking artwork as template images via NSImage setTemplate: allows AppKit to automatically tint the icon for light, dark, and highlighted menu bar appearances, while a subtle monochrome dot signals unread or active states without injecting jarring color into the top right of the display.
The Four-State Icon Trace
Before drawing the popover view hierarchy, engineers map out the four states the 22-point template icon must represent without a single click: idle, active/running, unread activity, and disconnected or auth-expired. Tracing each visual state backward to the exact poll, local cache, or wake handler that drives it exposes architectural gaps early. Any icon state lacking a deterministic data source is a bug waiting in the status bar.
Because setting LSUIElement to YES in Info.plist hides the application from the Dock and the Command-Tab switcher, the background process runs continuously without receiving standard window-lifecycle hints from being minimized or hidden. Polling a remote web API every 60 to 120 seconds acts as a practical ceiling for ambient freshness. When network requests fail, the client must back off exponentially—doubling the interval up to a cap of 10 to 15 minutes—so a fleet of desktop companions never burns through the web service's rate limits. Setting a tolerance of roughly 10 percent of the interval on repeating NSTimer instances lets macOS coalesce process wakeups to save battery, while subscribing to NSWorkspace willSleep and didWake notifications allows the daemon to pause polling before sleep and fire one clean refresh upon wake instead of unleashing a burst of queued HTTP requests.
Silent Badges vs. Audible Banners in Ambient Mac Tools
A team-chat companion illustrates the classic notification trap: a client that posts a native system banner and audio ping for every new line typed in any room the user has joined. An active room can generate an exhausting stream of slide-in banners, prompting users to quit the app.
Ambient tools risk losing their status bar residency when they treat every remote webhook as a desktop emergency. A strict two-tier signaling model keeps routine activity separate from events that warrant interruption.
- Tier One (Passive Ambient Awareness): General room activity or background state shifts trigger only a template-icon change or a subtle dot in the menu bar. It produces no sound, no banner, and no lingering entry in Notification Center.
- Tier Two (Active Interruption): System user notifications are reserved exclusively for direct mentions, explicit user-configured keyword matches, and authentication or network errors that block the app's single core job.
One-Click Notification Triage: Place a snooze control at the very top of the status menu or popover offering fixed durations of 30 minutes, 1 hour, and "until tomorrow" so users silence a noisy afternoon in one click instead of quitting the process.
Context-aware duplicate suppression refines this boundary further. If the companion detects that the frontmost application is a web browser actively focused on the same service's domain, it holds back local alerts for that account. The web interface is already rendering the event directly in front of the user.
Ship the LSUIElement First and Let Users Demand the Window
A primary application window is something users should earn for a companion app through observed behavior rather than something designers propose on day one. When evaluating usage patterns and incoming support requests, teams can sort feedback against three concrete graduation triggers:
- Sustained keyboard navigation through long lists that exceed the height of a single fixed card.
- Multi-item drag-and-drop workflows that require staging files or records across containers.
- Side-by-side comparison of two or more project records simultaneously.
Starting in the menu bar makes eventual graduation effortless. When the sync engine, Keychain-stored credentials, and state models already live decoupled from the popover's view controller, adding a secondary NSWindowController later requires zero architectural rework. An LSUIElement application can present a standard NSWindow at any time; calling NSApp activateIgnoringOtherApps: when the window opens brings it cleanly to the front despite having no Dock icon. If that window eventually becomes the primary workspace, flipping the activation policy at runtime via NSApplicationActivationPolicyRegular restores the Dock icon and menu bar without rewriting the underlying client.
Set LSUIElement to YES in your Info.plist on day one, ship the 22-point menu bar version of your Mac companion app first, and refuse to build a main window until real daily usage proves the popover is too small.
Comments
No comments so far.
Join the Discussion