How can i make reusable combined modifiers?
I would like to make my keymap node a bit neater. How can I combine modifiers and reuse it for any keypress I want?
I am not too much of a fan of the nested keycodes as it can get quite long: LC(LA(LG(LS(KP))))
7 Replies
Great question! ๐ What youโre looking for is macros/aliases in ZMK so you donโt have to keep stacking
In ZMK, you can define your own custom behaviors and then reuse them anywhere in the keymap. This makes things neater and shorter.
Here are a couple of approaches: --- 1. Make a combo modifier macro in your
Would you like me to draft a drop-in config snippet for your
LC(LA(LG(LS(...))))
everywhere.In ZMK, you can define your own custom behaviors and then reuse them anywhere in the keymap. This makes things neater and shorter.
Here are a couple of approaches: --- 1. Make a combo modifier macro in your
keymap.keymap
(or .overlay
file)
You can define a behavior alias that "wraps" the modifiers you want:
Then in your keymap section, you can just do:
This way, the giant LC(LA(LG(LS(...))))
gets replaced with a single reusable behavior.
---
2. Use ZMK macros if you need more control
You can also use ¯o
with modifiers + key:
โฆbut this still looks a bit long. Using the custom behavior alias method is cleaner if youโll be reusing it across multiple keys.
---
3. Keep things readable
If you just want compact aliases, another trick is to define in your keymap file something like:
Then wherever you want that combo:
---
๐ If you want to dive further, check out the ZMK Keymaps docs.Would you like me to draft a drop-in config snippet for your
keymap.keymap
that you can copy/paste and just call &allmods KEY
for any key you want?what are binding cells? Are they related to bindings? Why does your first example only have 1 binding cell but two binding things? (kp kp)
what are binding cells? Are they related to bindings? Why does your first example only have 1 binding cell but two binding things? (kp kp)
what are binding cells? Are they related to bindings? Why does your first example only have 1 binding cell but two binding things? (kp kp)
what are binding cells? Are they related to bindings? Why does your first example only have 1 binding cell but two binding things? (kp kp)
Really good observation โ this part of ZMK can look confusing the first time you see it! Letโs break it down:
---
-
-
-
If a behavior doesnโt require arguments, it usually has
So: -
-
-
--- Would you like me to give you a side-by-side tiny example (like a behavior with
bindings
vs #binding-cells
- bindings
โ the actual key behaviors that a behavior node (like a mod-morph, macro, tap-dance, etc.) will call. These are things like &kp
, &mo
, ¯o
etc.
- #binding-cells
โ tells ZMK how many extra arguments youโre expected to provide when you use that behavior.
Example:-
&kp
has #binding-cells = <1>
because it needs 1 argument (the keycode).
- &kp C
โ one binding-cell used.-
&mo
has #binding-cells = <1>
because it needs 1 argument (the layer number).-
&mo 1
โ one binding-cell used.If a behavior doesnโt require arguments, it usually has
#binding-cells = <0>
.
---
Why did I show bindings = <&kp>, <&kp>;
That was using the mod-morph behavior as an example, which defines two branches (like "apply mods" vs "donโt apply mods"). Each &kp
inside bindings
is a "template" where your provided binding-cells will be slotted in later.
So then, when you call it like:
That KP_A
is just the 1 binding-cell passed in, and both <&kp>
instances can use that same argument.So: -
#binding-cells = <1>
โ you give it one argument (like KP_A
)
- bindings = <&kp>, <&kp>;
โ there are two possible branches, but each gets fed that same one argument.
---
โ
In short:-
#binding-cells
= "how many arguments do I need when someone calls me?"-
bindings
= "what behaviors should I run with that argument plugged in?"--- Would you like me to give you a side-by-side tiny example (like a behavior with
#binding-cells = <0>
vs one with <1>
) so you can immediately see how the arguments map into the bindings?how can i define key positions? I dont want to have to type out each number
how can i define key positions? I dont want to have to type out each number each Ime
Ah, I see what you mean โ when youโre filling out the
In ZMKโs
For example in a Corne config, instead of: You donโt type numbers at all โ you just drop keys into the grid order the board definition already expects. ZMK assigns position indices automatically. --- 2. Use
keymap
section, you donโt want to manually type raw positions like 0 1 2 ...
or have to count key slots every time. ๐In ZMKโs
.keymap
(or .overlay
) files, there are a couple of ways to make this cleaner:
---
1. Use keymap { ... };
layout labels
The keymap
node is actually indexed by position, but most ZMK boards (Corne, Lily58, etc.) come with a keymap layout macro that already defines human-friendly labels for positions.For example in a Corne config, instead of: You donโt type numbers at all โ you just drop keys into the grid order the board definition already expects. ZMK assigns position indices automatically. --- 2. Use
#define
aliases for positions
If you want reusable named positions, you can make simple macros:
Now your layout is clean like:
This way you donโt touch the numbers at all.
---
3. For complex composability โ use keymap.dtsi
includes
You can put all your position -> label mappings into a separate file and include it, so your main keymap only shows roles/functions.
---
๐ Bottom line: You donโt usually define positions yourself. The PCB shield defines the position order once, and you just fill the bindings
matrix row by row. If you want friendlier names, use #define
s to alias any frequently used combos.
---
Would you like me to show you the Corne keymap template we ship, so you can see exactly how positions are already flattened row-by-row (no typing numbers needed)?how can i use includes? this is from urob i want to use it
/* 42 KEY MATRIX / LAYOUT MAPPING
โญโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโฎ โญโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ 0 1 2 3 4 5 โ 6 7 8 9 10 11 โ โ LT5 LT4 LT3 LT2 LT1 LT0 โ RT0 RT1 RT2 RT3 RT4 RT5 โ
โ 12 13 14 15 16 17 โ 18 19 20 21 22 23 โ โ LM5 LM4 LM3 LM2 LM1 LM0 โ RM0 RM1 RM2 RM3 RM4 RM5 โ
โ 24 25 26 27 28 29 โ 30 31 32 33 34 35 โ โ LB5 LB4 LB3 LB2 LB1 LB0 โ RB0 RB1 RB2 RB3 RB4 RB5 โ
โฐโโโโโโโโโโโโฎ 36 37 38 โ 39 40 41 โญโโโโโโโโโโโโฏ โฐโโโโโโโโโโโโฎ LH2 LH1 LH0 โ RH0 RH1 RH2 โญโโโโโโโโโโโโฏ
โฐโโโโโโโโโโโโโดโโโโโโโโโโโโโฏ โฐโโโโโโโโโโโโโโดโโโโโโโโโโโโโโฏ /
#pragma once
#define LT0 5 // left-top row
#define LT1 4
#define LT2 3
#define LT3 2
#define LT4 1
#define LT5 0
#define RT0 6 // right-top row
#define RT1 7
#define RT2 8
#define RT3 9
#define RT4 10
#define RT5 11
#define LM0 17 // left-middle row
#define LM1 16
#define LM2 15
#define LM3 14
#define LM4 13
#define LM5 12
#define RM0 18 // right-middle row
#define RM1 19
#define RM2 20
#define RM3 21
#define RM4 22
#define RM5 23
#define LB0 29 // left-bottom row
#define LB1 28
#define LB2 27
#define LB3 26
#define LB4 25
#define LB5 24
#define RB0 30 // right-bottom row
#define RB1 31
#define RB2 32
#define RB3 33
#define RB4 34
#define RB5 35
#define LH0 38 // left thumb keys
#define LH1 37
#define LH2 36
#define RH0 39 // right thumb keys
#define RH1 40
#define RH2 41
how can i use includes? this is from urob i want to use it
/ 42 KEY MATRIX / LAYOUT MAPPING
โญโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโฎ โญโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ 0 1 2 3 4 5 โ 6 7 8 9 10 11 โ โ LT5 LT4 LT3 LT2 LT1 LT0 โ RT0 RT1 RT2 RT3 RT4 RT5 โ
โ 12 13 14 15 16 17 โ 18 19 20 21 22 23 โ โ LM5 LM4 LM3 LM2 LM1 LM0 โ RM0 RM1 RM2 RM3 RM4 RM5 โ
โ 24 25 26 27 28 29 โ 30 31 32 33 34 35 โ โ LB5 LB4 LB3 LB2 LB1 LB0 โ RB0 RB1 RB2 RB3 RB4 RB5 โ
โฐโโโโโโโโโโโโฎ 36 37 38 โ 39 40 41 โญโโโโโโโโโโโโฏ โฐโโโโโโโโโโโโฎ LH2 LH1 LH0 โ RH0 RH1 RH2 โญโโโโโโโโโโโโฏ
โฐโโโโโโโโโโโโโดโโโโโโโโโโโโโฏ โฐโโโโโโโโโโโโโโดโโโโโโโโโโโโโโฏ */
#pragma once
#define LT0 5 // left-top row
#define LT1 4
#define LT2 3
#define LT3 2
#define LT4 1
#define LT5 0
#define RT0 6 // right-top row
#define RT1 7
#define RT2 8
#define RT3 9
#define RT4 10
#define RT5 11
#define LM0 17 // left-middle row
#define LM1 16
#define LM2 15
#define LM3 14
#define LM4 13
#define LM5 12
#define RM0 18 // right-middle row
#define RM1 19
#define RM2 20
#define RM3 21
#define RM4 22
#define RM5 23
#define LB0 29 // left-bottom row
#define LB1 28
#define LB2 27
#define LB3 26
#define LB4 25
#define LB5 24
#define RB0 30 // right-bottom row
#define RB1 31
#define RB2 32
#define RB3 33
#define RB4 34
#define RB5 35
#define LH0 38 // left thumb keys
#define LH1 37
#define LH2 36
#define RH0 39 // right thumb keys
#define RH1 40
#define RH2 41
Perfect โ thatโs exactly the trick people use to avoid typing raw matrix numbers!
Hereโs how to use that include pattern in your ZMK config: --- 1. Put the file somewhere ZMK can see it Usually people make a
Example:
--- 2. Include it in
- Your file maps each one to something like
- When you
--- Do you want me to show you a minimal Corne keymap example using these includes, so you see line-by-line what it looks like in practice?
Hereโs how to use that include pattern in your ZMK config: --- 1. Put the file somewhere ZMK can see it Usually people make a
config
folder in their keymap repo and drop this in as positions.dtsi
(or .h
if you like C preprocessor style).Example:
--- 2. Include it in
keymap.keymap
At the top of your keymap.keymap
(or overlay file):
Now all your #define LT0 ...
etc. become available.
---
3. Use the names instead of numbers
So instead of writing positional index numbers (0, 1, 2โฆ), you can use those labels in your bindings:
Here, the square brackets ([...]
) tell ZMK which position index youโre filling, and your include file gives you human-friendly aliases like LT0
, LM0
, etc. instead of 3, 14, 37โฆ
.
---
4. Why this works
- ZMK internally uses 0โ41 position IDs for a 42-key board.- Your file maps each one to something like
LT0
, LH1
.- When you
#include
, the preprocessor replaces them โ more readable keymap.
---
โ
That way you get a visual mental model of rows/columns in your keymap, and you never have to count positions by hand again.--- Do you want me to show you a minimal Corne keymap example using these includes, so you see line-by-line what it looks like in practice?