Skip to main content
Copy for LLM

Shortcuts

Put x-wk-shortcut on the element a shortcut acts on, a button, a link or a field, and the shortcut does what a click on it does. Place <x-wirekit::shortcuts /> once in your layout: ? opens an overview of the shortcuts that apply at that moment, with a switch that turns off the shortcuts made of a single key. <x-wirekit::shortcuts.trigger /> opens the same overview from a button, for a device without a keyboard.

Usage

Shortcuts on a page

A shortcut clicks its element, so it does exactly what the pointer does: a wire:click runs, a link with wire:navigate navigates as Livewire does, a disabled button does nothing. .focus moves the focus to the element instead, which is what a search field wants. A key held down fires its shortcut once, as a held mouse button clicks once.

Writing a Shortcut

Keys of one press are joined by +, with the key last; the presses of a sequence are separated by a space.

Written Means
cmd+s ⌘ S on a Mac, Ctrl+S elsewhere: cmd is Cmd or Ctrl, whichever the reader holds
ctrl+s Ctrl+S on every platform, Control on a Mac
alt+n Alt+N, or Option+N on a Mac
shift+? Shift and the key that types ?
/ The key that types /
g o G, then O within a second
cmd+enter ⌘ ↩ on a Mac, Ctrl+Enter elsewhere

option is another name for alt, and meta for cmd. A key that types no character has a name: enter, esc, space, tab, backspace, delete, up, down, left, right, home, end, pageup, pagedown and f1 to f24. Any other name is compared with the browser's KeyboardEvent.key as written, MediaPlayPause for example.

Which Key Press Counts

A key is recognized by the character it types, so ? is the key that types ? on any layout: Shift+/ on a US keyboard, Shift+ß on a German one. With Alt or Shift in the shortcut a letter or a digit is also recognized by its place on the keyboard, because Option+N types ˜ on a Mac.

A shortcut without a modifier needs none of Cmd, Ctrl and Alt to be held: s is not ⌘ S. Shift does not turn a letter into its shortcut either, so Shift+G is not g.

Areas

A shortcut belongs to an area, and an open dialog decides which areas count:

Area When it applies
The page No modal dialog is open
A dialog The element sits in that dialog, and it is the topmost open one
Everywhere Always: add .global

A Modal or a Drawer therefore hides the page's shortcuts while it is open, and its own apply. A search on ⌘ K that should open from inside a dialog as well takes .global:

<x-wirekit::button x-wk-shortcut.global="cmd+k" x-on:click="$dispatch('wirekit-command-palette-show')">Search</x-wirekit::button>

Sequences

g o waits up to one second for the O after the G. A press that continues no sequence ends the wait, and is then judged on its own. A G that starts no sequence is just a key nobody bound: it reaches the page as it would without the shortcut.

What Goes First

A key press belongs to whoever takes it first, in this order:

  1. A control that took the key. A menu's type-ahead, a select-only combobox or a tree takes a letter for itself, and the shortcut does not fire.
  2. A field the reader types in. A shortcut without Cmd, Ctrl or Alt never fires while the focus is in a text field, a text area or an editor: the character stays a character. Shortcuts with Cmd, Ctrl or Alt work there too, so ⌘ S saves from inside a field.
  3. The switch for single keys, below.
  4. The area, above.

A key that the IME of a Chinese, Japanese or Korean keyboard is composing is never a shortcut.

Single Keys

A shortcut without Cmd, Ctrl or Alt, such as ?, / or g o, is a single-key shortcut, and the reader can switch those off in the overview. A person who controls the computer by voice says words, and each word arrives as keys: without the switch, dictating a sentence fires every shortcut whose letters it contains. That is why WCAG 2.1.4 requires a way to turn them off.

The switch keeps its position in the browser. To keep it with the person instead, pass the stored choice to the overview and store each change, which the overview announces as wirekit:shortcuts-single-keys-changed on window:

# 1. A column for the choice: null until the person flips the switch, so the browser's memory counts until then
php artisan make:migration add_single_key_shortcuts_to_users_table --table=users
// database/migrations/xxxx_xx_xx_xxxxxx_add_single_key_shortcuts_to_users_table.php
public function up(): void
{
    Schema::table('users', function (Blueprint $table) {
        // 2. true, false, or null for "not chosen yet"
        $table->boolean('single_key_shortcuts')->nullable();
    });
}
// routes/web.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

