# Shortcuts

Register keyboard shortcuts and show the right keys for the user's platform.

`useShortcut` registers a shortcut while your component is mounted. For a component that already accepts `shortcut`, such as a dialog trigger, use that prop directly.

```tsx title="basic.tsx"
import { useShortcut, useShortcutHint } from "@ui-sdk/solid/shortcuts"
import { createSignal } from "solid-js"

export function Example() {
  const [status, setStatus] = createSignal("Press N or the keys to save.")
  const newNote = () => {
    setStatus("Note created")
  }
  const save = () => {
    setStatus("Saved")
  }

  useShortcut("n", newNote, { description: "New note" })
  useShortcut("mod+s", save, { description: "Save" })
  const saveHint = useShortcutHint("mod+s")

  return (
    <div data-slot="stack">
      <div data-slot="row">
        <button type="button" onClick={newNote} aria-keyshortcuts="N">
          New note
          <kbd aria-hidden="true" data-slot="kbd">
            N
          </kbd>
        </button>
        <button
          type="button"
          onClick={save}
          aria-keyshortcuts={saveHint.ariaKeyshortcuts()}
        >
          Save
          <kbd aria-hidden="true" data-slot="kbd">
            {saveHint.text()}
          </kbd>
        </button>
      </div>
      <output data-slot="output">{status()}</output>
    </div>
  )
}
```

Press N to create a note. Press Command+S on Mac or Control+S elsewhere to save. The buttons run the same actions.

## Register a Shortcut

Keys are joined with `+`. `mod` means Command on Apple platforms and Control elsewhere. This example also displays the keys using `useShortcutHint`:

```tsx
import { useShortcut, useShortcutHint } from "@ui-sdk/solid/shortcuts"
import { createSignal } from "solid-js"

function Example() {
  const [count, setCount] = createSignal(0)
  const addNote = () => setCount((count) => count + 1)
  useShortcut("mod+enter", addNote, { description: "New note" })
  const hint = useShortcutHint("mod+enter")

  return (
    <button onClick={addNote} aria-keyshortcuts={hint.ariaKeyshortcuts()}>
      New note ({count()}) <kbd aria-hidden="true">{hint.text()}</kbd>
    </button>
  )
}

export { Example }
```

Call the helper inside a component. It unregisters when the component goes away. Keys and options can be getters; return `null` for keys to leave the shortcut unregistered.

## Show the Keys

`useShortcutHint` supplies display text and `aria-keyshortcuts`. It keeps the server and first client render consistent, then updates for the user's platform after mount.

Its fields are accessors. Call `hint.text()` and `hint.ariaKeyshortcuts()` in JSX.

Outside rendering, use `formatShortcut` for display text or `ariaKeyshortcuts` for the ARIA value, from the same entry.

## While Someone Is Typing

Shortcuts with Command, Control, or Option can run in text fields. Plain letters and other keys stay available for typing. Set `allowInInput: true` when a shortcut should work there too, such as Escape to clear a search.

## Limit a Shortcut to an Area

Use a named `scope` for an editor or another area. Turn it on while that area has focus:

```tsx
import { shortcuts, useShortcut } from "@ui-sdk/solid/shortcuts"
import { createSignal, onCleanup } from "solid-js"

function Example() {
  const [text, setText] = createSignal("")
  useShortcut("escape", () => setText(""), {
    scope: "note",
    allowInInput: true,
  })
  onCleanup(() => shortcuts.setScope("note", false))

  return (
    <textarea
      aria-label="Note"
      value={text()}
      onInput={(event) => setText(event.currentTarget.value)}
      onFocus={() => shortcuts.setScope("note", true)}
      onBlur={() => shortcuts.setScope("note", false)}
    />
  )
}

export { Example }
```

Scope names are shared, so choose one for each area you want to enable together. A `when` function can add another condition, checked each time the keys are pressed.

```tsx title="scope.tsx"
import { shortcuts, useShortcut } from "@ui-sdk/solid/shortcuts"
import { createSignal } from "solid-js"

export function Example() {
  const [text, setText] = createSignal("")
  const [status, setStatus] = createSignal(
    "Focus the note to turn on its shortcuts."
  )

  useShortcut(
    "mod+enter",
    () => {
      setStatus(`Characters saved: ${text().trim().length}`)
    },
    {
      scope: "note",
      when: () => text().trim() !== "",
      description: "Save the note",
    }
  )
  useShortcut(
    "escape",
    () => {
      setText("")
      setStatus("Cleared")
    },
    { scope: "note", allowInInput: true, description: "Clear the note" }
  )

  return (
    <div data-slot="stack">
      <textarea
        aria-label="Note"
        placeholder="Write a note…"
        value={text()}
        onInput={(event) => setText(event.currentTarget.value)}
        onFocus={() => shortcuts.setScope("note", true)}
        onBlur={() => shortcuts.setScope("note", false)}
      />
      <output data-slot="output">{status()}</output>
    </div>
  )
}
```

Focus the note and type some text. Press Command+Enter on Mac or Control+Enter elsewhere to save, or Escape to clear it. Move focus outside the note to turn off its shortcuts.

## Inside a Popup

Page shortcuts pause while a modal is open. Pass `layer`, an element inside the popup or a getter that returns one, to make a shortcut belong to that popup. It runs only while that popup is open.

When shortcuts share the same keys, the topmost open layer wins, then the most recently registered shortcut.

## List Available Shortcuts

Call `shortcuts.list()` when a help screen opens. It returns the shortcuts that can run now, including their `keys`, formatted `hint`, and `description`. Add descriptions when registering shortcuts so the help screen can explain each action.
