
Karabiner
- 48 installs
- 24 repo stars
- Updated December 19, 2025
- johnlindquist/claude
Configure Karabiner keyboard remapping with complex conditionals, layers, and device-specific rules.
About
Generates Karabiner-Elements JSON for advanced keyboard customization. Supports layers, app conditionals, and complex mappings.
- Complex key remapping with layer switching
- Conditional remapping based on app or device
Karabiner by the numbers
- 48 all-time installs (skills.sh)
- +2 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #1,641 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/johnlindquist/claude --skill karabinerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 48 |
|---|---|
| repo stars | ★ 24 |
| Last updated | December 19, 2025 |
| Repository | johnlindquist/claude ↗ |
What it does
Configure Karabiner keyboard remapping with complex conditionals, layers, and device-specific rules.
Files
Karabiner (via Goku)
Configure macOS keyboard remapping with GokuRakuJoudo - an EDN-based DSL that compiles to Karabiner-Elements JSON.
Why Goku?
Karabiner's native JSON is verbose (20,000+ lines). Goku's EDN format is 10-50x more concise:
;; Goku: 1 line
[:caps_lock :escape]
;; Karabiner JSON: ~30 linesPrerequisites
# Install Goku
brew install yqrashawn/goku/goku
# Start as service (watches ~/.config/karabiner.edn)
brew services start goku
# Or run once manually
gokuConfig location: ~/.config/karabiner.edn
Logs: ~/Library/Logs/goku.log
EDN Syntax Quick Reference
Basic Structure
{:main [{:des "Rule description"
:rules [[:from :to]
[:from2 :to2]]}]}Keycodes
All keys use keyword syntax: :a, :1, :f19, :spacebar, :return_or_enter
Find keycodes:
- Run Karabiner-EventViewer.app
- See keys_info.clj
Modifier Syntax
| Symbol | Modifier | Example |
|---|---|---|
!C | Left Command | :!Ca = Cmd+A |
!T | Left Control | :!Ta = Ctrl+A |
!O | Left Option | :!Oa = Opt+A |
!S | Left Shift | :!Sa = Shift+A |
!Q | Right Command | :!Qa |
!W | Right Control | :!Wa |
!E | Right Option | :!Ea |
!R | Right Shift | :!Ra |
!F | Fn | :!Fa |
!P | Caps Lock | :!Pa |
!! | Hyper (Cmd+Ctrl+Opt+Shift) | :!!a |
## | Optional any modifier | :##a |
Combine modifiers: :!CTSa = Cmd+Ctrl+Shift+A
Rule Format
[:from :to] ;; Basic
[:from :to :condition] ;; With condition
[:from :to :condition {:alone :x}] ;; With optionsMultiple Keys
;; Press sequence
[:a [:1 :2 :3]] ;; a -> types 1, 2, 3
;; Simultaneous press (from)
[[:j :k] :escape] ;; j+k together -> escapeShell Commands
[:!!1 "open -a Safari"] ;; Hyper+1 runs shell command---
Conditions
Application Conditions
{:applications {:chrome ["^com\\.google\\.Chrome$"]
:code ["com.microsoft.VSCode"]}
:main [{:des "Chrome only"
:rules [[:a :b :chrome]]}]}Device Conditions
{:devices {:hhkb [{:vendor_id 1278 :product_id 51966}]}
:main [{:rules [[:a :b :hhkb]]}]}Input Source Conditions
{:input-sources {:us {:input_source_id "com.apple.keylayout.US"}}
:main [{:rules [[:a :b :us]]}]}Combining & Negating Conditions
[:a :b [:chrome :hhkb]] ;; Both conditions
[:a :b [:!chrome]] ;; NOT in Chrome---
Layers
Simlayers (Recommended)
Fast, simultaneous-key based layers - best for typing speed:
{:simlayers {:w-mode {:key :w}} ;; Hold W activates layer
:main [{:des "w-mode shortcuts"
:rules [:w-mode ;; Apply to this layer
[:e "open -a Finder"] ;; W+E opens Finder
[:r "open -a Safari"] ;; W+R opens Safari
]}]}Simlayer options:
{:simlayers {:w-mode {:key :w
:modi {:mandatory [:left_control]}}}} ;; Ctrl+W activatesStandard Layers (Tap/Hold)
Different behavior on tap vs hold:
{:layers {:caps-mode {:key :caps_lock
:alone {:key :escape}}}} ;; Tap=Esc, Hold=layerManual Layer Variables
;; Set variable on keydown, clear on keyup
[:w ["w-mode" 1] nil {:afterup ["w-mode" 0] :alone :w}]
;; Use the variable as condition
[:e :!Ce ["w-mode" 1]] ;; W+E -> Cmd+E (only when w-mode=1)---
Templates
Reusable shell command patterns:
{:templates {:open "open -a '%s'"
:launch "/path/to/script.sh %s"
:alfred "osascript -e 'tell application \"Alfred\" to run trigger \"%s\"'"}
:main [{:rules [[:!!1 [:open "Safari"]] ;; %s replaced with Safari
[:!!2 [:launch "arg1"]]]}]}---
Predefined Aliases
:froms (Input Keys)
{:froms {:delete {:key :delete_or_backspace}
:return {:key :return_or_enter}
:mouse1 {:pkey :button1}}}:tos (Output Actions)
{:tos {:spotlight {:key :spacebar :modi :command}
:paste {:key :v :modi :command}
:shift-click {:pkey :button1 :modi :left_shift}}}Custom Modifier Sets
{:modifiers {:hyper [:command :shift :control :option]
:meh [:shift :control :option]}}---
Advanced Options
Fourth position in rules:
[:from :to :condition {
:alone :key ;; to_if_alone
:held :key ;; to_if_held_down
:afterup :key ;; to_after_key_up
:delayed {:invoked :x :canceled :y}
:params {:alone_timeout 200}
}]---
Profile Settings
{:profiles {:Default {:default true
:sim 250 ;; Simultaneous threshold (ms)
:delay 500 ;; Delayed action time (ms)
:alone 1000 ;; to_if_alone timeout (ms)
:held 500}}} ;; Held threshold (ms)---
Common Workflows
Add a Simple Remap
{:main [{:des "Caps to Escape"
:rules [[:caps_lock :escape]]}]}Add App-Specific Shortcut
{:applications {:chrome ["com.google.Chrome"]}
:main [{:des "Chrome shortcuts"
:rules [[:!Cl :!Ct :chrome]]}]} ;; Cmd+L -> Cmd+T in ChromeCreate a Layer
{:simlayers {:nav {:key :spacebar}}
:main [{:des "Navigation layer"
:rules [:nav
[:h :left_arrow]
[:j :down_arrow]
[:k :up_arrow]
[:l :right_arrow]]}]}Hyper Key Setup
{:main [{:des "Caps Lock to Hyper"
:rules [[:caps_lock :!CTOSleft_shift nil {:alone :escape}]]}]}---
CLI Commands
goku # Compile karabiner.edn once
gokuw # Watch mode (compile on change)
goku -h # Help
# Service management
brew services start goku # Start watch service
brew services stop goku # Stop service
brew services restart goku # Restart after changes
# Check logs
tail -f ~/Library/Logs/goku.log---
Validation & Debugging
1. Check syntax: Goku reports EDN parse errors 2. View output: ~/.config/karabiner/karabiner.json 3. Use EventViewer: Karabiner-EventViewer.app shows key events 4. Check logs: tail ~/Library/Logs/goku.log
---
Best Practices
1. Use simlayers for frequently-accessed shortcuts (faster than hold-layers) 2. Group rules by purpose with descriptive :des tags 3. Use templates for repeated shell command patterns 4. Define aliases in :froms/:tos for complex key definitions 5. Test incrementally - add one rule, verify, repeat 6. Back up your config before major changes
---
Resources
EDN Syntax Reference
Comprehensive reference for Goku's EDN format.
Complete Config Structure
{
;; Global timing settings
:default true
:alone 500 ;; to_if_alone timeout (ms)
:delay 200 ;; sequence key delay (ms)
:held 500 ;; held threshold (ms)
:simlayer-threshold 140 ;; simultaneous detection (ms)
;; Custom modifier combinations
:modifiers {:hyper [:command :shift :control :option]
:meh [:shift :control :option]
:cos [:command :option :shift]}
;; Input key aliases
:froms {:delete {:key :delete_or_backspace}
:return {:key :return_or_enter}
:mouse1 {:pkey :button1}}
;; Output action aliases
:tos {:spotlight {:key :spacebar :modi :command}
:alfred {:key :spacebar :modi :option}}
;; Shell command templates
:templates {:open "open -a '%s'"
:script "/path/to/script.sh %s"}
;; Application bundle IDs
:applications {:chrome ["com.google.Chrome"]
:code ["com.microsoft.VSCode"]}
;; Device identifiers
:devices {:hhkb [{:vendor_id 1278 :product_id 51966}]}
;; Input sources
:input-sources {:us {:input_source_id "com.apple.keylayout.US"}}
;; Simlayer definitions
:simlayers {:w-mode {:key :w}}
;; Standard layer definitions
:layers {:caps-mode {:key :caps_lock :alone {:key :escape}}}
;; Main rules
:main [{:des "Description" :rules []}]
}Keycode Categories
Letters & Numbers
:a :b ... :z :1 :2 ... :0
Function Keys
:f1 ... :f24
Modifiers
:left_command :right_command :left_control :right_control :left_option :right_option :left_shift :right_shift :caps_lock :fn
Navigation
:up_arrow :down_arrow :left_arrow :right_arrow :page_up :page_down :home :end
Editing
:return_or_enter :escape :delete_or_backspace :delete_forward :tab :spacebar
Punctuation
:hyphen :equal_sign :open_bracket :close_bracket :backslash :non_us_backslash :semicolon :quote :grave_accent_and_tilde :comma :period :slash
Special
:vk_none (no output) :f13 ... :f24 (unused function keys for custom mappings)
Modifier Prefix Table
| Prefix | Meaning | Full Name |
|---|---|---|
! | Mandatory | Required to be held |
# | Optional | May or may not be held |
C | Left Command | left_command |
T | Left Control | left_control |
O | Left Option | left_option |
S | Left Shift | left_shift |
Q | Right Command | right_command |
W | Right Control | right_control |
E | Right Option | right_option |
R | Right Shift | right_shift |
F | Fn | fn |
P | Caps Lock | caps_lock |
!! | Hyper | Cmd+Ctrl+Opt+Shift |
## | Any | Any modifier(s) |
Examples
:!Ca ;; Cmd+A (mandatory)
:!CTSa ;; Cmd+Ctrl+Shift+A
:!!a ;; Hyper+A
:##escape ;; Escape with any modifiers
:#Ca ;; A with optional CmdRule Formats
Basic Mapping
[:from :to]
[:a :b] ;; a -> b
[:!Ca :!Cb] ;; Cmd+A -> Cmd+BWith Condition
[:from :to :condition]
[:a :b :chrome] ;; a -> b only in Chrome
[:a :b [:chrome :!safari]] ;; in Chrome, not SafariWith Options
[:from :to nil {:alone :key}]
[:from :to :cond {:alone :key :held :other}]Multiple Outputs
[:a [:b :c :d]] ;; a -> b, c, d sequence
[:a [{:key :b} {:key :c}]] ;; explicit key mapsSimultaneous Input
[[:j :k] :escape] ;; j+k together -> escape
[[:a :s :d] :f13] ;; a+s+d together -> f13Option Keys
{
:alone :key ;; to_if_alone
:held :key ;; to_if_held_down
:afterup :key ;; to_after_key_up
:delayed {:invoked :x ;; to_delayed_action
:canceled :y}
:params {:basic.to_if_alone_timeout_milliseconds 200
:basic.to_if_held_down_threshold_milliseconds 200}
}Variable Operations
Set Variable
[:w ["mode" 1]] ;; w sets mode=1
[:w [:w ["mode" 1]]] ;; w outputs w AND sets mode=1Use Variable as Condition
[:a :b ["mode" 1]] ;; a -> b when mode=1
[:a :b ["mode" 0]] ;; a -> b when mode=0Toggle Pattern
[:w ["mode" 1] nil {:afterup ["mode" 0] :alone :w}]
;; Hold w: mode=1, Release: mode=0, Tap: outputs wLayer Patterns
Simlayer (Simultaneous)
{:simlayers {:nav {:key :spacebar}}
:main [{:rules [:nav
[:h :left_arrow]
[:j :down_arrow]]}]}Simlayer with Modifier Requirement
{:simlayers {:nav {:key :spacebar
:modi {:mandatory [:left_control]}}}}Standard Layer (Tap/Hold)
{:layers {:caps {:key :caps_lock
:alone {:key :escape}}}}Layer with Afterup Reset
{:layers {:esc {:key :escape
:alone [{:key :escape}
{:set ["mode1" 0]}
{:set ["mode2" 0]}]}}}Template Syntax
Templates use Java format specifiers:
{:templates {:single "command %s" ;; One argument
:multi "cmd %s %s" ;; Two arguments
:typed "cmd %d items"}} ;; Integer
;; Usage
[:a [:single "arg1"]]
[:b [:multi "arg1" "arg2"]]Mouse/Pointing Device
Mouse Buttons
{:pkey :button1} ;; Left click
{:pkey :button2} ;; Right click
{:pkey :button3} ;; Middle click
{:pkey :button4} ;; Back
{:pkey :button5} ;; ForwardMouse Key Actions
{:mkey {:x 100}} ;; Move mouse X
{:mkey {:y 100}} ;; Move mouse Y
{:mkey {:vwheel -32}} ;; Scroll vertical
{:mkey {:hwheel 32}} ;; Scroll horizontal
{:mkey {:speed 0.5}} ;; Speed multiplierShell Command Patterns
Direct Command
[:!!1 "open -a Safari"]With Template
{:templates {:app "open -a '%s'"}}
[:!!1 [:app "Safari"]]AppleScript
[:!!1 "osascript -e 'tell app \"Finder\" to activate'"]Complex AppleScript
{:templates {:paste "osascript -e '
set the clipboard to \"%s\"
tell application \"System Events\"
keystroke \"v\" using command down
end tell'"}}Application Bundle IDs
Find bundle IDs:
osascript -e 'id of app "App Name"'
mdls -name kMDItemCFBundleIdentifier /Applications/App.appCommon IDs:
{:applications {
:chrome ["com.google.Chrome"]
:safari ["com.apple.Safari"]
:firefox ["org.mozilla.firefox"]
:vscode ["com.microsoft.VSCode"]
:cursor ["com.todesktop.230313mzl4w4u92"]
:terminal ["com.apple.Terminal"]
:iterm ["com.googlecode.iterm2"]
:wezterm ["com.github.wez.wezterm"]
:finder ["com.apple.finder"]
:slack ["com.tinyspeck.slackmacgap"]
}}Device Identification
Find device IDs: 1. Open Karabiner-Elements Preferences 2. Go to Devices tab 3. Note Vendor ID and Product ID
{:devices {:my-keyboard [{:vendor_id 1234
:product_id 5678}]}}Profile Configuration
{:profiles {
:Default {:default true
:sim 250 ;; Simultaneous window
:delay 500 ;; Delayed action delay
:alone 1000 ;; to_if_alone timeout
:held 500} ;; to_if_held_down threshold
:Gaming {:default false
:sim 100}}}