diff --git a/c_src/keyio.c b/c_src/keyio.c
--- a/c_src/keyio.c
+++ b/c_src/keyio.c
@@ -12,7 +12,7 @@
   return ioctl(fd, EVIOCGRAB, grab);
 }
 
-// Acquire a filedescriptor as a uinput keyboard
+// Acquire a file descriptor as a uinput keyboard
 int acquire_uinput_keysink(int fd, char *name, int vendor, int product, int version) {
 
   // Designate fd as a keyboard of all keys
diff --git a/c_src/mac/dext.cpp b/c_src/mac/dext.cpp
--- a/c_src/mac/dext.cpp
+++ b/c_src/mac/dext.cpp
@@ -1,3 +1,5 @@
+#include <filesystem> // Include this before virtual_hid_device_service.hpp to avoid compile error
+
 #include "keyio_mac.hpp"
 
 #include "virtual_hid_device_driver.hpp"
@@ -16,13 +18,8 @@
 
 int init_sink() {
     pqrs::dispatcher::extra::initialize_shared_dispatcher();
-    std::filesystem::path client_socket_file_path("/tmp/karabiner_driverkit_virtual_hid_device_service_client.sock");
-    client = new pqrs::karabiner::driverkit::virtual_hid_device_service::client(client_socket_file_path);
+    client = new pqrs::karabiner::driverkit::virtual_hid_device_service::client();
     auto copy = client;
-    client->async_driver_loaded();
-    client->async_driver_version_matched();
-    client->async_virtual_hid_keyboard_ready();
-    client->async_virtual_hid_pointing_ready();
     /**/
     client->connected.connect([copy] {
                                   std::cout << "connected" << std::endl;
@@ -37,19 +34,20 @@
     client->error_occurred.connect([](auto&& error_code) {
                                        std::cout << "error_occurred " << error_code << std::endl;
                                    });
-    client->driver_loaded_response.connect([](auto&& driver_loaded) {
+    client->driver_connected.connect([](auto&& driver_connected) {
                                                static std::optional<bool> previous_value;
 
-                                               if (previous_value != driver_loaded) {
-                                                   std::cout << "driver_loaded " << driver_loaded << std::endl;
-                                                   previous_value = driver_loaded;
+                                               if (previous_value != driver_connected) {
+                                                   std::cout << "driver_connected " << driver_connected << std::endl;
+                                                   previous_value = driver_connected;
                                                }
                                            });
-    client->driver_version_matched_response.connect([](auto&& driver_version_matched) {
+    client->driver_version_mismatched.connect([](auto&& driver_version_mismatched) {
                                                         static std::optional<bool> previous_value;
-                                                        if (previous_value != driver_version_matched) {
-                                                            std::cout << "driver_version_matched " << driver_version_matched << std::endl;
-                                                            previous_value = driver_version_matched;
+
+                                                        if (previous_value != driver_version_mismatched) {
+                                                            std::cout << "driver_version_mismatched " << driver_version_mismatched << std::endl;
+                                                            previous_value = driver_version_mismatched;
                                                         }
                                                     });
     /**/
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -12,6 +12,39 @@
 
 ### Fixed
 
+## 0.4.3 – 2024-09-11
+
+### Added
+
+- Added `stepped`. It performs the next button from a circular sequence
+  whenever it is pressed.
+- Implemented named source blocks.
+  To name a source block add `:name <name>` at the beginning of the
+  `defsrc` block. To use it add `:source <name>` after the layer name to the
+  `deflayer` block. (#831)
+- Added `around-only`, which works like `around` but releases the outer button
+  when others are pressed. (#859)
+- Added `around-when-alone`, which also represses the outer button when only
+  the inner button is pressed and all others have been released. (#859)
+- Allow customization of implicit `around`s (#859)
+
+### Changed
+
+- Update Karabiner-DriverKit to 3.1.0 (#780)
+- Added tests to check that every button has documentation (#857)
+- `defsrc` with duplicate keycodes are now forbidden (#860)
+
+### Fixed
+
+- Fixed crash on non-US backslash under MacOS (#766)
+- Fixed broken keyboard due to circular event handling under MacOS (#781)
+- Fixed crash on unhandled buttons by ignoring them (#807)
+- Fixed parse errors relating to whitespace (#796, #875)
+- Fixed broken compose sequences (#823, #869)
+- Fixed parse errors when using keys only available on Darwin OS (#828)
+- Fixed `around-next` wasn't parsable (#857)
+- Fixed most buttons which behave weird in nested tap situations (#873)
+
 ## 0.4.2 – 2023-10-07
 
 ### Added
diff --git a/doc/quick-reference.md b/doc/quick-reference.md
new file mode 100644
--- /dev/null
+++ b/doc/quick-reference.md
@@ -0,0 +1,463 @@
+<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
+**Table of Contents**
+
+- [Introduction](#introduction)
+- [Defcfg](#defcfg)
+    - [Input and Output](#input-and-output)
+    - [Other Configuration Options](#other-configuration-options)
+    - [Full Example](#full-example)
+- [Buttons](#buttons)
+    - [Modded Buttons](#modded-buttons)
+    - [General Purpose Buttons](#general-purpose-buttons)
+    - [Tap buttons](#tap-buttons)
+- [Layers](#layers)
+    - [Operating on Layers](#operating-on-layers)
+
+<!-- markdown-toc end -->
+
+# Introduction
+Welcome to the KMonad quick reference guide. This document aims to provide a
+simple definition and a usable example for all possible configuration options in
+the KMonad program to act as a quick reference that you look into when forgetting
+a function or/and wanting to know something quickly. Note that this document
+does not try to be a full guide or a detailed documentation, this is the aim of
+[tutorial.kbd](../keymap/tutorial.kbd).
+
+# Defcfg
+
+The `defcfg` block is where you can define all of your configuration
+rules like setting up in- and output devices or certain global
+configuration options.
+
+A block can take the following arguments:
+
+## Input and Output
+
++ `input`: define the input keyboard which the program will capture.
+
++ `output`: define the output keyboard which KMonad will create, with
+  additional options to execute upon starting KMonad.
+
+Here is how you would define the basic input and output settings on all
+supported systems:
+
+  - GNU/Linux:
+
+    ```clojure
+    input  (device-file "/dev/input/by-id/usb-04d9_daskeyboard-event-kbd")
+    output (uinput-sink "My KMonad output")
+    ```
+
+  - Windows:
+
+    ```clojure
+    input  (low-level-hook)
+    output (send-event-sink)
+    ```
+
+  - MacOS:
+
+    ```clojure
+    input  (iokit-name "my-keyboard-product-string")
+    output (kext)
+    ```
+
+## Other Configuration Options
+
+The following are all global config options that one can set in the
+`defcfg` block.
+
++ `fallthrough` (boolean, defaults to `false`): re-emit keys that are
+  not defined in the corresponding `defsrc` block.
+
+  This allows one to only specify certain parts of a layout, with all
+  other keys having their "default" meaning.
+
++ `allow-cmd` (boolean, defaults to `false`): allow arbitrary shell
+  commands to be run as keys.
+
+  NOTE: This can be dangerous since someone with access to your keyboard
+  file could bind `rm -rf ~/` to a key. Think about whether you really
+  want this behaviour.
+
++ `cmp-seq` (key, defaults to `RightAlt`): compose key for Unicode input
+  (X11 specific).
+
++ `cmp-seq-delay` (natural number): delay between each pressed key in a
+  compose-key sequence.
+
++ `implicit-around` (around variant, defaults to `around`):
+  Specifies the variant of `around` to use in implicit around constructs
+  like `A` or `S-a`.
+  You can also specify `disabled`, which causes implicit arounds to
+  result in errors.
+
+## Full Example
+
+Below is an example of a full `defcfg` block for a GNU/Linux system.
+
+```clojure
+(defcfg
+  input  (device-file "/dev/input/by-id/usb-04d9_daskeyboard-event-kbd")
+  output (uinput-sink
+          "My KMonad output"                           ;; name of the created keyboard
+          "sleep 1 && setxkbmap -option compose:ralt") ;; additional, environment-specific, information
+  cmp-seq ralt
+  cmp-seq-delay 5
+  fallthrough true
+  allow-cmd true
+)
+```
+
+# Buttons
+
+Defining fancy buttons is why we're here, right? There are a variety of
+these things here, as well as some helpers to make entering them into
+layers (more below) much easier.
+
+## Press- or release-only buttons
+
+- `(press-only x)` : Send the *press* of x when this button is tapped
+- `(release-only x)` : Send the *release* of x when this button is tapped
+
+It is possible to define buttons that only press or release a virtual output
+button. They are useful in tap-macros, especially ones that are going to be
+executed in a 'known context'. Assume you want to use your physical alt button
+to *both* open a layer, but also to function as a basic `alt` key. This can be
+achieved by `(around met (layer-toggle my-layer))`. However, if you have a macro
+inside `my-layer` that taps alt, then this would release alt until the layer is
+reactivated (by physically releasing and repressing the `alt` key). The macro,
+however, can instead be affixed by `(press-only met)`, making the last step of
+the macro the reactivation of the `alt` key, solving the problem.
+
+## Modded Buttons
+
+To make key-entry easier, KMonad already provides some syntax for
+Emacs-like specification of key chords. They are defined like this:
+
+  - `C-` : `(around-implicit lctl X)`
+  - `A-` : `(around-implicit lalt X)`
+  - `M-` : `(around-implicit lmet X)`
+  - `S-` : `(around-implicit lsft X)`
+
+Then `RC-`, `RA-`, `RM-`, and `RS-` behave exactly the same, except
+using the right-modifier.
+
+The definition of a key chord then looks like this:
+
+```clojure
+(defalias Ca C-a) ;; this is equivalent to (defalias Ca (around-implicit Ctl a))
+```
+
+## General Purpose Buttons
+
++ `defalias`: define a name for a button. This can then be referenced in
+  a layer using the `@name` syntax (see [layers](#layers) below).
+
+  ```clojure
+  (defalias
+    num (layer-toggle numbers) ;; Bind num to a button that switches to a layer
+    kil C-A-del                ;; Bind kil to a button that Ctrl+Alt+deletes
+  )
+  ```
+
++ `around`: combine two keys into one.
+
+  ```clojure
+  (defalias ad (around alt del)) ;; this is like pressing Alt+Del
+  ```
+
++ `around-only`: `around` but release the outer button as soon as others are pressed.
+
+    ```clojure
+    (defalias
+      A (around lsft a)
+      A' (around-only lsft a)
+    )
+    ```
+
+    `@A'` is simply an uppercase letter but with the following difference to the `@A`:
+
+    P@A Pb R@A Rb -> AB
+    P@A' Pb R@A' Rb -> Ab
+
++ `around-when-alone`: similar to `around-only` but when all other buttons have been released
+    the outer button is repressed.
+
+    ```clojure
+    (defalias
+        A'' (around-when-alone lsft a)
+    )
+    ```
+
+    P@A'' Tb Tc R@A'' -> Plsft Pa Rlsft Tb Tlsft Tc Plsft Ra Rlsft
+
++ `around-implicit`: the around variant used implicit
+
+    ```closure
+    (defalias
+        =A (around-implicit lsft a)
+    )
+    ```
+
+    `@=A` is the desugared form of `A`
+
++ `around-next`: perform the next button-press inside some context (like
+  `layer-next` but more generalized)
+
+  ```clojure
+  (defalias ns  (around-next sft))  ;; Shift the next press
+  ```
+
++ `around-next-single`: perform only the next event (keypress or release)
+  inside some context.
+
++ `before-after-next`: tap a button prior to key press and tap another
+  after key release.
+  ```clojure
+  (before-after-next tab S-tab a)
+  ```
+
++ `around-next-timeout`: like `around-next` except that if other button press is not detected within
+  some timeout, some other button is tapped.
+
+  ```clojure
+  (around-next-timeout 500 sft XX)
+  ```
+
++ `sticky-key`: act like the key is held temporarily after just one
+  press for the given amount of time (in ms).
+
+  ```clojure
+  (defalias slc (sticky-key 500 lctl))
+  ```
+
++ `stepped`: perform the next button in the circular sequence
+  whenever it is pressed.
+  ```clojure
+  (stepped (press-only lctl) (release-only lctl))
+  ```
+
+  This button:
+  - presses control the first time it is tapped
+  - releases control the second time it is tapped
+
+
++ `pause`: pause for the given number of ms.
+
+  ```clojure
+  (defalias
+    ta3 (tap-macro K P5 M P4 o P3 n P6 a (pause 5) d) ;; P5 = (pause 5)
+  )
+  ```
+
++ `cmd-button`: take two arguments, the second one of which is
+  optional. These represent the commands to be executed on pressing and
+  releasing the button respectively
+
+  ```clojure
+  (defalias
+    dat (cmd-button "date >> /tmp/kmonad_example.txt")   ;; Append date to tmpfile
+    pth (cmd-button "echo $PATH > /tmp/kmonad_path.txt") ;; Write out PATH
+    ;; `dat' on press and `pth' on release
+    bth (cmd-button "date >> /tmp/kmonad_example.txt"
+                    "echo $PATH > /tmp/kmonad_path.txt")
+  )
+  ```
+
+## Tap buttons
+
+Tap buttons are an integral part of KMonad. Everyone has different
+preferences—that's why there are so many! Particularly when using
+home-row modifiers, you will find some of the more crazy seeming buttons
+to be the most comfortable.
+
++ `tap-macro`: take a sequence of keys and tap them, but don’t release
+  the last key until the button is released.
+
+  ```clojure
+  (defalias ta1 (tap-macro K M o n a d))
+  ```
+
+  A `tap-macro` can take an optional `:delay` keyword (in ms)—this will
+  wait for that amount of time after each keypress:
+
+  ```clojure
+  (defalias
+    ta1 (tap-macro K M o n a d :delay 5)
+    ;; equivalent to: (tap-macro K P5 M P5 o P5 n P5 a P5 d)
+  )
+  ```
+
++ `tap-macro-release`: like `tap-macro` but don’t press the last key
+  until the button is released.
+
+  ```clojure
+  (defalias ta2 (tap-macro-release K M o n a d))
+  ```
+
++ `multi-tap`: combine a sequence of keys into one key with a timeout
+  between each key, as well as a last (default) button without a delay.
+
+  ```clojure
+  (defalias mt  (multi-tap 300 a 300 b 300 c 300 d e))
+  ```
+
++ `tap-next`: combine 2 buttons, one for when the button is tapped and
+  one for when it is held. The decision of what to execute is based upon
+  whether the next button is the buttons own release or not.
+
+  ```clojure
+  (defalias tan (tap-next a sft))
+  ```
+
++ `tap-hold`: like `tap-next` but with a timeout for tapping. If the key
+  is released before the timeout it taps, if not then it holds.
+
+  ```clojure
+  (defalias tah (tap-hold 200 a sft))
+  ```
+
++ `tap-hold-next`: a combination of `tap-next` and `tap-hold`: like
+  `tap-next`, but _also_ switch to hold after a period of time.
+
+  ```clojure
+  (defalias thn (tap-hold-next 1000 a sft))
+  ```
+
++ `tap-next-release`: like `tap-next` but decide whether to tap or hold
+  based on the next release of a key (that was not already pressed when
+  this button was pressed).
+
+  ```clojure
+  (defalias tnr (tap-next-release a sft))
+  ```
+
++ `tap-next-press`: like `tap-next` but decide whether to tap or hold
+  based on whether another key is pressed before this one is released.
+
+  ```clojure
+  (defalias tnp (tap-next-press a sft))
+  ```
+
++ `tap-hold-next-release`: like `tap-next-release` but with an
+  additional timeout. This is just like `tap-next-release`, but with
+  `tap-next` swapped out for `tap-next-release`.
+
+  ```clojure
+  (defalias thr (tap-hold-next-release 1000 a sft))
+  ```
+
++ `tap-hold-next` and `tap-hold-next-release` can take an optional
+  `:timeout-button` keyword to specify a button other than the
+  hold button which should be held when the timeout expires.
+
+  ```clojure
+  (defalias
+    thn (tap-hold-next 200 a lsft :timeout-button x)
+    thr (tap-hold-next-release 200 a lsft :timeout-button x)
+  )
+  ```
+
+# Layers
+
+KMonad allows you to define and operate on several _layers_; these are
+just collections of keys.
+
++ `defsrc`: define the input keys of your physical keyboard. This should
+  reflect the keys on your keyboard as closely as possible. Although a
+  `defsrc` is very similar to a layer visually, it is not a one and will
+  thus not be used as one! It only serves to define where the different
+  keys are and what kind of layout kmonad is initially dealing with.
+  It also supports giving a name via `:name <my-source-name>` as the
+  first argument.
+
+  For example, an ANSI 60% keyboard may be represented as:
+
+  ```
+  (defsrc
+    grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
+    tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
+    caps a    s    d    f    g    h    j    k    l    ;    '    ret
+    lsft z    x    c    v    b    n    m    ,    .    /    rsft
+    lctl lmet lalt           spc            ralt rmet cmp  rctl
+  )
+  ```
+
+  See the [keyboard templates](../keymap/template) for further
+  inspiration.
+
++ `deflayer`: defines a layer to be used later.
+
+   For example, defining a qwerty layer, as well as one for special
+   symbols and numbers:
+
+   To use a named source block add `:source <my-source-name>` after
+   the layer name.
+
+   you can also overwrite the `implicit-around` setting by adding
+   `:implicit-around <setting>`.
+
+  ```
+  (deflayer qwerty
+    grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
+    tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
+    caps a    s    d    f    g    h    j    k    l    ;    '    ret
+    lsft z    x    c    v    b    n    m    ,    .    /    rsft
+    lctl @num lalt           spc            ralt rmet @sym @kil
+  )
+
+  (deflayer numbers
+    _    _    _    _    _    _    _    _    _    _    _    _    _    _
+    _    _    _    _    _    XX   /    7    8    9    -    _    _    _
+    _    _    _    _    _    XX   *    4    5    6    +    _    _
+    _    _    \(   \)   .    XX   0    1    2    3    _    _
+    _    _    _              _              _    _    _    _
+  )
+  ```
+
+## Operating on Layers
+
+There are many buttons that can operate on layers.
+
++ `layer-toggle`: toggles to a specific layer as long as the key is held.
+
+  ```clojure
+  (defalias ket (layer-toggle toggled-layer))
+  ```
+
++ `layer-delay`: change to the layer temporarily for the given amount of
+  time (in ms).
+
+  ```clojure
+  (defalias ked (layer-delay 500 delayed-layer))
+  ```
+
++ `layer-next`: the next key press is handled by another layer
+  (leader-key style).
+
+  ```clojure
+  (defalias ken (layer-next next-layer))
+  ```
+
++ `layer-switch`: change the base layer; i.e., deregister the
+  bottom-most layer and swap it out with another one.
+
+  ```clojure
+  (defalias kes (layer-switch switch-layer))
+  ```
+
++ `layer-add` and `layer-rem`: overlay a layer on top of the current
+  layer resp. remove the overlayed layer from the current layer.
+
+  NOTE: The above two commands can be particularly dangerous if used
+  alone and should really only be used together. Be careful that you
+  don't make a configuration in which you can't switch back to your base
+  layer!
+
+  ```clojure
+  (defalias
+    add (layer-add multi-overlay)
+    rem (layer-rem multi-overlay)
+  )
+  ```
diff --git a/keymap/tutorial.kbd b/keymap/tutorial.kbd
new file mode 100644
--- /dev/null
+++ b/keymap/tutorial.kbd
@@ -0,0 +1,1119 @@
+#| --------------------------------------------------------------------------
+
+                              KMonad: Guided tour
+
+  Welcome to the KMonad configuration tutorial. This document aims to explain:
+  1. The configuration syntax
+  2. The required information
+  3. All possible configuration options
+
+  This document should be a runnable configuration, so with some tweaks to the
+  `defcfg` block (see below) you should be able to try out all the examples
+  interactively.
+
+  -------------------------------------------------------------------------- |#
+
+
+#| --------------------------------------------------------------------------
+
+                     Basic syntax: comments and parentheses
+
+  KMonad's configuration language is styled on various lisps, like scheme or
+  Common Lisp. In a lisp, every statement is entered between '(' and ')'s. If
+  you are more used to Fortran style languages (python, ruby, C, Java, etc.), the
+  change is quite straightforward: the function name moves into the parentheses,
+  and you don't use commas to separate arguments. I.e.
+
+  This:     my_function(a, 3, "Alakazam")
+  Becomes:  (my_function a 3 "Alakazam")
+
+  The reason for this is because Lisp-style languages are very easy to parse and
+  write syntax-highlighters for.
+
+  We also provide standard Lisp syntax for comments:
+  - block comments between: #| and its reverse
+  - line comments following: ;;
+
+  Unlike standard lisp, a single ; does not denote a comment, but instead the
+  keycode for semicolon.
+
+  Also, as you might have noticed, whitespace is possible anywhere.
+
+  To check for syntax errors while editing, invoke KMonad with the -d option.
+
+  -------------------------------------------------------------------------- |#
+
+
+#| --------------------------------------------------------------------------
+                         Necessary: the `defcfg` block
+
+  There are a few bits of information that are required to be present in a
+  KMonad configuration file. One of these is the existence of exactly 1 `defcfg`
+  statement. This statement is used to customize various configuration settings.
+  Many of these settings have default values, but a minimal definition must
+  include at least an 'input' field and an 'output' field. These describe how
+  KMonad captures its inputs and how it emits its outputs.
+
+  First, let's go over the optional, non-OS specific settings. Currently there
+  are only 2:
+
+  - fallthrough: `true` or `false`, defaults to `false`
+
+    KMonad catches input events and tries to match them to various handlers. If
+    it cannot match an event to any handler (for example, if it isn't included
+    in the corresponding `defsrc` block, or if it is, but the current keymap
+    does not map any buttons to it), then the event gets quietly ignored. If
+    `fallthrough` is set to `true`, any unhandled events simply get reemitted.
+
+    In more practical terms, this allows you to only specify the keys that
+    you want to overwrite in your `defsrc' block. For example, the following
+    configuration would rebind Caps Lock to Escape only when tapped.
+
+      (defcfg
+        input … output …
+        fallthrough true)
+      (defsrc caps)
+      (deflayer my-layer (tap-next esc caps))
+
+  - allow-cmd: `true` or `false`, defaults to `false`
+
+    If this is set to `false`, any action that runs a shell-command will simply
+    log to `stdout` without ever running (log-level info). Don't ever enable
+    this on a configuration that you do not trust, because:
+
+      (cmd-button "rm -rf ~/*")
+
+    is a thing. For more information on the `cmd-button' function, see the
+    section on Command buttons below.
+
+  - implicit-around: `around` (default), `around-only`, `around-when-alone` or `disabled`
+
+    An implicit around may be in `A` or `_` but also in modded buttons (`S-a`).
+    This changes how implicit arounds are handled, for more details see `around`
+    below.
+    `disabled` results in an error when implicit around is used.
+
+  There are also some optional OS specific settings that we support:
+
+  - `cmp-seq': KEY, defaults to `RightAlt' (Linux X11 specific)
+
+    This sets your compose key for Unicode input. For more information, as well
+    as a workaround to also make this work on windows, see the section on
+    Compose-key sequences below.
+
+  - `cmp-seq-delay': NUMBER (in milliseconds)
+
+    This sets a delay between each pressed key in a compose-key sequence.  Some
+    environments may have troubles recognizing the key sequence if it's pressed
+    too rapidly; if you experience any problems in this direction, you can try
+    setting this value to `5' or `10' and see if that helps.
+
+  Secondly, let's go over how to specify the `input` and `output` fields of a
+  `defcfg` block. This differs between OSes, and so do the capabilities of
+  these interfaces.
+
+
+  -- Linux ------
+
+  In Linux we deal with input by performing an ioctl-grab on a specific
+  device-file. This allows us to hook KMonad on the input of exactly 1 keyboard,
+  and allows you to run multiple instances of KMonad for different keyboards. We
+  make an input using:
+    (device-file "/dev/input/by-id/my-keyboard-kbd")
+
+  NOTE: Any valid path to a device-file will work, but it is recommended to use
+  the 'by-id' directory, since these names will not change if you replug the
+  device.
+
+  We deal with output by creating a 'uinput' device. This requires that the
+  'uinput' kernel module is loaded. The easiest way to ensure this is by calling
+  'sudo modprobe uinput'. We create a uinput device using:
+    (uinput-sink "name" "optional post-init command")
+
+
+  -- Windows ----
+
+  In Windows we do not get such fine-grained control. We use a low-level
+  keyboard hook to intercept all non-injected keyboard events. There is
+  currently an open issue to improve the C-bindings used to capture windows
+  keyevents, and if you have a better way to approach this issue, help is deeply
+  appreciated. You specify a windows input using:
+    (low-level-hook)
+
+  Similarly, the output in Windows lacks the fine-grained control. We use the
+  SendEvent API to emit key events directly to Windows. Since these are
+  'artificial' events we won't end up catching them again by the
+  `low-level-hook`. It is very likely that KMonad does not play well with other
+  programs that capture keyboard input like AHK. You specify windows output using:
+    (send-event-sink)
+
+  Specific to Windows, KMonad also handles key auto-repeat.  Therefore your
+  Windows system settings for key repeat delay and key repeat rate will have no
+  effect when KMonad is running.  To set the repeat delay and rate from KMonad,
+  pass the optional arguments pair to `send-event-sink`:
+    (send-event-sink [ <delay> <rate> ])
+  where:
+    <delay> : how many ms before a key starts repeating
+    <rate>  : how many ms between each repeat event
+  A value of 500 ms delay and 30 ms rate should mimic the default Windows
+  settings pretty well:
+    (send-event-sink 500 30)
+
+
+  -- Mac OS -----
+
+  For Mac questions I suggest filing an issue and tagging @thoelze1, he wrote
+  the MacOS API. However, input using:
+    (iokit-name "optional product string")
+
+  By default this should grab all keyboards, however if a product string is
+  provided, KMonad will only capture those devices that match the provided
+  product string. If you would like to provide a product string, you can run
+  `make; ./list-keyboards' in c_src/mac to list the product strings of all
+  connected keyboards.
+
+  You initialize output on MacOS using:
+    (kext)
+
+  -------------------------------------------------------------------------- |#
+
+(defcfg
+  ;; For Linux
+  input  (device-file "/dev/input/by-id/usb-04d9_daskeyboard-event-kbd")
+  output (uinput-sink "My KMonad output"
+    ;; To understand the importance of the following line, see the section on
+    ;; Compose-key sequences at the near-bottom of this file.
+    "/run/current-system/sw/bin/sleep 1 && /run/current-system/sw/bin/setxkbmap -option compose:ralt")
+  cmp-seq ralt    ;; Set the compose key to `RightAlt'
+  cmp-seq-delay 5 ;; 5ms delay between each compose-key sequence press
+
+  ;; For Windows
+  ;; input  (low-level-hook)
+  ;; output (send-event-sink)
+
+  ;; For MacOS
+  ;; input  (iokit-name "my-keyboard-product-string")
+  ;; output (kext)
+
+  ;; Comment this if you want unhandled events not to be emitted
+  fallthrough true
+
+  ;; Set this to false to disable any command-execution in KMonad
+  allow-cmd true
+
+  ;; Set the implicit around to `around`
+  implicit-around around
+)
+
+
+#| --------------------------------------------------------------------------
+                      Necessary: at least 1 `defsrc` block
+
+  It is difficult to explain the `defsrc` block without immediately going into
+  `deflayer` blocks as well. Essentially, KMonad maps input-events to various
+  internal actions, many of which generate output events. The `defsrc` block
+  explains the layout on which we specify our `deflayer`s down the line.
+
+  It is important to realize that the `defsrc` block doesn't *necessarily* have
+  to coincide with your actual input keyboard. You can specify a full 100%
+  `defsrc` block, but only use a 40% keyboard. This will mean that every
+  `deflayer` you specify will also have to match your 100% `defsrc`, and that
+  your actual keyboard would be physically unable to trigger about 60% of your
+  keymap, but it would be perfectly valid syntax.
+
+  The dual of this (and more useful) is that it is also perfectly valid to only
+  specify that part of your keyboard in `defsrc` that you want to remap. If you
+  use a 100% keyboard, but don't want to remap the numpad at all you can simply
+  leave the numpad out of your `defsrc`, and it should work just fine. In that
+  particular case you probably want to set `fallthrough` to `true` in your
+  `defcfg` block though.
+
+  There is also support for named `defsrc` blocks. They contain `:name <my-source>`
+  as the first argument in their definition.
+
+  The layouting in the `defsrc` block is completely free, whitespace simply gets
+  ignored. We strive to provide a name for every keycode that is no longer than
+  4 characters, so we find that laying out your keymap in columns of 5 works out
+  quite nicely (although wider columns will allow for more informative aliases,
+  see below).
+
+  Most keycodes should be obvious. If you are unsure, check
+  './src/KMonad/Keyboard/Keycode.hs'. Every Keycode has a name corresponding to
+  its Keycode name, but all lower-case and with the 'Key' prefix removed. There
+  are also various aliases for Keycodes starting around line 350. If you are
+  trying to bind a key and there is not a 4-letter alias, please file an issue,
+  or better yet, a pull-request, and it will be added promptly.
+
+  Also, you can consult './keymap/template/' for various input templates to use
+  directly or to look up keycodes by position. Here we use the input-template
+  for 'us_ansi_60.kbd'
+
+  -------------------------------------------------------------------------- |#
+
+(defsrc
+  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
+  tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
+  caps a    s    d    f    g    h    j    k    l    ;    '    ret
+  lsft z    x    c    v    b    n    m    ,    .    /    rsft
+  lctl lmet lalt           spc            ralt rmet cmp  rctl
+)
+
+(defsrc :name numpad
+  nlck kp/ kp* kp-
+  kp7  kp8 kp9 kp+
+  kp4  kp5 kp6
+  kp1  kp2 kp3 kprt
+  kp0    kp.
+)
+
+
+#| --------------------------------------------------------------------------
+                        Optional : `defalias` statements
+
+  KMonad will let you specify some very specific, crazy buttons. These
+  definitions can get pretty long, though, and would make `deflayer` blocks
+  nearly impossible to read. Therefore we provide the ability to alias names to
+  these buttons, to keep the actual `deflayer` statements orderly.
+
+  A `defalias` can contain any number of aliases, and it can refer backwards or
+  forwards to layers without issue. The only sequencing that needs to be kept in
+  mind is that a `defalias` cannot refer forward to another `defalias` that is
+  not yet defined.
+
+  Here we define a few aliases, but we will define more later. Notice that we
+  try to only use 3 letter names for aliases. If that is not enough to be clear,
+  consider widening all columns to 6 or 7 characters (or be content with a messy
+  config).
+
+  -------------------------------------------------------------------------- |#
+
+(defalias
+  num  (layer-toggle numbers) ;; Bind num to a button that switches to a layer
+  kil  C-A-del                ;; Bind kil to a button that Ctrl-Alt-deletes
+)
+
+
+#| NOTE: The above code could just as easily have been written as:
+(defalias num (layer-toggle numbers))
+(defalias kil C-A-del)
+|#
+
+
+#| --------------------------------------------------------------------------
+                     Necessary: at least 1 `deflayer` block
+
+  As explained in the `defsrc` section, a `deflayer` will define a button for
+  each corresponding entry in the `defsrc` definition. A `deflayer` statement
+  consists of the `deflayer` keyword, followed by the name used to identify this
+  layer, followed by N 'statements-that-evaluate-to-a-button', where N is
+  exactly how many entries are defined in the `defsrc` statement.
+
+  Optionally you can add a `:source <my-source>` parameter after the name to map
+  a layer using a named `defsrc` block.
+
+  You can also overwrite the option `implicit-around` for a layer by adding
+  `:implicit-around <setting>`.
+
+  It is also important to mention that the 'keymap' in KMonad is modelled as a
+  stack of layers (just like in QMK). When an event is registered we look in the
+  top-most layer for a handler. If we don't find one we try the next layer, and
+  then the next.
+
+  Exactly what 'evaluates-to-a-button' will be expanded on in more detail below.
+  There are very many different specialist buttons in KMonad that we will touch
+  upon. However, for now, these 4 are a good place to begin:
+
+  1. Any keycode evaluates to a button that, on press, emits the press of that
+     keycode, and on release, emits the release of that keycode. Just a 'normal'
+     button. The exception is '\', which gets used as an escape character. Use
+     '\\' instead. Other characters that need to be escaped to match the literal
+     character are '(', ')', and '_'.
+
+  2. An @-prefixed name evaluates to an alias lookup. We named two buttons in
+     the `defalias` block above, we could now refer to these buttons using
+     `@num` and `@kil`. This is also why we only use alias-names no longer than
+     3 characters in this tutorial. Also, note that we are already referencing
+     some aliases that have not yet been defined, this is not an issue.
+
+  3. The '_' character evaluates to transparent. I.e. no handler for that
+     key-event in this layer, causing this event to be handed down the layer
+     stack to perhaps be handled by the next layer.
+
+  4. The 'XX' character evaluates to blocked. I.e. no action bound to that
+     key-event in this layer, but do actually catch event, preventing any
+     underlying layer from handling it.
+
+  Finally, it is important to note that the *first* `deflayer` statement in a
+  KMonad config will be the layer that is active when KMonad starts up.
+
+  -------------------------------------------------------------------------- |#
+
+
+(deflayer qwerty
+  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
+  tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
+  caps a    s    d    f    g    h    j    k    l    ;    '    ret
+  lsft z    x    c    v    b    n    m    ,    .    /    rsft
+  lctl @num lalt           spc            ralt rmet @sym @tst
+)
+
+(deflayer phone :source numpad
+  :implicit-around disabled
+  nlck kp/ kp* kp-
+  kp1  kp2 kp3 kp+
+  kp4  kp5 kp6
+  kp7  kp8 kp9 kprt
+  kp0    kp.
+)
+
+
+#| --------------------------------------------------------------------------
+                     Optional: as many layers as you please
+
+  We had already defined `num` as referring to a `(layer-toggle numbers)`. We
+  will get into layer-manipulation soon, but first, let's just create a second
+  layer that overlays a numpad under our right-hand.
+
+  To easily specify layers it is highly recommended to create an empty
+  `deflayer` statement as a comment at the top of your config, so you can simply
+  copy-paste this template. There are also various empty layer templates
+  available in the './keymap/template' directory.
+
+  -------------------------------------------------------------------------- |#
+
+(deflayer numbers
+  _    _    _    _    _    _    _    _    _    _    _    _    _    _
+  _    _    _    _    _    XX   /    7    8    9    -    _    _    _
+  _    _    _    _    _    XX   *    4    5    6    +    _    _
+  _    _    \(   \)   .    XX   0    1    2    3    _    _
+  _    _    _              _              _    _    _    _
+)
+
+
+#| --------------------------------------------------------------------------
+                  Optional: press-only / release-only buttons
+
+  - `(press-only x)` : Send the *press* of x when this button is tapped
+  - `(release-only x)` : Send the *release* of x when this button is tapped
+
+  It is possible to define buttons that only press or release a virtual output
+  button. They are useful in tap-macros, especially ones that are going to be
+  executed in a 'known context'. Assume you want to use your physical alt button
+  to *both* open a layer, but also to function as a basic `alt` key. This can be
+  achieved by `(around met (layer-toggle my-layer))`. However, if you have a macro
+  inside `my-layer` that taps alt, then this would release alt until the layer is
+  reactivated (by physically releasing and repressing the `alt` key). The macro,
+  however, can instead be affixed by `(press-only met)`, making the last step of
+  the macro the reactivation of the `alt` key, solving the problem.
+
+  -------------------------------------------------------------------------- |#
+
+
+#| --------------------------------------------------------------------------
+                          Optional: modded buttons
+
+  Let's start by exploring the various special buttons that are supported by
+  KMonad by looking at 'modded' buttons, that is to say, buttons that activate
+  some kind of 'mod', then perform some button, and finally release that 'mod'
+  again.
+
+  We have already seen an example of this style of button, our `kil` button is
+  one such button. Let's look at it in more detail:
+    C-A-del
+
+  This looks like a simple declarative statement, but it's helpful to realize
+  that is simply syntactic sugar around 2 function calls. This statement is
+  equivalent to:
+    (around-implicit ctl (around alt del))
+
+  This highlights a core design principle in KMonad: we try to provide very
+  simple buttons, and then we provide rules and functions for combining them
+  into new buttons. Although note: still very much a work in progress.
+
+  So, looking at this statement:
+    (around foo bar)
+
+  Here, `around` is a function that takes two buttons and creates a new button.
+  This new button will, on a press, first press foo, then press bar, and on a
+  release first release bar, and then foo. Once created, this new button can be
+  passed to anything in KMonad that expects a button.
+
+  There also exists some variants like `around-only` or `around-when-alone`.
+  `around-only` also releases the outer button just before another button is
+  pressed. If no other button has been pressed prior to it's release it releases
+  the inner than the outer button similarly to `around`.
+
+  `around-when-alone` is another variant which works like `around-only` but
+  represses the outer button when all other which have been pressed after
+  itself have been released.
+
+  `around-implicit` is one of the above variants and is specified `defcfg` block.
+
+  We have already seen other examples of modded buttons, \(, \), *, and +. There
+  are no keycodes for these buttons in KMonad, but they are buttons. They simply
+  evaluate to `(around-implicit lsft x)`. All shifted numbers have their
+  corresponding characters, the same is true for all capitals,
+  and < > : ~ " | { } \_ + and ?.
+
+  To wrap up 'modded-buttons', let's look back at C-A-del. We have 8 variants:
+    C- : (around-implicit lctl X)
+    A- : (around-implicit lalt X)
+    M- : (around-implicit lmet X)
+    S- : (around-implicit lsft X)
+
+  Then RC-, RA-, RM-, and RS- behave exactly the same, except using the
+  right-modifier.
+
+  These can be combined however you please:
+    C-A-M-S-x          ;; Perfectly valid
+    C-%                ;; Perfectly valid: same as C-S-5
+    C-RC-RA-A-M-S-RS-m ;; Sure, but why would you?
+
+  Also, note that although we provide special syntax for certain modifiers,
+  these buttons are in no way 'special' in KMonad. There is no concept of
+  'modifier':
+    (around a (around b c)) ;; Perfectly valid
+
+  -------------------------------------------------------------------------- |#
+
+(defalias
+
+  ;; Something useful
+  cpy C-c
+  pst C-v
+  cut C-x
+
+  ;; Something silly
+  md1 (around a (around b c))    ;; abc
+  md2 (around a (around-only lsft b)) ;; aB
+  md3 C-A-M-S-l
+  md4 (around % b)               ;; BEWARE: %B, not %b, do you see why?
+  md5 (around-when-alone lctl c)
+)
+
+#| --------------------------------------------------------------------------
+                          Optional: sticky keys
+
+  KMonad also supports so called "sticky keys".  These are keys that will
+  behave as if they were pressed after just tapping them.  This behaviour
+  wears off after the next button is pressed, which makes them ideal for
+  things like a quick control or shift.  For example, tapping a sticky and
+  then pressing `abc' will result in `Abc'.
+
+  You can create these keys with the `sticky-key' keyword:
+
+    (defalias
+      slc (sticky-key 500 lctl))
+
+  The number after `sticky-key' is the timeout you want, in milliseconds.  If
+  a key is tapped and that time has passed, it won't act like it's pressed
+  down when we receive the next keypress.
+
+  It is also possible to combine sticky keys.  For example, to
+  get a sticky shift+control you can do
+
+    (defalias
+      ssc (around
+           (sticky-key 500 lsft)
+           (sticky-key 500 lctl)))
+
+  -------------------------------------------------------------------------- |#
+
+;; Let's make both shift keys sticky
+(defalias
+  sl (sticky-key 300 lsft)
+  sr (sticky-key 300 rsft))
+
+#| --------------------------------------------------------------------------
+                           Optional: stepped buttons
+
+  Stepped buttons are a bit out there. They allow you to define a button
+  which does something different depending on how often you have pressed it
+  in the past.
+  Why is this useful?
+  Simple, if you want a ctrl-lock which should work like caps-lock but for
+  ctrl you can use `stepped` in combination with `press-only` and create
+  `@ctl-lck` (see below).
+
+  -------------------------------------------------------------------------- |#
+
+(defalias
+  ctl-lck (stepped (press-only lctl) (release-only lctl)))
+
+
+;; Now we define the 'tst' button as opening and closing a bunch of layers at
+;; the same time. If you understand why this works, you're starting to grok
+;; KMonad.
+;;
+;; Explanation: we define a bunch of testing-layers with buttons to illustrate
+;; the various options in KMonad. Each of these layers makes sure to have its
+;; buttons not overlap with the buttons from the other layers, and specifies all
+;; its other buttons as transparent. When we use the nested `around` statement,
+;; whenever we push the button linked to '@tst' (check `qwerty` layer, we bind
+;; it to `rctl`), any button we press when holding `rctl` will be pressed in the
+;; context of those 4 layers overlayed on the stack. When we release `rctl`, all
+;; these layers will be popped again.
+(defalias tst (around (layer-toggle macro-test)
+                (around (layer-toggle layer-test)
+                  (around (layer-toggle around-next-test)
+                    (around (layer-toggle command-test)
+                            (layer-toggle modded-test))))))
+
+(deflayer modded-test
+  _    _    _    _    _    _    _    _    _    _    _    _    _    _
+  _    _    @md4 _    _    _    _    _    _    _    _    _    _    _
+  _    _    @md1 @md2 @md3 _    _    _    _    _    _    _    _
+  _    _    @cut @cpy @pst _    _    _    _    _    _    _
+  _    _    _              _              _    _    _    _
+)
+
+#| --------------------------------------------------------------------------
+                              Optional: tap-macros
+
+  Let's look at a button we haven't seen yet, tap-macros.
+
+  `tap-macro` is a function that takes an arbitrary number of buttons and
+  returns a new button. When this new button is pressed it rapidly taps all its
+  stored buttons in quick succession except for its last button, which it only
+  presses. This last button gets released when the `tap-macro` gets released.
+
+  There are two ways to define a `tap-macro`, using the `tap-macro` function
+  directly, or through the #() syntactic sugar. Both evaluate to exactly the
+  same button.
+
+    (tap-macro K M o n a d)
+    #(K M o n a d)
+
+  If you are going to use a `tap-macro` to perform a sequence of actions inside
+  some program you probably want to include short pauses between inputs to give
+  the program time to register all the key-presses. Therefore we also provide
+  the 'pause' function, which simply pauses processing for a certain amount of
+  milliseconds. Pauses can be created like this:
+
+    (pause 20)
+    P20
+
+  You can also pause between each key stroke by specifying the `:delay' keyword,
+  as well as a time in ms, at the end of a `tap-macro':
+
+    (tap-macro K M o n a d :delay 5)
+    #(K M o n a d :delay 5)
+
+  The above would be equivalent to
+
+    (tap-macro K P5 M P5 o P5 n P5 a P5 d)
+
+  The `tap-macro-release` is like `tap-macro`, except that it
+  waits to press the last button when the `tap-macro-release`
+  gets released.  It might be useful when combined with a
+  footswitch that sends keyboard scan codes.
+
+    (tap-macro-release i K M o n a d esc)
+
+  WARNING: DO NOT STORE YOUR PASSWORDS IN PLAIN TEXT OR IN YOUR KEYBOARD
+
+  I know it might be tempting to store your password as a macro, but there are 2
+  huge risks:
+  1. You accidentally leak your config and expose your password
+  2. Anyone who knows about the button can get clear-text representation of your
+     password with any text editor, shell, or text-input field.
+
+  Support for triggering shell commands directly from KMonad is described in the
+  command buttons section below.
+
+  This concludes this public service announcement.
+
+  -------------------------------------------------------------------------- |#
+
+(defalias
+  mc1 #(K M o n a d)
+  mc2 #(C-c P50 A-tab P50 C-v) ;; Careful, this might do something
+  mc3 #(P200 h P150 4 P100 > < P50 > < P20 0 r z 1 ! 1 ! !)
+  mc4 (tap-macro a (pause 50) @md2 (pause 50) c)
+  mc5 (tap-macro-release esc esc esc)
+  mc6 #(@mc3 spc @mc3 spc @mc3)
+)
+
+(deflayer macro-test
+  _    @mc1 @mc2 @mc3 @mc4 @mc5 @mc6 _    _    _    _    _    _    _
+  _    _    _    _    _    _    _    _    _    _    _    _    _    _
+  _    _    _    _    _    _    _    _    _    _    _    _    _
+  _    _    _    _    _    _    _    _    _    _    _    _
+  _    _    _              _              _    _    _    _
+)
+
+
+#| --------------------------------------------------------------------------
+                          Optional: layer manipulation
+
+  You have already seen the basics of layer-manipulation. The `layer-toggle`
+  button. This button adds a layer to the top of KMonad's layer stack when
+  pressed, and removes it again when released. There are a number of other ways
+  to manipulate the layer stack, some safer than others. Let's go through all of
+  them from safest to least safe:
+
+  `layer-toggle` works as described before, 2 things to note:
+  1. If you are confused or worried about pressing a key, changing layers, and
+     then releasing a key and this causing issues: don't be. KMonad handles
+     presses and releases in very different ways. Presses get passed directly to
+     the stacked keymap as previously described. When a KMonad button has its
+     press-action triggered, it then registers a callback that will catch its
+     own release before we ever touch the keymap. This guarantees that the
+     button triggered by the press of X *will be* the button whose release is
+     triggered by the release of X (the release of X might trigger other things
+     as well, but that is besides the point.)
+  2. If `layer-toggle` can only ever add and then necessarily remove 1 layer
+     from the stack, then it will never cause a permanent change, and is
+     perfectly safe.
+
+  `layer-delay`, once pressed, temporarily switches to some layer for some
+  milliseconds. Just like `layer-toggle` this will never permanently mess-up the
+  layer stack. This button was initially implemented to provide some
+  'leader-key' style behavior. Although I think in the future better solutions
+  will be available. For now this will temporarily add a layer to the top of the
+  stack:
+    (layer-delay 500 my-layer)
+
+  `layer-next`, once pressed, primes KMonad to handle the next press from some
+  arbitrary layer. This aims to fill the same usecase as `layer-delay`: the
+  beginnings of 'leader-key' style behavior. I think this whole button will get
+  deleted soon, because the more general `around-next` now exists (see below)
+  and this is nothing more than:
+    (around-next (layer-toggle layer-name))
+  Until then though, use `layer-next` like this:
+    (layer-next layer-name)
+
+  `layer-switch`: change the base-layer of KMonad. As described at the top of
+  this document, the first `deflayer` statement is the layer that is active when
+  KMonad starts. Since `layer-toggle` can only ever add on and remove from the
+  top of that, it can never change the base-layer. The following button will
+  unregister the bottom-most layer of the keymap, and replace it with another
+  layer:
+    (layer-switch my-layer)
+
+  This is where things start getting potentially dangerous (i.e. get KMonad into
+  an unusable state until a restart has occurred). It is perfectly possible to
+  switch into a layer that you can never get out of. Or worse, you could
+  theoretically have a layer full of only `XX`s and switch into that, rendering
+  your keyboard unusable until you somehow manage to kill KMonad (without using
+  your keyboard).
+
+  However, when handled well, `layer-switch` is very useful, letting you switch
+  between 'modes' for your keyboard. I have a tiny keyboard with a weird keymap,
+  but I switch into a simple 'qwerty' keymap shifted 1 button to the right for
+  gaming. Just make sure that any 'mode' you switch into has a button that
+  allows you to switch back out of the 'mode' (or content yourself restarting
+  KMonad somehow).
+
+  `layer-add` and `layer-rem`. This is where you can very quickly cause yourself
+  a big headache. Originally I didn't expose these operations, but someone
+  wanted to use them, and I am not one to deny someone else a chainsaw. As the
+  names might give away:
+    (layer-add name) ;; Add a layer to the top of the stack
+    (layer-rem name) ;; Remove a layer by name (noop if no such layer)
+
+  To use `layer-add` and `layer-rem` well, you should take a moment to think
+  about how to create a layout that will prevent you from getting into
+  situations where you enter a key-configuration you cannot get out of again.
+  These two operations together, however, are very useful for activating a
+  permanent overlay for a while. This technique is illustrated in the tap-hold
+  overlay a bit further down.
+
+
+  -------------------------------------------------------------------------- |#
+
+(defalias
+
+  yah (layer-toggle asking-for-trouble) ;; Completely safe
+  nah (layer-add asking-for-trouble)    ;; Completely unsafe
+
+  ld1 (layer-delay 500 numbers) ;; One way to get a leader-key
+  ld2 (layer-next numbers)      ;; Another way to get a leader key
+
+  ;; NOTE, this is safe because both `qwerty` and `colemak` contain the `@tst`
+  ;; button which will get us to the `layer-test` layer, which itself contains
+  ;; both `@qwe` and `@col`.
+  qwe (layer-switch qwerty) ;; Set qwerty as the base layer
+  col (layer-switch colemak) ;; Set colemak as the base layer
+)
+(deflayer layer-test
+  @qwe _    _    _    _    _    _    _    _    _    _    @add _    @nah
+  @col _    _    _    _    _    _    _    _    _    _    _    _    @yah
+  _    _    _    _    _    _    _    _    _    _    _    _    _
+  _    _    _    _    _    _    _    _    _    @ld1 @ld2 _
+  _    _    _              _              _    _    _    _
+)
+
+;; Exactly like qwerty, but with the letters switched around
+(deflayer colemak
+  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
+  tab  q    w    f    p    g    j    l    u    y    ;    [    ]    \
+  @xcp a    r    s    t    d    h    n    e    i    o    '    ret
+  @sl  z    x    c    v    b    k    m    ,    .    /    @sr
+  lctl @num lalt           spc            ralt rmet @sym @tst
+)
+
+(defalias lol #(: - D))
+
+;; Contrived example
+(deflayer asking-for-trouble
+  @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol
+  @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol
+  @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol
+  @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol @lol
+  @lol @lol @lol           @lol           @lol @lol @lol @lol
+)
+
+;; One way to safely use layer-add and layer-rem: the button bound to layer-add
+;; is the same button bound to layer-rem in the layer that `add` adds to the
+;; stack. I.e., it becomes impossible to add or remove multiple copies of a
+;; layer.
+(defalias
+  add (layer-add multi-overlay) ;; multi-overlay is defined in the next
+  rem (layer-rem multi-overlay) ;; section below this
+)
+
+#| --------------------------------------------------------------------------
+                          Optional: Multi-use buttons
+
+  Perhaps one of the most useful features of KMonad, where a lot of work has
+  gone into, but also an area with many buttons that are ever so slightly
+  different. The naming and structuring of these buttons might change sometime
+  soon, but for now, this is what there is.
+
+  For the next section being able to talk about examples is going to be handy,
+  so consider the following scenario and mini-language that will be the same
+  between scenarios:
+
+    - We have some button `foo` that will be different between scenarios
+    - `foo` is bound to 'Esc' on the input keyboard
+    - the letters a s d f are bound to themselves
+    - Px signifies the press of button x on the keyboard
+    - Rx signifies the release of said button
+    - Tx signifies the sequential and near instantaneous press and release of x
+    - 100 signifies 100ms pass
+
+  So for example:
+    Tesc Ta:
+      tap of 'Esc' (triggering `foo`), tap of 'a' triggering `a`
+    Pesc 100 Ta Tb Resc:
+      press of 'Esc', 100ms pause, tap of 'a', tap of 'b', release of 'Esc'
+
+  The `tap-next` button takes 2 buttons, one for tapping, one for holding, and
+  combines them into a single button. When pressed, if the next event is its own
+  release, we tap the 'tapping' button. In all other cases we first press the
+  'holding' button then we handle the event. Then when the `tap-next` gets
+  released, we release the 'holding' button.
+
+  So, using our mini-language, we set foo to:
+    (tap-next x lsft)
+  Then:
+    Tesc            -> x
+    Tesc Ta         -> xa
+    Pesc Ta Resc    -> A
+    Pesc Ta Tr Resc -> AR
+
+  The `tap-hold` button is very similar to `tap-next` (a theme, trust me). The
+  difference lies in how the decision is made whether to tap or hold. A
+  `tap-hold` waits for a particular timeout, if the `tap-hold` is released
+  anywhere before that moment we execute a tap immediately. If the timeout
+  occurs and the `tap-hold` is still held, we switch to holding mode.
+
+  The additional feature of a `tap-hold` is that it pauses event-processing
+  until it makes its decision and then rolls back processing when the decision
+  has been made.
+
+  So, again with the mini-language, we set foo to:
+    (tap-hold 200 x lsft) ;; Like tap-next, but with a 200ms timeout
+  Then:
+    Tesc            -> x
+    Tesc Ta         -> xa
+    Pesc 300 a      -> A (the moment you press a)
+    Pesc a 300      -> A (after 200 ms)
+    Pesc a 100 Resc -> xa (both happening immediately on Resc)
+
+  The `tap-hold-next` button is a combination of the previous 2. Essentially,
+  think of it as a `tap-next` button, but it also switches to held after a
+  period of time. This is useful, because if you have a (tap-next ret ctl) for
+  example, and you press it thinking you want to press C-v, but then you change
+  your mind, you now cannot release the button without triggering a 'ret', that
+  you then have to backspace. With the `tap-hold-next` button, you simply
+  outwait the delay, and you're good. I see no benefit of `tap-next` over
+  `tap-hold-next` with a decent timeout value.
+
+  You can use the `:timeout-button` keyword to specify a button other than the
+  hold button which should be held when the timeout expires. For example, we
+  can construct a button which types one x when tapped, multiple x's when held,
+  and yet still acts as shift when another button is pressed before the timeout
+  expires. So, using the minilanguage and foo as:
+    (tap-hold-next 200 x lsft :timeout-button x)
+  Then:
+    Tesc           -> Tx
+    Pesc 100 a     -> A (the moment you press a)
+    Pesc 5000 Resc -> xxxxxxx (some number of auto-repeated x's)
+
+  Note that KMonad does not itself auto-repeat the key. In this last example,
+  KMonad emits 200 Px 4800 Rx, and the operating system's auto-repeat feature,
+  if any, emits multiple x's because it sees that the x key is held for 4800 ms.
+
+  A note about tap action duration:
+  For simplicity we reuse the `tap-next` example above, set foo to:
+    (tap-next x lsft)
+  Now, any keystroke performed by baseline human will have some duration, a
+  'Tesc' is actually 'Pesc <some time passed> Resc'.  A true tap 'Tesc' with no
+  delay between the press and release will sometime experience registration
+  problems in programs.  However the tap action performed by KMonad IS this kind
+  of 'true tap', that is:
+    Tesc (Pesc 100 Resc) -> Px Rx
+  For various reasons we do not want KMonad to have some default duration in the
+  tap action it performs.  If you are having issues in programs, you can instead
+  use the aforementioned `around` and `pause` function to give the tap action
+  some duration.  Set foo to:
+    (tap-next (around x (pause 2000)) lsft)
+  or equivalently:
+    (tap-next (around x P2000) lsft)
+  then we have:
+    Tesc (Pesc 100 Resc) -> Px 2000 Rx
+  2000 ms is just for you to distinctively see the effect, in practice 35 ms
+  should be enough for most scenarios (slightly longer than 2 frames in 60 fps).
+
+  The `tap-next-release` is like `tap-next`, except it decides whether to tap or
+  hold based on the next release of a key that was *not* pressed before us. This
+  also performs rollback like `tap-hold`. So, using the minilanguage and foo as:
+    (tap-next-release x lsft)
+  Then:
+    Tesc Ta         -> xa
+    Pa Pesc Ra Resc -> ax (because 'a' was already pressed when we started, so
+                           foo decides it is tapping)
+    Pesc Pa Resc Ra -> xa (because the first release we encounter is of esc)
+    Pesc Ta Resc    -> A (because a was pressed *and* released after we started,
+                          so foo decides it is holding)
+
+  `tap-next-press` is also a lot like `tap-next`, but decides whether to tap or
+  hold based on whether another key is pressed before this one is released.
+  Using the minilanguage:
+    (tap-next-press x lsft)
+  Then:
+    Tesc Ta -> xa
+    Pa Pesc Ra Resc -> ax (because esc is released before another key is pressed)
+    Pesc Pa Resc Ra -> A (because a is pressed before esc is released)
+    Pesc Ta Resc    -> A (a is pressed before esc is released here as well)
+
+  These increasingly stranger buttons are, I think, coming from the stubborn
+  drive of some of my more eccentric (and I mean that in the most positive way)
+  users to make typing with modifiers on the home-row more comfortable.
+  Especially layouts that encourage a lot of rolling motions are nicer to use
+  with the `release` style buttons.
+
+  The `tap-hold-next-release` (notice a trend?) is just like `tap-next-release`,
+  but it comes with an additional timeout that, just like `tap-hold-next` will
+  jump into holding-mode after a timeout.
+
+  I honestly think that `tap-hold-next-release`, although it seems the most
+  complicated, probably is the most comfortable to use. But I've put all of them
+  in a testing layer down below, so give them a go and see what is nice.
+
+  -------------------------------------------------------------------------- |#
+
+
+(defalias
+  xtn (tap-next x lsft)         ;; Shift that does 'x' on tap
+  xth (tap-hold 400 x lsft)     ;; Long delay for easier testing
+  thn (tap-hold-next 400 x lsft)
+  tnr (tap-next-release x lsft)
+  tnp (tap-next-press x lsft)
+  tnh (tap-hold-next-release 2000 x lsft)
+
+  ;; Used it the colemak layer
+  xcp (tap-hold-next 400 esc ctl)
+)
+
+;; Some of the buttons used here are defined in the next section
+(deflayer multi-overlay
+  @mt  _    _    _    _    _    _    _    _    _    _    _    @rem _
+  _    _    _    _    _    _    _    _    _    _    _    _    _    _
+  @thn _    _    _    _    _    _    _    _    _    _    _    _
+  @xtn _    _    _    _    _    _    _    _    _    _    @xth
+  @tnr @tnp _              _              _    _    _    @tnh
+)
+
+
+#| --------------------------------------------------------------------------
+                              Optional: Multi-tap
+
+  Besides the tap-hold style buttons there is another multi-use button (with
+  only 1 variant, at the moment). The `multi-tap`.
+
+  A `multi-tap` codes for different buttons depending on how often it is tapped.
+  It is defined by a series of delays and buttons, followed by a last button
+  without delay. As long as you tap the `multi-tap` within the delay specified,
+  it will jump to the next button. Once the delay is exceeded the selected
+  button is pressed. If the last button in the list is reached, it is
+  immediately pressed. When another key is pressed down while we're tapping,
+  `multi-tap' also immediately exits and taps the current button.
+
+  Note that you can actually hold the button, so in the below example, going:
+  tap-tap-hold (wait 300ms) will get you a pressed c, until you release again.
+
+  -------------------------------------------------------------------------- |#
+
+(defalias
+  mt  (multi-tap 300 a 300 b 300 c 300 d e))
+
+
+#| --------------------------------------------------------------------------
+                              Optional: Around-next
+
+  The `around-next` function creates a button that primes KMonad to perform the
+  next button-press inside some context. This could be the context of 'having
+  Shift pressed' or 'being inside some layer' or, less usefully, 'having d
+  pressed'. It is a more general and powerful version of `layer-next`.
+
+  There is also an `around-next-timeout` button that does the same thing as
+  `around-next`, except that if some other button press is not detected within
+  some timeout, some other button is tapped. This can be used to create a
+  leader-key that simply times out (by passing a non-button), or a key that can
+  still function as a normal key, but also as a leader key when used slowly.
+
+  The variant `around-next-single` does the same thing as `around-next` except
+  it really only concerns the next event (press or release).
+  Consider the button `@ns` (see below) in the following scenario:
+
+    P@ns Pa Tb Ra Tc -> A B c
+
+  If we had used `@ns'` instead, we would get a result we would expect more:
+
+    P@ns' Pa Tb Ra Tc -> A b c
+
+  but we get the following problem instead, since it handles any following
+  event, not just key presses:
+
+    Pa P@ns' Ra Tb -> a b
+
+  Another button which may be seen as a variant is `before-after-next`.
+  It taps a button before and another after the actual button.
+  It can be used to recreate `(around-next a)` via:
+
+    (before-after-next (press-only a) (release-only a))
+
+  but it can also be used in a more productive sense like `@inn`.
+  If you can switch with `tab` and `S-tab` between something,
+  you can use `@inn` to type your next key there:
+
+    T@inn Pctl Ta Rctl
+
+  would commonly select everything in the next container.
+
+  I think expansion of this button-style is probably the future of leader-key,
+  hydra-style functionality support in KMonad.
+
+  -------------------------------------------------------------------------- |#
+
+(defalias
+  ns  (around-next sft)        ;; Shift until the release of the next press
+  ns' (around-next-single sft) ;; Shift the next event
+  nnm (around-next @num)       ;; Perform next press in numbers layer
+  ntm (around-next-timeout 500 sft XX)
+  inn (before-after-next tab S-tab)
+
+
+)
+
+(deflayer around-next-test
+  _    _    _    _    _    _    _    _    _    _    _    _    _    _
+  _    _    _    _    _    _    _    _    _    _    _    _    _    _
+  @ns  _    _    _    _    _    _    _    _    _    _    _    _
+  @nnm _    _    _    _    _    _    _    _    _    _    _
+  @ntm _    _              _              _    _    _    _
+)
+
+#| --------------------------------------------------------------------------
+                        Optional: Compose-key sequences
+
+  Compose-key sequences are series of button-presses that your operating system
+  will interpret as the insertion of a special character, like accented
+  characters, or various special-languages. In that sense, they are just
+  syntactic sugar for keyboard macros.
+
+  To get this to work on Linux you will need to set your compose-key with a tool
+  like `setxkbmap', as well as tell KMonad that information. See the `defcfg'
+  block at the top of this file for a working example. Note that you need to
+  wait ever so slightly for the keyboard to register with Linux before the
+  command gets executed, that's why the `sleep 1`. Also, note that all the
+  `/run/current-system' stuff is because the author uses NixOS. Just find a
+  shell-command that will:
+
+    1. Sleep a moment
+    2. Set the compose-key to your desired key
+
+  Please be aware that what `setxkbmap' calls the `menu' key is not actually the
+  `menu' key! If you want to use the often suggested
+
+      setxkbmap -option compose:menu
+
+  you will have to set your compose key within KMonad to `compose' and not
+  `menu'.
+
+  After this, this should work out of the box under Linux. Windows does not
+  recognize the same compose-key sequences, but WinCompose will make most of the
+  sequences line up with KMonad: http://wincompose.info/
+  This has not in any way been tested on Mac.
+
+  In addition to hard-coded symbols, we also provide 'uncompleted' macros. Since
+  a compose-key sequence is literally just a series of keystrokes, we can omit
+  the last one, and enter the sequence for 'add an umlaut' and let the user then
+  press some letter to add this umlaut to. These are created using the `+"`
+  syntax.
+
+  -------------------------------------------------------------------------- |#
+
+(defalias
+  sym (layer-toggle symbols)
+
+)
+
+(deflayer symbols
+  _    _    _    _    _    _    _    _    _    _    _    _    _    _
+  _    ä    é    ©    _    _    _    _    _    _    _    _    _    _
+  _    +'   +~   +`   +^   _    _    _    _    _    _    _    _
+  _    +"   +,   _    _    _    _    _    _    _    _    _
+  _    _    _              _              _    _    _    _)
+
+
+#| --------------------------------------------------------------------------
+                        Optional: Command buttons
+
+  Currently we also provide the ability to launch arbitrary shell-commands from
+  inside KMonad. These commands are simply handed off to the command-shell
+  without any further checking or waiting.
+
+  NOTE: currently only tested on Linux, but should work on any platform, as long
+  as the command is valid for that platform.
+
+  The `cmd-button' function takes two arguments, the second one of which is
+  optional. These represent the commands to be executed on pressing and
+  releasing the button respectively.
+
+  BEWARE: never run anyone's configuration without looking at it. You wouldn't
+  want to push:
+
+    (cmd-button "rm -rf ~/*") ;; Delete all this user's data
+
+
+  -------------------------------------------------------------------------- |#
+
+(defalias
+  dat (cmd-button "date >> /tmp/kmonad_example.txt")   ;; Append date to tmpfile
+  pth (cmd-button "echo $PATH > /tmp/kmonad_path.txt") ;; Write out PATH
+  ;; `dat' on press and `pth' on release
+  bth (cmd-button "date >> /tmp/kmonad_example.txt"
+                  "echo $PATH > /tmp/kmonad_path.txt")
+)
+
+(deflayer command-test
+  _    _    _    _    _    _    _    _    _    _    _    _    _    _
+  _    _    _    _    _    _    _    _    _    _    _    _    _    _
+  _    _    _    _    _    _    _    _    _    _    _    _    _
+  _    _    _    _    _    _    _    _    _    @dat @pth _
+  _    _    _              _              _    _    _    _
+)
diff --git a/kmonad.cabal b/kmonad.cabal
--- a/kmonad.cabal
+++ b/kmonad.cabal
@@ -1,150 +1,208 @@
-cabal-version:      2.2
-name:               kmonad
-version:            0.4.2
-license:            MIT
-license-file:       LICENSE
-maintainer:         janssen.dhj@gmail.com
-author:             David Janssen
-synopsis:           Advanced keyboard remapping utility
+cabal-version: 2.2
+
+name:           kmonad
+category:       Application
+version:        0.4.3
+synopsis:       Advanced keyboard remapping utility
+author:         David Janssen
+maintainer:     janssen.dhj@gmail.com
+license:        MIT
+license-file:   LICENSE
+build-type:     Simple
 description:
-    KMonad is a cross-platform command-line utility that runs as a daemon. It
-    captures keyboard input (exactly how depends on the OS) and remaps it. The
-    mapping is highly configurable, and provides options like (transparent) layer
-    overlays, tap-mod buttons, multi-tap buttons, leader-key style buttons, and
-    keyboard macros. Functionality heavily inspired by the QMK-firmware.
+  KMonad is a cross-platform command-line utility that runs as a daemon. It
+  captures keyboard input (exactly how depends on the OS) and remaps it. The
+  mapping is highly configurable, and provides options like (transparent) layer
+  overlays, tap-mod buttons, multi-tap buttons, leader-key style buttons, and
+  keyboard macros. Functionality heavily inspired by the QMK-firmware.
 
-category:           Application
-build-type:         Simple
-extra-source-files: changelog.md
+extra-source-files:
+    changelog.md
+    doc/quick-reference.md
+    keymap/tutorial.kbd
 
 flag kext
     description: build against the kext [macOS only]
-    default:     False
+    default: False
 
 flag dext
     description: build against the dext [macOS only]
-    default:     False
+    default: False
 
 library
-    exposed-modules:
-        KMonad.App
-        KMonad.App.Main
-        KMonad.App.Types
-        KMonad.Args
-        KMonad.Args.Cmd
-        KMonad.Args.Parser
-        KMonad.Args.Joiner
-        KMonad.Args.TH
-        KMonad.Args.Types
-        KMonad.Model
-        KMonad.Model.Action
-        KMonad.Model.BEnv
-        KMonad.Model.Button
-        KMonad.Model.Dispatch
-        KMonad.Model.Hooks
-        KMonad.Model.Keymap
-        KMonad.Model.Sluice
-        KMonad.Gesture
-        KMonad.Keyboard
-        KMonad.Keyboard.ComposeSeq
-        KMonad.Keyboard.IO
-        KMonad.Keyboard.Keycode
-        KMonad.Keyboard.Ops
-        KMonad.Keyboard.Types
-        KMonad.Parsing
-        KMonad.Prelude
-        KMonad.Prelude.Imports
-        KMonad.Prelude.Definitions
-        KMonad.Util
-        KMonad.Util.LayerStack
-        KMonad.Util.MultiMap
-        Paths_kmonad
-
-    hs-source-dirs:     src
-    autogen-modules:    Paths_kmonad
-    default-language:   Haskell2010
-    default-extensions:
-        ConstraintKinds DeriveFunctor DeriveGeneric DeriveTraversable
-        FlexibleContexts FlexibleInstances FunctionalDependencies
-        GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses
-        MultiWayIf NoImplicitPrelude OverloadedStrings RankNTypes
-        TemplateHaskell TupleSections TypeFamilies
-
-    ghc-options:        -Wall -Wno-name-shadowing -Wno-unused-imports
-    build-depends:
-        base <4.18,
-        cereal <0.6,
-        lens <5.3,
-        megaparsec <9.4,
-        mtl <2.3,
-        optparse-applicative <0.18,
-        resourcet <1.3,
-        rio <0.2,
-        time <1.13,
-        unliftio <0.3,
-        template-haskell <2.20
-
-    if os(linux)
-        exposed-modules:
-            KMonad.Keyboard.IO.Linux.DeviceSource
-            KMonad.Keyboard.IO.Linux.Types
-            KMonad.Keyboard.IO.Linux.UinputSink
-
-        c-sources:       c_src/keyio.c
-        build-depends:   unix <2.8
-
-    if os(windows)
-        exposed-modules:
-            KMonad.Keyboard.IO.Windows.LowLevelHookSource
-            KMonad.Keyboard.IO.Windows.SendEventSink
-            KMonad.Keyboard.IO.Windows.Types
+  default-language:
+      Haskell2010
+  hs-source-dirs:
+      src
+  ghc-options:
+    -Wall
+    -Wno-name-shadowing
+    -Wno-unused-imports
+  build-depends:
+      base >= 4.12 && < 5
+    , cereal
+    -- , hspec
+    , lens
+    , megaparsec
+    , mtl
+    , optparse-applicative
+    , resourcet
+    , rio
+    , time
+    , unliftio
+    , template-haskell
+  default-extensions:
+      ConstraintKinds
+      DeriveFunctor
+      DeriveGeneric
+      DeriveDataTypeable
+      DeriveTraversable
+      FlexibleContexts
+      FlexibleInstances
+      FunctionalDependencies
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NoImplicitPrelude
+      OverloadedStrings
+      RankNTypes
+      TemplateHaskell
+      TupleSections
+      TypeFamilies
 
-        c-sources:       c_src/keyio_win.c
-        build-depends:   Win32
+  autogen-modules:
+      Paths_kmonad
 
-    if os(osx)
-        exposed-modules:
-            KMonad.Keyboard.IO.Mac.IOKitSource
-            KMonad.Keyboard.IO.Mac.KextSink
-            KMonad.Keyboard.IO.Mac.Types
+  exposed-modules:
+      KMonad.App
+      KMonad.App.Main
+      KMonad.App.Types
+      KMonad.Args
+      KMonad.Args.Cmd
+      KMonad.Args.Parser
+      KMonad.Args.Joiner
+      KMonad.Args.TH
+      KMonad.Args.Types
+      KMonad.Model
+      KMonad.Model.Action
+      KMonad.Model.BEnv
+      KMonad.Model.Button
+      KMonad.Model.Dispatch
+      KMonad.Model.Hooks
+      KMonad.Model.Keymap
+      KMonad.Model.Sluice
+      KMonad.Gesture
+      KMonad.Keyboard
+      KMonad.Keyboard.ComposeSeq
+      KMonad.Keyboard.IO
+      KMonad.Keyboard.Keycode
+      KMonad.Keyboard.Ops
+      KMonad.Keyboard.Types
+      KMonad.Parsing
+      KMonad.Prelude
+      KMonad.Prelude.Imports
+      KMonad.Prelude.Definitions
+      KMonad.Util
+      KMonad.Util.LayerStack
+      KMonad.Util.MultiMap
+      Paths_kmonad
 
-        frameworks:      CoreFoundation IOKit
-        extra-libraries: c++
-        build-depends:   unix <2.8
+  if os(linux)
+    exposed-modules:
+      KMonad.Keyboard.IO.Linux.DeviceSource
+      KMonad.Keyboard.IO.Linux.Types
+      KMonad.Keyboard.IO.Linux.UinputSink
+    c-sources:
+      c_src/keyio.c
+    build-depends:
+      unix
 
-        if flag(kext)
-            cxx-options: -std=c++14
-            cxx-sources: c_src/mac/kext.cpp
+  if os(windows)
+    exposed-modules:
+      KMonad.Keyboard.IO.Windows.LowLevelHookSource
+      KMonad.Keyboard.IO.Windows.SendEventSink
+      KMonad.Keyboard.IO.Windows.Types
+    c-sources:
+      c_src/keyio_win.c
+    build-depends:
+      Win32
 
-        if flag(dext)
-            cxx-options: -std=c++2a
-            cxx-sources: c_src/mac/dext.cpp
+  if os(darwin)
+    exposed-modules:
+      KMonad.Keyboard.IO.Mac.IOKitSource
+      KMonad.Keyboard.IO.Mac.KextSink
+      KMonad.Keyboard.IO.Mac.Types
+    if flag(kext)
+      cxx-sources:
+        c_src/mac/kext.cpp
+      cxx-options:
+        -std=c++14
+    if flag(dext)
+      cxx-sources:
+        c_src/mac/dext.cpp
+      cxx-options:
+        -std=c++2a
+    extra-libraries:
+      c++
+    build-depends:
+      unix
+    frameworks:
+      CoreFoundation
+      IOKit
 
 executable kmonad
-    main-is:          Main.hs
-    hs-source-dirs:   app
-    default-language: Haskell2010
-    ghc-options:      -threaded -rtsopts -with-rtsopts=-N
-    build-depends:
-        base <4.18,
-        kmonad
+  ghc-options:
+      -threaded
+      -rtsopts
+      -with-rtsopts=-N
+  main-is:
+      Main.hs
+  default-language:
+      Haskell2010
+  hs-source-dirs:
+      app
+  build-depends:
+      base >= 4.12 && < 5
+    , kmonad
 
 test-suite spec
-    type:               exitcode-stdio-1.0
-    main-is:            Spec.hs
-    build-tool-depends: hspec-discover:hspec-discover >=2 && <3
-    hs-source-dirs:     test
-    other-modules:      KMonad.GestureSpec
-    default-language:   Haskell2010
-    default-extensions:
-        ConstraintKinds DeriveFunctor DeriveGeneric DeriveTraversable
-        FlexibleContexts FlexibleInstances FunctionalDependencies
-        GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses
-        MultiWayIf NoImplicitPrelude OverloadedStrings RankNTypes
-        TemplateHaskell TupleSections TypeFamilies
-
-    ghc-options:        -Wall
-    build-depends:
-        base <4.18,
-        kmonad,
-        hspec <2.11
+  main-is: Spec.hs
+  type: exitcode-stdio-1.0
+  hs-source-dirs:
+      test
+  ghc-options:
+      -Wall
+  build-depends:
+      base >= 4.12 && < 5
+    , kmonad
+    , hspec
+    , rio
+  other-modules:
+      KMonad.ButtonDocSpec
+      KMonad.GestureSpec
+      KMonad.ComposeSeqSpec
+  default-language:
+      Haskell2010
+  build-tool-depends: hspec-discover:hspec-discover == 2.*
+  default-extensions:
+      ConstraintKinds
+      DeriveFunctor
+      DeriveGeneric
+      DeriveDataTypeable
+      DeriveTraversable
+      FlexibleContexts
+      FlexibleInstances
+      FunctionalDependencies
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NoImplicitPrelude
+      OverloadedStrings
+      RankNTypes
+      TemplateHaskell
+      TupleSections
+      TypeFamilies
diff --git a/src/KMonad/App/Main.hs b/src/KMonad/App/Main.hs
--- a/src/KMonad/App/Main.hs
+++ b/src/KMonad/App/Main.hs
@@ -55,6 +55,7 @@
 -- 3. Maybe start KMonad
 runCmd :: Cmd -> IO ()
 runCmd c = do
+  hSetBuffering stdout LineBuffering
   o <- logOptionsHandle stdout False <&> setLogMinLevel (c^.logLvl)
   withLogFunc o $ \f -> runRIO f $ do
     cfg <- loadConfig c
@@ -79,7 +80,7 @@
   -- Wait a bit for the user to release the 'Return' key with which they started KMonad
   threadDelay $ fromIntegral (cfg^.startDelay) * 1000
 
-  -- Acquire the keysource and keysink
+  -- Acquire the key source and key sink
   snk <- using $ cfg^.keySinkDev
   src <- using $ cfg^.keySourceDev
 
diff --git a/src/KMonad/App/Types.hs b/src/KMonad/App/Types.hs
--- a/src/KMonad/App/Types.hs
+++ b/src/KMonad/App/Types.hs
@@ -144,6 +144,6 @@
     spawnCommand cmd = void $ createProcess_ "spawnCommand"
       (shell cmd){ -- We don't want the child process to inherit things like
                    -- our keyboard grab (this would, for example, make it
-                   -- impossible for a command to restart kmonad).
+                   -- impossible for a command to restart KMonad).
                    close_fds   = True
                  }
diff --git a/src/KMonad/Args.hs b/src/KMonad/Args.hs
--- a/src/KMonad/Args.hs
+++ b/src/KMonad/Args.hs
@@ -60,7 +60,7 @@
   cliList :: DefSettings
   cliList = catMaybes $
        map flagToMaybe [cmd^.cmdAllow, cmd^.fallThrgh]
-    <> [cmd^.iToken, cmd^.oToken, cmd^.cmpSeq, cmd^.initStr]
+    <> [cmd^.iToken, cmd^.oToken, cmd^.cmpSeq, cmd^.implArnd]
 
   -- | Convert command line flags to a 'Maybe' type, where the non-presence, as
   -- well as the default value of a flag will be interpreted as @Nothing@
diff --git a/src/KMonad/Args/Cmd.hs b/src/KMonad/Args/Cmd.hs
--- a/src/KMonad/Args/Cmd.hs
+++ b/src/KMonad/Args/Cmd.hs
@@ -17,7 +17,7 @@
 where
 
 import KMonad.Prelude hiding (try)
-import KMonad.Args.Parser (itokens, keywordButtons, noKeywordButtons, otokens, symbol, numP)
+import KMonad.Args.Parser (itokens, keywordButtons, noKeywordButtons, otokens, symbol, numP, implArndButtons)
 import KMonad.Args.TH (gitHash)
 import KMonad.Args.Types (DefSetting(..))
 import KMonad.Util
@@ -44,8 +44,8 @@
     -- All 'KDefCfg' options of a 'KExpr'
   , _cmdAllow  :: DefSetting       -- ^ Allow execution of arbitrary shell-commands?
   , _fallThrgh :: DefSetting       -- ^ Re-emit unhandled events?
-  , _initStr   :: Maybe DefSetting -- ^ TODO: What does this do?
   , _cmpSeq    :: Maybe DefSetting -- ^ Key to use for compose-key sequences
+  , _implArnd  :: Maybe DefSetting -- ^ How to handle implicit `around`s
   , _oToken    :: Maybe DefSetting -- ^ How to emit the output
   , _iToken    :: Maybe DefSetting -- ^ How to capture the input
   }
@@ -83,8 +83,8 @@
       <*> startDelayP
       <*> cmdAllowP
       <*> fallThrghP
-      <*> initStrP
       <*> cmpSeqP
+      <*> implArndP
       <*> oTokenP
       <*> iTokenP
 
@@ -131,15 +131,6 @@
   <> help "Whether to simply re-emit unhandled events"
   )
 
--- | TODO what does this do?
-initStrP :: Parser (Maybe DefSetting)
-initStrP = optional $ SInitStr <$> strOption
-  (  long "init"
-  <> short 't'
-  <> metavar "STRING"
-  <> help "TODO"
-  )
-
 -- | Key to use for compose-key sequences
 cmpSeqP :: Parser (Maybe DefSetting)
 cmpSeqP = optional $ SCmpSeq <$> option
@@ -148,6 +139,16 @@
   <> short 's'
   <> metavar "BUTTON"
   <> help "Which key to use to emit compose-key sequences"
+  )
+
+-- | How to handle implicit `around`s
+implArndP :: Parser (Maybe DefSetting)
+implArndP = optional $ SImplArnd <$> option
+  (maybeReader $ \x -> implArndButtons ^? each . filtered ((x ==) . unpack . view _1) . _2)
+  (  long "implicit-around"
+  <> long "ia"
+  <> metavar "AROUND"
+  <> help "How to translate implicit arounds (`A`, `S-a`)"
   )
 
 -- | Where to emit the output
diff --git a/src/KMonad/Args/Joiner.hs b/src/KMonad/Args/Joiner.hs
--- a/src/KMonad/Args/Joiner.hs
+++ b/src/KMonad/Args/Joiner.hs
@@ -50,7 +50,7 @@
 
 import Control.Monad.Except
 
-import RIO.List (headMaybe, intersperse, uncons)
+import RIO.List (headMaybe, intersperse, uncons, sort, group)
 import RIO.Partial (fromJust)
 import qualified KMonad.Util.LayerStack  as L
 import qualified RIO.HashMap      as M
@@ -65,11 +65,16 @@
   | MissingBlock     Text
   | DuplicateAlias   Text
   | DuplicateLayer   Text
+  | DuplicateSource  (Maybe Text)
+  | DuplicateKeyInSource (Maybe Text) [Keycode]
   | MissingAlias     Text
   | MissingLayer     Text
+  | MissingSource    (Maybe Text)
   | MissingSetting   Text
   | DuplicateSetting Text
+  | DuplicateLayerSetting Text Text
   | InvalidOS        Text
+  | ImplArndDisabled
   | NestedTrans
   | InvalidComposeKey
   | LengthMismatch   Text Int Int
@@ -80,11 +85,22 @@
     MissingBlock      t   -> "Missing at least 1 block of type: "    <> T.unpack t
     DuplicateAlias    t   -> "Multiple aliases of the same name: "   <> T.unpack t
     DuplicateLayer    t   -> "Multiple layers of the same name: "    <> T.unpack t
+    DuplicateSource   t   -> case t of
+      Just t  -> "Multiple sources of the same name: " <> T.unpack t
+      Nothing -> "Multiple default sources"
+    DuplicateKeyInSource   t ks   -> case t of
+      Just t  -> "Keycodes appear multiple times in source `" <> T.unpack t <> "`:" <> ((' ' :) . show =<< ks)
+      Nothing -> "Keycodes appear multiple times in default source: " <> ((' ' :) . show =<< ks)
     MissingAlias      t   -> "Reference to non-existent alias: "     <> T.unpack t
     MissingLayer      t   -> "Reference to non-existent layer: "     <> T.unpack t
+    MissingSource     t   -> case t of
+      Just t  -> "Reference to non-existent source: " <> T.unpack t
+      Nothing -> "Reference to non-existent default source"
     MissingSetting    t   -> "Missing setting in 'defcfg': "         <> T.unpack t
     DuplicateSetting  t   -> "Duplicate setting in 'defcfg': "       <> T.unpack t
+    DuplicateLayerSetting t s -> "Duplicate setting in 'deflayer '"  <> T.unpack t <> "': " <> T.unpack s
     InvalidOS         t   -> "Not available under this OS: "         <> T.unpack t
+    ImplArndDisabled      -> "Implicit around via `A` or `S-a` are disabled in your config"
     NestedTrans           -> "Encountered 'Transparent' ouside of top-level layer"
     InvalidComposeKey     -> "Encountered invalid button as Compose key"
     LengthMismatch t l s  -> mconcat
@@ -98,6 +114,7 @@
 -- | Joining Config
 data JCfg = JCfg
   { _cmpKey  :: Button  -- ^ How to prefix compose-sequences
+  , _implArnd :: ImplArnd -- ^ How to handle implicit `around`s
   , _kes     :: [KExpr] -- ^ The source expresions we operate on
   }
 makeLenses ''JCfg
@@ -105,6 +122,7 @@
 defJCfg :: [KExpr] ->JCfg
 defJCfg = JCfg
   (emitB KeyRightAlt)
+  IAAround
 
 -- | Monad in which we join, just Except over Reader
 newtype J a = J { unJ :: ExceptT JoinError (Reader JCfg) a }
@@ -168,8 +186,8 @@
   -- Extract the other blocks and join them into a keymap
   let als = extract _KDefAlias es
   let lys = extract _KDefLayer es
-  src      <- oneBlock "defsrc" _KDefSrc
-  (km, fl) <- joinKeymap src als lys
+  let srcs = extract _KDefSrc es
+  (km, fl) <- joinKeymap srcs als lys
 
   pure $ CfgToken
     { _snk   = o
@@ -189,12 +207,14 @@
 -- | Return a JCfg with all settings from defcfg applied to the env's JCfg
 getOverride :: J JCfg
 getOverride = do
+  -- FIXME: duplicates don't throw errors
   env <- ask
   cfg <- oneBlock "defcfg" _KDefCfg
   let getB = joinButton [] M.empty
   let go e v = case v of
         SCmpSeq b  -> getB b >>= maybe (throwError InvalidComposeKey)
                                        (\b' -> pure $ set cmpKey b' e)
+        SImplArnd ia -> pure $ set implArnd ia e
         _ -> pure e
   foldM go env cfg
 
@@ -320,6 +340,12 @@
 unnest :: J (Maybe Button) -> J Button
 unnest = (maybe (throwError NestedTrans) pure =<<)
 
+fromImplArnd :: DefButton -> DefButton -> ImplArnd -> J DefButton
+fromImplArnd _ _ IADisabled        = throwError ImplArndDisabled
+fromImplArnd o i IAAround          = pure $ KAround o i
+fromImplArnd o i IAAroundOnly      = pure $ KAroundOnly o i
+fromImplArnd o i IAAroundWhenAlone = pure $ KAroundWhenAlone o i
+
 -- | Turn a button token into an actual KMonad `Button` value
 joinButton :: LNames -> Aliases -> DefButton -> J (Maybe Button)
 joinButton ns als =
@@ -377,12 +403,16 @@
     KTapHoldNextRelease ms t h mtb
       -> jst $ tapHoldNextRelease (fi ms) <$> go t <*> go h <*> traverse go mtb
     KTapNextPress t h  -> jst $ tapNextPress       <$> go t <*> go h
+    KAroundOnly o i    -> jst $ aroundOnly         <$> go o <*> go i
+    KAroundWhenAlone o i -> jst $ aroundWhenAlone  <$> go o <*> go i
+    KAroundImplicit o i  -> joinButton ns als =<< fromImplArnd o i =<< view implArnd
     KAroundNext b      -> jst $ aroundNext         <$> go b
     KAroundNextSingle b -> jst $ aroundNextSingle <$> go b
     KAroundNextTimeout ms b t -> jst $ aroundNextTimeout (fi ms) <$> go b <*> go t
     KPause ms          -> jst . pure $ onPress (pause ms)
     KMultiTap bs d     -> jst $ multiTap <$> go d <*> mapM f bs
       where f (ms, b) = (fi ms,) <$> go b
+    KStepped bs        -> jst $ steppedButton <$> mapM go bs
     KStickyKey s d     -> jst $ stickyKey (fi s) <$> go d
 
     -- Non-action buttons
@@ -391,17 +421,37 @@
 
 
 --------------------------------------------------------------------------------
+-- $src
+
+type Sources = M.HashMap (Maybe Text) DefSrc
+
+-- | Build up a hashmap of text to source mappings.
+joinSources :: [DefSrc] -> J Sources
+joinSources = foldM joiner mempty
+  where
+   joiner :: Sources -> DefSrc -> J Sources
+   joiner sources src@DefSrc{ _srcName = n, _keycodes = ks }
+     | n `M.member` sources = throwError $ DuplicateSource n
+     | not (null dups)      = throwError $ DuplicateKeyInSource n dups
+     | otherwise            = pure $ M.insert n src sources
+    where
+     dups :: [Keycode]
+     dups = concatMap (take 1) . filter ((> 1) . length) . group . sort $ ks
+
+--------------------------------------------------------------------------------
 -- $kmap
 
 -- | Join the defsrc, defalias, and deflayer layers into a Keymap of buttons and
 -- the name signifying the initial layer to load.
-joinKeymap :: DefSrc -> [DefAlias] -> [DefLayer] -> J (LMap Button, LayerTag)
-joinKeymap _   _   []  = throwError $ MissingBlock "deflayer"
-joinKeymap src als lys = do
+joinKeymap :: [DefSrc] -> [DefAlias] -> [DefLayer] -> J (LMap Button, LayerTag)
+joinKeymap []   _   _   = throwError $ MissingBlock "defsrc"
+joinKeymap _    _   []  = throwError $ MissingBlock "deflayer"
+joinKeymap srcs als lys = do
   let f acc x = if x `elem` acc then throwError $ DuplicateLayer x else pure (x:acc)
-  nms  <- foldM f [] $ map _layerName lys   -- Extract all names
-  als' <- joinAliases nms als               -- Join aliases into 1 hashmap
-  lys' <- mapM (joinLayer als' nms src) lys -- Join all layers
+  nms   <- foldM f [] $ map _layerName lys     -- Extract all names
+  als'  <- joinAliases nms als                 -- Join aliases into 1 hashmap
+  srcs' <- joinSources  srcs                   -- Join all sources into 1 hashmap
+  lys'  <- mapM (joinLayer als' nms srcs') lys -- Join all layers
   -- Return the layerstack and the name of the first layer
   pure (L.mkLayerStack lys', _layerName . fromJust . headMaybe $ lys)
 
@@ -409,11 +459,17 @@
 joinLayer ::
      Aliases                       -- ^ Mapping of names to buttons
   -> LNames                        -- ^ List of valid layer names
-  -> DefSrc                        -- ^ Layout of the source layer
+  -> Sources                       -- ^ Mapping of names to source layer
   -> DefLayer                      -- ^ The layer token to join
   -> J (Text, [(Keycode, Button)]) -- ^ The resulting tuple
-joinLayer als ns src DefLayer{_layerName=n, _buttons=bs} = do
+joinLayer als ns srcs l@(DefLayer n settings) = do
+  let bs = settings ^.. each . _LButton
+  assocSrc <- getAssocSrc l
+  implAround <- getImplAround l
 
+  src <- case M.lookup assocSrc srcs of
+    Just src -> pure $ src^.keycodes
+    Nothing  -> throwError $ MissingSource assocSrc
   -- Ensure length-match between src and buttons
   when (length bs /= length src) $
     throwError $ LengthMismatch n (length bs) (length src)
@@ -422,8 +478,20 @@
   let f acc (kc, b) = joinButton ns als b >>= \case
         Nothing -> pure acc
         Just b' -> pure $ (kc, b') : acc
-  (n,) <$> foldM f [] (zip src bs)
+  maybe id (local . set implArnd) implAround $
+    (n,) <$> foldM f [] (zip src bs)
 
+getAssocSrc :: DefLayer -> J (Maybe Text)
+getAssocSrc (DefLayer n settings) = case onlyOne (settings ^.. each . _LSrcName) of
+  Right x        -> pure $ Just x
+  Left None      -> pure Nothing
+  Left Duplicate -> throwError $ DuplicateLayerSetting n "source"
+
+getImplAround :: DefLayer -> J (Maybe ImplArnd)
+getImplAround (DefLayer n settings) = case onlyOne (settings ^.. each . _LImplArnd) of
+  Right x        -> pure $ Just x
+  Left None      -> pure Nothing
+  Left Duplicate -> throwError $ DuplicateLayerSetting n "implicit-around"
 
 --------------------------------------------------------------------------------
 -- $test
diff --git a/src/KMonad/Args/Parser.hs b/src/KMonad/Args/Parser.hs
--- a/src/KMonad/Args/Parser.hs
+++ b/src/KMonad/Args/Parser.hs
@@ -27,6 +27,8 @@
   -- * Parsers for Tokens and Buttons
   , otokens
   , itokens
+  , buttonP
+  , implArndButtons
   , keywordButtons
   , noKeywordButtons
   )
@@ -182,7 +184,7 @@
 
 -- | Different ways to refer to shifted versions of keycodes
 shiftedNames :: [(Text, DefButton)]
-shiftedNames = let f = second $ \kc -> KAround (KEmit KeyLeftShift) (KEmit kc) in
+shiftedNames = let f = second $ \kc -> KAroundImplicit (KEmit KeyLeftShift) (KEmit kc) in
                  map f $ cps <> num <> oth <> lng
   where
     cps = zip (map T.singleton ['A'..'Z'])
@@ -200,7 +202,7 @@
 buttonNames :: [(Text, DefButton)]
 buttonNames = shiftedNames <> escp <> util
   where
-    emitS c = KAround (KEmit KeyLeftShift) (KEmit c)
+    emitS c = KAroundImplicit (KEmit KeyLeftShift) (KEmit c)
     -- Escaped versions for reserved characters
     escp = [ ("\\(", emitS Key9), ("\\)", emitS Key0)
            , ("\\_", emitS KeyMinus), ("\\\\", KEmit KeyBackslash)]
@@ -212,7 +214,7 @@
 
 -- | Parse "X-b" style modded-sequences
 moddedP :: Parser DefButton
-moddedP = KAround <$> prfx <*> buttonP
+moddedP = KAroundImplicit <$> prfx <*> buttonP
   where mods = [ ("S-", KeyLeftShift), ("C-", KeyLeftCtrl)
                , ("A-", KeyLeftAlt),   ("M-", KeyLeftMeta)
                , ("RS-", KeyRightShift), ("RC-", KeyRightCtrl)
@@ -240,6 +242,7 @@
 
   -- If matching, parse a button-sequence from the stored text
   case runParser (some buttonP) "" s of
+    -- Parse error never reaches the user. They simply get a message about an unexpected character.
     Left  _ -> fail "Could not parse compose sequence"
     Right b -> pure b
 
@@ -262,10 +265,11 @@
 -- @(keyword, how to parse the token)@
 keywordButtons :: [(Text, Parser DefButton)]
 keywordButtons =
-  [ ("around"         , KAround      <$> buttonP     <*> buttonP)
+  [ ("around-implicit", KAroundImplicit <$> buttonP <*> buttonP)
   , ("press-only"     , KPressOnly   <$> keycodeP)
   , ("release-only"   , KReleaseOnly <$> keycodeP)
   , ("multi-tap"      , KMultiTap    <$> timed       <*> buttonP)
+  , ("stepped"        , KStepped     <$> some buttonP)
   , ("tap-hold"       , KTapHold     <$> lexeme numP <*> buttonP <*> buttonP)
   , ("tap-hold-next"
     , KTapHoldNext <$> lexeme numP <*> buttonP <*> buttonP
@@ -287,6 +291,7 @@
   , ("layer-delay"    , KLayerDelay  <$> lexeme numP <*> lexeme word)
   , ("layer-next"     , KLayerNext   <$> lexeme word)
   , ("around-next"    , KAroundNext  <$> buttonP)
+  , ("around-next-single", KAroundNextSingle <$> buttonP)
   , ("before-after-next", KBeforeAfterNext <$> buttonP <*> buttonP)
   , ("around-next-timeout", KAroundNextTimeout <$> lexeme numP <*> buttonP <*> buttonP)
   , ("tap-macro"
@@ -294,13 +299,21 @@
   , ("tap-macro-release"
     , KTapMacroRelease <$> lexeme (some buttonP) <*> optional (keywordP "delay" numP))
   , ("cmd-button"     , KCommand     <$> lexeme textP <*> optional (lexeme textP))
-  , ("pause"          , KPause . fromIntegral <$> numP)
+  , ("pause"          , KPause . fromIntegral <$> lexeme numP)
   , ("sticky-key"     , KStickyKey   <$> lexeme numP <*> buttonP)
   ]
+  ++ map (\(nm,_,btn) -> (nm, btn <$> buttonP <*> buttonP)) implArndButtons
  where
   timed :: Parser [(Int, DefButton)]
   timed = many ((,) <$> lexeme numP <*> lexeme buttonP)
 
+implArndButtons :: [(Text, ImplArnd, DefButton -> DefButton -> DefButton)]
+implArndButtons = sortBy (flip compare `on` (T.length . view _1)) -- Prevents early return due to `around`
+  [ ("around"           , IAAround         , KAround)
+  , ("around-only"      , IAAroundOnly     , KAroundOnly)
+  , ("around-when-alone", IAAroundWhenAlone, KAroundWhenAlone)
+  ]
+
 -- | Parsers for buttons that do __not__ have a keyword at the start
 noKeywordButtons :: [Parser DefButton]
 noKeywordButtons =
@@ -324,9 +337,9 @@
 -- | Input tokens to parse; the format is @(keyword, how to parse the token)@
 itokens :: [(Text, Parser IToken)]
 itokens =
-  [ ("device-file"   , KDeviceSource <$> (T.unpack <$> textP))
+  [ ("device-file"   , KDeviceSource <$> (T.unpack <$> lexeme textP))
   , ("low-level-hook", pure KLowLevelHookSource)
-  , ("iokit-name"    , KIOKitSource <$> optional textP)]
+  , ("iokit-name"    , KIOKitSource <$> optional (lexeme textP))]
 
 -- | Parse an output token
 otokenP :: Parser OToken
@@ -335,10 +348,16 @@
 -- | Output tokens to parse; the format is @(keyword, how to parse the token)@
 otokens :: [(Text, Parser OToken)]
 otokens =
-  [ ("uinput-sink"    , KUinputSink <$> lexeme textP <*> optional textP)
-  , ("send-event-sink", KSendEventSink <$> optional ((,) <$> lexeme numP <*> numP))
+  [ ("uinput-sink"    , KUinputSink <$> lexeme textP <*> optional (lexeme textP))
+  , ("send-event-sink", KSendEventSink <$> optional ((,) <$> lexeme numP <*> lexeme numP))
   , ("kext"           , pure KKextSink)]
 
+-- | Parse an impl arnd token
+implArndP :: Parser ImplArnd
+implArndP = lexeme . choice $
+  try (IADisabled <$ symbol "disabled")
+  : map (\(s, v, _) -> try $ v <$ symbol s) implArndButtons
+
 -- | Parse the DefCfg token
 defcfgP :: Parser DefSettings
 defcfgP = some (lexeme settingP)
@@ -350,10 +369,10 @@
     [ SIToken      <$> f "input"         itokenP
     , SOToken      <$> f "output"        otokenP
     , SCmpSeq      <$> f "cmp-seq"       buttonP
-    , SInitStr     <$> f "init"          textP
     , SFallThrough <$> f "fallthrough"   bool
     , SAllowCmd    <$> f "allow-cmd"     bool
     , SCmpSeqDelay <$> f "cmp-seq-delay" numP
+    , SImplArnd    <$> f "implicit-around" implArndP
     ])
 
 --------------------------------------------------------------------------------
@@ -367,10 +386,21 @@
 -- $defsrc
 
 defsrcP :: Parser DefSrc
-defsrcP = many $ lexeme keycodeP
-
+defsrcP =
+  DefSrc
+    <$> optional (keywordP "name" word)
+    <*> many (lexeme keycodeP)
 
 --------------------------------------------------------------------------------
 -- $deflayer
+
 deflayerP :: Parser DefLayer
-deflayerP = DefLayer <$> lexeme word <*> many (lexeme buttonP)
+deflayerP = DefLayer <$> lexeme word <*> many (lexeme layerSettingP)
+
+layerSettingP :: Parser DefLayerSetting
+layerSettingP =
+  lexeme . choice . map try $
+    [ LSrcName <$> keywordP "source" word
+    , LImplArnd <$> keywordP "implicit-around" implArndP
+    , LButton <$> buttonP
+    ]
diff --git a/src/KMonad/Args/TH.hs b/src/KMonad/Args/TH.hs
--- a/src/KMonad/Args/TH.hs
+++ b/src/KMonad/Args/TH.hs
@@ -16,15 +16,18 @@
 
 import Language.Haskell.TH (Exp, Q)
 import Language.Haskell.TH.Syntax (runIO)
+import UnliftIO.Directory (findExecutable)
 import UnliftIO.Process (readProcessWithExitCode)
 
-
--- | Get the git hash of the current revision at compile time
+-- | Get the git hash of the current commit at compile time.
 gitHash :: Q Exp
 gitHash = do
   str <- runIO do
-    (exitCode, hash, _) <- readProcessWithExitCode "git" ["rev-parse", "HEAD"] ""
-    pure case exitCode of
-      ExitSuccess -> takeWhile (/= '\n') hash
-      _           -> ""
+    findExecutable "git" >>= \case
+      Nothing  -> pure ""                         -- Git not present
+      Just git -> do
+        (exitCode, hash, _) <- readProcessWithExitCode git ["rev-parse", "HEAD"] ""
+        pure case exitCode of
+          ExitSuccess -> takeWhile (/= '\n') hash
+          _           -> ""                       -- Not in a git repo
   [| fromString str |]
diff --git a/src/KMonad/Args/Types.hs b/src/KMonad/Args/Types.hs
--- a/src/KMonad/Args/Types.hs
+++ b/src/KMonad/Args/Types.hs
@@ -16,13 +16,15 @@
 
     -- * $but
   , DefButton(..)
+  , ImplArnd(..)
 
     -- * $tls
   , DefSetting(..)
   , DefSettings
   , DefAlias
+  , DefLayerSetting(..)
   , DefLayer(..)
-  , DefSrc
+  , DefSrc(..)
   , KExpr(..)
 
     -- * $defio
@@ -32,6 +34,8 @@
     -- * $lenses
   , AsKExpr(..)
   , AsDefSetting(..)
+  , HasDefSrc(..)
+  , AsDefLayerSetting(..)
 ) where
 
 
@@ -71,7 +75,11 @@
   | KAroundNext DefButton                  -- ^ Surround a future button
   | KAroundNextSingle DefButton            -- ^ Surround a future button
   | KMultiTap [(Int, DefButton)] DefButton -- ^ Do things depending on tap-count
+  | KStepped [DefButton]                   -- ^ Do different things, one-by-one
   | KAround DefButton DefButton            -- ^ Wrap 1 button around another
+  | KAroundOnly DefButton DefButton        -- ^ Wrap 1 button only around another
+  | KAroundWhenAlone DefButton DefButton   -- ^ Wrap 1 button around another when it's "alone"
+  | KAroundImplicit DefButton DefButton    -- ^ Wrap 1 button around another
   | KAroundNextTimeout Int DefButton DefButton
   | KTapMacro [DefButton] (Maybe Int)
     -- ^ Sequence of buttons to tap, possible delay between each press
@@ -87,13 +95,22 @@
   | KBeforeAfterNext DefButton DefButton   -- ^ Surround a future button in a before and after tap
   | KTrans                                 -- ^ Transparent button that does nothing
   | KBlock                                 -- ^ Button that catches event
-  deriving Show
+  deriving (Show, Eq, Typeable, Data)
 
+instance Plated DefButton
 
+-- | Possible values for implicit around
+data ImplArnd
+  = IADisabled
+  | IAAround
+  | IAAroundOnly
+  | IAAroundWhenAlone
+  deriving (Show, Eq)
+
 --------------------------------------------------------------------------------
 -- $cfg
 --
--- The Cfg token that can be extracted from a config-text without ever enterring
+-- The Cfg token that can be extracted from a config-text without ever entering
 -- IO. This will then directly be translated to a DaemonCfg
 --
 
@@ -116,18 +133,31 @@
 -- A collection of all the different top-level statements possible in a config
 -- file.
 
--- | A list of keycodes describing the ordering of all the other layers
-type DefSrc = [Keycode]
+-- | A list of keycodes describing the ordering used by all other layers
+-- | which is associated with a name.
+data DefSrc = DefSrc
+  { _srcName  :: Maybe Text -- ^ A unique name used to refer to this layer.
+  , _keycodes :: [Keycode]  -- ^ Layer settings containing also the buttons.
+  }
+  deriving (Show, Eq)
+makeClassy ''DefSrc
 
 -- | A mapping from names to button tokens
 type DefAlias = [(Text, DefButton)]
 
+data DefLayerSetting
+  = LSrcName Text
+  | LImplArnd ImplArnd
+  | LButton DefButton
+  deriving (Show, Eq)
+makeClassyPrisms ''DefLayerSetting
+
 -- | A layer of buttons
 data DefLayer = DefLayer
-  { _layerName :: Text        -- ^ A unique name used to refer to this layer
-  , _buttons   :: [DefButton] -- ^ A list of button tokens
+  { _layerName :: Text
+  , _layerSettings :: [DefLayerSetting]
   }
-  deriving Show
+  deriving (Show, Eq)
 
 
 --------------------------------------------------------------------------------
@@ -140,25 +170,25 @@
   = KDeviceSource FilePath
   | KLowLevelHookSource
   | KIOKitSource (Maybe Text)
-  deriving Show
+  deriving (Show)
 
 -- | All different output-tokens KMonad can take
 data OToken
   = KUinputSink Text (Maybe Text)
   | KSendEventSink (Maybe (Int, Int))
   | KKextSink
-  deriving Show
+  deriving (Show)
 
 -- | All possible single settings
 data DefSetting
   = SIToken      IToken
   | SOToken      OToken
   | SCmpSeq      DefButton
-  | SInitStr     Text
   | SFallThrough Bool
   | SAllowCmd    Bool
   | SCmpSeqDelay Int
-  deriving Show
+  | SImplArnd    ImplArnd
+  deriving (Show)
 makeClassyPrisms ''DefSetting
 
 -- | 'Eq' instance for a 'DefSetting'. Because every one of these options may be
@@ -168,9 +198,9 @@
   SIToken{}      == SIToken{}      = True
   SOToken{}      == SOToken{}      = True
   SCmpSeq{}      == SCmpSeq{}      = True
-  SInitStr{}     == SInitStr{}     = True
   SFallThrough{} == SFallThrough{} = True
   SAllowCmd{}    == SAllowCmd{}    = True
+  SImplArnd{}    == SImplArnd{}    = True
   _              == _              = False
 
 -- | A list of different 'DefSetting' values
@@ -185,7 +215,7 @@
   | KDefSrc   DefSrc
   | KDefLayer DefLayer
   | KDefAlias DefAlias
-  deriving Show
+  deriving (Show, Eq)
 makeClassyPrisms ''KExpr
 
 
diff --git a/src/KMonad/Keyboard/ComposeSeq.hs b/src/KMonad/Keyboard/ComposeSeq.hs
--- a/src/KMonad/Keyboard/ComposeSeq.hs
+++ b/src/KMonad/Keyboard/ComposeSeq.hs
@@ -20,6 +20,7 @@
 where
 
 import KMonad.Prelude
+import RIO.Text.Partial (replace)
 
 --------------------------------------------------------------------------------
 
@@ -30,16 +31,21 @@
 -- 3. A descriptive-name
 --
 ssComposed :: [(Text, Char, Text)]
-ssComposed =
+ssComposed = composeSeqs & (each . _1) %~ sanitize
+ where
+  sanitize :: Text -> Text
+  sanitize = replace "(" "\\("
+           . replace ")" "\\)"
+           . replace "_" "\\_"
+
+  composeSeqs :: [(Text, Char, Text)]
+  composeSeqs =
     [ ("' '"      , '´'     , "acute")
     , ( "^ -"     , '¯'     , "macron" )
     , ( "spc ("   , '˘'     , "breve" )
     , ( "\" \""   , '¨'     , "diaeresis" )
     , ("spc <"    , 'ˇ'     , "caron")
-    , ("` spc"    , '`'     , "grave")
     , (", spc"    , '¸'     , "cedilla")
-    , ("spc spc"  , ' '     , "nobreakspace")
-    , ("spc ."    , ' '     , "U2008")
     , ("o c"      , '©'     , "copyright")
     , ("o r"      , '®'     , "registered")
     , (". >"      , '›'     , "U203a")
@@ -728,11 +734,13 @@
     , ("_ '"      , '⍘'     , "U2358")
     , ("0 ~"      , '⍬'     , "U236c")
     , ("| ~"      , '⍭'     , "U236d")
-    , ("c /"      , '¢'     , "cent" )
     , ("< _"      , '≤'     , "U2264")
     , ("> _"      , '≥'     , "U2265")
 
     -- Sequences that should exist but do not work
+    --, ("` spc"    , '`'     , "grave") -- recursive and incorrect. It's <dead_grave> <space> and <dead_grave> is not mapped in en_US
     --, ("^ spc", '^', "asciicircum") -- This overlaps with the normal 'shifted-6' macro for
     -- , ("' j", 'j́', "jacute")
+    --, ("spc spc"  , ' '     , "nobreakspace") -- cannot be parsed since it is matched by `lexeme` of previous token
+    --, ("spc ."    , ' '     , "U2008") -- see above
     ]
diff --git a/src/KMonad/Keyboard/IO/Linux/Types.hs b/src/KMonad/Keyboard/IO/Linux/Types.hs
--- a/src/KMonad/Keyboard/IO/Linux/Types.hs
+++ b/src/KMonad/Keyboard/IO/Linux/Types.hs
@@ -103,16 +103,17 @@
 --           see: https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
 --         for sync: always 0
 
--- | Translate a 'LinuxKeyEvent' to a kmonad 'KeyEvent'
+-- | Translate a 'LinuxKeyEvent' to a KMonad 'KeyEvent'
 fromLinuxKeyEvent :: LinuxKeyEvent -> Maybe KeyEvent
 fromLinuxKeyEvent (LinuxKeyEvent (_, _, typ, c, val))
+  | c > 255 = Nothing
   | typ == 1 && val == 0 = Just . mkRelease $ kc
   | typ == 1 && val == 1 = Just . mkPress   $ kc
   | otherwise = Nothing
   where
     kc = toEnum . fromIntegral $ c -- This is theoretically partial, but practically not
 
--- | Translate kmonad 'KeyEvent' along with a 'SystemTime' to 'LinuxKeyEvent's
+-- | Translate KMonad 'KeyEvent' along with a 'SystemTime' to 'LinuxKeyEvent's
 -- for writing.
 toLinuxKeyEvent :: KeyEvent -> SystemTime -> LinuxKeyEvent
 toLinuxKeyEvent e (MkSystemTime s ns)
diff --git a/src/KMonad/Keyboard/IO/Mac/Types.hs b/src/KMonad/Keyboard/IO/Mac/Types.hs
--- a/src/KMonad/Keyboard/IO/Mac/Types.hs
+++ b/src/KMonad/Keyboard/IO/Mac/Types.hs
@@ -214,7 +214,7 @@
   , ((0x7,0x61), KeyKp9)
   , ((0x7,0x62), KeyKp0)
   , ((0x7,0x63), KeyKpDot)
-  -- , ((0x7,0x64), KeyNonUSBackslash)
+  , ((0x7,0x64), Key102nd)
   -- , ((0x7,0x65), KeyApplication)
   , ((0x7,0x66), KeyPower)
   , ((0x7,0x67), KeyKpEqual)
diff --git a/src/KMonad/Keyboard/Keycode.hs b/src/KMonad/Keyboard/Keycode.hs
--- a/src/KMonad/Keyboard/Keycode.hs
+++ b/src/KMonad/Keyboard/Keycode.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE DeriveAnyClass, CPP #-}
+{-# LANGUAGE DeriveAnyClass #-}
 {-|
 Module      : KMonad.Keyboard.Keycode
 Description : Description of all possible keycodes.
@@ -37,7 +37,7 @@
 -- headers:
 -- https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h.
 --
--- Anywhere there are missing regions in the linux headers, we've defined
+-- Anywhere there are missing regions in the Linux headers, we've defined
 -- @MissingXX@ values for the ADT.
 --
 -- Calling 'RIO.Partial.toEnum' on a Linux keycode value should produce the
@@ -301,14 +301,13 @@
   | Missing253
   | Missing254
   | Missing255
-#ifdef darwin_HOST_OS
+  -- Darwin
   | KeyFn
   | KeyLaunchpad
   | KeyMissionCtrl
   | KeySpotlight
   | KeyDictation
-#endif
-  deriving (Eq, Show, Bounded, Enum, Ord, Generic, Hashable)
+  deriving (Eq, Show, Bounded, Enum, Ord, Generic, Hashable, Typeable, Data)
 
 
 instance Display Keycode where
@@ -353,65 +352,69 @@
 -- | A collection of useful aliases to refer to keycode names
 aliases :: Q.MultiMap Keycode Text
 aliases = Q.mkMultiMap
-  [ (KeyEnter,          ["ret", "return", "ent"])
-  , (KeyMinus,          ["min", "-"])
-  , (KeyEqual,          ["eql", "="])
-  , (KeySleep,          ["zzz"])
-  , (KeySpace,          ["spc"])
-  , (KeyPageUp,         ["pgup"])
-  , (KeyPageDown,       ["pgdn"])
-  , (KeyInsert,         ["ins"])
-  , (KeyDelete,         ["del"])
-  , (KeyVolumeUp,       ["volu"])
-  , (KeyVolumeDown,     ["voldwn", "vold"])
-  , (KeyBrightnessUp,   ["brup", "bru"])
-  , (KeyBrightnessDown, ["brdown", "brdwn", "brdn"])
-  , (KeyLeftAlt,        ["lalt", "alt"])
-  , (KeyRightAlt,       ["ralt"])
-  , (KeyCompose,        ["comp", "cmps", "cmp"])
-  , (KeyLeftShift,      ["lshift", "lshft", "lsft", "shft", "sft"])
-  , (KeyRightShift,     ["rshift", "rshft", "rsft"])
-  , (KeyLeftCtrl,       ["lctrl", "lctl", "ctl"])
-  , (KeyRightCtrl,      ["rctrl", "rctl"])
-  , (KeyLeftMeta,       ["lmeta", "lmet", "met"])
-  , (KeyRightMeta,      ["rmeta", "rmet"])
-  , (KeyBackspace,      ["bks", "bspc"])
-  , (KeyCapsLock,       ["caps"])
-  , (Key102nd,          ["102d", "lsgt", "nubs"])
-  , (KeyForward,        ["fwd"])
-  , (KeyScrollLock,     ["scrlck", "slck"])
-  , (KeyScrollUp,       ["scrup", "sup"])
-  , (KeyScrollDown,     ["scrdn", "sdwn", "sdn"])
-  , (KeyPrint,          ["prnt"])
-  , (KeyWakeUp,         ["wkup"])
-  , (KeyLeft,           ["lft"])
-  , (KeyRight,          ["rght"])
-  , (KeyLeftBrace,      ["lbrc", "["])
-  , (KeyRightBrace,     ["rbrc", "]"])
-  , (KeySemicolon,      ["scln", ";"])
-  , (KeyApostrophe,     ["apos", "'", "apo"])
-  , (KeyGrave,          ["grv", "`"])
-  , (KeyBackslash,      ["bksl", "\\"]) -- NOTE: "\\" here is a 1char string, the first \ is consumed by Haskell as an escape character
-  , (KeyComma,          ["comm", ","])
-  , (KeyDot,            ["."])
-  , (KeySlash,          ["/"])
-  , (KeyNumLock,        ["nlck"])
-  , (KeyKpSlash,        ["kp/"])
-  , (KeyKpEnter,        ["kprt"])
-  , (KeyKpPlus,         ["kp+"])
-  , (KeyKpAsterisk,     ["kp*"])
-  , (KeyKpMinus,        ["kp-"])
-  , (KeyKpDot,          ["kp."])
-  , (KeySysRq,          ["ssrq", "sys"])
-  , (KeyKbdIllumDown,   ["bldn"])
-  , (KeyKbdIllumUp,     ["blup"])
-  , (KeyNextSong,       ["next"])
-  , (KeyPlayPause,      ["pp"])
-  , (KeyPreviousSong,   ["prev"])
-#ifdef darwin_HOST_OS
-  , (KeyLaunchpad,      ["lp"])
-  , (KeyMissionCtrl,    ["mctl"])
-  , (KeySpotlight,      ["spot"])
-  , (KeyDictation,      ["dict"])
-#endif
+  [ (KeyEnter,            ["ret", "return", "ent"])
+  , (KeyMinus,            ["min", "-"])
+  , (KeyEqual,            ["eql", "="])
+  , (KeySleep,            ["zzz"])
+  , (KeySpace,            ["spc"])
+  , (KeyPageUp,           ["pgup"])
+  , (KeyPageDown,         ["pgdn"])
+  , (KeyInsert,           ["ins"])
+  , (KeyDelete,           ["del"])
+  , (KeyVolumeUp,         ["volu"])
+  , (KeyVolumeDown,       ["voldwn", "vold"])
+  , (KeyBrightnessUp,     ["brup", "bru"])
+  , (KeyBrightnessDown,   ["brdown", "brdwn", "brdn"])
+  , (KeyLeftAlt,          ["lalt", "alt"])
+  , (KeyRightAlt,         ["ralt"])
+  , (KeyCompose,          ["comp", "cmps", "cmp"])
+  , (KeyLeftShift,        ["lshift", "lshft", "lsft", "shft", "sft"])
+  , (KeyRightShift,       ["rshift", "rshft", "rsft"])
+  , (KeyLeftCtrl,         ["lctrl", "lctl", "ctl"])
+  , (KeyRightCtrl,        ["rctrl", "rctl"])
+  , (KeyLeftMeta,         ["lmeta", "lmet", "met"])
+  , (KeyRightMeta,        ["rmeta", "rmet"])
+  , (KeyBackspace,        ["bks", "bspc"])
+  , (KeyCapsLock,         ["caps"])
+  , (Key102nd,            ["102d", "lsgt", "nubs"])
+  , (KeyForward,          ["fwd"])
+  , (KeyScrollLock,       ["scrlck", "slck"])
+  , (KeyScrollUp,         ["scrup", "sup"])
+  , (KeyScrollDown,       ["scrdn", "sdwn", "sdn"])
+  , (KeyPrint,            ["prnt"])
+  , (KeyWakeUp,           ["wkup"])
+  , (KeyLeft,             ["lft"])
+  , (KeyRight,            ["rght"])
+  , (KeyLeftBrace,        ["lbrc", "["])
+  , (KeyRightBrace,       ["rbrc", "]"])
+  , (KeySemicolon,        ["scln", ";"])
+  , (KeyApostrophe,       ["apos", "'", "apo"])
+  , (KeyGrave,            ["grv", "`"])
+  , (KeyBackslash,        ["bksl", "\\"]) -- NOTE: "\\" here is a 1char string, the first \ is consumed by Haskell as an escape character
+  , (KeyComma,            ["comm", ","])
+  , (KeyDot,              ["."])
+  , (KeySlash,            ["/"])
+  , (KeyNumLock,          ["nlck"])
+  , (KeyKpSlash,          ["kp/"])
+  , (KeyKpEnter,          ["kprt"])
+  , (KeyKpPlus,           ["kp+"])
+  , (KeyKpAsterisk,       ["kp*"])
+  , (KeyKpMinus,          ["kp-"])
+  , (KeyKpDot,            ["kp."])
+  , (KeySysRq,            ["ssrq", "sys"])
+  , (KeyKbdIllumDown,     ["bldn"])
+  , (KeyKbdIllumUp,       ["blup"])
+  , (KeyNextSong,         ["next"])
+  , (KeyPlayPause,        ["pp"])
+  , (KeyPreviousSong,     ["prev"])
+  -- Darwin
+  , (KeyLaunchpad,        ["lp"])
+  , (KeyMissionCtrl,      ["mctl"])
+  , (KeySpotlight,        ["spot"])
+  , (KeyDictation,        ["dict"])
+  -- Japanese
+  , (KeyZenkakuHankaku,   ["zeh"])
+  , (KeyMuhenkan,         ["muh"])
+  , (KeyHenkan,           ["hen"])
+  , (KeyKatakanaHiragana, ["kah"])
   ]
diff --git a/src/KMonad/Model/Action.hs b/src/KMonad/Model/Action.hs
--- a/src/KMonad/Model/Action.hs
+++ b/src/KMonad/Model/Action.hs
@@ -66,7 +66,7 @@
 --------------------------------------------------------------------------------
 -- $keyfun
 
--- | Boolean isomorph signalling wether an event should be caught or not
+-- | Boolean isomorph signalling whether an event should be caught or not.
 data Catch = Catch | NoCatch deriving (Show, Eq)
 
 instance Semigroup Catch where
diff --git a/src/KMonad/Model/Button.hs b/src/KMonad/Model/Button.hs
--- a/src/KMonad/Model/Button.hs
+++ b/src/KMonad/Model/Button.hs
@@ -20,6 +20,7 @@
   , HasButton(..)
   , onPress
   , onRelease
+  , onTap
   , mkButton
   , around
   , tapOn
@@ -39,6 +40,8 @@
 
   -- * Button combinators
   -- $combinators
+  , aroundOnly
+  , aroundWhenAlone
   , aroundNext
   , aroundNextTimeout
   , aroundNextSingle
@@ -54,6 +57,7 @@
   , tapNextPress
   , tapMacro
   , tapMacroRelease
+  , steppedButton
   , stickyKey
   )
 where
@@ -64,6 +68,7 @@
 import KMonad.Keyboard
 import KMonad.Util
 
+import qualified RIO.HashSet as S
 
 --------------------------------------------------------------------------------
 -- $but
@@ -72,11 +77,15 @@
 -- 'Button' is essentially a collection of 2 different actions, 1 to perform on
 -- 'Press' and another on 'Release'.
 
--- | A 'Button' consists of two 'MonadK' actions, one to take when a press is
+-- | A 'Button' consists of three 'MonadK' actions, one to take when a press is
 -- registered from the OS, and another when a release is registered.
+-- With a third when both should happen in sequence. This will only be used
+-- by other button such as `tap-macro`.
+-- Use 'mkButton' instead of this constructor.
 data Button = Button
   { _pressAction   :: !Action -- ^ Action to take when pressed
   , _releaseAction :: !Action -- ^ Action to take when released
+  , _tapAction     :: !Action -- ^ Action to take when tapped as a sub button
   }
 makeClassy ''Button
 
@@ -87,8 +96,15 @@
 -- therefore can only rely on functionality from 'MonadK'. I.e. the actions must
 -- be pure 'MonadK'.
 mkButton :: AnyK () -> AnyK () -> Button
-mkButton a b = Button (Action a) (Action b)
+mkButton a b = mkButton' a b $ a *> b
 
+-- | Create a 'Button' out of a press, release action and tap action
+--
+-- The non standard tap action is useful when inside other buttons
+-- like `tap-macro`
+mkButton' :: AnyK () -> AnyK () -> AnyK () -> Button
+mkButton' a b c = Button (Action a) (Action b) (Action c)
+
 -- | Create a new button with only a 'Press' action
 onPress :: AnyK () -> Button
 onPress p = mkButton p $ pure ()
@@ -96,6 +112,13 @@
 onRelease :: AnyK () -> Button
 onRelease = mkButton (pure ())
 
+onTap :: AnyK () -> Button
+onTap = mkButton' (pure ()) (pure ())
+
+-- | Like 'onPress' but with an alternative button to use for tapping
+onPress' :: Button -> AnyK () -> Button
+onPress' (Button{_tapAction = Action t}) p = mkButton' p (pure ()) t
+
 --------------------------------------------------------------------------------
 -- $running
 --
@@ -103,9 +126,7 @@
 
 -- | Perform both the press and release of a button immediately
 tap :: MonadK m => Button -> m ()
-tap b = do
-  runAction $ b^.pressAction
-  runAction $ b^.releaseAction
+tap b = runAction $ b^.tapAction
 
 -- | Perform the press action of a Button and register its release callback.
 --
@@ -149,9 +170,10 @@
 
 -- | Create a button that toggles a layer on and off
 layerToggle :: LayerTag -> Button
-layerToggle t = mkButton
+layerToggle t = mkButton'
   (layerOp $ PushLayer t)
   (layerOp $ PopLayer  t)
+  (pure ())
 
 -- | Create a button that switches the base-layer on a press
 layerSwitch :: LayerTag -> Button
@@ -186,10 +208,70 @@
      Button -- ^ The outer 'Button'
   -> Button -- ^ The inner 'Button'
   -> Button -- ^ The resulting nested 'Button'
-around outer inner = Button
-  (Action (runAction (outer^.pressAction)   *> runAction (inner^.pressAction)))
-  (Action (runAction (inner^.releaseAction) *> runAction (outer^.releaseAction)))
+around outer inner = mkButton
+  (runAction (outer^.pressAction)   *> runAction (inner^.pressAction))
+  (runAction (inner^.releaseAction) *> runAction (outer^.releaseAction))
 
+-- | A variant of `around`, which releases its outer button when another key
+-- is pressed.
+aroundOnly ::
+     Button -- ^ The outer 'Button'
+  -> Button -- ^ The inner 'Button'
+  -> Button -- ^ The resulting nested 'Button'
+aroundOnly outer inner = onPress' (around outer inner) $ do
+  runAction $ outer^.pressAction
+  runAction $ inner^.pressAction
+  go =<< matchMy Release
+ where
+  go :: KeyPred -> AnyK ()
+  go isMyRelease = hookF InputHook $ \e ->
+    if
+      | isMyRelease e -> do
+        runAction $ inner^.releaseAction
+        runAction $ outer^.releaseAction
+        pure Catch
+      -- Another key is pressed, so release the modifier immediately.
+      | isPress e -> do
+        runAction $ outer^.releaseAction
+        await isMyRelease $ \_ -> do
+          runAction (inner^.releaseAction)
+          pure Catch
+        pure NoCatch
+      | otherwise ->
+        go isMyRelease $> NoCatch
+
+-- | A variant of `around-only` that represses its outer button when all other
+-- keys after it have been released.
+aroundWhenAlone ::
+     Button -- ^ The outer 'Button'
+  -> Button -- ^ The inner 'Button'
+  -> Button -- ^ The resulting nested 'Button'
+aroundWhenAlone outer inner = onPress' (around outer inner) $ do
+  runAction $ outer^.pressAction
+  runAction $ inner^.pressAction
+  go S.empty =<< matchMy Release
+  pure ()
+ where
+  go :: HashSet Keycode -> KeyPred -> AnyK ()
+  go pressed isMyRelease = hookF InputHook $ \e ->
+    if
+      | isMyRelease e -> do
+        runAction $ inner^.releaseAction
+        when (null pressed) . runAction $ outer^.releaseAction
+        pure Catch
+      | isPress e -> do
+        let pressed' = S.insert (e^.keycode) pressed
+        when (null pressed) . runAction $ outer^.releaseAction
+        go pressed' isMyRelease
+        pure NoCatch
+      | otherwise -> do -- some release
+        let pressed' = S.delete (e^.keycode) pressed
+        let shouldPressOuter = S.member (e^.keycode) pressed && null pressed'
+        inject e
+        when shouldPressOuter . after 3 . runAction $ outer^.pressAction
+        await isRelease $ \_ -> go pressed' isMyRelease $> NoCatch
+        pure Catch
+
 -- | A 'Button' that, once pressed, will surround the next button with another.
 --
 -- Think of this as, essentially, a tappable mod. For example, an 'aroundNext
@@ -244,14 +326,14 @@
      Switch -- ^ Which 'Switch' should trigger the tap
   -> Button -- ^ The 'Button' to tap
   -> Button -- ^ The tapping 'Button'
-tapOn Press   b = mkButton (tap b)   (pure ())
-tapOn Release b = mkButton (pure ()) (tap b)
+tapOn Press   b = onPress $ tap b
+tapOn Release b = onRelease $ tap b
 
 -- | Create a 'Button' that performs a tap of one button if it is released
 -- within an interval. If the interval is exceeded, press the other button (and
 -- release it when a release is detected).
 tapHold :: Milliseconds -> Button -> Button -> Button
-tapHold ms t h = onPress $ withinHeld ms (matchMy Release)
+tapHold ms t h = onPress' t $ withinHeld ms (matchMy Release)
   (press h)                     -- If we catch timeout before release
   (const $ tap t $> Catch) -- If we catch release before timeout
 
@@ -259,7 +341,7 @@
 -- own release, or else switches to holding some other button if the next event
 -- is a different keypress.
 tapNext :: Button -> Button -> Button
-tapNext t h = onPress $ hookF InputHook $ \e -> do
+tapNext t h = onPress' t $ hookF InputHook $ \e -> do
   p <- matchMy Release
   if p e
     then tap t   $> Catch
@@ -296,7 +378,7 @@
 -- It does all of this while holding processing of other buttons, so time will
 -- get rolled back like a TapHold button.
 tapNextRelease :: Button -> Button -> Button
-tapNextRelease t h = onPress $ do
+tapNextRelease t h = onPress' t $ do
   hold True
   go []
   where
@@ -373,7 +455,7 @@
 -- 2. It is the press of some other button, we hold
 -- 3. It is the release of some other button, ignore.
 tapNextPress :: Button -> Button -> Button
-tapNextPress t h = onPress go
+tapNextPress t h = onPress' t go
   where
     go :: MonadK m => m ()
     go = hookF InputHook $ \e -> do
@@ -401,8 +483,12 @@
 -- into its list. The moment a delay is exceeded or immediately upon reaching
 -- the last button, that button is pressed.
 multiTap :: Button -> [(Milliseconds, Button)] -> Button
-multiTap l bs = onPress $ go bs
+multiTap l bs = onPress' tap' $ go bs
   where
+    tap' = case bs of
+      []           -> l
+      ((_, b) : _) -> b
+
     go :: [(Milliseconds, Button)] -> AnyK ()
     go []            = press l
     go ((ms, b):bs') = do
@@ -437,20 +523,22 @@
 -- | Create a 'Button' that performs a series of taps on press. Note that the
 -- last button is only released when the tapMacro itself is released.
 tapMacro :: [Button] -> Button
-tapMacro bs = onPress $ go bs
+tapMacro bs = mkButton' (go True bs) (pure ()) (go False bs)
   where
-    go []      = pure ()
-    go [b]     = press b
-    go (b:rst) = tap b >> go rst
+    go _ []      = pure ()
+    go False [b]     = press b
+    go True [b] = tap b
+    go forceTap (b:rst) = tap b >> go forceTap rst
 
 -- | Create a 'Button' that performs a series of taps on press,
 -- except for the last Button, which is tapped on release.
 tapMacroRelease :: [Button] -> Button
-tapMacroRelease bs = onPress $ go bs
+tapMacroRelease bs = mkButton' (go True bs) (pure ()) (go False bs)
   where
-    go []      = pure ()
-    go [b]     = awaitMy Release $ tap b >> pure Catch
-    go (b:rst) = tap b >> go rst
+    go _ []      = pure ()
+    go False [b]     = awaitMy Release $ tap b >> pure Catch
+    go True [b] = tap b
+    go forceTap (b:rst) = tap b >> go forceTap rst
 
 -- | Switch to a layer for a period of time, then automatically switch back
 layerDelay :: Milliseconds -> LayerTag -> Button
@@ -495,3 +583,16 @@
                *> inject (t^.event)
                *> after 3 (runAction $ b^.releaseAction)
                $> Catch)
+
+-- | Create a button that functions as a different button everything it is pushed
+--
+-- I.e: first it acts as the first button, then as the second, then as the
+-- third, and when finished rotates back to being the first button.
+steppedButton :: [Button] -> Button
+steppedButton bs = onPress $ go bs
+  where
+    go [] = undefined
+    go [b] = press b
+    go (b:bs') = do
+      press b
+      awaitMy Press $ go bs' $> Catch
diff --git a/src/KMonad/Model/Dispatch.hs b/src/KMonad/Model/Dispatch.hs
--- a/src/KMonad/Model/Dispatch.hs
+++ b/src/KMonad/Model/Dispatch.hs
@@ -26,7 +26,7 @@
 
 In the sequencing of components, the 'Dispatch' occurs first, which means that
 it reads directly from the KeySource. Any component after the 'Dispatch' need
-not worry about wether an event is being rerun or not, it simply treats all
+not worry about whether an event is being rerun or not, it simply treats all
 events as equal.
 
 -}
diff --git a/src/KMonad/Util.hs b/src/KMonad/Util.hs
--- a/src/KMonad/Util.hs
+++ b/src/KMonad/Util.hs
@@ -43,7 +43,7 @@
 
 -- | Newtype wrapper around 'Int' to add type safety to our time values
 newtype Milliseconds = Milliseconds { unMS :: Int }
-  deriving (Eq, Ord, Num, Real, Enum, Integral, Show, Read, Generic, Display)
+  deriving (Eq, Ord, Num, Real, Enum, Integral, Show, Read, Generic, Display, Typeable, Data)
 
 -- | Calculate how much time has elapsed between 2 time points
 tDiff :: ()
diff --git a/src/KMonad/Util/LayerStack.hs b/src/KMonad/Util/LayerStack.hs
--- a/src/KMonad/Util/LayerStack.hs
+++ b/src/KMonad/Util/LayerStack.hs
@@ -102,7 +102,7 @@
   => t1 (l, t2 (k, a)) -- ^ The /alist/ of /alists/ describing the mapping
   -> LayerStack l k a
 mkLayerStack nestMaps = let
-  -- Create a HashMap l (Layer k a) from the listlikes
+  -- Create a HashMap l (Layer k a) from the list-likes
   hms = M.fromList . map (over _2 mkLayer) $ toList nestMaps
 --   -- Create a HashMap (l, k) a from `hms`
   its = M.fromList $ hms ^@.. ifolded <.> (to unLayer . ifolded)
diff --git a/test/KMonad/ButtonDocSpec.hs b/test/KMonad/ButtonDocSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/KMonad/ButtonDocSpec.hs
@@ -0,0 +1,70 @@
+module KMonad.ButtonDocSpec (spec) where
+
+import KMonad.Args.Parser
+import KMonad.Args.Types
+import KMonad.Prelude
+
+import Data.Char (isSpace)
+import Data.Data
+import qualified RIO.Text as T
+
+import Test.Hspec
+
+spec :: Spec
+spec = do
+  docsExistForEveryButtons
+  tutorialMentionsEveryButton
+
+docsExistForEveryButtons :: Spec
+docsExistForEveryButtons =
+  (describe "button-docs" . traverse_ checkForDocsIn)
+    [ quickReferencePath
+    , tutorialPath
+    ]
+ where
+  checkForDocsIn file = do
+    cnt <- runIO $ readFileUtf8 file
+    describe file . sequence_ $ traverse checkForDoc keywordButtons (lines cnt)
+  checkForDoc btn cnt =
+    it ("Doc mentions " <> unpack (fst btn)) $
+      unless (buttonDocExist btn cnt) (expectationFailure "button is not mentioned in documentation")
+  buttonDocExist = any . checkerFromButton . fst
+  -- ignore deprecated names
+  checkerFromButton "momentary-layer" = const True
+  checkerFromButton "permanent-layer" = const True
+  checkerFromButton btn =
+    T.isPrefixOf ("- `(" <> btn <> " ") . T.dropWhile isSpace
+      <||> T.isInfixOf ("`" <> btn <> "`")
+      <||> T.isInfixOf ("`" <> btn <> "'")
+  (<||>) = liftA2 (||)
+  infixr 2 <||>
+
+tutorialMentionsEveryButton :: Spec
+tutorialMentionsEveryButton =
+  describe "button-usage" $
+    sequence_ . traverse checkButtonUsed buttonConstrs =<< runIO getTutorial
+ where
+  checkButtonUsed btn cnt =
+    it ("Buttontype `" <> showConstr btn <> "` appears outside of comments")
+      . unless (containsButton btn cnt)
+      $ expectationFailure ("Buttontype `" <> showConstr btn <> "` is never used")
+  containsButton btn = any . anyButton $ (== btn) . toConstr
+  anyButton f (KDefLayer DefLayer{_layerSettings = lyrBtns}) = anyOf (each . _LButton) (anySubButton f) lyrBtns
+  anyButton f (KDefAlias als) = any (anySubButton f . snd) als
+  anyButton _ _ = False
+  anySubButton f x = f x || anyOf plate f x
+  buttonConstrs = dataTypeConstrs $ dataTypeOf (undefined :: DefButton)
+
+getTutorial :: IO [KExpr]
+getTutorial =
+  either cannotParseTutorial pure . parseTokens
+    =<< readFileUtf8 tutorialPath
+ where
+  cannotParseTutorial err =
+    fail ("Could not parse `" <> tutorialPath <> "`:\n" <> show err)
+      $> []
+
+quickReferencePath :: FilePath
+quickReferencePath = "doc/quick-reference.md"
+tutorialPath :: FilePath
+tutorialPath = "keymap/tutorial.kbd"
diff --git a/test/KMonad/ComposeSeqSpec.hs b/test/KMonad/ComposeSeqSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/KMonad/ComposeSeqSpec.hs
@@ -0,0 +1,30 @@
+module KMonad.ComposeSeqSpec (spec) where
+
+import KMonad.Args.Parser
+import KMonad.Args.Types
+import KMonad.Keyboard.ComposeSeq
+import KMonad.Keyboard.Keycode
+import KMonad.Parsing
+import KMonad.Prelude
+
+import Test.Hspec
+
+import qualified RIO.Text as T
+
+spec :: Spec
+spec = describe "compose-sequences" $ traverse_ checkComposeSeq ssComposed
+ where
+  checkComposeSeq (expected, c, name) = describe ("Compose sequence for " <> unpack name) $ do
+    let c' = T.singleton c
+    let actualSeq = runParser buttonP "" c'
+    let expectedSeq = runParser (KComposeSeq <$> some buttonP) "" expected
+    let actualE2E = parseTokens $ "(deflayer <test> " <> c' <> " )"
+    let expectedE2E = first ParseError expectedSeq <&> \x -> [KDefLayer (DefLayer "<test>" [LButton x])]
+    it "Is compose sequence" $ actualSeq `shouldSatisfy` parsesAsValidComposeSeq
+    it "Matches expected" $ actualSeq `shouldBe` expectedSeq
+    it "Could parse in E2E" $ actualE2E `shouldBe` expectedE2E
+  parsesAsValidComposeSeq (Right (KComposeSeq seq')) = all isSimple seq'
+  parsesAsValidComposeSeq _ = False
+  isSimple (KEmit _) = True
+  isSimple (KAroundImplicit (KEmit KeyLeftShift) (KEmit _)) = True
+  isSimple _ = False
diff --git a/test/KMonad/GestureSpec.hs b/test/KMonad/GestureSpec.hs
--- a/test/KMonad/GestureSpec.hs
+++ b/test/KMonad/GestureSpec.hs
@@ -3,8 +3,6 @@
 import KMonad.Prelude
 import KMonad.Gesture
 
-import Data.Either (fromRight)
-
 import Test.Hspec hiding (around)
 
 r :: Either a b -> b
