Configuring Neovim and Kitty to follow Gnome dark/light theme

Make Kitty and Neovim follow your changing color scheme in gnome!

I'm one of the people that do not constantly use dark mode in their software, but instead prefer to match it with the time of day. In gnome this can be achieved using Night Theme Switcher extension.

Once enabled it will transition between night and dark themes automatically at sunrise and sunset. Additionally it allows to execute a script when it does so, which we will use for our implementation.

Night Theme Switcher extension showing two sh scripts setup to be run at sunrise and sunset.
Night Theme Switcher extension showing two sh scripts setup to be run at sunrise and sunset.

Kitty

Kitty supports functionality to change a theme via cli like so:

kitten themes --reload-in=all Rosé Pine Dawn

In case you are using home manager, a workaround is to create theme.conf file in $HOME/.config/kitty and include it with include theme.conf from your kitty.conf.

We leave file file empty so that we can later modify it with the theme of our choice.

echo "include themes/rose-pine-dawn.conf" >$HOME/.config/kitty/theme.conf
# Notify kitty procesed of changed configuration.
kill -SIGUSR1 $(pidof kitty)

Neovim

With kitty theme changing out of the way, we can focus on neovim. We could do similar approach as Kitty, by modifying a config file and reloading neovim configurations. But i took a different approach.

Instead of modifying config, i thought if there is a way of reading what theme is currently set, and decide based on that programmatically.

We can read gsettings to figure out current color scheme like so:

gsettings get org.gnome.desktop.interface color-scheme

Which outputs 'prefer-dark' when set to dark or 'default' for normal.

With plenary.job we can run that command and based on response decide what "color scheme" to set. Like so:

-- Create a job to detect current gnome color scheme and set background
local Job = require("plenary.job")
local set_background = function()
	local j = Job:new({ command = "gsettings", args = { "get", "org.gnome.desktop.interface", "color-scheme" } })
	j:sync()
	if j:result()[1] == "'default'" then
		vim.o.background = "light"
	else
		vim.o.background = "dark"
	end
end

-- Call imidiatly to set initially
set_background()

Last thing is then to somehow notify neovim instances about the theme change. It would be nice if we could hook in to dbus events and implement this without even the need of external tools. But for now, we can listen for a SIGUSR1 event instead and trigger the set_background() function.

-- Debounce to not call the method too often in case of multiple signals.
local debounce = function(ms, fn)
	local running = false
	return function()
		if running then
			return
		end
		vim.defer_fn(function()
			running = false
		end, ms)
		running = true
		vim.schedule(fn)
	end
end

-- Listen for SIGUSR1 signal to update background
local group = vim.api.nvim_create_augroup("BackgroundWatch", { clear = true })
vim.api.nvim_create_autocmd("Signal", {
	pattern = "SIGUSR1",
	callback = debounce(500, set_background),
	group = group,
})

Making it all work together

Now that we have both figured out, we have to implement our sunset.sh and sunrise.sh scripts.

sunrise.sh

#!/bin/env bash

# Kitty terminal
echo "include themes/rose-pine-dawn.conf" >$HOME/.config/kitty/theme.conf
kill -SIGUSR1 $(pidof kitty)

# Neovim
kill -SIGUSR1 $(pidof nvim)

sunset.sh

#!/bin/env bash

# Kitty terminal
echo "include themes/rose-pine-moon.conf" >$HOME/.config/kitty/theme.conf
kill -SIGUSR1 $(pidof kitty)

# Neovim
kill -SIGUSR1 $(pidof nvim)

Final look

0:00
/0:12

Final look at neovim and kitty terminal switching color scheme.

You can see my full implementation in my dotfiles repository.

This site is not using any commenting system, but you can share your thoughts with the author by sending an email to me@mnts.dev.