Ninja Keys
Keyboard shortcuts interface for your website. Working with Vanilla JS, Vue, React.
Demo
Why?
A lot of applications support that pattern, user hit ⌘+k (or ctrl+k) and search UI dialog appear.
I've seen recently in Notion, Slack, Linear, Vercel and Agolia, but I'm sure there are plenty more.
Also, there is a Apple Spotlight, Alfred and Raycast app that using this pattern too but different shortcuts.
There are already some libraries but they are too framework spesific, like larevel only or react only
Althought, mine is not a silver bullet and if you need more framework integration, check them out too.
I was needed an keyboard interface for navigation for static website without any frameworks.
In same time I have a few vue projects where it can be useful too.
So I decided to give first shot for Web Components and Lit Element.
Why "Ninja" ?
Because it appears from nowhere and executes any actions quickly.
Or because it allows your users to become keyboard ninja's 🙃
Install from NPM
npm i ninja-keys
Import if you are using webpack, rollup, vite or other build system.
import from 'ninja-keys';
Install from CDN
Mostly for usage in HTML/JS without build system.
<script type="module" src="https://unpkg.com/ninja-keys?module"></script>
or inside your module scripts
import {NinjaKeys} from 'https://unpkg.com/ninja-keys?module';
Usage
Add tag to your html.
<ninja-keys> </ninja-keys>
<script>
const ninja = document.querySelector('ninja-keys');
ninja.data = [
{
id: 'Projects',
title: 'Open Projects',
hotkey: 'ctrl+N',
icon: 'apps',
section: 'Projects',
handler: () => {
alert('Your logic to handle');
},
},
{
id: 'Theme',
title: 'Change theme...',
icon: 'desktop_windows',
children: ['Light Theme', 'Dark Theme', 'System Theme'],
hotkey: 'ctrl+T',
handler: () => {
ninja.open({ parent: 'Theme' });
return {keepOpen: true};
},
},
{
id: 'Light Theme',
title: 'Change theme to Light',
icon: 'light_mode',
parent: 'Theme',
handler: () => {
document.documentElement.classList.remove('dark');
},
},
{
id: 'Dark Theme',
title: 'Change theme to Dark',
icon: 'dark_mode',
parent: 'Theme',
handler: () => {
document.documentElement.classList.add('dark');
},
},
];
</script>
Library using flat data structure inside, as in example above. But you can also use tree structure as below:
{
id: 'Theme',
children: [
{ id: ':ight' title: 'light_mode', },
{ id: 'System Theme',
children: [
{ title: 'Sub item 1' },
{ title: 'Sub item 2' }
]
}
]
}
Attributes
Field | Default | Description |
---|
placeholder | Type a command or search... | Placeholder for search |
disableHotkeys | false | If attribute exist will register all hotkey for all actions |
hideBreadcrumbs | false | Hide breadcrumbs on header if true |
openHotkey | cmd+k,ctrl+k | Open or close shortcut |
navigationUpHotkey | up,shift+tab | Navigation up shortcuts |
navigationDownHotkey | down,tab | Navigation down shortcuts |
closeHotkey | esc | Close shotcut |
goBackHotkey | backspace | Go back on one level if has parent menu |
selectHotkey | enter | Select action and execute handler or open submenu |
hotKeysJoinedView | false | If exist/true will display hotkeys inside one element |
noAutoLoadMdIcons | false | If exist it disable load material icons font on connect |
Example
<ninja-keys placeholder="Must app is awesome" openHotkey="cmd+l" hideBreadcrumbs></ninja-keys>
Data
Array of INinjaAction
- interface properties below
Name | Type | Description |
---|
id | string | Unique id/text. Will be displayed as breadcrumb in multimenu |
title | string | Title of action |
hotkey | string(optional) | Shortcut to display and register |
handler | Function(optional) | Function to execute on select |
mdIcon | string(optional) | Material Design font icon name |
icon | string(optional) | Html to render as custom icon |
parent | string(optional) | If using flat structure use id of actions to make a multilevel menu |
keywords | string(optional) | Keywords to use for search |
children | Array(optional) | If using flat structure then ids of child menu actions. Not requried on tree structure |
section | string(optional) | Section text. Like a header will be group with other same sections |
Methods
Name | Arg | Description |
---|
open | { parent?: string } | Open menu with parent, if null them open root menu |
close | | Close menu |
setParent | parent?: string | Navigate to parent menu |
Example
const ninja = document.querySelector('ninja-keys');
ninja.open()
ninja.open({ parent: 'Theme' })
Themes
Component support dark theme out-of-box. You just need to add a class.
<ninja-keys class="dark"></ninja-keys>
If you need more style control, use css variable below.
CSS variables
Name | Default |
---|
--ninja-width | 640px; |
--ninja-backdrop-filter | saturate(180%) blur(2px); |
--ninja-overflow-background | rgba(255, 255, 255, 0.5); |
--ninja-text-color | rgb(60, 65, 73); |
--ninja-font-size | 16px; |
--ninja-top | 20%; |
--ninja-key-border-radius | 0.25em |
--ninja-accent-color | rgb(110, 94, 210); |
--ninja-secondary-background-color | rgb(239, 241, 244); |
--ninja-secondary-text-color | rgb(107, 111, 118); |
--ninja-selected-background | rgb(248, 249, 251); |
--ninja-icon-color | var(--ninja-secondary-text-color); |
--ninja-icon-size | 1.2em; |
--ninja-separate-border | 1px solid var(--ninja-secondary-background-color); |
--ninja-modal-background | #fff; |
--ninja-modal-shadow | rgb(0 0 0 / 50%) 0px 16px 70px; |
--ninja-actions-height | 300px; |
--ninja-group-text-color | rgb(144, 149, 157); |
--ninja-footer-background | rgba(242, 242, 242, 0.4); |
Example
ninja-keys {
--ninja-width: 400px;
}
Icons
By default component using icons from https://fonts.google.com/icons
For example, you can just set mdIcon
to light_mode
to render sun icon.
To add Material icons for website you need to add to html, for example
<link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet">
If want custom icons, you can use svg
or img
to insert it with icon
property for action with ninja-icon
class.
Example:
{
title: 'Search projects...',
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="ninja-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
</svg>`,
section: 'Projects',
},
Also, you can change width and font using css variables for it
ninja-keys {
--ninja-icon-size: 1em;
}
<ninja-keys>
<slot name="footer">Must custom footer or empty to hide</slot>
</ninja-keys>
Dev Server
npm run start
Linting
To lint the project run:
npm run lint
Formatting
Prettier is used for code formatting. It has been pre-configured according to the Lit's style.
License
Copyright (c) Sergei Sleptsov hey@sergei.ws
Licensed under the MIT license.