Components / More
HotkeyField
Records a keyboard shortcut for a keybindings page. It shows the shortcut as key caps beside a record toggle and a clear button; while recording, it takes the next chord itself, so the app's own shortcuts do not fire, and writes it the way useHotkey reads it ("mod+shift+p"; see CommandPalette for useHotkey).
When to useLink to "When to use"
- Shortcut settings: each action on a keybindings page gets a HotkeyField, with
recordingcontrolled so one row records at a time.
Use something else when: you only show a shortcut (Kbd), or people type a key name as text (TextField).
UsageLink to "Usage"
import { HotkeyField, useHotkey } from "@arshad-shah/cynosure";
const [keys, setKeys] = useState("mod+shift+p");
useHotkey(keys, openPalette);
<HotkeyField label="Command palette" value={keys} onChange={setKeys} name="palette" />One row at a timeLink to "One row at a time"
Control recording from the page, so starting one row ends the other. Starting it from outside (an Edit button) moves focus into the field so the keys reach it.
<HotkeyField
label={binding.label}
labelHidden
value={binding.keys}
onChange={(keys) => save(binding.id, keys)}
recording={editing === binding.id}
onRecordingChange={(on) => setEditing(on ? binding.id : null)}
/>Plain keysLink to "Plain keys"
By default a shortcut needs Control, Command or Alt, since a plain letter types in text fields (useHotkey ignores plain keys there). Function keys always count. allowBare accepts plain keys ("k", "shift+/") for shortcuts that only work outside text fields.
PropsLink to "Props"
| Prop | What it does | Type | Default |
|---|---|---|---|
labelRequired | The field's visible label (what the shortcut does); it names the group of controls. | ReactNode | |
labelHidden | Hide the label visually (a table row that already says what the shortcut does); it still names the field. | boolean | |
description | Help text under the field, read with it by screen readers. | ReactNode | |
error | An error under the field, announced. | ReactNode | |
value | The shortcut, written as useHotkey and Kbd read it ("mod+shift+p": mod is Command on Apple platforms and Control elsewhere), or "" for none. | string | |
defaultValue | The starting shortcut when value is not given. Default "". | string | "" |
onChange | A shortcut was recorded, or "" when it was cleared. | ((value: string) => void) | |
recording | Whether the field is waiting for a shortcut. Give it to control recording (one row at a time on a settings page). | boolean | |
defaultRecording | Whether it starts out recording, when recording is not given. Default false. | boolean | false |
onRecordingChange | Recording started (true) or ended (false): a shortcut was recorded, Escape was pressed or focus left. | ((recording: boolean) => void) | |
allowBare | Accept keys without Control, Command or Alt ("k", "shift+/"), for shortcuts that only work outside text fields. Default false: such a key is ignored while recording. Function keys (F1 to F24) are always accepted. | boolean | |
name | Form field name. The form receives the shortcut string. | string | |
form | The id of a form elsewhere on the page that the field belongs to. | string | |
disabled | Cannot be recorded, cleared or submitted. | boolean | |
size | Size of the keys and buttons. Default "md". | Size | "md" |
labels | Replace the built-in English text: the buttons, the prompt and the announcements. | Partial<HotkeyFieldLabels> | |
className | Added to the root element, for layout. | string | |
style | Layout only. Restyle through --cy-hotkey-field-* tokens. | CSSProperties | |
layout | stack puts the label above the control and the description and error below it. split puts the label and description at the start and the control at the end of one row (a settings row); when the field is too narrow for both, the control moves under the text. Default "stack". | "stack" | "split" | "stack" |
width | Width of the control, so the controls of a column of fields line up: "sm", "md" and "lg" are the --cy-field-control-width-* tokens (never wider than the field), "full" is the whole field. Default: the control's own width (the whole field in the stacked layout for TextField and the other fields). | FieldWidth |
Generated from the type definitions in @arshad-shah/cynosure 0.5.0.
Behaviour and accessibilityLink to "Behaviour and accessibility"
| Input | Idle | Recording |
|---|---|---|
| Enter, Space, click or tap on the record toggle | Starts recording | Stops, keeping the shortcut |
| Click or tap on the shortcut's box | Starts recording | – |
| A chord (Control+Shift+P) | The app's own shortcuts | Recorded, recording ends |
| Control, Shift, Alt, Command alone | – | Waits for the key |
| A plain letter, Enter, Space | – | Ignored (recorded with allowBare) |
| Escape | – | Cancels; with a modifier it is recorded (mod+escape) |
| Tab | Moves focus | Moves focus, which cancels |
- Capture: keys are taken in the capture phase on the field's own element, with propagation stopped and the default prevented, so neither
useHotkeynor any document listener sees them. It works inside shadow roots. - Key names: the key is the character typed or, when that is not a letter or digit (Option+K types "˚" on macOS, Shift+1 types "!", a dead key, another alphabet), the letter or digit on the physical key, as
matchesHotkeyreads it.modis Command on Apple platforms and Control elsewhere, so a recorded shortcut works on both. The mapping is exported ashotkeyFromEvent. - Names: the label names the group (a fieldset); the record toggle is a pressed/not-pressed button described by the shortcut, so it is read as "Record shortcut, Control plus Shift plus P".
- Announcements: the field's own
role="status"says "Recording, press a shortcut", then "Shortcut set to …", "Recording cancelled" or "Shortcut cleared" (labels), through a LiveRegion, so the same message twice (cancelling twice) is spoken twice. - Focus: clearing moves focus to the record toggle, since the clear button goes with the shortcut. Focus leaving the field while recording cancels it.
- Forms: with
name, the form receives the shortcut string; a reset restoresdefaultValue; a disabled field is not submitted. - Reduced motion: the recording light stops pulsing and stays lit.
TouchLink to "Touch"
The buttons keep a 44px hit area on coarse pointers. On a touch-only device there is usually no keyboard to record with; the field still shows and clears shortcuts.
ThemingLink to "Theming"
| Token | Default | What it changes |
|---|---|---|
--cy-hotkey-field-bg | --cy-field-control-bg, then --cy-color-surface | The shortcut's box |
--cy-hotkey-field-border | --cy-field-control-border, then --cy-color-line | The box's border |
--cy-hotkey-field-radius | --cy-field-control-radius, then from the control height | The box's corners |
--cy-hotkey-field-recording-border | --cy-color-accent | The box's border while recording |
--cy-hotkey-field-recording-color | --cy-color-accent-text | The prompt and its light |
--cy-hotkey-field-gap | --cy-space-2 | Between the box and the buttons |
The box shares TextField's skin through Field's --cy-field-control-* tokens, so restyling those restyles both; a disabled box takes --cy-field-control-sunken-bg.
The key caps are Kbd and the buttons IconButton: restyle them through their tokens.
RelatedLink to "Related"
Kbd · CommandPalette · Field
These examples run Cynosure 0.5.0 (commit 50ce30b), the version on npm.