Picking an emacs colour theme
TL;DR:
Here're a couple of handy functions to quickly try out all the dark-mode / light-mode themes you've already got installed, and pick the one that feels right for now.
There are too many choices
I know that emacs can be as pretty as I want it to be, but when I run
M-x load-theme TAB I just see a big list of more-or-less meaningless
names:
Type M-RET on a completion to select it. Type M-<down> or M-<up> to move point between completions. 23 possible completions: adwaita deeper-blue dichromacy leuven leuven-dark light-blue manoj-dark misterioso modus-operandi modus-operandi-deuteranopia modus-operandi-tinted modus-operandi-tritanopia modus-vivendi modus-vivendi-deuteranopia modus-vivendi-tinted modus-vivendi-tritanopia tango tango-dark tsdh-dark tsdh-light wheatgrass whiteboard wombat
They're not even organised by light-mode and dark-mode!
The UX of https://emacsthemes.org is better, because we get thumbnails of what all the themes look like. But there are way too many themes on there, and I often just want to pick the best of what I've got on my local machine. Whatever came with the default install.
M-x customize-themes is a bit better in that it gives me a text
description of each theme. But it's not very visual:
Available Custom Themes: [ ][ adwaita] -- Face colors similar to the default theme of Gnome 3 (Adwaita). [ ][ deeper-blue] -- Face colors using a deep blue background. [ ][ dichromacy] -- Face colors suitable for red/green color-blind users. ... [ ][ tsdh-light] -- A light Emacs theme. [ ][ wheatgrass] -- High-contrast green/blue/brown faces on a black background. [ ][ whiteboard] -- Face colors similar to markers on a whiteboard. [ ][ wombat] -- Medium-contrast faces with a dark gray background.
What I want
I want to make my theme decision in two parts:
- Light or dark?
- Which one looks nicest right now, for the stuff I'm working on?
So, if I'm in a dark room, I think I want a function to cycle through all the dark-themes I have installed, and just try them one after another.
(defun gds-try-next-dark-theme () "Try the next available dark theme. If we're currently using one or more themes, disable them first." (interactive) ... )
…and if I'm in a bright room, I'll want the light-mode equivalent:
(defun gds-try-next-light-theme () "Try the next available light theme. If we're currently using one or more themes, disable them first." (interactive) ... )
I'm going to do this by manually categorising the themes that I happen to have on my computer right now into two lists: one for dark themes, and the other for light.
Then I'm going to write the functions I actually want: for cycling through themes.
Finally, I want to be able to maintain the thing. So if a new theme appears in a new version of emacs, I'll want some way of detecting it:
(defun gds-check-for-new-themes () "Check for uncategorised themes. Print and return a list of themes that are available to use with `load-theme', but which are not yet categorised in either of `gds-builtin-light-color-themes' or `gds-builtin-dark-color-themes'." (interactive) ... )
How to get it…
This elisp should do the trick:
(defvar gds-builtin-light-color-themes [ adwaita dichromacy leuven modus-operandi modus-operandi-deuteranopia modus-operandi-tinted modus-operandi-tritanopia modus-vivendi tango tsdh-light whiteboard ] "A manually-curated array of light-mode color themes. You can cycle through them with `gds-try-next-light-theme'. See also `gds-next-light-theme-index' and `gds-try-next-dark-theme'.") (defvar gds-next-light-theme-index 0 "The index of the next light-mode color theme to try if you call `gds-try-next-light-theme'. See also `gds-builtin-light-color-themes' and `gds-try-next-dark-theme'.") (defvar gds-builtin-dark-color-themes [ deeper-blue leuven-dark manoj-dark misterioso modus-vivendi-deuteranopia modus-vivendi-tinted modus-vivendi-tritanopia tango-dark tsdh-dark wheatgrass wombat] "A manually-curated array of dark-mode color themes. You can cycle through them with `gds-try-next-dark-theme'. See also `gds-next-dark-theme-index' and `gds-try-next-light-theme'.") (defvar gds-next-dark-theme-index 0 "The index of the next light-mode color theme to try if you call `gds-try-next-dark-theme'. See also `gds-builtin-dark-color-themes' and `gds-try-next-light-theme'.") (defun gds-try-next-light-theme () "Try the next available light theme. If we're currently using one or more themes, disable them first. See also `gds-try-next-dark-theme' and `gds-check-for-new-themes'." (interactive) (seq-do 'disable-theme custom-enabled-themes) (load-theme (aref gds-builtin-light-color-themes gds-next-light-theme-index)) (setq gds-next-light-theme-index (gds-get-rotated-index-of gds-next-light-theme-index gds-builtin-light-color-themes))) (defun gds-try-next-dark-theme () "Try the next available dark theme. If we're currently using one or more themes, disable them first. See also `gds-try-next-light-theme' and `gds-check-for-new-themes'." (interactive) (seq-do 'disable-theme custom-enabled-themes) (load-theme (aref gds-builtin-dark-color-themes gds-next-dark-theme-index)) (setq gds-next-dark-theme-index (gds-get-rotated-index-of gds-next-dark-theme-index gds-builtin-dark-color-themes))) (defun gds-get-rotated-index-of (idx arr) "Rotate the index IDX into array ARR. ARR must be an array. IDX must be an int that indexes into that array. We return the index of the next element of the array." (% (+ 1 idx) (length arr))) (defun gds-check-for-new-themes () "Check for uncategorised themes. Print and return a list of themes that are available to use with `load-theme', but which are not yet categorised in either of `gds-builtin-light-color-themes' or `gds-builtin-dark-color-themes'." (interactive) (let ((new-themes (seq-filter 'gds-is-uncategorised-theme-p (custom-available-themes)))) (if new-themes (message "Detected new themes: %S" new-themes) (message "There are no new themes.")) new-themes)) (defun gds-is-uncategorised-theme-p (theme) "Check if we've yet to categorise THEME. Return t if we don't recognise THEME. Return nil if THEME is in our list of light or dark themes. Those are our main categories that we can rotate through with `gds-try-next-light-theme' and `gds-try-next-dark-theme' Return nil if THEME is `light-blue'. Because that one's apparently obsolete since Emacs 29.1, according to the warnings it prints." (not (or (eq theme 'light-blue) (seq-contains-p gds-builtin-light-color-themes theme) (seq-contains-p gds-builtin-dark-color-themes theme))))
There's no comments mechanism in this blog (yet?), but I welcome emails and fedi posts. If you choose to email me, you'll have to remove the .com from the end of my email address by hand.
You can also follow this blog with RSS.