Switching OS color mode
Posted 02.07.2022 ยท 4 min readMost of the time I prefer the color mode of my devices to be dark. I do not have a good answer to why. However, sometimes I switch for different reasons, especially in the summer times to deal with glare or when I am working outside ๐
To switch I use a neat little app called NightOwl. It allows to have hotkeys for switching, which I have set up to toggle based on ctrl + option + command. It is also schedule for switching or you can switch color mode by clicking the owl in the status bar.
Visual Studio Code
Vscode have support for setting a theme per color mode and automatically switch. To enable it add this to the settings.json and change to your preferred themes.
{ "window.autoDetectColorScheme": true, "workbench.preferredDarkColorTheme": "GitHub Dark", "workbench.preferredLightColorTheme": "GitHub Light"}
iTerm
To get iTerm to switch when the system color mode switches we need to write a script. Below is
a script based on this gist. Put the script in
$(HOME)/Library/Application\ Support/iTerm2/Scripts/AutoLaunch/color-switch.py
and go to Scripts
-> AutoLaunch
in the iTerm menu and check it. If you haven't used
any iTerm scripts before it will prompt to download the python runtime. After that is done
it should be working. In the case that it does not update you can check the logs by going to
Scripts
-> Manage
-> Console
to see the logs.
#!/usr/bin/env python3
import asyncioimport iterm2
LIGHT_THEME = "papercolor-light"DARK_THEME = "papercolor-dark"
async def main(connection): async with iterm2.VariableMonitor( connection, iterm2.VariableScopes.APP, "effectiveTheme", None ) as mon: while True: # Block until theme changes theme = await mon.async_get()
preset = await iterm2.ColorPreset.async_get( connection, DARK_THEME if "dark" in theme.split(" ") else LIGHT_THEME, )
# Update the list of all profiles and iterate over them. profiles = await iterm2.PartialProfile.async_query(connection) for partial in profiles: profile = await partial.async_get_full_profile() await profile.async_set_color_preset(preset)
iterm2.run_forever(main)
Shell script for detecting color mode
I created this tiny script that will print light
or dark
. I put it on path
the name color-mode
. This will be referred to by other things like my vim setup.
#!/bin/bash
mode="$(defaults read -g AppleInterfaceStyle 2>/dev/null || echo "light")"echo $mode | tr '[:upper:]' '[:lower:]'
Vim
In the vim setup I use the utility script color-mode
described in the section
above. This setup is maybe the part of this post that could be smoother, because
the switch is not automatic. Below there is a function that will use the color-mode
command to get the color mode and set the background
variable in vim when it is
running in iTerm. If it is not running in iterm it will use the COLOR_MODE
environment
variable, thus, enabling this to work on remote sessions through ssh. See the ssh section
for how to pass the environment variable.
function! SetBackgroundMode(...) let s:new_bg = "dark" if $TERM_PROGRAM ==? "iTerm.app" let s:mode = systemlist("color-mode") if len(s:mode) > 0 if s:mode[0] ==? "light" let s:new_bg = "light" else let s:new_bg = "dark" endif endif else if $COLOR_MODE ==? "light" let s:new_bg = "light" else let s:new_bg = "dark" endif endif if &background !=? s:new_bg let &background = s:new_bg endifendfunction
I set the background variable since the colorscheme I use, Papercolor, supports light and dark mode through that vairable. You could change this part to set colorscheme instead.
To enable this I call this function on vim startup in my vimrc file, and I have mapped
it to cc
so that I can quickly sync the color mode when I change the system one. I have
tried to do this automatically with a timer that would call the function every second, but
that caused some weird behaviour while typing.
colorscheme Papercolor
call SetBackgroundMode()nnoremap cc :call SetBackgroundMode()<cr>
SSH
The ssh setup is mostly for vim and other programs that have color schemes, because the regular terminal will follow the colorscheme of the client where it is rendered. To enable vim and other pograms to know the color mode of the client I have set this environment variable in my zsh config.
export COLOR_MODE=$(color-mode)
Then with SendEnv COLOR_MODE
in the .ssh/config
on the client and
AcceptEnv COLOR_MODE
in /etc/ssh/sshd_config
on the server this
environment variable will be accesible on the server. However, this will
not automatically update. I don't use it that much so in the current state
it is still nice to have.