A two-bar sketchybar setup for macOS — a top bar for notifications + status, a bottom bar for system stats and yabai spaces. Lean Bash plugins, a shared popup factory, and per-app notification widgets that read directly from each app's local data (no hosted services).
Top bar — right: datetime · battery · network · (opt-in: github)
Top bar — left: weather + a row of notification pills. Each notification pill shows a count on the bar and a hover-popup with per-item detail. All popups share the same chrome (rounded card, dim border, hover-highlight rows, clickable header that opens the app).
| Widget | Source | What it shows |
|---|---|---|
weather |
wttr.in | Current temp + 3-day forecast popup |
notif_calendar |
icalBuddy + Apple Calendar | Pill = next event title, popup = today + tomorrow |
notif_mail |
AppleScript Mail.app | Pill = unread count, popup = recent unread |
notif_whatsapp |
Dock badge + macOS notification DB | Pill = unread count, popup = recent chats |
notif_notion |
Notion REST API | Pill = open task count, popup = per-task with priority/deadline |
notif_slack |
Decrypted d cookie + Slack's client.counts |
Pill = mention count, popup = per-channel split into Mentions / Messages |
github |
gh api graphql |
Pill = total, popup = Mentions / Review Requested / Assigned |
Bottom bar — right: live system stats grouped into pill cards — [net] [cpu] [mem] [ssd] (a fifth sys_pill with loadavg + uptime is wired up but hidden by default — set HIDE_SYS_PILL=0 in sketchybarrc_bottom to surface it). Driven by stats_provider (1s tick) + macmon for CPU temperature.
Bottom bar — left: yabai spaces with per-app icons (active app bright, others dimmed).
# Required
brew tap FelixKratz/formulae
brew install sketchybar jq
# Bottom-bar stats engine
brew tap joncrangle/tap
brew install sketchybar-system-stats
brew install vladkens/tap/macmon # CPU temp (provider's CPU_TEMP is broken on Apple Silicon)
# Per-widget deps (only what you'll enable)
brew install ical-buddy # notif_calendar
brew install gh # github
brew install koekeishiya/formulae/yabai # bottom-bar spaces
# Nerd Font for icons
brew install --cask font-meslo-lg-nerd-font
# App-icon font for the yabai spaces row
# https://github.com/kvndrsslr/sketchybar-app-font (download the .ttf,
# double-click to install)git clone https://github.com/g-udi/gaudi-sketchybar ~/.config/sketchybar/gaudi-sketchybar
cd ~/.config/sketchybar/gaudi-sketchybar
./setup.sh # copies example configs into place
brew services start sketchybarsetup.sh is idempotent — re-running it never overwrites your customised configs.
If you don't have a ~/.config/sketchybar/sketchybarrc already, point it at
this repo:
mkdir -p ~/.config/sketchybar
cat > ~/.config/sketchybar/sketchybarrc <<'EOF'
#!/bin/bash
exec ~/.config/sketchybar/gaudi-sketchybar/sketchybarrc
EOF
chmod +x ~/.config/sketchybar/sketchybarrcThe bottom bar is spawned automatically by the top bar's sketchybarrc (see
its tail — it execs a renamed sketchybar_bottom binary).
# Create the renamed binary used by the bottom bar's mach service.
ln -sf "$(brew --prefix)/bin/sketchybar" "$(brew --prefix)/bin/sketchybar_bottom"Almost everything user-specific lives in two files (both .gitignored):
| File | What |
|---|---|
lib/configs.sh |
Layout arrays — which widgets show and in what order |
lib/plugins/notif_notion/config.json |
Notion DB(s) to query (template at config.example.json) |
Both are bootstrapped from their *.example.* counterparts by setup.sh.
Open lib/configs.sh and uncomment the relevant line under GAUDI_TOP_LEFT
(or GAUDI_TOP_RIGHT for github), then reload:
brew services restart sketchybarEach plugin's header comment (lib/plugins/<widget>/index.sh) has the
specific setup steps. The non-trivial ones:
- Create a Notion integration at https://www.notion.so/my-integrations, copy the secret
echo 'ntn_…' > ~/.config/gaudi/notion_token && chmod 600 ~/.config/gaudi/notion_token- Share each DB you want to query with the integration in Notion's UI
- Edit
lib/plugins/notif_notion/config.jsonwith the DB id(s) + filter
- Just have the Slack desktop app installed and logged in. On first launch the widget triggers a macOS keychain prompt for "Slack Safe Storage" — click Always Allow so the daemon stays headless.
- The widget decrypts the
dcookie from the Slack container, mines the activexoxcfrom leveldb strings, and hits Slack's undocumentedclient.counts. Tokens rotate every few months — re-launch Slack to refresh if the popup ever goes blank.
icalBuddyneeds Calendars permission. The widget runsicalBuddyfrom sketchybar's process tree which inherits sketchybar's own Calendar grant — usually no manual step needed. If the popup says ICALBUDDY NEEDS CALENDARS ACCESS, add/opt/homebrew/bin/icalBuddyto System Settings → Privacy & Security → Calendars manually.
- Install WhatsApp desktop from the Mac App Store; turn on notifications in both WhatsApp → Settings → Notifications and System Settings → Notifications → WhatsApp (otherwise the macOS notification DB stays empty and the popup has nothing to show).
- Top-bar widget order: edit
GAUDI_TOP_LEFT/GAUDI_TOP_RIGHTarrays inlib/configs.sh - Weather location:
export WEATHER_LOCATION="Berlin"before sketchybar starts - Colours:
lib/colors.sh— every widget's pill colour is one line - Popup chrome:
lib/popup.sh— header height, row hover speed, etc. - Padding / sizing:
style.sh—GAUDI_BOTTOM_Y_OFFSET, gauge widths, etc. - GitHub custom queries: drop a
lib/plugins/github/custom.sh(gitignored) that exportsCUSTOM_SECTIONS=("triage")+ arender_custom()function — the widget loads it and renders extra popup sections (e.g. an org-wide triage query). Example skeleton at the bottom oflib/plugins/github/index.sh.
sketchybarrc ← top bar entry point
sketchybarrc_bottom ← bottom bar entry point (sketchybar_bottom binary)
style.sh ← layout tokens + default-property arrays
lib/
colors.sh ← palette + per-widget colour aliases
icons.sh ← Nerd Font glyphs
common.sh ← sourced by every plugin (loads the three above)
configs.sh ← layout (gitignored; copy from configs.example.sh)
popup.sh ← shared popup primitives (header, sections, rows, empty state)
popup_hover.sh ← per-row mouse-hover dispatcher
popup_closer.sh ← click-outside-dismiss observer
plugins/<widget>/index.sh ← one folder per widget; each is self-contained
Two daemons:
sketchybarruns the top barsketchybar_bottomis a symlink to the same binary; macOS treats the different argv[0] as a separate mach service so the two bars don't fight
Click-outside-to-dismiss is handled by a single observer (popup_closer.sh)
subscribed to mouse.exited.global + front_app_switched. Add a new popup
widget by appending its item name to POPUP_OWNERS there.
MIT — see LICENSE.
- FelixKratz/SketchyBar — the bar engine
- joncrangle/sketchybar-system-stats — bottom-bar stats provider
- vladkens/macmon — CPU temperature on Apple Silicon
- kvndrsslr/sketchybar-app-font — yabai space app icons (and Notion + WhatsApp logos)
- Original gaudi.ubersicht.widget — colour palette and layout spirit