Skip to content
Cynosurev0.5.0
SearchStorybookGitHubnpmColour mode: System. Switch to Light

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).

Default

When to useLink to "When to use"

  • Shortcut settings: each action on a keybindings page gets a HotkeyField, with recording controlled 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"

tsx
Copy
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.

tsx
Copy
<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)}
/>
Controlled recording

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.

Allow bare records plain keys

PropsLink to "Props"

Every prop of HotkeyField
PropWhat it doesTypeDefault
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.booleanfalse
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"

Behaviour and accessibility, table 1
InputIdleRecording
Enter, Space, click or tap on the record toggleStarts recordingStops, keeping the shortcut
Click or tap on the shortcut's boxStarts recording–
A chord (Control+Shift+P)The app's own shortcutsRecorded, 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)
TabMoves focusMoves 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 useHotkey nor 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 matchesHotkey reads it. mod is Command on Apple platforms and Control elsewhere, so a recorded shortcut works on both. The mapping is exported as hotkeyFromEvent.
  • 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 restores defaultValue; a disabled field is not submitted.
  • Reduced motion: the recording light stops pulsing and stays lit.
Records achord from the keyboard

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"

Theming, table 2
TokenDefaultWhat it changes
--cy-hotkey-field-bg--cy-field-control-bg, then --cy-color-surfaceThe shortcut's box
--cy-hotkey-field-border--cy-field-control-border, then --cy-color-lineThe box's border
--cy-hotkey-field-radius--cy-field-control-radius, then from the control heightThe box's corners
--cy-hotkey-field-recording-border--cy-color-accentThe box's border while recording
--cy-hotkey-field-recording-color--cy-color-accent-textThe prompt and its light
--cy-hotkey-field-gap--cy-space-2Between 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.

Kbd · CommandPalette · Field

These examples run Cynosure 0.5.0 (commit 50ce30b), the version on npm.

Getting started
Theming
Icons
Accessibility
Code editors and highlighters
Using Cynosure without React
Build-tool adapters
Components
Accordion
AppShell
AspectRatio
Breadcrumbs
Calendar
Card
Container
Disclosure
Divider
DocumentViewport
Grid
Inline
PageHeader
PanZoomCanvas
ScrollArea
SideNav
SortableList
SplitPane
Stack
StatusBar
Tabs
TitleBar
Toolbar
Tree
VirtualList
ZoomControls
Announcer
Avatar
Badge
CodeBlock
DescriptionList
EmptyState
ErrorBoundary
Field
Heading
IconTile
Image
Kbd
List
LoadingDots
Notice
ProgressBar
Prose
Skeleton
Stat
Steps
Table
Text
Button
Checkbox
Checklist
ColorField
ColorSwatches
Combobox
CompareSlider
CopyButton
DateField
FileDrop
IconButton
KeyValueEditor
Link
NumberField
Pagination
PathField
RadioGroup
SearchField
SegmentedControl
Select
SelectionFrame
SignaturePad
Slider
Switch
TagInput
TextArea
TextField
ThemeToggle
TimeField
ButtonBase
ChoiceBase
DialogBase
FileInputBase
HiddenValue
Icon
InputBase
LinkBase
LiveRegion
TextAreaBase
ChatMessage
Choice
DataGrid
DeviceFrame
HighlightedText
HotkeyField
ListRow
OptionList
OverlayFrame
QRCode
RowActions
Section
SiteFooter
SiteHeader
SkipLink
SortButton
VisuallyHidden
CommandPalette
ContextMenu
Dialog
FloatingToolbar
HoverCard
Menu
Menubar
Popover
ProgressOverlay
Sheet
Toast
Tooltip
Changelog