Bitten by complicated FP once again

October 10, 2024

Before falling asleep I had some ideas for this website. I want to add a dark mode, use the colors from my Neovim theme and also make some layout changes. I thought using the colors from my theme would be straight forward, since I can just generate some CSS variables as an additional output. There’s a new-ish light-dark CSS function that I can use and generate CSS like this:

--color-yellow-fg: light-dark(#000, #fff);

Except that I can’t. The way my theme works is that it all starts with a configuration table that has two entries, for the light and the dark colors. Each variant is then passed to a make_themes function, which is a mini pipeline that outputs the different light or dark themes (Alacritty, Fish, …). The key realization here is that the pipeline only ever has access to light or dark colors, but never both. Now go back and look at the light-dark function. Yes, it needs both.

This is yet another case of making a system unnecessarily constrained. I’m so often reminded of Rich Hickey these days. If I had just dumped all colors into a map that has the complete “theme context”, and passed that around, it would be much simpler to make modifications now. What makes matters worse is that I wrote all of this in a style that resembles functional programming more than idiomatic Lua. There’s lots of mapkv(fn, merge(tableA, tableB)). This also makes it hard to modify my own code, which I wrote mere months ago.

I guess I’ll start by solving the immediate problem and somehow make the full theme context available everywhere. Next, I’ll untangle the FP stuff.


I stepped away from the computer for a bit which brought me some much needed mental clarity. In the end it wasn’t as difficult as I thought. In fact, a pretty sizeable Lua refactor worked on first try, which is really rare for me, when programming in a dynamic language like Lua. I created a context table that has both light and dark and passed this to the theme functions, which now return a table that has a light and a dark key. I tried relying less on FP constructs but the code is still a bit of a mess. At least I managed to always keep the code in a working state. The last hurdle is that the string producting template functions are all called with a huge table that has the key/value color pairs from all themes in it. The idea behind this is that one theme can reference colors from another. This is actually one of the two key ideas behind this theme. Why is this annoying now? I want to create one CSS variable for every base color value my theme has (light.sucess.fg, dark.success.fg, …). The way this works in all themes so far is that you create a template string:

--color-light-success-fg: light-dark(${light-success-fg}, ${dark-success-fg})

This feels tedious though. Can’t I just generate those lines automatically based on the variable names? The problem here is that I have this huge blob of stuff and there’s currently no way to differentiate which slice of keys comes from which theme. It would be nice, in retrospect, if that big table worked like this

local colors = {
  nvim = {
    Normal = {},
  },
  css = {
    LightSuccess = {}
  }
}

instead of

local colors = {
  Normal = {},
  LightSuccess = {},
}

The pragmatic thing for now is to stick with the repetitive string templating.