App UX: keyboard shortcuts and common paradigms
User preference
Match desktop-app and modern web conventions by default. Users should not need to hunt for close affordances or discover shortcuts only by accident.
Apply only the patterns relevant to the interaction being built or reviewed.
This skill does not authorize unrelated UI changes, and its tables are defaults,
not a product-wide acceptance checklist. Accessibility, prevention of unintended
destructive action, and avoiding silent data loss remain blocking where the
touched interaction creates those risks. Stop when the requested interaction
works in its relevant input and state variants; report adjacent consistency
improvements as follow-ups.
Pair with the sync-url-navigation skill for bookmarkable nav/filter state.
Keyboard shortcuts
Common expectations for touched interactions
| Key / chord |
Expected behavior |
| Esc |
Close the topmost overlay: modal, drawer, popover, context menu, inline edit. Do not close if a destructive save is in flight—block Esc while saving or show a clear state. |
| ⌘/Ctrl+Enter |
Submit primary form or send message in chat/composer (when focus is in a text field). |
| Enter |
Submit single-line fields; in multi-line fields, Enter = newline unless Shift+Enter is documented as send. |
| ⌘/Ctrl+letter |
Use for global toggles (e.g. ⌘/Ctrl+J = open assistant panel). Always support both metaKey (Mac) and ctrlKey (Windows/Linux). |
Recommended implementation notes
- Attach document-level
keydown listeners when an overlay opens; remove on unmount.
- Call
event.preventDefault() on Esc when handling it so nested browser UI does not steal the key.
- Show shortcuts in
title tooltips on icon buttons (e.g. Close (Esc)).
- Surface the same shortcuts in an in-app Help section with
<kbd> styling.
- Avoid shortcut conflicts: check
!event.shiftKey / !event.altKey when the chord is specific (e.g. Ctrl+J vs Ctrl+Shift+K).
Optional but valued
| Pattern |
Use when |
| ⌘/Ctrl+K |
Command palette / quick open (if the app has one) |
| ⌘/Ctrl+Shift+K |
Secondary global action (e.g. clear chat / reset context)—only if distinct from Ctrl+K |
| ? |
Toggle keyboard shortcut help (when not typing in an input) |
| / |
Focus search (when not in an input) |
Modals and overlays
| Paradigm |
Expected behavior when applicable |
| Esc |
Closes modal (see above). |
| Backdrop click |
Clicking outside may close lightweight dialogs; keep explicit confirmation dialogs or forms with unsaved work open when dismissal would be unsafe. |
| × control |
Top-right (or consistent corner), type="button", labeled title with Esc hint. |
| Click propagation |
stopPropagation on panel content so inner clicks do not close. |
| Focus |
Move focus into modal on open; return focus to trigger on close when practical. |
| Scroll |
Lock or contain body scroll for full-screen overlays; allow scroll inside tall forms. |
Use a consistent close model across comparable overlays. Do not add every close
mechanism when it would discard unsaved work or weaken an explicit confirmation.
Menus and transient UI
- Context menus: Esc closes; click outside closes (
mousedown on document).
- Hover cards / tooltips: Do not trap Esc unless they are modal; dismiss on pointer leave or Esc if sticky.
- Inline edit (e.g. grid cell): Esc cancels edit without saving.
Forms and async actions
- Disable primary submit while saving; label button
Saving….
- Show inline error under the form, not only
alert.
- Required fields: when the touched form has them, validate on submit and focus the first invalid field.
- Clear resets filters to empty defaults and syncs URL if using query-param routing.
Lists, tables, and selection
- Row click opens editor or expands detail (consistent per table).
- Shift+click multi-select when desktop bulk selection is in scope; show selection chrome and a bulk action bar.
- ID chips: click copies to clipboard with brief status feedback (
Copied).
- Empty states: explain why no rows (no data vs no matches vs filters too narrow).
- Unavailable states: when primary content cannot be produced, do not show
the normal success workspace with irrelevant export, share, filter, or edit
controls. Explain the missing prerequisite and offer the most useful recovery
action. Analytical publishability belongs to
data-visualization-quality.
- Load more for long tables instead of rendering thousands of rows at once.
Search and filters
- Use
type="search" where appropriate.
- Changing filters resets pagination/limit to the first page.
- Debounce URL sync is optional; immediate URL sync is fine for admin tools.
Feedback and safety
- Status line or toast for export, save, copy, undo—not silent success.
- Undo for reversible schedule/data edits when the backend supports it.
- Destructive actions (delete, rollback): confirm in-modal or
confirm(); never rely only on icon buttons.
- Role-gated UI: hide or disable edit actions for viewers; explain in help text.
Accessibility baseline
- Visible focus rings on interactive elements.
- Buttons for actions; avoid
div onClick without role and keyboard support.
- Meaningful
title / aria-label on icon-only controls.
- Do not rely on color alone for status (use text labels or pills).
Selective review lens
Select only items relevant to the changed interaction. Unselected items are not
failed gates, and a review request does not authorize implementation.
Anti-patterns
- Ordinary dismissible modal with only × and no keyboard or safe outside-dismiss path
- Shortcut only in README, not in UI
- Mac-only
metaKey with no ctrlKey
- Esc closes modal but leaves a nested context menu open underneath
- Silent failures on save/export/copy
1---2name: app-ux-paradigms3description: Apply conventional interaction behavior for keyboard shortcuts, focus, dialogs, overlays, menus, forms, and asynchronous controls. Use when those interaction mechanics are being built or reviewed; do not use for general page aesthetics, information architecture, or visualization quality.4---56# App UX: keyboard shortcuts and common paradigms78## User preference910**Match desktop-app and modern web conventions by default.** Users should not need to hunt for close affordances or discover shortcuts only by accident.1112Apply only the patterns relevant to the interaction being built or reviewed.13This skill does not authorize unrelated UI changes, and its tables are defaults,14not a product-wide acceptance checklist. Accessibility, prevention of unintended15destructive action, and avoiding silent data loss remain blocking where the16touched interaction creates those risks. Stop when the requested interaction17works in its relevant input and state variants; report adjacent consistency18improvements as follow-ups.1920Pair with the **sync-url-navigation** skill for bookmarkable nav/filter state.2122## Keyboard shortcuts2324### Common expectations for touched interactions2526| Key / chord | Expected behavior |27|-------------|-------------------|28| **Esc** | Close the topmost overlay: modal, drawer, popover, context menu, inline edit. Do not close if a destructive save is in flight—block Esc while `saving` or show a clear state. |29| **⌘/Ctrl+Enter** | Submit primary form or send message in chat/composer (when focus is in a text field). |30| **Enter** | Submit single-line fields; in multi-line fields, Enter = newline unless Shift+Enter is documented as send. |31| **⌘/Ctrl+letter** | Use for global toggles (e.g. **⌘/Ctrl+J** = open assistant panel). Always support **both** `metaKey` (Mac) and `ctrlKey` (Windows/Linux). |3233### Recommended implementation notes3435- Attach **document-level** `keydown` listeners when an overlay opens; remove on unmount.36- Call `event.preventDefault()` on Esc when handling it so nested browser UI does not steal the key.37- Show shortcuts in **`title` tooltips** on icon buttons (e.g. `Close (Esc)`).38- Surface the same shortcuts in an in-app **Help** section with `<kbd>` styling.39- Avoid shortcut conflicts: check `!event.shiftKey` / `!event.altKey` when the chord is specific (e.g. Ctrl+J vs Ctrl+Shift+K).4041### Optional but valued4243| Pattern | Use when |44|---------|----------|45| **⌘/Ctrl+K** | Command palette / quick open (if the app has one) |46| **⌘/Ctrl+Shift+K** | Secondary global action (e.g. clear chat / reset context)—only if distinct from Ctrl+K |47| **?** | Toggle keyboard shortcut help (when not typing in an input) |48| **/** | Focus search (when not in an input) |4950## Modals and overlays5152| Paradigm | Expected behavior when applicable |53|----------|-------------|54| **Esc** | Closes modal (see above). |55| **Backdrop click** | Clicking outside may close lightweight dialogs; keep explicit confirmation dialogs or forms with unsaved work open when dismissal would be unsafe. |56| **× control** | Top-right (or consistent corner), `type="button"`, labeled `title` with Esc hint. |57| **Click propagation** | `stopPropagation` on panel content so inner clicks do not close. |58| **Focus** | Move focus into modal on open; return focus to trigger on close when practical. |59| **Scroll** | Lock or contain body scroll for full-screen overlays; allow scroll inside tall forms. |6061Use a consistent close model across comparable overlays. Do not add every close62mechanism when it would discard unsaved work or weaken an explicit confirmation.6364## Menus and transient UI6566- **Context menus**: Esc closes; click outside closes (`mousedown` on document).67- **Hover cards / tooltips**: Do not trap Esc unless they are modal; dismiss on pointer leave or Esc if sticky.68- **Inline edit** (e.g. grid cell): Esc cancels edit without saving.6970## Forms and async actions7172- Disable primary submit while **saving**; label button `Saving…`.73- Show **inline error** under the form, not only `alert`.74- **Required fields**: when the touched form has them, validate on submit and focus the first invalid field.75- **Clear** resets filters to empty defaults and syncs URL if using query-param routing.7677## Lists, tables, and selection7879- **Row click** opens editor or expands detail (consistent per table).80- **Shift+click** multi-select when desktop bulk selection is in scope; show selection chrome and a bulk action bar.81- **ID chips**: click copies to clipboard with brief status feedback (`Copied`).82- **Empty states**: explain why no rows (no data vs no matches vs filters too narrow).83- **Unavailable states**: when primary content cannot be produced, do not show84 the normal success workspace with irrelevant export, share, filter, or edit85 controls. Explain the missing prerequisite and offer the most useful recovery86 action. Analytical publishability belongs to `data-visualization-quality`.87- **Load more** for long tables instead of rendering thousands of rows at once.8889## Search and filters9091- Use `type="search"` where appropriate.92- Changing filters resets pagination/limit to the first page.93- Debounce URL sync is optional; immediate URL sync is fine for admin tools.9495## Feedback and safety9697- **Status line** or toast for export, save, copy, undo—not silent success.98- **Undo** for reversible schedule/data edits when the backend supports it.99- **Destructive actions** (delete, rollback): confirm in-modal or `confirm()`; never rely only on icon buttons.100- **Role-gated UI**: hide or disable edit actions for viewers; explain in help text.101102## Accessibility baseline103104- Visible focus rings on interactive elements.105- Buttons for actions; avoid `div onClick` without `role` and keyboard support.106- Meaningful `title` / `aria-label` on icon-only controls.107- Do not rely on color alone for status (use text labels or pills).108109## Selective review lens110111Select only items relevant to the changed interaction. Unselected items are not112failed gates, and a review request does not authorize implementation.113114- [ ] Esc closes overlay / menu / inline edit115- [ ] Backdrop + × use same close handler116- [ ] Global shortcuts use ⌘ and Ctrl117- [ ] Shortcuts documented in tooltips and Help118- [ ] Submit disabled while saving; Esc blocked if close would lose in-flight save119- [ ] Click-outside closes non-modal menus120- [ ] Primary action discoverable without reading source121122## Anti-patterns123124- Ordinary dismissible modal with only × and no keyboard or safe outside-dismiss path125- Shortcut only in README, not in UI126- Mac-only `metaKey` with no `ctrlKey`127- Esc closes modal but leaves a nested context menu open underneath128- Silent failures on save/export/copy