Skip to content

ShortcutsDialog

Terminal window
$ npx shadcn@latest add https://lacodda.github.io/dowel/r/shortcuts-dialog.json
See it liveThe sheet of a small application, opened by ?, with a screen's own keys in it.→

It has no rows of its own. It lists what is declared through commands now, under the headings the commands give themselves, with the keys they answer to after a rebinding and without any lost to a conflict. A command declared by the open screen appears while that screen is open, and is gone when it is not. A hand-written sheet is a version behind what the application answers to from the day after it is written; this one cannot be.

It wires itself. It declares its own command — under the title the product gives it, on ? unless told otherwise — so ? opens it with nothing else written, the palette offers it, and it lists itself.

import { ShortcutsDialog } from '@/components/ui/shortcuts-dialog'
<ShortcutsDialog
title={t('keys.title')}
closeLabel={t('close')}
description={t('keys.typing')}
group={t('keys.everywhere')}
/>

Mount it once, in the shell.

It opens on the list, not on the close button. A dialog puts the focus on the first thing that can take it, which here is the cross — so the Enter that someone presses to see what happens would close the sheet, and the arrows that should scroll it would do nothing. A sheet is read, not answered. The focus goes to the list; Escape and the button close it.

? is a plain key, so it is not answered while someone types, nor inside another dialog or a menu — see where a key is answered. description is the place to say so in the product’s words.

What keys cannot say goes after the list, as children: a gesture with the pointer, a note about a mode.

<ShortcutsDialog title={t('keys.title')} closeLabel={t('close')}>
<p className="mt-4 text-xs text-faint">{t('keys.stressGesture')}</p>
</ShortcutsDialog>

Escape closes a dialog, and that is Base UI’s doing, not a command’s. Declare it without a run and the sheet lists it like any other — and a command bound to Escape in the same place is told, by the console, that it never will be:

useCommand({ id: 'close', label: t('keys.close'), group: t('keys.everywhere'), keys: 'Escape' })

ShortcutList is the list on its own, without the dialog: the same groups and rows, for a page that shows the keys in place. A command with no keys has nothing to show in either and is left out.

SHORTCUTS_COMMAND is the id of the command the dialog declares - for setKeymap({ [SHORTCUTS_COMMAND]: 'F1' }), or runCommand(SHORTCUTS_COMMAND) from a help menu. ShortcutList takes a <div>’s props and lays the groups out in two columns from sm up; a group never breaks across them.

Props

Read from the types in ui/shortcuts-dialog.tsx, the file a product copies - so the description of each prop is also what an editor shows over it there.

ShortcutList

The list on its own, for a settings page that shows the keys in place. A command with no keys has nothing to show and is left out.

PropTypeDefault
level1 | 2 | 3 | 4 | 5 | 63Where the group headings stand in the outline: 3 by default - under the sheet's title, or a settings page's section.

And every prop of React's HTMLAttributes<HTMLDivElement>, passed on.

ShortcutsDialog

PropTypeDefault
titlerequiredstringThe heading, and the name of the command that opens it: in the palette and in the list itself.
closeLabelrequiredstringWhat the close button is called, for a screen reader. No default: a word the component invents is a word the product cannot translate.
descriptionReactNodeThe line under the heading.
keysstring'?'The keys that open it.
groupstringThe heading its own line is listed under.
openbooleanControlled, for a product that opens it from somewhere else as well.
onOpenChange(open: boolean) => voidCalled with the new state whenever it opens or closes, whether by the keys, runCommand or the close button.
childrenReactNodeAfter the list: what keys cannot say - a gesture with the pointer, a note that letters wait while someone is typing.

Accessibility

The examples on the stand, through axe 4.14.0 in a browser, in both themes, against WCAG 2.2 A and AA and axe's best practices: 6 rules applied and passed, and none failed. The accessibility report has every item.

History

Each release that changed ui/shortcuts-dialog.tsx - the file a product copies - newest first.

  1. v0.41.0
    • docs read every props table from the component's types ee3b159
  2. v0.38.0
    • feat a heading stands at the level the page gives it 101c183
  3. v0.35.0breaking
    • feat one list of commands for keys, palette and sheet dd9a125