There is a category of desktop tool where the keyboard shortcut is not a convenience layered on top of the interface. It is the interface. If your app is meant to interrupt whatever someone is doing, do a small job, and get out of the way, then every millisecond spent reaching for a mouse is the app failing at its one purpose.
Designing for that is a discipline, not a feature you add at the end. It changes what you build, what you ask permission for, and what you let the user change.
Global and in-app shortcuts are different animals
An in-app shortcut works when your app is frontmost. The system routes the key event to your app, you handle it, done. It is cheap to add and it is what most shortcuts should be.
A global hot key fires no matter which app is in front. That is a different mechanism: you register a specific key combination with the system, and the system calls you when it fires. You are not reading the keyboard. You are asking to be told about one combination, and only that one.
The distinction matters because global keys are a scarce shared resource. Every app that registers one is taking a combination away from every other app on the machine, and the user did not agree to that ordering. Two rules follow. Register as few as possible, and make every one of them optional. An app with six global hot keys enabled by default is an app that broke something in the user's workflow before it did anything useful.
Users have to set their own
You cannot pick a good global shortcut on someone else's behalf. You do not know what else they run, what their keyboard layout is, whether the combination you chose is already muscle memory for something they use all day, or whether the system claimed it in a newer release.
So ship a default, and make changing it a first-class part of settings rather than something buried. The pattern that works is direct: the user clicks the shortcut field, presses the keys they want, and the app records exactly that combination. No dropdown of modifiers, no text field where you type a string. And give them two more things people forget to build. A way to clear a shortcut entirely, because some users want the menu bar icon and nothing else. A way to restore defaults, because everyone eventually creates a conflict they cannot find.
Voice ships this way: every action has a shortcut you can record yourself in settings, including push to talk, showing the panel without recording, copying the last transcript, and canceling from any app. Only one of those needed a default. The rest are yours to set or leave empty.
Conflicts are inevitable, so handle them out loud
Registering a global hot key can fail. Another app may already hold the combination, or the system may reserve it. The worst thing your app can do is fail silently, because the user then presses the key, nothing happens, and they conclude your app is broken. They are not wrong.
- Check the result of registration and surface it. If a combination could not be claimed, say so in the settings row where the user set it, right then, not in a log file.
- Refuse your own duplicates. If two actions in your app want the same keys, that is your bug to catch, and it is easy to catch.
- Do not fight over reserved combinations. System shortcuts win. Tell the user to pick another one rather than trying to be cleverer than the window server.
- Re-register when the world changes. Hot keys can be lost across some state changes, and an app whose shortcut works until you log out is worse than one that never had a shortcut.
Show the key in the interface
A shortcut nobody knows about is dead code. The cheapest teaching mechanism in desktop software is putting the key hint next to the thing it triggers, so a user who is clicking a button today reads the hint enough times to stop clicking it.
That means the hint lives next to the action, not in a help page. It shows the actual current binding rather than the default, which is the detail that gets skipped in apps that let users rebind. And it should be quiet: dimmer than the label, small, no border competing with the button. The user is not meant to read it, they are meant to absorb it over a week.
Escape is the way out, everywhere
One key should always mean stop. Escape closes the panel, cancels the in-progress operation, and dismisses the thing that just appeared, in that order of what is currently true. No exceptions for modal dialogs you are proud of.
The reason is confidence. A user will try a tool that interrupts their work only if they trust they can get rid of it instantly, without finding a close button or wondering whether the thing is still recording in the background. Escape is how that trust is established, and it is why a cancel action is worth a global shortcut of its own: getting out should work even from an app you switched to while the panel was still open.
What macOS asks permission for, and what it does not
This is the part that surprises people, and it shapes what a keyboard-first Mac app can do without a permissions conversation on first launch.
Registering a global hot key does not require Accessibility permission. You are asking the system to notify you about one specific combination, not to observe the keyboard, and the system treats that as a normal thing for an app to want.
Three related things do require permission. Watching all keystrokes with an event tap needs Input Monitoring. Reading or controlling other applications' windows and controls needs Accessibility. And posting synthetic keystrokes, which is how an app types text into another app on your behalf, also needs Accessibility, because an app that can send keys to any window can do anything you can do.
That distinction is a design constraint worth leaning into. Voice does not type into your apps. It puts reviewed text on the clipboard and you paste it, which keeps a dictation tool out of the category of software that can drive your keyboard. Activity sits on the same line from the other direction: app timing works with no extra permissions, while counting global keystrokes is the feature that requires Accessibility.
When you can build the keyboard-first experience without asking for control of the machine, do it. Every permission prompt is a moment where a reasonable person decides whether to trust you, and the ones you do not have to ask for are the ones you never have to justify.
The takeaway
Keyboard-first is not about having shortcuts. It is about deciding that the fastest path through your app is the keyboard, then paying for that decision: few global keys, all of them rebindable, failures reported out loud, hints visible where the action is, and Escape always meaning stop. Do that and the app disappears into the work, which for this kind of tool is the entire goal.
Filed Under
Written by
Isaac Juracich
Full-stack engineer building production software for businesses that need it done right. Based in La Crosse, WI.
More about Isaac