Bat: Default theme compatible with light/dark terminals

Created on 8 Jul 2020  路  18Comments  路  Source: sharkdp/bat

Hi! 馃憢
First of all, thanks for making such a great tool!

One reason preventing me from using bat much is because there's no theme that's readable on both light and dark terminals. For instance, the default theme shows text as white, which is invisible on a white background.

I think this might be fairly common use case, as macOS' default terminal theme automatically switches between light/dark as the system theme changes.

I wonder if there's any plans of making the default theme compatible with both types of terminals? I think one major improvement would be to show text in the default text color, instead of white.

I'd be happy to help if this would seem reasonable.

feature-request

All 18 comments

That鈥檚 good to know, thanks! It鈥檒l only work on macOS however, and I鈥檓 sure people like to use bat in a variety of environments.

I recently moved to Linux (Pop OS, i.e. Gnome with Pop Shell) from macOS and I am switching between dark mode and light mode. Bat stands out as a program I use constantly that doesn't play well with my light mode daytime theme.

How do other programs detect dark mode? Does your terminal style change? Do other terminal programs react to the change?

In Gnome Terminal, in my profile -> Colors -> Text and Background Color, I checked "Use colors from system theme", which makes it respect Dark Mode. The terminal background and text colors change as the system theme changes between light and dark themes.

Web pages in web browsers use the prefers-color-scheme media query but I'm not sure if that's relevant here.

In Gnome Terminal, in my profile -> Colors -> Text and Background Color, I checked "Use colors from system theme", which makes it respect Dark Mode. The terminal background and text colors change as the system theme changes between light and dark themes.

If the terminal theme changes, you could use bats base16 theme (which changes with the terminal theme). It's not ideal though, as the color choice is severely limited.

A better choice would be to automatically select a pre-configured dark/light theme (like Monokai vs. GitHub). But this would require us to somehow get a hold of that Gnome property.

Here's an interesting thread on detecting the terminal background color: https://unix.stackexchange.com/questions/245378/common-environment-variable-to-set-dark-or-light-terminal-background

I'm not knowledgeable enough to know if this is useful info.

The easiest way support both light and dark themes is to use the standard ANSI colors, default text color for text (i.e. not explicitly specify white or black), and no background color.

The easiest way support both light and dark themes is to use the standard ANSI colors, default text color for text (i.e. not explicitly specify white or black), and no background color.

Maybe. But it's also a way that restricts the color output to 256 colors. Something that I simply refuse to fall back to (as a standard / by default) in 2020.

I didn't do any extensive research, but I guess there could be a way to query the current theme with something like

gsettings get org.gnome.desktop.interface gtk-theme

Meanwhile, here's an alternative that displays nicely on any old light/dark ANSI terminal:

wat() { 
    if [ -t 1 ]; then
        highlight --force --stdout -O ansi "$@" | less -RFX
    else
        cat "$@"
    fi
}

Requires brew install highlight or whatever your package manager is.

I didn't do any extensive research, but I guess there could be a way to query the current theme with something like

gsettings get org.gnome.desktop.interface gtk-theme

That appears to work for me, the light theme is 'Pop' and the dark theme is 'Pop-dark'. Now what do we do with that info? :)

gsettings get org.gnome.desktop.interface gtk-theme

That appears to work for me, the light theme is 'Pop' and the dark theme is 'Pop-dark'. Now what do we do with that info? :)

A wrapper command could work:

bat() {
    local sys_theme="$(gsettings get org.gnome.desktop.interface gtk-theme)"
    local bat_theme
    case "${sys_theme}" in
    "Pop") bat_theme="GitHub" ;;
    "Pop-dark") bat_theme="default" ;;
    *) bat_theme="default" ;;
    esac
    command bat --theme="${bat_theme}"
}

If you add that to your bash profile, it should work just by using the bat command as usual.

But it's also a way that restricts the color output to 256 colors.

This is a good thing. It means that you have consistent colors throughout your terminal usage, instead of each CLI utility using its own themes with clashing colors; and when you want to change theme you can do so in one location instead of having to recustomise every single utility.

I've given up on having utilities that work correctly across light and dark backgrounds and have reconfigured my light themes so that white -> black and black -> white, but defaulting to ansi-dark would at least get you most of the way there to supporting users custom terminal themes without configuration.

This is a good thing. It means that you have consistent colors throughout your terminal usage, instead of each CLI utility using its own themes with clashing colors

