Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ai/keyux
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.6.2
Choose a base ref
...
head repository: ai/keyux
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Loading
Showing with 4,151 additions and 3,402 deletions.
  1. +1 −1 .github/workflows/release.yml
  2. +9 −8 .github/workflows/test.yml
  3. +1 −0 .npmignore
  4. +37 −1 CHANGELOG.md
  5. +125 −17 README.md
  6. +16 −0 compat.js
  7. +1 −1 eslint.config.js
  8. +149 −0 focus-group-polyfill.js
  9. +3 −5 focus-group.js
  10. +53 −29 hotkey.js
  11. +50 −5 index.d.ts
  12. +8 −8 index.js
  13. +1 −10 jump.js
  14. +18 −0 overrides.js
  15. +20 −20 package.json
  16. +2,318 −2,795 pnpm-lock.yaml
  17. +28 −5 press.js
  18. +30 −0 test/compat.test.ts
  19. +31 −1 test/demo/index.html
  20. +118 −12 test/demo/index.tsx
  21. BIN test/demo/video.mp4
  22. +502 −0 test/focus-group-polyfill.test.ts
  23. +387 −391 test/focus-group.test.ts
  24. +5 −26 test/hidden.test.ts
  25. +139 −20 test/hotkey.test.ts
  26. +3 −1 test/index.test.ts
  27. +14 −35 test/jump.test.ts
  28. +22 −11 test/press.test.ts
  29. +62 −0 test/utils.ts
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT
- name: Create the release
if: steps.changelog.outputs.changelog_content != ''
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
name: ${{ github.ref_name }}
body: '${{ steps.changelog.outputs.changelog_content }}'
17 changes: 9 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -14,13 +14,13 @@ jobs:
- name: Checkout the repository
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: 8
version: 10
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 21
node-version: 23
cache: pnpm
- name: Install dependencies
run: pnpm install --ignore-scripts
@@ -31,16 +31,17 @@ jobs:
strategy:
matrix:
node-version:
- 22
- 20
- 18
name: Node.js ${{ matrix.node-version }} Quick
steps:
- name: Checkout the repository
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: 8
version: 10
- name: Install Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
@@ -64,13 +65,13 @@ jobs:
with:
persist-credentials: false
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: 8
version: 10
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --ignore-scripts
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
coverage/
tsconfig.json
test/demo/demo_video.mp4
38 changes: 37 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,42 @@
# Change Log

