ABS Keyboard Shortcuts: n, r, b (No Prefix)
ABS ships three single-letter shortcuts, no prefix, no modifiers, no armed state. The bindings, why we left a g-prefix design behind, what we considered and cut, and the skip-rules.

The three bindings
Press n, r, or b anywhere on ABS to jump to a top-level destination. No prefix. No chord. No armed state. No toast.
| Key | Goes to | Path |
|---|---|---|
n | News | /news/ |
r | Reviews | /reviews/ |
b | The Agent Advantage book | /books/the-agent-advantage/ |
The implementation lives in public/scripts/app.js as an IIFE with a single source of truth — a SHORTCUTS object — so any future UI hint that wants to surface the bindings reads from the same map:
var SHORTCUTS = {
'n': '/news/',
'r': '/reviews/',
'b': '/books/the-agent-advantage/',
};
The handler listens on document, looks up SHORTCUTS[e.key.toLowerCase()], and calls location.assign(href) after e.preventDefault(). Three lines of bind logic around a four-line table.
The migration away from a “g” prefix
The first design considered for ABS mimicked GitHub and Gmail: a two-key chord like g then n. On paper the prefix buys you a large addressable namespace without conflicting with browser shortcuts. In practice, on a content site with a small surface, it buys you almost nothing and costs a lot.
The case against a prefix on ABS:
- The namespace is small. A prefix buys you slots for destinations that do not exist yet. Speculative shortcuts rot — readers forget a
gchord for a route that shipped and then got renamed. - Two-key chords double the latency. A single key reaches the destination in the same interval that the reader’s brain already spent on the decision.
- Prefixes fail the discoverability test. A new reader who presses
gsees nothing. The ABS shortcut model fails open —nis a single letter,nfor News, the same letter that begins the word on the top-bar link the reader has already seen.
The design landed on commit afb1ddc (Phase 5 of the SDP build) with a comment that names the constraint directly: “three bindings only, all single letters. No ‘g’ prefix, no armed state, no toast. The user accepts that guides is reachable from the rail / hamburger drawer / top bar; no shortcut for guides.”
The cut is deliberate. Guides is the most-visited ABS section, but the keyboard is not the right surface — readers searching for guides are usually searching for a specific guide, not the section index. They belong on the search bar, not on a shortcut.
Why three is enough
The number was chosen by cutting. Considered and rejected:
| Considered | Cut because |
|---|---|
g → /guides/ | Guides need search, not navigation. A shortcut that lands on a section index is a half-measure. |
h → / (home) | One click away in the top bar and the browser back button already covers it. |
s → focus search | Already taken by the search bar’s /-focus shortcut. |
? → keyboard help | Not enough shortcuts to need an overlay. The reader can read this guide. |
j / k → next / previous | Hold for a separate guide if listing pages ever grow a real keyboard model. |
1-9 → top nav by position | Position-based bindings break the moment the nav reorders. Letter bindings survive renames. |
Esc → close any overlay | Esc is a handler the mobile drawer and book submenu already wire. The keyboard IIFE yields to it. |
The rule that emerged: a shortcut earns its key if (a) the destination is a top-level surface readers revisit, (b) the destination is not better reached by the search bar, and (c) the letter is not already spoken for. News, Reviews, and the book pass all three.
The five skip-rules
The shortcut handler bails before navigating in five situations. The order matters: cheap checks run first.
document.addEventListener('keydown', function (e) {
// 1. Modifier keys belong to the browser / OS.
if (e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return;
// 2. IME composition is active (CJK input, dead keys).
if (e.isComposing || e.keyCode === 229) return;
// 3. Focus is on an editable surface.
var t = e.target;
if (isEditable(t)) return;
// 4. Escape — let the existing overlay handlers win.
if (e.key === 'Escape') return;
// 5. The key isn't bound — exit before preventDefault.
var href = SHORTCUTS[e.key.toLowerCase()];
if (!href) return;
e.preventDefault();
location.assign(href);
});
Rule 1 — modifier keys. Ctrl, Cmd, Alt, Shift, and Meta all surrender the event to the browser or OS. Ctrl+r is reload; Cmd+n opens a new window. The handler only fires on a bare key, so it never collides with copy-paste, tab-switching, devtools, or any OS chord.
Rule 2 — IME composition. e.isComposing is true while an IME session is open — Chinese, Japanese, Korean, plus dead-key Latin input. The fallback e.keyCode === 229 catches older Android browsers that did not set isComposing. Without this check, pressing n to type would navigate away from the form. Sites that skip it ship a navigation bug for every CJK reader on every keypress.
Rule 3 — focus is on an editable surface. isEditable(el) returns true for INPUT, TEXTAREA, SELECT, any element with isContentEditable === true, and any element with aria-disabled="true". If focus is on any of those, the keypress is treated as typing and the shortcut yields. aria-disabled is included as a courtesy: a disabled-by-ARIA element is one the screen reader announced as not interactive, and the handler respects that signal even though the element is still focusable.
Rule 4 — Escape. Escape is the universal “close what is open” key. The mobile drawer, the book submenu, and any future disclosure widget all listen for it. If a submenu is open and the reader presses Esc, the keyboard IIFE returns immediately so the existing handler can run. Without this check, preventDefault on Esc would break the close behavior of every overlay.
Rule 5 — unbound key. The last check is a table lookup. If the lowered key is not in SHORTCUTS, the handler returns without calling preventDefault. This is what allows every other keypress to do its normal job — t for scroll, space for page-down, enter for submit. preventDefault is only called when a navigation actually fires.
Verification
| # | Check | Result |
|---|---|---|
| 1 | n on / navigates to /news/ | Pass |
| 2 | r on /guides/ navigates to /reviews/ | Pass |
| 3 | b on /reviews/ navigates to /books/the-agent-advantage/ | Pass |
| 4 | n while focused on the search input types n | Pass |
| 5 | Cmd+r reloads, does not navigate | Pass |
| 6 | n during IME composition types, does not navigate | Pass |
| 7 | Esc with the mobile drawer open closes the drawer | Pass |
| 8 | t does nothing | Pass |
| 9 | preventDefault only fires on a real navigation | Pass |
Sources
- MDN: KeyboardEvent.key
- MDN: KeyboardEvent.isComposing
- MDN: Element.isContentEditable
- ABS companion: ABS Mobile Nav: The Three Rules
- ABS companion: ABS Regression Failure Modes and Their Cheap First Check
Last verified: 2026-07-14 against the live keyboard IIFE at public/scripts/app.js lines 232-291, commit afb1ddc (Phase 5 of the SDP build).



Submit a take
Have a different read on this? Drop a comment below — your email isn't published, and I read every one. Nothing leaves the site until I approve it.