// 3. Store the switch with the signed-in person
Route::put('/settings/single-key-shortcuts', function (Request $request) {
    $request->user()->forceFill(['single_key_shortcuts' => $request->boolean('enabled')])->save();

    return response()->noContent();
})->middleware('auth');
// resources/js/app.js
// 4. The overview announces each flip of its switch on the window
window.addEventListener('wirekit:shortcuts-single-keys-changed', (event) => {
    // 5. Laravel checks the CSRF token of a PUT: add <meta name="csrf-token" content="{{ csrf_token() }}"> to your layout's <head>
    fetch('/settings/single-key-shortcuts', {
        method: 'PUT',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
        },
        body: JSON.stringify({ enabled: event.detail.enabled }),
    });
});
{{-- resources/views/components/layouts/app.blade.php --}}
{{-- 6. The stored choice; null for a guest or a person who never chose, and then the browser's memory counts --}}
<x-wirekit::shortcuts :single-keys="auth()->user()?->single_key_shortcuts" />

A Screen That Turns Them Off

A barcode scanner types each code as a fast run of keys, and on a screen with a scanner every one of them could fire a shortcut. Put data-wk-single-keys="off" on any element of such a screen: while it is in the page, single-key shortcuts do not fire, and the overview says so instead of showing the switch. Shortcuts with Cmd, Ctrl or Alt still work.

<section data-wk-single-keys="off">
    <x-wirekit::input label="Scan a parcel" name="code" autofocus />
</section>

The Overview

<x-wirekit::shortcuts /> renders a Modal that ? opens and closes. It lists the shortcuts that apply at the moment it opens, grouped as Everywhere, In this dialog and On this page, each with its keys as the reader's platform names them. A shortcut that does not apply, because a dialog hides it, its element is hidden or disabled, or single keys are off, is not listed.

Each shortcut is listed under its element's accessible name: the text of a button or a link, the label of a field, or its aria-label. Where that name is not the right one for a list of shortcuts, data-wk-shortcut-label gives another:

<x-wirekit::button size="sm" surface="outline" icon-only x-wk-shortcut="n" data-wk-shortcut-label="New order">
    <x-slot:iconLeft><x-wirekit::icon name="plus" size="sm" aria-hidden="true" /></x-slot:iconLeft>
    New
</x-wirekit::button>

? is a single-key shortcut itself, so it obeys the switch. With single keys off, the overview opens from a button.

Opening It from a Button

<x-wirekit::shortcuts.trigger /> is a Button that opens the overview, labeled "Keyboard shortcuts" in the reader's language unless you give it a slot. It is the way in on a tablet or a phone, where ? cannot be typed. Anything else can open it by dispatching wirekit:shortcuts-open on window:

<x-wirekit::shortcuts.trigger intent="neutral" surface="ghost" size="sm">Shortcuts</x-wirekit::shortcuts.trigger>

<x-wirekit::dropdown.item x-on:click="$dispatch('wirekit:shortcuts-open')">Keyboard shortcuts</x-wirekit::dropdown.item>

A Shortcut Bound Twice

Two elements with the same shortcut in the same area cannot both fire: the first one bound wins. A single key that also starts a sequence in that area, g beside g o, means the sequence never fires. With APP_DEBUG on, the overview's page warns about both in the browser console, naming the two elements.

The Directive

Directive Does
x-wk-shortcut="cmd+s" Clicks the element when the shortcut is pressed in its area
x-wk-shortcut.focus="/" Focuses the element instead
x-wk-shortcut.global="cmd+k" Applies everywhere, with a dialog open as well. Combines with .focus

The value is read as written, not evaluated. The element gets aria-keyshortcuts with the shortcut in the form assistive technology reads, unless it has one already.

Attribute On Does
data-wk-shortcut-label the element with x-wk-shortcut Its name in the overview
data-wk-single-keys="off" any element Turns single-key shortcuts off while it is in the page

Props

Prop Type Default Description
single-keys bool|null null The stored choice for single-key shortcuts. Null uses the choice this browser remembers
heading string|null null The overview's heading. Without it: "Keyboard shortcuts", in the reader's language
level int 2 The level of the area headings in the overview, from 1 to 6. Another value keeps 2
scope string|null null Scoped personalization key

<x-wirekit::shortcuts.trigger> props

Prop Type Default Description
intent string|null null The button's intent, as on Button
surface string|null null The button's surface
size string|null null The button's size
scope string|null null Scoped personalization key

Events

Event Direction Detail
wirekit:shortcuts-open you dispatch it on window, and the trigger does none. Opens the overview
wirekit:shortcuts-single-keys-changed the overview dispatches it on window when the reader flips the switch enabled: whether single-key shortcuts now work

Accessibility

  • Every element with a shortcut of one press carries aria-keyshortcuts, Control+S Meta+S for cmd+s, so a screen reader can tell the reader the shortcut. A sequence has no form in that attribute, so it is named in the overview only.
  • Single-key shortcuts can be switched off (WCAG 2.1.4), and they never fire while the reader types in a field.
  • A shortcut does what a click does, so a reader who uses it reaches the same place and hears the same result as one who clicks.
  • The overview is a modal dialog with its heading as its name: the focus moves into it, stays there while it is open, and returns to where it was when it closes.
  • The keys are drawn as kbd elements. On a Mac they are the symbols the system's menus use, ⌘ ⌥ ⇧ ⌃, which VoiceOver reads by their names; elsewhere they are written out.

Keyboard Interaction

Key Action
? Opens the overview, and closes it while it is open
Escape Closes the overview
Tab Moves through the switch and the close button inside the overview
Space Flips the switch for single keys

Pitfalls

  • A shortcut on an element that is not rendered does nothing. An element hidden by display: none, inside an inert region or disabled neither acts nor is listed.
  • Browsers keep a few shortcuts for themselves. ⌘ W, ⌘ T and ⌘ N close or open windows and tabs before a page sees the press. Prefer a key the browser leaves to the page.
  • The overview lists what is bound when it opens. A shortcut whose element a later Livewire update adds is listed the next time the overview opens.

Design Tokens

Token Used for
--color-wk-text A shortcut's name and the area headings
--color-wk-text-muted "then" between the presses of a sequence, and the notes
--color-wk-border-subtle The line between two shortcuts
--text-wk-sm The names, the headings and the notes
--gap-wk-lg The space between two areas
--gap-wk-xs The space between keys
--padding-wk-y-xs The space above and below a shortcut

The keys themselves are Kbd, and the dialog is a Modal, each with its own tokens.

Further Reading

Was this page helpful?

Thank you for your feedback!

Voting requires cookies or local storage. What we store