DEV Community

Alexandru Bucur
Alexandru Bucur

Posted on • Originally published at alexandrubucur.com on

Simple Script to Generate Color Scheme Switchable Css

While I was working at yet another secret project that will probably never see the light of day, I wanted to blog about the experience on bootstrapping the project.
As any developer, that means that instead of writing that blog post, I had to dust off my website theme, update hugo, and do other fixes.

I've settled on PaperMod as the theme, since it's simple and to the point, and it also changes the style based on the preferred color scheme.

However, by default there's no way to have the code highlight theme change based on the preferred color scheme, but since I hopefully still know my css, and with the help of phind to generate some shell scripting, we now got this nifty script that can help you do just that, be it in a hugo theme, or any other kind of system as long as you "abuse" hugo for the generation of the theme only.

You see, hugo uses chroma for its theme support, so that makes it pretty flexible to generate the theme css using hugo gen chromastyles --style=your_style_here_from_the_big_list.

After picking the two color schemes I wanted, namely tango and nord (and fixing one of them, twice) I've set up to ... instruct my lovely assistant to help me build what I need.

After some back and forth, and some suggestions on what's wrong on the generated script based on ShellCheck, since Phind has an obsession it seems with escaping $, we got to this lovely code (Github Gist for convenience).

#!/usr/bin/env bash
set -euo pipefail

# Default values
light_theme=""
dark_theme=""
destination_path="assets/css/extended/syntax.css"

# Help message
usage() {
  cat <<EOF
Usage: $0 [options] [--] [light-theme] [dark-theme]

Options:
  -h, --help                Show this help message and exit
  --light-theme=LIGHT_THEME Set the light theme
  --dark-theme=DARK_THEME   Set the dark theme
  --destination-path=PATH   Set the destination path for the output file

Positional arguments:
  light-theme              Light theme (alternative to --light-theme)
  dark-theme               Dark theme (alternative to --dark-theme)
EOF
}

positional_arg_counter=0

# Parse options and arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    -h|--help)
      usage
      exit 0
      ;;
    --light-theme=*)
      light_theme="\${1#*=}"
      positional_arg_counter=$((positional_arg_counter + 1))
      shift
      ;;
    --dark-theme=*)
      dark_theme="\${1#*=}"
      positional_arg_counter=$((positional_arg_counter + 1))
      shift
      ;;
    --destination-path=*)
      destination_path="\${1#*=}"
      shift
      ;;
    *)
      if [[ $positional_arg_counter -eq 0 ]]; then
        light_theme="$1"
      elif [[ $positional_arg_counter -eq 1 ]]; then
        dark_theme="$1"
      else
        echo "Error: Too many arguments" >&2
        usage
        exit 1
      fi
      positional_arg_counter=$((positional_arg_counter + 1))
      shift
      ;;
  esac
done

if [[ -z "$light_theme" ]] || [[ -z "$dark_theme" ]]; then
  echo "Error: Missing arguments" >&2
  usage
  exit 1
fi

hugo gen chromastyles --style="$light_theme" > "$destination_path"
{
    echo "@media (prefers-color-scheme: dark) {"
    hugo gen chromastyles --style="$dark_theme" | sed -E 's/(^\/\*[^*]*\*\/)?(.+)/\1 .dark\2/'
    echo "}"
} >> "$destination_path"
Enter fullscreen mode Exit fullscreen mode

The script does add .dark as an extra class in front of all the classes from the "dark-theme" setup to match the class that's added by PaperMod.

For PaperMod specifically I had to do a few tweaks.

  1. Update config.yml|toml|xml
pygmentsUseClasses: true
markup:
  highlight:
    codeFences: true
    guessSyntax: true
    lineNos: true
    noClasses: false
    style: nord
Enter fullscreen mode Exit fullscreen mode
  1. Update the css with some custom overwrites to make sure we're not overwriting the theme background color and fix a few rounded margins
.post-content .highlight table {
    border-radius: var(--radius);
    background: var(--code-bg);
}

.post-content .highlight:not(table):not(:has(.chroma)) {
    margin: 10px auto;
    background: var(--hljs-bg) !important;
    border-radius: var(--radius);
    direction: ltr;
}

.post-content .highlight .lntable .lntd:first-child code {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

.post-content .highlight .lntable .lntd:last-child code {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

.post-content .highlight .lntable .lntd:not(:first-child, :last-child) code {
    border-radius: 0;
}
Enter fullscreen mode Exit fullscreen mode

Needed changes might be slightly different for your theme!

How are you handling code highlights in your theme ?

Top comments (0)