# Keyboard Shortcuts

**Date:** 2026-06-01
**Status:** Draft
**Spec:** 160
**Tier:** All tiers
**Depends on:** `foundation-auth-rbac`, `app-shell`
**Referenced by:** —

---

## Overview

No spec defines keyboard shortcuts for zync-app. Power users and accessibility users benefit from keyboard navigation shortcuts, a command palette, and action shortcuts on key pages. This spec defines the shortcut system, command palette (`⌘K` / `Ctrl+K`), and per-page action shortcuts.

---

## Route

No new page. Keyboard shortcuts are global overlays and in-page bindings within `zync-app`.

---

## Command Palette (`⌘K` / `Ctrl+K`)

Triggered from any page. Floating modal, centered, full-width on mobile.

```
┌────────────────────────────────────────────────────────────┐
│  🔍  Search commands, pages, or customers…                  │
│  ────────────────────────────────────────────────────────  │
│  Recent                                                    │
│  > Dashboard                               ⌘ 1            │
│  > Invoices                                ⌘ 2            │
│  ────────────────────────────────────────────────────────  │
│  Actions                                                   │
│  > New invoice                             N I            │
│  > New customer                            N C            │
│  > New proposal                            N P            │
│  > New task                                N T            │
│  ────────────────────────────────────────────────────────  │
│  Navigation                                                │
│  > Go to settings                          G S            │
│  > Go to reports                           G R            │
└────────────────────────────────────────────────────────────┘
```

- Type to filter. `↑`/`↓` navigate. `Enter` executes. `Esc` closes.
- Results include: commands, navigation routes, recently visited customers/projects by name.
- Customer/project search hits the same API as the global search bar (`GET /api/search`).

---

## Global Shortcuts

Available from any page in `zync-app`:

| Shortcut | Action | Permission check |
|----------|--------|-----------------|
| `⌘K` / `Ctrl+K` | Open command palette | None |
| `?` | Show keyboard shortcut help overlay | None |
| `G` then `D` | Go to Dashboard | None |
| `G` then `I` | Go to Invoices | `invoices:read` |
| `G` then `P` | Go to Proposals | `marketing:read` |
| `G` then `T` | Go to Tasks | `projects:read` |
| `G` then `C` | Go to Customers | `customers:read` |
| `G` then `S` | Go to Settings | `users:manage` |
| `G` then `R` | Go to Reports | `reports:read` |
| `N` then `I` | New invoice | `invoices:write` |
| `N` then `C` | New customer | `customers:write` |
| `N` then `P` | New proposal | `marketing:write` |
| `N` then `T` | New task | `projects:write` |

Sequence shortcuts (`G` then `D`, `N` then `I`): second key must follow within 1,000ms, otherwise first key is discarded. No shortcut fires if focus is inside an `<input>`, `<textarea>`, or `[contenteditable]`.

---

## Per-Page Shortcuts

### Invoices List (`/invoices`)

| Shortcut | Action |
|----------|--------|
| `/` | Focus search field |
| `E` | Export current filter results as CSV |

### Invoice Detail (`/invoices/:id`)

| Shortcut | Action |
|----------|--------|
| `E` | Edit invoice (DRAFT only) |
| `S` | Send invoice (DRAFT only) |
| `P` | Print/PDF |

### Proposals List (`/proposals`)

| Shortcut | Action |
|----------|--------|
| `/` | Focus search field |

### Proposal Editor (`/proposals/:id/edit`)

| Shortcut | Action |
|----------|--------|
| `⌘S` / `Ctrl+S` | Save draft |
| `⌘⇧P` / `Ctrl+Shift+P` | Preview proposal |

### Tasks Board (`/projects/:id/tasks`)

| Shortcut | Action |
|----------|--------|
| `N` | New task (opens create modal) |
| `/` | Focus search/filter |

---

## Shortcut Help Overlay (`?`)

Modal listing all active shortcuts, grouped by category:

```
┌─────────────────────────────────────────────────────────┐
│  Keyboard shortcuts                              [✕]    │
│                                                         │
│  Global                                                 │
│  ⌘K          Open command palette                       │
│  ?           Show this overlay                          │
│                                                         │
│  Navigation (press G then…)                             │
│  D           Dashboard                                  │
│  I           Invoices                                   │
│  P           Proposals                                  │
│  T           Tasks                                      │
│  C           Customers                                  │
│  S           Settings                                   │
│  R           Reports                                    │
│                                                         │
│  Create (press N then…)                                 │
│  I           New invoice                                │
│  C           New customer                               │
│  P           New proposal                               │
│  T           New task                                   │
│                                                         │
│  On this page                                           │
│  /           Focus search                               │
│                                                         │
└─────────────────────────────────────────────────────────┘
```

"On this page" section is dynamic — populated from the current page's registered shortcuts.

---

## Implementation

### Shortcut Registry

Centralized hook in `zync-app/src/hooks/useKeyboardShortcuts.ts`:

```ts
// Global shortcuts registered once at app root
useGlobalShortcuts()

// Per-page shortcuts registered via hook (auto-deregistered on unmount)
usePageShortcuts([
  { keys: ['/', 'slash'], action: focusSearch, label: 'Focus search' },
])
```

Shortcuts gated by permissions are registered unconditionally but their action function checks permission and silently no-ops (or navigates to a permission-denied state) if the user lacks access — consistent with how nav links behave.

---

## Schema Delta

None. Shortcut bindings are hardcoded in the frontend; no user-configurable shortcut persistence.

---

## Architecture Decisions

| Decision | Choice | Reason |
|----------|--------|--------|
| No user-customizable shortcuts | Hardcoded bindings | Adds significant complexity (storage, conflict detection, UI); hardcoded covers >95% of use cases; can add customization later |
| Sequence shortcuts (G→D) | Not modifier-key combos | Modifier combos conflict with OS/browser defaults (⌘I = italics, ⌘T = new tab); vim-style sequences avoid conflicts |
| Shortcuts disabled in inputs | Explicit focus check | Standard pattern; prevents shortcut fire while typing |
| Per-page shortcuts via hook | Not event delegation | Hook pattern auto-cleans up on unmount; consistent with React lifecycle |
