Ankura
A type-safe configuration tool for Karabiner-Elements using Apple's Pkl configuration language.
Ankura brings the power of Pkl to Karabiner-Elements configuration, allowing you to define keymaps in a declarative manner. Pkl provides autocomplete, type safety, validation, and excellent editor support that catches errors as you type. As a full programming language, Pkl lets you create your own abstractions and reusable patterns. You write simple, readable Pkl configurations that compile to Karabiner JSON. The live-reload daemon applies your changes instantly.
Features:
- Type-safe declarative DSL - Write keyboard configurations in Pkl with full type checking, validation, documentation and editor support
- Built-in helpers for common patterns - Pre-built abstractions for hyper keys, symbol layers, key swaps, and dual-use keys
- First-class macOS integrations - Native support for popular window managers (yabai, AeroSpace) and system automation through shell commands
- Live-reload daemon - File watching with instant configuration updates
# Install Karabiner-Elements if not already installed
brew install --cask karabiner-elements
# Install ankura
brew install lrangell/ankura/ankuraCreate or edit ~/.config/ankura.pkl:
extends "/opt/homebrew/var/lib/ankura/config.pkl"
name = "My Config"
keys = new Keys{}
actions = new Actions {}
mods = new Modifiers {}
rules = List(
// Your rules go here
)Select "My Config" profile in the Karabiner menu bar icon.
If you're new to Pkl, we recommend reading the Pkl Language Reference to understand the language fundamentals.
For the best experience, add Pkl support to your editor:
- Neovim: pkl-neovim
- VSCode: Pkl extension installation guide
Editor support provides autocomplete, type checking, inline documentation, and error highlighting as you write your configurations.
Create modal layers for complex workflows.
new Layer {
modifier = mods.ctrl
h = keys.left
j = keys.down
k = keys.up
l = keys.right
}Transform keys to behave differently when tapped vs held.
new DualUse {
key = keys.capsLock
tap = keys.esc
hold = keys.ctrl
}Activate arrow keys while holding a trigger.
new SimLayer {
trigger = "f"
h = keys.left
j = keys.down
k = keys.up
l = keys.right
}Basic key remapping.
new BasicMap {
key = keys.capsLock
action = keys.esc
}Space key + other keys for quick shortcuts.
new SpaceMode {
h = keys.left
j = keys.down
k = keys.up
l = keys.right
i = keys.del
comma = keys.option.and(keys.comma)
period = keys.option.and(keys.period)
semicolon = keys.returnOrEnter
}Execute shell commands, launch applications, and control system functions.
// Launch or focus application
new BasicMap {
key = "s"
mod = mods.cmd
action = actions.focusOrLaunchApp("Slack")
}
// Run shell commands and open URLs
new SimLayer {
trigger = "d"
y = actions.runShell("/opt/homebrew/bin/yabai -m space --focus recent")
u = actions.openUrl("https://example.com")
}
// Type text when held, normal key when tapped
new DualUse {
key = keys.comma
hold = actions.typeText("[email protected]")
tap = keys.comma
}Click to expand all available actions
actions.launchApp("Google Chrome") // Launch application
actions.focusOrLaunchApp("Slack") // Focus or launch if not running
actions.closeWindow() // Close current window (⌘W)
actions.closeApp() // Quit application (⌘Q)actions.runShell("/opt/homebrew/bin/yabai -m space --focus recent")
actions.runBin("/usr/local/bin/custom-script", List("arg1", "arg2"))
actions.openUrl("https://example.com")actions.typeText("[email protected]")
actions.showNotification("Build Complete", "All tests passed!")actions.lockScreen() // Lock screen
actions.sleep() // Put system to sleep
actions.volumeUp() // Increase volume
actions.volumeDown() // Decrease volume
actions.mute() // Toggle mute
actions.brightnessUp() // Increase brightness
actions.brightnessDown() // Decrease brightnessactions.screenshot() // Screenshot to clipboard
actions.screenshotSelection() // Screenshot selection
actions.screenshotWindow() // Screenshot windowactions.showLaunchpad() // Open Launchpad
actions.openActivityMonitor() // Open Activity MonitorPre-built patterns for common keyboard customizations.
// Hyper key - map caps_lock to all four modifiers (⌃⌥⇧⌘)
builtins.hyperKey(keys.capsLock)
// Dual-use hyper key - ⌃⌥⇧⌘ when held, escape when tapped
builtins.hyperKeyDualUse(keys.capsLock)
// Symbol layer - hold right_shift for quick access to programming symbols
builtins.symbolLayer(keys.rightShift)
// Key swaps
builtins.swapKeys(keys.tab, keys.escape) // swap tab and escape
builtins.swapSemicolon() // swap ; and :For a complete working example with all features, see readme_examples_profile.pkl.
Click to expand yabai window management examples
Control yabai window manager directly from your keyboard:
yabai {
modifier = "d"
window {
focus {
west = "h"
south = "j"
north = "k"
east = "l"
}
swap {
modifier = List(mods.cmd, mods.shift)
west = "h"
south = "j"
north = "k"
east = "l"
}
resize {
modifier = List(mods.ctrl, mods.opt)
left = "h"
down = "j"
up = "k"
right = "l"
}
}
space {
focus {
mappings {
["1"] = "u"
["2"] = "i"
["3"] = "o"
["4"] = "p"
["5"] = keys.openBracket
}
prev = "n"
next = "m"
}
}
display {
focus {
modifier = List(mods.cmd, mods.opt)
mappings {
["1"] = "1"
["2"] = "2"
["3"] = "3"
}
prev = keys.comma
next = keys.period
}
}
toggles {
modifier = List(mods.cmd, mods.opt, mods.shift)
float = "f"
fullscreen = "m"
sticky = "s"
zoom = "z"
}
}Click to expand AeroSpace window management examples
Control AeroSpace tiling window manager with keyboard shortcuts:
aerospace {
modifier = "f"
window {
focus {
left = "h"
down = "j"
up = "k"
right = "l"
dfsNext = "n"
dfsPrev = "p"
}
move {
left = "h"
down = "j"
up = "k"
right = "l"
}
resize {
modifier = List(mods.opt, mods.ctrl)
width = "w"
height = "h"
smart = "s"
amount = 100
}
layout {
tiling = "t"
floating = "f"
fullscreen = "m"
}
}
workspace {
focus {
mappings {
["1"] = "u"
["2"] = "i"
["3"] = "o"
}
next = keys.tab
prev = "h"
}
move {
modifier = List(mods.opt, mods.shift)
mappings {
["1"] = "1"
["2"] = "2"
["3"] = "3"
}
prev = "n"
next = "m"
}
}
}Inspired by GokuRakuJoudo
