Hello
I mainly use Dracula as my color theme for my CLI tools.
When I ported this theme to Kakoune a few years ago, I arbitrarily decided that the primary selection would be pink and the secondary selections would be purple:
black="rgb:282a36"
pink="rgb:ff79c6"
purple="rgb:bd93f9"
cyan="rgb:8be9fd"
orange="rgb:ffb86c"
…
face global PrimarySelection $black,$pink
face global PrimaryCursor $black,$cyan
face global PrimaryCursorEol $black,$cyan
face global SecondarySelection $black,$purple
face global SecondaryCursor $black,$orange
face global SecondaryCursorEol $black,$orange
Which visually looks like this:
On the screenshot, 3 lines are selected, the last one is the main selection.
I was never truly satisfied by this choice, but it was “good enough”.
Blend()
Recently, there were discussions about Neovim and “fake transparency”. And so I thought : “Why do color themes should be restrained to the exact few colors they define? What about playing with derived colors?”
So the idea is: what if the primary selection remain pink, but the secondary selections would be a “semi transparent pink” instead?
Here’s what it looks now:
On the screenshot above, I computed colors which are at midpoint (0.5) between the pink and the blackish background and use them for secondary selections.
To do so, I wrote this ugly shell function:
blend () {
hexR1=$(printf "$1" | cut -c 5-6)
hexG1=$(printf "$1" | cut -c 7-8)
hexB1=$(printf "$1" | cut -c 9-10)
decR1=$(printf "%d" 0x$hexR1)
decG1=$(printf "%d" 0x$hexG1)
decB1=$(printf "%d" 0x$hexB1)
hexR2=$(printf "$2" | cut -c 5-6)
hexG2=$(printf "$2" | cut -c 7-8)
hexB2=$(printf "$2" | cut -c 9-10)
decR2=$(printf "%d" 0x$hexR2)
decG2=$(printf "%d" 0x$hexG2)
decB2=$(printf "%d" 0x$hexB2)
decR=$(echo "$decR1 + $3 * ($decR2 - $decR1)" | bc)
decG=$(echo "$decG1 + $3 * ($decG2 - $decG1)" | bc)
decB=$(echo "$decB1 + $3 * ($decB2 - $decB1)" | bc)
intR=$(printf "%0.f" $decR)
intG=$(printf "%0.f" $decG)
intB=$(printf "%0.f" $decB)
printf "rgb:%x%x%x" $intR $intG $intB
}
It’s a proof of concept, and I’m sure a shell guru can produce the same result in half the code.
Then to use it:
pink50=$(blend $black $pink 0.5)
cyan50=$(blend $black $cyan 0.5)
…
face global SecondarySelection $black,$pink50
face global SecondaryCursor $black,$cyan50
face global SecondaryCursorEol $black,$cyan50
Here’s the complete script .config/kak/colors/dracula.kak
:
# dracula theme
# https://draculatheme.com/
evaluate-commands %sh{
blend () {
hexR1=$(printf "$1" | cut -c 5-6)
hexG1=$(printf "$1" | cut -c 7-8)
hexB1=$(printf "$1" | cut -c 9-10)
decR1=$(printf "%d" 0x$hexR1)
decG1=$(printf "%d" 0x$hexG1)
decB1=$(printf "%d" 0x$hexB1)
hexR2=$(printf "$2" | cut -c 5-6)
hexG2=$(printf "$2" | cut -c 7-8)
hexB2=$(printf "$2" | cut -c 9-10)
decR2=$(printf "%d" 0x$hexR2)
decG2=$(printf "%d" 0x$hexG2)
decB2=$(printf "%d" 0x$hexB2)
decR=$(echo "$decR1 + $3 * ($decR2 - $decR1)" | bc)
decG=$(echo "$decG1 + $3 * ($decG2 - $decG1)" | bc)
decB=$(echo "$decB1 + $3 * ($decB2 - $decB1)" | bc)
intR=$(printf "%0.f" $decR)
intG=$(printf "%0.f" $decG)
intB=$(printf "%0.f" $decB)
printf "rgb:%x%x%x" $intR $intG $intB
}
black="rgb:282a36"
gray="rgb:44475a"
white="rgb:f8f8f2"
pink="rgb:ff79c6"
purple="rgb:bd93f9"
blue="rgb:6272a4"
cyan="rgb:8be9fd"
green="rgb:50fa7b"
yellow="rgb:f1fa8c"
orange="rgb:ffb86c"
red="rgb:ff5555"
pink50=$(blend $black $pink 0.5)
cyan50=$(blend $black $cyan 0.5)
echo "
face global value $green
face global type $purple
face global variable $red
face global function $red
face global module $red
face global string $yellow
face global error $red
face global keyword $cyan
face global operator $orange
face global attribute $pink
face global comment $blue+i
face global meta $red
face global builtin $white+b
face global title $red
face global header $orange
face global bold $pink
face global italic $purple
face global mono $green
face global block $cyan
face global link $green
face global bullet $green
face global list $white
face global Default $white,$black
face global PrimarySelection $black,$pink
face global PrimaryCursor $black,$cyan
face global PrimaryCursorEol $black,$cyan
face global SecondarySelection $black,$pink50
face global SecondaryCursor $black,$cyan50
face global SecondaryCursorEol $black,$cyan50
face global MatchingChar $black,$blue
face global Search $blue,$green
face global CurrentWord $white,$blue
# listchars
face global Whitespace $gray,$black+f
# ~ lines at EOB
face global BufferPadding $gray,$black
face global LineNumbers $gray,$black
# must use -hl-cursor
face global LineNumberCursor $white,$gray+b
face global LineNumbersWrapped $gray,$black+i
# when item focused in menu
face global MenuForeground $blue,$white+b
# default bottom menu and autocomplete
face global MenuBackground $white,$blue
# complement in autocomplete like path
face global MenuInfo $cyan,$blue
# clippy
face global Information $yellow,$gray
face global Error $black,$red
# all status line: what we type, but also client@[session]
face global StatusLine $white,$black
# insert mode, prompt mode
face global StatusLineMode $black,$green
# message like '1 sel'
face global StatusLineInfo $purple,$black
# count
face global StatusLineValue $orange,$black
face global StatusCursor $white,$blue
# like the word 'select:' when pressing 's'
face global Prompt $black,$green
"
}
Takeaway
Obviously you don’t have to use the dracula theme to benefit from this technique. You can just include the blend()
function in your favorite color theme and then generate new colors to be used to mark subtle gradient effects. For instance, this could be a good idea to used dim colors for phantom selections https://github.com/occivink/kakoune-phantom-selection
Don’t hesitate to share your ideas / screenshots.