Shortcut
Keyboard shortcuts for a whole application from one element: each binding fires an action event, runs a handler or activates a target.
c2-shortcut turns key presses into actions. It renders nothing: one element holds every shortcut of an application
(or of one region) in its bindings list, and a single shortcut event tells you which one fired, so the app has one
listener and one switch. A binding can also click or focus another element through for, which needs no script at all.
Import
import '@c2n/components/shortcut'Usage
Press
One element for the whole application
List every shortcut once and switch on detail.action. Assigning a new array to bindings replaces them all, which is
how an app swaps its shortcuts per route.
<c2-shortcut
id="shortcuts"
bindings='[
{ "keys": "mod+k, mod+/", "action": "search", "description": "Open search" },
{ "keys": "mod+s", "action": "save", "description": "Save" },
{ "keys": "alt+g alt+d", "action": "go-dashboard", "description": "Go to dashboard" },
{ "keys": "escape", "action": "close-panel", "scope": ".side-panel", "allowInInputs": true },
{ "keys": "alt+n", "for": "new-item", "description": "New item" }
]'
></c2-shortcut>
<script type="module">
import '@c2n/components/shortcut'
const shortcuts = document.querySelector('#shortcuts')
shortcuts.addEventListener('shortcut', (event) => {
switch (event.detail.action) {
case 'search':
openSearch()
break
case 'save':
save()
break
case 'go-dashboard':
router.go('/dashboard')
break
}
})
</script>From script, a binding can carry a handler instead of an action, and entries returns each binding with its
platform-aware label (⌘K, ⌘/ on macOS, Ctrl+K, Ctrl+/ elsewhere) for a cheatsheet:
shortcuts.bindings = [...shortcuts.bindings, { keys: 'mod+shift+?', handler: () => cheatsheet.show(), description: 'Show shortcuts' }]
for (const { label, description } of shortcuts.entries) console.log(label, description)Key syntax
keys | Meaning |
|---|---|
mod+k | ⌘K on macOS and iOS, Ctrl+K elsewhere |
ctrl+shift+p | Every modifier listed must be held, and no other |
mod+k, mod+/ | Alternatives, separated by commas |
alt+g alt+d | A sequence: |
escape, f1 | The only keys that may be bound without a modifier |
plus, comma, space, esc, up | Aliases for keys the syntax itself uses, and short names |
Every stroke must hold Ctrl, ⌘ or Alt (mod counts; Shift alone does not, since shift+a is typing). Escape and
F1–F24 are the only exceptions. An alternative with a bare key (/, g d, shift+?) is ignored with a console
warning. Key names are KeyboardEvent.key values, case-insensitive.
Letters and digits also match by physical key when the typed character is not plain ASCII, so alt+k works with
Option on macOS and on non-Latin layouts.
Behaviour
- Typing. Shortcuts with Ctrl, ⌘ or Alt fire in fields too. Escape and the function keys, which need no
modifier, are ignored while focus is in an input, textarea, select or contenteditable;
allowInInputslifts that guard for one binding. - Scope.
scope: "parent"limits a binding to focus inside the element’s parent; any other value is a CSS selector matched against the focused element and its ancestors. When several bindings match, the most narrowly scoped one wins, across everyc2-shortcuton the page, then the first one listed. - Firing. The key event’s default is prevented (
allowDefaultopts out), thenshortcutfires on the element. Unless it is cancelled, the binding’shandlerruns and itsfortarget is activated: a text field — native or a component wrapping one, such asc2-text-field— is focused, anything else is clicked. For a component such asc2-switchorc2-button, the control inside its shadow root is clicked, so it toggles or activates as a real click would. - Keys already handled. A key another component has handled (it called
preventDefault(), as a list does for its arrow keys) is left alone. - Held keys. Auto-repeat is ignored unless the binding sets
repeat. - Accessibility. Each
fortarget receivesaria-keyshortcuts(unless it already has one) so assistive technology announces the shortcut. Show the shortcut visually withc2-kbd. - Pausing.
disabledon the element pauses all of its bindings;disabledon a binding turns that one off. Removing the element unregisters everything.