a pomodoro timer for your macOS menubar. one ring. no dock icon. start a session, hear rain, get to work.
most pomodoro apps either do too much or apologise for existing. pomo is opinionated: one popover, one accent colour, one decision at a time. the session ends with a spoken quote from someone who worked harder than you.
- configurable sessions. double-click the timer. 1–120 minutes. pick a number and defend it.
- automatic cycle. work, short break, long break after four sessions. you don't have to think about the structure. that's the point.
- ambient sound. rain, fire, lofi, cafe. persists across restarts. CC0 licensed, looped. the rain is better than your rain.
- session history. weekly dot grid, daily progress, streak count. evidence that you showed up.
- progress ring. fills as you work. visible in the menubar even when the popover is closed. quiet accountability.
- spoken quotes. eames, rams, jobs, morris, da vinci, michelangelo. never the same quote twice in a row. occasionally humbling.
- liquid glass controls. macOS 26.
- spring animations. on completion only. respects reduced motion. motion is a reward, not a fidget.
git clone https://github.com/stanleyraywood/pomo.git
cd pomo
./build.sh installbuilds a release binary, bundles Pomo.app, copies to /Applications. launch from spotlight.
to run from source: swift build && swift run
requires macOS 26+ and swift 6.2+.
the sequence matters. it was designed, not defaulted.
- t-10s: ambient sound begins fading out.
- t-0s: timer reaches 00:00. ring holds at 100%. the word "done." appears.
- +0.0s: completion chime. macOS notification. session recorded to disk.
- +0.5s: ring pulse. scale bounce. session dot pops in.
- +0.8s: a design quote, spoken aloud. randomly selected. occasionally devastating.
- +2.0s: advances to break. ring resets. silence.
in the menubar, the ring transitions from coral to green during the completing phase. even closed, it tells you something happened.
jetbrains mono, not SF mono. the typeface is part of the identity. no dynamic type. fixed sizes tuned for a 300pt popover. acceptable for a single-purpose utility. would revisit for a windowed app. probably won't build a windowed app.
type scale: 44 / 24 / 13 / 11. four sizes, two weights. every size has one job. if a size doesn't have a job it doesn't exist.
8pt grid. spacing values: 40, 32, 24, 16, 8. sub-grid at 4pt for tight elements. session dots are 8pt. ring stroke is 2pt. these numbers were chosen, not approximated.
one accent colour. coral #F27340 for everything interactive. system green for breaks. does not adapt between light and dark mode. confidence, not flexibility.
200pt ring, 2pt stroke. thin stroke, large face. 50pt breathing room each side in a 300pt popover. the whitespace is structural.
motion budget. categorised by frequency:
- per-interaction: spring scale on play (0.88x, 200ms). crossfade on state text.
- per-completion: ring pulse, scale bounce (1.06x), dot pop, ambient fadeout, spoken quote.
- never animated: timer ticking, popover open/close, ambient selection, history toggle.
all motion disabled when reduce motion is on. accessibility is not a feature. it's a constraint.
right-click quit. no settings panel. no preferences window. the app has six decisions and you've already made them.
swift package manager. swiftUI + appKit. zero external dependencies.
Sources/
App/ entry point, AppDelegate (NSStatusItem, popover, right-click menu)
Models/ TimerState (state machine), SessionStore (JSON persistence), AmbientPlayer
Views/ TimerPopover, HistoryView, AmbientPicker, MenuBarIcon
Utilities/ Theme, QuotePlayer, FontRegistration, SoundPlayer, NotificationManager
Resources/ JetBrains Mono (Light/Regular/Medium), 4 ambient loops (~17MB)
start()
idle ──────────► running
▲ │
│ reset() │ pause()
│ ▼
│ paused
│ │
└──────────────────┘
reset()
timer hits 0:00 → completing (2s hold) → advance → idle
four phases. no fifth phase. four is enough.
sessions save to ~/Library/Application Support/Pomo/sessions.json. ambient preference and work duration in UserDefaults. nothing leaves your machine.
- keyboard: space toggles play/pause
- reduced motion: all animations respect the system preference
- quit: right-click the menubar icon
MIT