This project adheres to [Semantic Versioning](http://semver.org/).

## 0.11.1
* Fixed hotkeys with `Alt` on MacOS (by @mira-kovar).

## 0.11.0
* Added `focusgroup` limited polyfill (by @echo-vladimir & @mezhnin).

## 0.10.0
* Added support for hotkey with any modifier (`Alt`/`Meta`) inside text input.
* Added click by `Space` key support to `pressKeyUX()`.

## 0.9.0
* Added `inert` and `aria-hidden` support.

## 0.8.3
* Fixed `contenteditable` support (by @vitalybaev).

## 0.8.2
* Fixed hotkey with space support (by @vitalybaev).

## 0.8.1
* Fixed hotkeys with space (by @vitalybaev).

## 0.8.0
* Added ability to put focus to text input by hotkey.

## 0.7.2
* Fixed Safari support.

## 0.7.1
* Add support for hotkey with `Alt` on focus in text fields.

## 0.7.0
* Changed hotkeys override API with transformers (by @myandrienko).
* Added `hotkeyMacCompat` transformer (by @myandrienko).

## 0.6.2
* Reduced npm package size.

@@ -24,7 +60,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## 0.3.0
* Changed modifier order for Windows canonical order (by @myandrienko).
* Improved MacOS X support in `getHotKeyHint` (by @myandrienko).
* Improved Mac OS X support in `getHotKeyHint` (by @myandrienko).
* Fixed listeners leak in `pressKeyUX` (by @ilyhryh).
* Fixed docs (by @myandrienko, @lentsd, and @maximal).

142 changes: 125 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -24,15 +24,21 @@ export const Button = ({ hotkey, children }) => {
```

See [demo page](https://ai.github.io/keyux/)
and [example](./test/demo/index.tsx).
and [example](./test/demo/index.tsx):

https://github.com/user-attachments/assets/bcd78271-cf76-45a3-8beb-4f3cea69c143

---

- [Install](#install)
- [Hotkeys](#hotkeys)
- [Hotkeys Hint](#hotkeys-hint)
- [Pressed State](#pressed-state)
- [Hotkeys Override](#hotkeys-override)
- [Hotkeys for List](#hotkeys-for-list)
- [Meta instead of Ctrl on Mac](#meta-instead-of-ctrl-on-mac)
- [Focus Groups](#focus-groups)
- [`focusgroup` attribute](#focusgroup-attribute)
- [Menu](#menu)
- [Listbox](#listbox)
- [Tablist](#tablist)
@@ -42,7 +48,7 @@ and [example](./test/demo/index.tsx).

---

<img src="https://cdn.evilmartians.com/badges/logo-no-label.svg" alt="" width="22" height="16" /> Made in <b><a href="https://evilmartians.com/devtools?utm_source=keyux&utm_campaign=devtools-button&utm_medium=github">Evil Martians</a></b>, product consulting for <b>developer tools</b>.
<img src="https://cdn.evilmartians.com/badges/logo-no-label.svg" alt="" width="22" height="16" /> Made at <b><a href="https://evilmartians.com/devtools?utm_source=keyux&utm_campaign=devtools-button&utm_medium=github">Evil Martians</a></b>, product consulting for <b>developer tools</b>.

---

@@ -59,17 +65,20 @@ Then add the `startKeyUX` call with the necessary features to the main JS file.
import {
hiddenKeyUX,
hotkeyKeyUX,
hotkeyOverrides,
jumpKeyUX,
focusGroupKeyUX,
focusGroupPolyfill,
pressKeyUX,
startKeyUX
} from 'keyux'

const overrides = {}
const overrides = hotkeyOverrides({})

startKeyUX(window, [
hotkeyKeyUX(overrides),
hotkeyKeyUX([overrides]),
focusGroupKeyUX(),
focusGroupPolyfill(),
pressKeyUX('is-pressed'),
jumpKeyUX(),
hiddenKeyUX()
@@ -85,10 +94,16 @@ with the same hotkey in `aria-keyshortcuts`.
For instance, KeyUX will click on this button if user press
<kbd>Alt</kbd>+<kbd>B</kbd> or <kbd>⌥</kbd> <kbd>B</kbd>.

```js
```jsx
<button aria-keyshortcuts="alt+b">Bold</button>
```

You can use hotkey to move focus to text input or textarea:

```jsx
<input type="search" aria-keyshortcuts="s" placeholder="S" />
```

The hotkey pattern should contain modifiers like `meta+ctrl+alt+shift+b`
in this exact order.

@@ -102,6 +117,18 @@ startKeyUX(window, [
])
```

Hotkeys inside block with `inert` or `aria-hidden` attribute will be ignored.
You can use it, to disable page’s hotkeys when dialog is shown:

```html
<main inert>
<button aria-keyshortcuts="h">Help</button> <!-- Will be ignored -->
</main>
<dialog>
</dialog>
```


### Hotkeys Hint

@@ -127,6 +154,29 @@ external keyboard).
For instance, for `alt+b` it will return `Alt + B` on Windows/Linux or `⌥ B`
on Mac.

If you’re using overrides, pass the same override config both to `hotkeyKeyUX()`
and `getHotKeyHint()` for accurate hints:

```js
import {
getHotKeyHint,
hotkeyOverrides,
hotkeyKeyUX,
startKeyUX
} from 'keyux'

let config = { 'alt+b': 'b' }

startKeyUX(window, [
hotkeyKeyUX([hotkeyOverrides(config)]) // Override B to Alt + B
])
getHotKeyHint(window, 'b', [hotkeyOverrides(config)]) // Alt + B
```

One-letter hotkeys (like <kbd>B</kbd>) will be ignored if user’s focus is inside
text inputs or [focus groups](#focus-groups). This is why for general hotkeys
we recommend add some modifier like <kbd>Alt</kbd>+<kbd>B</kbd>.


### Pressed State

@@ -150,7 +200,7 @@ button {
}
}
```

overriding
You can use
[`postcss-pseudo-classes`](https://github.com/giuseppeg/postcss-pseudo-classes)
to automatically add class for every `:active` state in your CSS.
@@ -161,8 +211,8 @@ to automatically add class for every `:active` state in your CSS.
Many users want to override hotkeys because your hotkeys can conflict with
their browser’s extensions, system, or screen reader.

KeyUX allows overriding hotkeys using the `overrides` object.
Both `hotkeyKeyUX()` and `getHotKeyHint()` accept it as an argument.
KeyUX allows overriding hotkeys using tranforms. Use the `hotkeyOverrides()`
tranformer with `hotkeyKeyUX()` and `getHotKeyHint()`.

You will need to create some UI for users to fill this object like:

@@ -174,7 +224,8 @@ const overrides = {

Then KeyUX will click on `aria-keyshortcuts="b"` when
<kbd>Alt</kbd>+<kbd>B</kbd> is pressed, and
`getHotKeyHint(window, 'b', overrides)` will return `Alt + B`/`⌥ B`.
`getHotKeyHint(window, 'b', [hotkeyOverrides(overrides)])` will return
`Alt + B`/`⌥ B`.


### Hotkeys for List
@@ -188,9 +239,9 @@ To implement it:
KeyUX ignores `data-keyux-ignore-hotkeys`.

```jsx
<li data-keyux-ignore-hotkeys tabindex="0">
<li data-keyux-ignore-hotkeys tabIndex={0}>
{product.title}
<button aria-keyshortcuts="a">Add to card</button>
<button aria-keyshortcuts="a" tabIndex={-1}>Add to card</button>
</li>
```

@@ -200,22 +251,78 @@ If you have common panel with actions for focused item, you can use
```js
<ul>
{products.map(product => {
return <li data-keyux-hotkeys="panel" tabindex="0" key={product.id}>
return <li data-keyux-hotkeys="panel" tabIndex={0} key={product.id}>
{product.title}
</li>
})}
</ul>
<div id="panel" data-keyux-ignore-hotkeys>
<button aria-keyshortcuts="a">Add to card</button>
<button aria-keyshortcuts="a" tabIndex={-1}>Add to card</button>
</div>
```


### Meta instead of Ctrl on Mac

It’s common to use the <kbd>Meta</kbd> (or <kbd>⌘</kbd>) modifier for hotkeys
on Mac, while Windows and Linux usually favor the <kbd>Ctrl</kbd> key. To provide
familiar experience on all platforms, enable the Mac compatibility transform:

```js
import {
hotkeyMacCompat,
hotkeyKeyUX,
startKeyUX,
getHotKeyHint
} from 'keyux'

const mac = hotkeyMacCompat();
startKeyUX(window, [hotkeyKeyUX([mac])])
getHotKeyHint(window, 'ctrl+b', [mac]) // Ctrl+B on Windows/Linux and ⌘+b on Mac
```

Hotkeys pressed with the <kbd>Meta</kbd> modifier will work as if
the <kbd>Ctrl</kbd> modifier was pressed.


## Focus Groups

Using only <kbd>Tab</kbd> for navigation is not very useful. User may need to
press it too many times to get to their button (also non-screen-reader users
don’t have quick navigation).


### `focusgroup` attribute

Key UX has limited polyfill for [`focusgroup` attribute](https://open-ui.org/components/focusgroup.explainer/) to mark groups where user will move `:focus`
by arrows.

```html
<div focusgroup>
<button type="button">Copy</button>
<button type="button">Paste</button>
<button type="button">Cut</button>
</div>
```

Key UX supports (you can combine these features):
- `focusgroup="block"` for vertical arrows.
- `focusgroup="no-memory"` to not restore last focus position.
- `focusgroup="wrap"` enables cyclic focus movement within a group.
- `focusgroup="none"` excludes element from arrow key navigation.

Key UX doesn’t support `grid` feature.

To enable this feature, call `focusGroupPolyfill`.

```js
import { focusGroupPolyfill } from 'keyux'

startKeyUX(window, [
focusGroupPolyfill()
])
```

### Menu

To reduce Tab-list you can group website’s menu
@@ -232,7 +339,8 @@ with arrow navigation.

Users will use <kbd>Tab</kbd> to get inside the menu, and will use either
arrows or <kbd>Home</kbd>,
<kbd>End</kbd> or an item name to navigate inside.
<kbd>End</kbd> or an item name to navigate inside. User can search the menu item
by typing the first characters of the item text.

To enable this feature, call `focusGroupKeyUX`.

@@ -307,8 +415,9 @@ startKeyUX(window, [
### Toolbar

The [`role="toolbar"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/toolbar_role)
defines the containing element as a collection of commonly used function buttons or controls represented in a compact visual forms.
Buttons inside the `toolbar` must have `type="button"` attribute because the default one is `submit`.
defines the containing element as a collection of commonly used function buttons
or controls represented in a compact visual forms. Buttons inside the `toolbar`
must have `type="button"` attribute because the default one is `submit`.

```html
<div role="toolbar">
@@ -337,7 +446,6 @@ startKeyUX(window, [
])
```


## Focus Jumps

After finishing in one section, you can move user’s focus to the next step
Loading