Open the modal then press Escape or Delete
Installation
pnpm add @hookraft/use-key-cursoruseKeyCursor
Animate a circle cursor to any element when the user presses a keyboard key. The cursor flies from a corner of the screen, lands on the target, squeezes to simulate a click, then fades out. Designed for modal close buttons, confirm dialogs, and any keyboard-driven UX that needs to feel alive.
Bindings are automatically silenced when the user is typing inside an input, textarea, select, or contenteditable element — so you never interfere with normal keyboard input.
import { useKeyCursor } from "@hookraft/use-key-cursor"useKeyCursor({
keys: {
Escape: closeRef,
Enter: confirmRef,
},
origin: "top-right",
color: "#000000",
theme: "system",
})Full Example
"use client"
import { useRef } from "react"
import { useKeyCursor } from "@hookraft/use-key-cursor"
function DeleteModal({ onClose, onConfirm }) {
const closeRef = useRef<HTMLButtonElement>(null)
const confirmRef = useRef<HTMLButtonElement>(null)
useKeyCursor({
keys: {
Escape: closeRef,
Enter: confirmRef,
},
origin: "top-right",
color: "#dc2626",
theme: "system",
onTrigger: (key, el) => {
console.log(`${key} triggered`, el.textContent)
},
})
return (
<div className="modal">
<h2>Delete file?</h2>
<p>This action cannot be undone.</p>
<div className="actions">
<button ref={closeRef} onClick={onClose}>Cancel</button>
<button ref={confirmRef} onClick={onConfirm}>Delete</button>
</div>
</div>
)
}You can also target elements using a CSS selector string instead of a ref:
useKeyCursor({
keys: {
Escape: "#cancel-btn",
Enter: "#confirm-btn",
d: deleteRef,
},
})Options
| Prop | Type | Default |
|---|---|---|
keys | Record<string, useKeyCursor.KeyTarget> | - |
origin | useKeyCursor.Origin | "top-right" |
color | string | "#000000" |
theme | useKeyCursor.Theme | "system" |
onTrigger | (key: string, element: HTMLElement) => void | - |
ignoreWhen | () => boolean | - |
Examples
Origin
Change where the cursor spawns from. Useful for matching the position of a keyboard shortcut hint in your UI.
Color
The ring color is the only visual property you can customize. Use it to match your brand or signal intent — red for destructive actions, green for confirm, and so on.
Notes
The cursor element is appended directly to document.body and removed on unmount — no portals or extra wrappers needed z-index is 999999 so the cursor renders above modals and overlays
Fires a real .click() on the target element so all existing onClick handlers work without any changes
If the cursor is already animating, subsequent keypresses are ignored until the animation finishes
Works in Next.js App Router — add "use client" to the component that calls the hook