You can still have widely different themes with 256 colors. Maybe what you meant are the base 16 colors that are usually customized to a certain theme? (the 256 colors can also be customized, but usually are not).

but defaulting to ansi-dark would at least get you most of the way there to supporting users custom terminal themes without configuration

The problem with the base16 (and even the 256 color) themes is that I find that really limiting. I'm old enough to remember the times when I was able to switch Windows from 256 to "true color". And I do not want to go back there. Using 24bit colors should be a standard.

In any case, bat leaves the choice to the user. It's also really easy to change a theme.

Closing this for now, as I don't see what we could do inside bat.

If anyone's curious, here's what I did with fish shell in Gnome Terminal. I wrote a function I called theme_bat:

function theme_bat
    switch (gsettings get org.gnome.desktop.interface gtk-theme)
    case "'Pop'"
        set -gx BAT_THEME GitHub
    case "'Pop-dark'"
        set -gx BAT_THEME default
    case '*'
        echo "I don't recognize the GTK theme"
    end
end

Then in Gnome Terminal, I went to Preferences -> Profiles -> MyThemeName -> Command, checked "Run a custom command instead of my shell" and for my custom command I entered /usr/bin/fish -c 'theme_bat';/usr/bin/fish.

This means when I open Gnome Terminal in my default theme, it first runs a script that checks whether the GTK theme is light or dark, then sets the $BAT_THEME shell variable to the appropriate theme. Then it runs my regular shell.

Note that this doesn't affect other terminal emulators such as Alacritty (which doesn't currently integrate with the GTK light/dark mode), they can continue with the default bat theme or whatever configuration is best for them.

Alternative 1: Gnome Night Mode + Shell alias

  1. Create a shell script (i.e. ~/.local/bin/is_night):
#!/usr/bin/env bash

# This relies on the user defined time window within Gnome Night Mode settings.
NOW=$(date +'%H.0')
NIGHT_TIME_START=$(gsettings get org.gnome.settings-daemon.plugins.color night-light-schedule-from)
NIGHT_TIME_END=$(gsettings get org.gnome.settings-daemon.plugins.color night-light-schedule-to)

# d=day n=night
function isNight()
{
  if [ $(echo "${NIGHT_TIME_START} < ${NIGHT_TIME_END}" | bc -q) == 1 ]
  then
    # pattern: dnd
    OP='-a'
  else 
    # pattern: ndn
    OP='-o'
  fi

  [ $(echo "${NOW} > ${NIGHT_TIME_START}" | bc -q) == 1  "$OP"  $(echo "${NOW} < ${NIGHT_TIME_END}" | bc -q) == 1 ]
}

isNight

Make sure ~/.local/bin is part of your $PATH and the script is executable. Then adjust the suggested shell alias as follows:

alias cat="bat --theme=\$(is_night && echo myNightTheme || echo myDayTheme)"

Alternative 2: Use Gnome Extension and modify bat's config file

Here is another, maybe more consistent way, to handle the switch, that is based on the Night Theme Switcher extension for the Gnome Shell.

Create the script night_mode, mark it executable and a link to it called day_mode:

#!/usr/bin/env bash

update_bat()
{
  CONFIG_FILE_PATH="$(bat --config-file)"
  THEME="${1/day/GitHub}"
  THEME="${THEME/night/1337}"

  if [ ! -f "$CONFIG_FILE_PATH" ]
  then
    bat --generate-config-file
    if [ ! -f "$CONFIG_FILE_PATH" ]
    then
      echo "Could neither find nor create config file \"$CONFIG_FILE_PATH\""
      exit 1
    fi
  fi

  # switch bat
  sed --in-place "s/^--theme=.*$/--theme=$THEME/"  "$CONFIG_FILE_PATH"
}

SCRIPT_NAME=$(basename $0)
MODE=${SCRIPT_NAME/%_mode/} 

echo "Switching to $MODE mode"
update_bat $MODE

Then configure the scripts to be executed by the Night Switch Shell Extension on night/day switch respectively.

Thanks for all the workarounds!

I still think bat should be able to provide a theme that only uses ANSI colors, so that (1) it automatically works with light and dark terminals without hacks, and (2) works consistently on any ANSI terminal (even in a vanilla tmux) without the dependency on special terminal capabilities (e.g. 256 colors), which often isn't the default. This is how most UNIX tools with colors work.

Was this page helpful?
0 / 5 - 0 ratings