Configuring a terminal emulator for a light theme
Some have called me mad, but I prefer dark text on a light background ("light theme") to light text on a dark background ("dark theme"). Terminals almost always use dark themes by default, so this article will describe how to reconfigure a terminal to use a light theme.
I use Windows Terminal (on Windows) to use both Powershell and various command lines on Linux through Windows Subsystem for Linux. This article will cover changing the colours for Powershell as well as for Linux terminals.
Changing the terminal colour palette
Most of the colours in a terminal draw from a palette of 16 colours which is defined by the terminal emulator being used. All common terminal emulators can be configured to use different colours for these colours. How to do this depends on which terminal emulator you are using, so I won't cover all the possibilities here. But as an illustrative example, here's the dialogue in Windows Terminal for changing the colour scheme.
Immediately after changing the terminal colour scheme, you should notice that the colours used by most applications have changed significantly. Most programs draw all their colours from the terminal palette.
Changing the colours used by Powershell
Changing the terminal palette didn't change every colour, so I started looking for ways to change the colours which hadn't changed. I noticed that Powershell's command completion suggestions were almost invisible against my new light background, so I looked for a way to change the colours it uses.
I found this excellent guide which describes how to change Powershell's colours, and also provides a pre-formulated colour palette which works well for light mode. (Thank you to the authors of this article!)
Command completions in Powershell are apparently under the control of
PSReadLine
, Powershell's command editor. PSReadLine
chooses colours
from its options, which can be changed using the Set-PSReadLineOption
cmdlet. The linked article suggests defining a variable, $ISETheme
,
whose value is a mapping between syntax elements and colours:
$ISETheme = @{
Command = $PSStyle.Foreground.FromRGB(0x0000FF)
Comment = $PSStyle.Foreground.FromRGB(0x006400)
ContinuationPrompt = $PSStyle.Foreground.FromRGB(0x0000FF)
Default = $PSStyle.Foreground.FromRGB(0x0000FF)
Emphasis = $PSStyle.Foreground.FromRGB(0x287BF0)
Error = $PSStyle.Foreground.FromRGB(0xE50000)
InlinePrediction = $PSStyle.Foreground.FromRGB(0x93A1A1)
Keyword = $PSStyle.Foreground.FromRGB(0x00008b)
ListPrediction = $PSStyle.Foreground.FromRGB(0x06DE00)
Member = $PSStyle.Foreground.FromRGB(0x000000)
Number = $PSStyle.Foreground.FromRGB(0x800080)
Operator = $PSStyle.Foreground.FromRGB(0x757575)
Parameter = $PSStyle.Foreground.FromRGB(0x000080)
String = $PSStyle.Foreground.FromRGB(0x8b0000)
Type = $PSStyle.Foreground.FromRGB(0x008080)
Variable = $PSStyle.Foreground.FromRGB(0xff4500)
ListPredictionSelected = $PSStyle.Background.FromRGB(0x93A1A1)
Selection = $PSStyle.Background.FromRGB(0x00BFFF)
}
Powershell also uses colours not from the terminal palette in output,
which must be configured separately from PSReadLine
. The article
provides another set of example colours which look better with a light
background and dark foreground (thank you!).
As the article explains, Powershell uses another different set of colours
to colourise the output of commands (from Powershell 7.2 forwards). These
colours are defined by the variable $PSStyle
. The article suggests
setting $PSStyle
like this for a light background:
$PSStyle.Formatting.FormatAccent = "`e[32m"
$PSStyle.Formatting.TableHeader = "`e[32m"
$PSStyle.Formatting.ErrorAccent = "`e[36m"
$PSStyle.Formatting.Error = "`e[31m"
$PSStyle.Formatting.Warning = "`e[33m"
$PSStyle.Formatting.Verbose = "`e[33m"
$PSStyle.Formatting.Debug = "`e[33m"
$PSStyle.Progress.Style = "`e[33m"
$PSStyle.FileInfo.Directory = $PSStyle.Background.FromRgb(0x2f6aff) +
$PSStyle.Foreground.BrightWhite
$PSStyle.FileInfo.SymbolicLink = "`e[36m"
$PSStyle.FileInfo.Executable = "`e[95m"
$PSStyle.FileInfo.Extension['.ps1'] = "`e[36m"
$PSStyle.FileInfo.Extension['.ps1xml'] = "`e[36m"
$PSStyle.FileInfo.Extension['.psd1'] = "`e[36m"
$PSStyle.FileInfo.Extension['.psm1'] = "`e[36m"
These colour options will not persist once your Powershell session
ends, so to keep the options set, the commands you ran to change the
colours need to be written to your Powershell profile. The variable
$PROFILE
stores the location of your profile. You can print the its
value at the Powershell prompt like this:
PS C:\Users\adeptangel> $PROFILE
C:\Users\adeptangel\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
PS C:\Users\adeptangel>
I got stuck here for a little while trying to open the profile in Notepad.
Passing the value of $PROFILE
to be opened in Notepad with notepad.exe $PROFILE
didn't work because the directory Documents/Powershell
didn't
actually exist. Once I realised what was going on, I created the directory
and then wrote these lines to the file:
$ISETheme = @{
Command = $PSStyle.Foreground.FromRGB(0x0000FF)
Comment = $PSStyle.Foreground.FromRGB(0x006400)
ContinuationPrompt = $PSStyle.Foreground.FromRGB(0x0000FF)
Default = $PSStyle.Foreground.FromRGB(0x0000FF)
Emphasis = $PSStyle.Foreground.FromRGB(0x287BF0)
Error = $PSStyle.Foreground.FromRGB(0xE50000)
InlinePrediction = $PSStyle.Foreground.FromRGB(0x93A1A1)
Keyword = $PSStyle.Foreground.FromRGB(0x00008b)
ListPrediction = $PSStyle.Foreground.FromRGB(0x06DE00)
Member = $PSStyle.Foreground.FromRGB(0x000000)
Number = $PSStyle.Foreground.FromRGB(0x800080)
Operator = $PSStyle.Foreground.FromRGB(0x757575)
Parameter = $PSStyle.Foreground.FromRGB(0x000080)
String = $PSStyle.Foreground.FromRGB(0x8b0000)
Type = $PSStyle.Foreground.FromRGB(0x008080)
Variable = $PSStyle.Foreground.FromRGB(0xff4500)
ListPredictionSelected = $PSStyle.Background.FromRGB(0x93A1A1)
Selection = $PSStyle.Background.FromRGB(0x00BFFF)
}
$PSStyle.Formatting.FormatAccent = "`e[32m"
$PSStyle.Formatting.TableHeader = "`e[32m"
$PSStyle.Formatting.ErrorAccent = "`e[36m"
$PSStyle.Formatting.Error = "`e[31m"
$PSStyle.Formatting.Warning = "`e[33m"
$PSStyle.Formatting.Verbose = "`e[33m"
$PSStyle.Formatting.Debug = "`e[33m"
$PSStyle.Progress.Style = "`e[33m"
$PSStyle.FileInfo.Directory = $PSStyle.Background.FromRgb(0x2f6aff) +
$PSStyle.Foreground.BrightWhite
$PSStyle.FileInfo.SymbolicLink = "`e[36m"
$PSStyle.FileInfo.Executable = "`e[95m"
$PSStyle.FileInfo.Extension['.ps1'] = "`e[36m"
$PSStyle.FileInfo.Extension['.ps1xml'] = "`e[36m"
$PSStyle.FileInfo.Extension['.psd1'] = "`e[36m"
$PSStyle.FileInfo.Extension['.psm1'] = "`e[36m"
Set-PSReadLineOption -Colors $ISETheme
Changing LS_COLORS
GNU ls
, which is the version of ls
distributed with most Linux
distributions, will display different colours for different kinds of file
in a terminal (so long as the --color
option is not set to none
). GNU
ls
has a built-in mapping of colours to file types, but this can be
overriden with the environment variable LS_COLORS
.
I was expecting to find the default foreground colours for ls
had poor
contrast against a light background, but they're actually fine, especially
since most of them are chosen from the 16-colour terminal palette. However,
there is one particularly problematic colour: the colour for the "other
writeable" file type. By default, files of that type will display with light
blue text on a light blue background, as depicted here, taken from a Unix
& Linux stack exchange
question:
I find this very difficult to read so I set about trying to change it.
I spent some time using the GNU program dircolors
, which can be used
to generate a value for LS_COLORS
from a more verbose file which is
easier to understand. But as far as I could tell, the only way to automate
the process of generating LS_COLORS
from the dircolors
file was to use
eval
to execute some lines of shell script. I'm not confident about what
exactly eval
does, and I only wanted to make a single minor change to the
colours anyway, so eventually I set LS_COLORS
manually in my shell
runcommands file:
# ls_colors: change the colour of Other Writeable to make it readable
LS_COLORS=$LS_COLORS:"ow=30;106"
This makes those entries much more readable. Yay! Now if only I knew what an "other writeable" actually is.
Changing the colours used by programs on Linux
Most Linux terminal programs use the terminal's colour palette only, which means that they should work properly straight away! I did encounter two issues while using some more complicated programs, however.
The Invisible Cursor Problem
I use the text editor Neovim in the terminal, which can be programmed to use custom colour schemes. Neovim will change the background colour according to the colour scheme, so contrast with the standard terminal background colour is not a problem. But the colour of the cursor (which, in Windows Terminal, is part of the terminal colour palette) won't change. If the normal terminal background colour is dark, the cursor will be light, and the same reversed. So if Neovim changes the background to a dark colour, and the terminal colour scheme background colour is light, the dark cursor will become invisible against the dark background (and the same reversed).
I don't know any solution to this, apart from using a terminal emulator which chooses the colour for the cursor dynamically to avoid this problem. I don't know whether any terminal emulators do this.
The Terminal "White" Colour Problem
I use the terminal IRC client
catgirl
. By default, catgirl
picks colours for IRC nicks from a wide range of colours, including the 16
terminal colours but not limited to them. Before I ran catgirl
with a light
background, I added the line hash = 0,15
to my configuration file. This
limited the colours used for the nicks to the colours from the terminal
palette (colours 0 through 15), in case the default colour palette included
colours with poor contrast against a light background.
Once I started catgirl
, I noticed that system messages, like direct
messages from the NickServ
bot, were almost entirely unreadable. At
first I worried that catgirl
had hardcoded a colour which would be
illegible with a light background. Then I opened an IRC channel, and saw
that, despite limiting the colours to only the terminal palette, some of
the nicks were the same unreadable colour as the messages from NickServ
.
I realised that one of the foreground colours from the terminal palette
must have contrast too poor against the background to be visible. Sure
enough, the "white" colour was, well, white. This made it unreadable
against the background, which was only a different shade of white.
I solved this problem by changing my terminal colour scheme again, to one where the "white" colour is actually dark, which gives it contrast against a light background. The colour scheme I chose is Rosé Pine Light.