complex_modifications for Karabiner-Elements.
https://ke-complex-modifications.pqrs.org/
Rule files can use any of the following formats:
- A JSON ruleset containing
titleandrulesunderpublic/json. - A JSON file containing one rule (
descriptionandmanipulators) underpublic/json. - A JavaScript file under
public/jswhose final expression returns one rule (descriptionandmanipulators).
Karabiner-Elements 16.2.0 and later can import single-rule JSON and JavaScript
directly. To support Karabiner-Elements 16.1.0 and earlier, the build also
converts these sources to the title and rules ruleset format. The rule's
description is used as the ruleset title.
A single JSON rule is replaced by its ruleset form at the same distribution
path, so there is only one distributed JSON file. JavaScript source remains
available unchanged, and its generated ruleset JSON is written beside it with
the .ruleset.json suffix.
public/json/example.json→dist/json/example.jsonpublic/js/example.js→dist/js/example.js- generated JavaScript ruleset JSON →
dist/js/example.ruleset.json
The generated dist.json index contains lightweight metadata for display and
search. Full manipulators remain in the distributed JSON files and are not
duplicated in this index.
JSON rulesets bundle multiple rules into a single file so users can pick and enable what they need. For example, the "Emacs key bindings" ruleset includes several rules for different use cases.
| The distributed JSON file | Complex Modifications |
|---|---|
![]() |
![]() |
To bundle them this way, the JSON has the following structure. Each element under rules is a single rule as defined in Complex Modifications.
If you list GitHub usernames in the "maintainers" field, the distribution site will automatically link to those accounts: https://ke-complex-modifications.pqrs.org/
{
"title": "Emacs key bindings (rev XXX)",
"maintainers": ["tekezo"],
"rules": [
{
"description": "Emacs key bindings [control+keys] (rev XXX)",
"manipulators": [
{
"type": "basic",
"from": {},
"to": []
}
]
},
{
"description": "Emacs key bindings [option+keys] (rev XXX)",
"manipulators": [
{
"type": "basic",
"from": {},
"to": []
}
]
}
]
}The object normally placed inside rules can also be saved directly:
{
"description": "Change caps_lock to escape",
"manipulators": [
{
"type": "basic",
"from": { "key_code": "caps_lock" },
"to": [{ "key_code": "escape" }]
}
]
}Optional ruleset attribution fields such as maintainers and author can be
included in the same object. They are moved to the ruleset level during the build.
The distributed json/example.json contains the converted ruleset form. The
distribution site imports this file directly and does not provide a separate
compatible JSON action for single JSON rules.
JavaScript is useful when a rule contains repeated definitions. Helper functions can
be defined in the same file. The last expression must return a single rule
(description and manipulators). JSON ruleset objects containing title and
rules are not supported under public/js. For example:
// JavaScript must be written in ECMAScript 5.1.
function main() {
return {
description: 'Change h/j/k/l to arrow keys',
manipulators: [
makeManipulator('h', 'left_arrow'),
makeManipulator('j', 'down_arrow'),
makeManipulator('k', 'up_arrow'),
makeManipulator('l', 'right_arrow'),
],
}
}
function makeManipulator(from, to) {
return {
type: 'basic',
from: { key_code: from },
to: [{ key_code: to }],
}
}
main()A source named public/js/example.js is distributed as dist/js/example.js.
Its evaluated and normalized JSON ruleset is distributed as
dist/js/example.ruleset.json.
Use js/example.js as its path in public/groups.json.
The build uses the value of the last JavaScript expression; standard output from
the script is ignored.
For JavaScript rules, the distribution site's import menu provides both of the following actions:
Import JavaScript codeImport JSON compatible with Karabiner-Elements 16.1.0 or earlier
The menu can also display the generated JSON with Show compatible JSON and
copy its URL with Copy compatible JSON URL.
Follow the steps below to create a PR and add your settings!
-
Fork this repository on GitHub.
-
Clone the repository in Terminal.
git clone --depth 1 https://github.com/{your_account}/KE-complex_modifications.git cd KE-complex_modifications git submodule update --init --recursive --depth 1 -
Create a git branch in Terminal.
git switch -c my-settings
-
Put a JSON file into public/json or a JavaScript file into public/js. A JSON file can contain either a full ruleset or a single rule. A JavaScript file must return a single rule.
Existing generator files in src/json are also supported. Their names must end in
.json.js; generated JSON is written topublic/jsonas before. -
(Optional) Update public/groups.json if you want to add your rules to a particular category.
Add the following entry into public/groups.json.
{ "path": "json/your_awesome_configuration.json", // required "extra_description_path": "extra_descriptions/your_awesome_configuration.html" // optional },
You can use the tag
<kbd>⇧Shift</kbd>to make a nice ⇧Shift in your html. -
Run
makecommand in Terminal to validate your files.
JavaScript files are evaluated for validation. JSON is written underdist/json, and JavaScript is copied todist/js. Each JavaScript rule also produces a sibling*.ruleset.jsonfile. Single JSON rules are converted to ruleset form at their original path underdist/json. Files insrc/jsonalso update their corresponding generated files underpublic/json.make all
If there is a problem, an error message is displayed. Fix your files until no errors are shown.
"../public/json/personal_tekezo.json": `Personal rules (@tekezo) (rev 47)` error: `to` entry error: `key_code` error: unknown key_code: `"space"` make[1]: *** [all] Error 1 make: *** [all] Error 2 -
Test your files
Copy the file to test to
~/.config/karabiner/assets/complex_modifications. For a JSON source, copy the built JSON ruleset. For a JavaScript source, copy the JavaScript file itself without converting it to JSON.cp dist/json/your_awesome_configuration.json ~/.config/karabiner/assets/complex_modifications # For a JavaScript source: cp dist/js/your_awesome_configuration.js ~/.config/karabiner/assets/complex_modifications
Import rules from
Karabiner-Elements Settings > Complex Modifications > Rules > Add rule. -
(Optional) Test your files on local web server.
Run local web server by the following command in Terminal.
make preview-server
Then open http://localhost:8000.
-
Run
git commitandgit pushin Terminal.git add -A git commit -m 'your commit message' git push --set-upstream origin my-settings -
Create a PR on GitHub.
Use the description field to explain how each distributed JSON rule works.
If a one-line description is not enough (e.g., for complex rules), you can add supplementary text on the site:
- Place an HTML file under
public/extra_descriptions/. - Reference that HTML file from
public/groups.json.
The additional description will then appear on the site as shown in the screenshot.
| JSON only | With extra description |
|---|---|
![]() |
![]() |
- Example of extra description:
- Example of HTML file:
- Example entry in groups.json:
{
"path": "json/emacs_key_bindings.json",
"extra_description_path": "extra_descriptions/emacs_key_bindings.json.html"
}- Bootstrap's CSS are applied automatically, so you can adjust spacing with utility classes like mt-4, etc.
- Do not include
<html>and<body>tags. Write only the HTML for the description section. - You can include images with tags like
<img src="multitouch-extension/enable-multitouch-extension.png" class="img-fluid border" />. Be sure to add the images to the repository. - As described above, you can preview your HTML by
make preview-servercommand and openhttp://localhost:8000/in your browser. The HTML file will not be loaded unless you specifyextra_description_pathinpublic/groups.json, so be sure to updategroups.json. - After modifying an HTML file, reload the page in your browser to reflect the changes on the preview server. Automatic hot reload is not supported.
To update your previously forked repositories, run the following command in Terminal.
git remote add upstream https://github.com/pqrs-org/KE-complex_modifications.git# switch to main branch
git switch main
# fetch upstream
git fetch --all --prune --prune-tags
# update local repository
git reset --hard upstream/main
# update submodules
git submodule update --init --recursive
# clean files
git clean -x -d -f .
# update GitHub repository
git pushOn macOS, the code in public/js/*.js and src/json/*.js is executed by
Duktape, which is built into the Karabiner-Elements
command line interface (karabiner_cli). The Linux deployment build evaluates
public/js/*.js with Node.js because karabiner_cli is not available there.
JavaScript must remain compatible with the Duktape environment so both builds
produce the same result.
Unlike the latest Node.js, the basic language specification is ES5.1, so the following features cannot be used.
let(constis specially supported)- Arrow functions
- Default parameters
- Spread (...) syntax
- Template literals
There are many existing codes. Here are some of the more distinctive ones among them.
- Use the predefined list of bundle identifiers in
frontmost_application_if. - Generate remappings from a list of characters
- Include file from another file
- Generate rules from key combinations



