Use macOS System Selection Colors in LIN for Emacs Line-Selection UIs

The Apple’s Human Interface Guidelines have a list of named system colors for macOS. These color names can be used for Emacs “faces”, i.e. color-and-font settings of text.

LIN to the left in a (squished) email list, hl-mode to the right in regular text

Recently, this came in handy when Protesilaos Stavrou published his LIN package source code – it’s basically an extension of the built-in hl-mode, read: highlight-line-mode. The default highlights the current line. That’s kind of useful to find your insertion point more quickly. But it’s also used in selection interfaces to highlight the currently selected line: email modes like message-mode, notmuch, mu4e, and feed reader elfeed use this to show that the actions you can perform pertains to the currently focused or highlighted line. That’s where Prot’s LIN package comes into play: it helps distinguish between highlighting the line in text editing modes and highlighting the line in selection interfaces, so you can use different colors.

You could say that LIN adds a semantic layer on top of “highlight this line”: it can distinguish between “highlight this line of text” and “highlight the selection”.

Being on macOS, I wanted the system default selection color. Blue background with white foreground color.

I used this approach for hl-line-mode since May and wrote about this back then. I couldn’t use hl-line to highlight my line in text documents if I wanted to, though, because of the color choice. This new approach improves that thanks to LIN.

To use macOS’s named colors for selections as LIN’s background and foreground instead of specifying color values directly, tweak the two LIN faces:

(when (memq window-system '(mac ns))
  (set-face-attribute 'lin-hl nil
                      :background "selectedContentBackgroundColor")

  ;; To also override the foreground (see `lin-override-foreground'):
  (set-face-attribute 'lin-hl-override-fg nil
                      :foreground "alternateSelectedControlTextColor"
                      :background "selectedContentBackgroundColor"))

The cool part about named system colors on macOS is that they are “Dynamic System Colors”: in dark mode, they produce a different value than in light mode. That means you don’t need to pick dark and light colors individually. The same color names will work.

If you switch your OS or app’s appearance to dark mode, though, you need to effectively reload the colors. Unlike native macOS apps, Emacs won’t automatically use the correct color for the current appearance.

So you need to trigger an update of LIN’s faces to pick up the color values if you change the appearance from dark to light, or light to dark. macOS builds of Emacs have a hook for this, and you can perform a color update by adding a function to ns-system-appearance-change-functions:

(defun my-lin-macos-system-colors ()
  (when (memq window-system '(mac ns))
    (set-face-attribute 'lin-hl nil
                        :background "selectedContentBackgroundColor")

    ;; To also override the foreground (see `lin-override-foreground'):
    (set-face-attribute 'lin-hl-override-fg nil
                        :foreground "alternateSelectedControlTextColor"
                        :background "selectedContentBackgroundColor")))

(when (memq window-system '(mac ns))
  (add-hook 'ns-system-appearance-change-functions #'my-lin-macos-system-colors))

This information is now also part of LIN’s README and, if Prot’s past efforts to document his packages holds true, will also become part of the manual when you install this package.