●Jan Layola
Blog/06/Tooling · macOS

A menu bar that knows about my meetings.

When I'm heads down, notifications are off, and a calendar reminder is exactly what gets silenced. So my menu bar shows the next meeting whatever Focus mode is doing, and treats everything it reads from my calendar and my screen as private data.

Illustration: a menu bar showing a focus timer and a meeting pill, while a notification is silenced
The bar, recreated/placeholder content
EXT 1 2 3 6 Chat 7 Calendar 8 Email MAC 4 Code
Placeholder song – Artist
MTG 12m Design review MTG NOW FOCUS 18:42 PRESENT RAM 78% CPU 86% 42% Wed 23 Sep14:48
Try the states. In a focus session the timer counts down and the meeting pill still turns up half an hour before the call. Presenting hides every title while the camera card lights up. The layout and colours follow the real config; the content is made up. On a phone, scroll the bar sideways.

01 — The situationHeads down

The best part of my day is the stretch where I'm deep in a diff or a design problem and nothing interrupts me. To protect it, I switch notifications off, either with macOS Focus or by starting a 25- or 50-minute focus timer in my own menu bar. Then I get on with the work.

The catch is obvious once you say it out loud. A calendar reminder is a notification, so the one thing Focus mode reliably silences is the thing I can't afford to miss: the meeting where a client, a teammate or a product decision is waiting for me. I wanted deep focus and a guarantee that the important meeting still reaches me.

The second requirement came straight after. Anything that can see my calendar and my screen is handling personal data, mine and the people I meet with, so it has to be built with the care I'd expect from any product feature that touches personal data. Privacy and security have been interests of mine for a long time, and a personal tool doesn't get a pass.

Both answers ended up in the same place: the menu bar. My desktop runs on AeroSpace, a tiling window manager that keeps its own workspaces, so macOS's own bar can't show them anyway. I cover the rest of the setup in My setup, and how I got here. The bar is SketchyBar, an empty strip where every item is a small script. In June it was SketchyBar's sample config. By September it had become a small, tested piece of software built around these two ideas.

02 — FocusThe meeting that matters

The trick is that the meeting pill isn't a notification at all. Focus mode filters notifications; it doesn't hide the menu bar. The MTG pill is drawn by SketchyBar from data a small calendar helper keeps up to date, so it appears whether Focus is on, off or halfway through a 50-minute session. The bar doesn't know or care what Focus is doing, and that's the point.

  • It shows up early. MTG appears 30 minutes before a meeting starts, with a countdown in minutes (12m) that becomes NOW at the start time.
  • It stays until it stops being useful. The pill goes away five minutes after the start or when the meeting ends, whichever comes first, so a late join is still one click away.
  • It sits next to the focus timer, not under it. Starting a focus session adds a FOCUS countdown to the bar and hides nothing. When a meeting comes into range, both pills are visible side by side. The timer ends with a short chime and DONE, and clicking DONE starts the break.
  • One meeting at a time. If two meetings overlap, it shows the one that starts first.
clickJoinOpens the call link: Google Meet, Zoom, Teams, Webex, Whereby, Jitsi or a Slack huddle. If the event has no call link, it switches to my calendar workspace instead, so I still land on the details.
right-clickDetailsShows the meeting's time, the same join action and a shortcut to record.
micRecord with GranolaA microphone next to MTG starts a new Granola note with transcription on. It's a launcher, not a recording indicator: Granola owns capture, pause and stop.

The last rule matters as much as the pill itself: a wrong meeting is worse than no meeting. If the calendar helper hasn't refreshed in the last three minutes, or calendar access hasn't been granted, the pill simply isn't drawn. It won't offer a link from a cache that has gone stale. The test for it is named exactly that: stale meeting never opens old link.

03 — PrivacyA calendar is personal data

Behind the pill is a native helper, written in Swift, that reads the calendars macOS already syncs. It refreshes every minute and whenever a calendar changes. A calendar holds names, client meetings, private appointments and call links, so I held the helper to the same standard as a feature that handles someone else's data:

  1. Read-only.macOS calls the permission "Full Access", but the helper only reads events. It never creates, edits or deletes one, and the permission prompt says exactly that.
  2. As little as possible, for as short as possible.It only looks at the next 24 hours and skips all-day, cancelled and declined events. For each meeting it keeps four things: start, end, title and a vetted call link. Notes and locations are scanned for a link, but never stored.
  3. Owner-only storage.The cache is written atomically into an owner-only folder under ~/Library/Caches, and no credentials live in the dotfiles.
  4. Links are data, not commands.A join link must be HTTPS, on a known meeting domain, with no embedded username or password. It's checked once when the helper stores it and again before it's opened, then passed to open as a single argument. The controller's first line says it plainly: "External names and URLs never become shell code."
  5. Quiet logs.When something fails, the log records the type of error and nothing else: no titles, no URLs, no arguments.

None of this is exotic. It's data minimisation, least privilege and input validation, the same rules I'd ask for in a code review. They just apply to a menu bar this time.

04 — Screen sharingSafe to share

