Live-tuning ef-themes diff fringe colors and contrast tables
- YT :: https://www.youtube.com/watch?v=JnDcXPzFsfA
- Original title :: Emacs: refining ef-themes Diff/Magit colors
A spontaneous live stream in which Prot fixes inconsistent diff colors across his ef-themes package. The diff-hl fringe indicators looked wrong because ef-themes, unlike the underlying Modus themes, lacked dedicated bg-*-fringe palette entries. He writes throwaway Elisp to add bg-added-fringe, bg-changed-fringe and bg-removed-fringe to every theme. Then he hand-tunes each value, theme by theme, by eye. Finally he generates per-theme Org contrast tables for the diff colors. Along the way he answers chat questions on thread-last, his hex-color preview, Herbstluftwm, his writing, and AI-assisted programming.
The problem
- In
diff-mode, Magit and Ediff the ef-themes diff colors looked fine; onlydiff-hlfringe indicators were off, and inconsistent between themes. - The Modus themes define
bg-changed-fringe(and friends) as more saturated variants of the subtle diff backgrounds. ef-themes had faint and refine variants but no fringe, so diff-hl fell back to unsuitable colors.
Generating the new palette entries
- A test git repo with added, changed and removed lines gives a live preview.
- A command finds all
ef-*-theme.elfiles (directory-filespiped throughseq-filterviathread-last). In each file it regex-searches forbg-added,bg-changedandbg-removed, then inserts a*-fringeentry on the next line, and finally re-indents and saves. - The starting value comes from
modus-themes-adjust-value: brighten for dark themes, darken for light ones (direction picked withmodus-themes-color-dark-p). 20% was too intense, so he settled on 10–15%. - Small debugging detours: capturing the quotes inside the regex group, using
match-string-no-properties, and newline placement.
Hand-tuning every theme
- Automated adjustment is "technically correct" but often looks wrong. Prot cycles through every dark theme, then every light theme, and edits hex values directly, relying on years of intuition about color.
- Recurring adjustments: make reds less saturated or more orange, make greens less blue (more olive/neutral), and make yellows less greenish.
- Color has to be judged against the theme's background and neighbouring colors, not in a vacuum: a yellow with good contrast can still vanish against a greenish background.
- Light themes are harder. Pure yellow is unreadable on light backgrounds, so "yellow" turns brownish or golden, while red has to lean pinkish to stay distinct from it. Mistakes show more on light themes; dark themes hide them from the untrained eye.
- Color-deficiency variants keep their intent: the tritanopia themes use cyan rather than green for "added".
- After the fringes he reviews Ediff refined-change colors across all themes and makes a few subtle ones more pronounced.
- His point: attention to these small details is what makes a good theme, and it is very time-consuming.
Contrast tables
- A second throwaway function reads each theme file into a temp buffer. It collects foreground plus default/faint/refine backgrounds for added/changed/removed, builds three Org tables with a luminance-contrast formula, and appends them under the theme's heading in the contrast-ratios Org file.
- A keyboard macro then recalculates formulas and fixes the headings across all themes.
- Contrast is generally high. Some refine values fall below WCAG AA, which he accepts because refined diffs are for quick scanning, not reading prose.
- He notes Emacs now shows a new Dired process-output limit warning, and that pushing to two remotes in VC is clunky compared with Magit.
Chat topics
thread-lastpasses each form's result as the last argument of the next form;thread-firstpasses it as the first. It is purely a matter of style.- The hex-color swatches come from his own small hack, not
rainbow-mode: he wanted the swatch beside the text rather than replacing it, and fewer quirks withhl-line-mode. - Herbstluftwm ships no bar; he uses Polybar.
- He writes a lot: his 2026 output alone exceeds many blogs' lifetime output, and his writing is philosophical whatever the topic.
- On AI: the technology case is made and AI is here to stay; the business case (real quality and productivity) is unproven. Writing code yourself builds the intuition needed to judge AI output. Delegating thinking instead of augmenting it makes you less capable relative to your own baseline.
- He will look at community themes when time allows, but a proper theme review requires actually using it.