The other place a menu bar leaks is a screen share. Everyone on the call sees the app you're in, the song that's playing and the title of your next meeting. Three things keep that under control:

  • Presentation mode (⌥⇧P) hides app, media and meeting titles with one shortcut. The meeting pill keeps its countdown but drops the title, and even its menu says "Upcoming meeting". An orange PRESENT pill stays visible so I can't forget it's on, clicking it turns it off, and the mode survives a reload of the bar.
  • No flash of private titles. Window-focus events are coalesced, so an event already in flight can't briefly show an app name after presentation mode has been switched on.
  • A mic/camera card appears only while one of them is actually live: amber for the microphone, red for the camera. It asks CoreAudio and CoreMediaIO the same question that drives macOS's own orange and green dots, rather than guessing from which apps are open, and it ignores output-only devices so music never lights it up.

On the laptop screen a compact profile goes further and drops app, media and meeting titles entirely, because the space beside the camera notch is too small for them.

05 — Design ruleQuiet by default

A bar that shouts all day would undo the focus it's meant to protect. The rule that shapes everything is a comment at the top of the config:

“Ordinary information sits directly on the bar. Only focus and alerts get a surface.”

  • Metric cards stay hidden until they matter. CPU only appears above 70%, in amber, and turns red at 90%. Memory also appears above 70%, but its colour follows the kernel's memory-pressure level, which is what actually predicts slowdowns. Clicking either opens btop. With coding agents building in the background, that's my early warning.
  • Battery only speaks when it has something to say. It hides while charging unless it's low, shows time left on battery, and shows the charge rate while charging.
  • One accent colour means "attention". Orange marks the focused workspace, the meeting pill and presentation mode, and it matches the outline JankyBorders draws around the focused window.

Getting here took a round trip. In August I added network and disk cards; in September I took them out again, along with the list of apps in every workspace. If it doesn't help me decide something right now, it doesn't get a place.

06 — Under the hoodBuilt like a product

There's no framework and nothing exotic. The pieces are small, and each has one job:

~/.config/sketchybar
sketchybarrc            # the bar, its defaults, which items exist
items/*.sh              # declare each item and when it updates
plugins/*.sh            # fill items in from AeroSpace, pmset, vm_stat…
plugins/toolbar.py      # one controller: menus, timers, meetings, privacy
native/ToolbarBridge    # Swift: calendars, audio outputs, screens
bin/capture_state       # Swift: is the mic or camera live?
tests/test_toolbar.py   # privacy, meetings, launching, timers, profiles
  • Source, not binaries. The Swift helpers are compiled from source on first run and whenever that source changes, so the repo only carries code I can read.
  • One owner for shared state. The Python controller takes a file lock, writes its state atomically with owner-only permissions, and is the only thing that decides what's visible. A display change can't interrupt a timer or switch presentation mode off.
  • Cheap when idle. The controller only ticks every second while a timer is running; otherwise it checks in every few seconds.
  • Works on any desk. The bar measures the camera notch instead of hard-coding it, gives the cards that won't fit beside it a twin on the other side, and switches layout profiles when I dock or undock.
  • Tested like a feature. The test suite reads like a spec for the two promises in this post. A few of its names:
  • meeting window
  • meeting excludes stale ended and old
  • join link validation
  • stale meeting never opens old link
  • presentation survives refresh and hides titles
  • granola visibility and render never start recording

A menu bar with a test suite sounds like overkill until you remember what it touches: my calendar, my microphone and whatever is on my screen when I share it.

07 — IterationReproducible, and still changing

The bar lives in mac-setup next to the rest of my dotfiles, and GNU Stow symlinks it into ~/.config. Editing the bar is editing the repo, and a new Mac gets it with the same single command as everything else. AeroSpace starts SketchyBar and JankyBorders at login.

  1. June
    The sample config. Workspaces with their app names, the date, volume, battery and CPU, always on.
  2. August
    Awareness. Notch detection and twin cards, the mic/camera indicator, colour thresholds, and network and disk cards.
  3. September
    Focus and privacy. The meeting pill and Granola shortcut, the focus timer, presentation mode, the read-only calendar helper, display profiles and the tests. Network, disk and the per-workspace app lists went back out.
  4. Next
    Whatever gets in my way. That's been the only roadmap so far.

The honest trade-offs

  • It's a lot of machinery for a menu bar. Shell, Python and Swift in one folder is powerful, but I'm the only maintainer.
  • It depends on the calendar being right. The pill is only as good as what's synced to macOS. A meeting that isn't on my calendar won't show up, and a stale sync hides the pill on purpose.
  • It leans on other people's behaviour. Granola's desktop link could change in a future version, and macOS permission prompts shift between releases.

Standing on shoulders

If you want to borrow one idea, take this one: put the signal you can't miss somewhere that Focus mode doesn't reach, and treat everything it reads as private.

Written by
Senior Engineer, Experience Team Lead at InSpace · Breda, NL

I build NOVA's client platform and the design system behind it, and write about product engineering, frontend craft and shipping software with coding agents.

Show me your menu bar.

Questions, pushback or your own take? I read every email.