Writing
Ivory, a near-monochromatic Emacs theme
Introducing Ivory, a pair of near-monochromatic Emacs themes.
I’ve been building an Emacs theme on and off for a while, and it’s reached the point where I use it every day. It’s called Ivory, and it ships as a pair: ivory-light and ivory-dark. It’s near-monochromatic, meaning almost every color in it is a shade of gray. I’d tried a few other monochrome themes first, kept catching on small things I didn’t like, and eventually just rolled my own.

The case against syntax color
Open any popular theme and look at a function body. Keywords get one color, strings another, types a third, constants a fourth, comments a fifth, and a builtin or two picks up its own accent somewhere in there. The pitch is always that color helps you parse code at a glance.
I don’t think it really does. When I read code I’m not hunting for the purple tokens. I’m following structure: this is a function, here’s its body, this is what it returns, that branch is a comment I can skip. The color rides along on top of structure I already get from indentation and the shape of the tokens. Most of the time it’s decoration that happens to line up with meaning.
And decoration costs something. Six saturated hues fighting for attention in one buffer is just noise. Past a certain density a heavily highlighted file stops looking like information and starts looking like unicorn vomit, color sprayed everywhere with no hierarchy to it. It’s the kind of loud you stop noticing, the way you stop hearing a fan, except your eyes are still sorting it whether you asked them to or not. And once one accent starts to bug you, you start seeing all of them.
The actual point of syntax highlighting was never color for its own sake. It’s contrast. You want the tokens that matter to stand out from the ones that don’t, so your eye knows where to land. Color is just the most common way themes reach for that contrast. It’s not the only way, and I don’t think it’s even the best one. So Ivory drops the syntax palette. The color, not the contrast. Which leaves the real question: with color gone, what pulls things apart?
What carries the distinction instead
Three things, mostly.
Foreground intensity. A gray buffer still runs from near-background to full strength. Primary code text sits near the top; comments and other quiet text sit lower. That’s a hierarchy for free, and it tracks importance instead of some arbitrary token category.
Weight. Bold does most of the work worth doing. Type names, function names, and constants stay bold, because in a gray buffer those are the anchors your eye actually wants. Keywords and builtins carry weight too. Variables and properties stay normal weight, a notch quieter. It’s on by default; flip ivory-themes-bold-constructs if you don’t want it.
Surfaces. Backgrounds pull more weight than you’d expect. A code block, a highlighted region, the active mode line, a search match: each gets a background shift instead of a foreground color. The text on top stays legible, and the area still reads as special.
Italics are off by default. In code I find them more distracting than useful, but there’s an option if you disagree.
None of these are color decisions. They’re attention decisions. Weight and intensity say look here or skip this, which is the only thing I actually want a theme to tell me.
The grayscale ladder
Hex is an awkward way to describe grays. #999999, #8a8a8a, #595959 are all just “some gray,” and you can’t tell which is lighter without decoding the channels.
Gray has one property the rest of the color space doesn’t: the three channels are equal, so a single number pins it down completely. #8a8a8a is 138, 138, 138. In Ivory’s palette source that color is written (gray 138). (gray 0) is black, (gray 255) is white, and the middle of the buffer lands around (gray 138).
That turns the palette into one ladder. Want softer comments? Raise the number a few rungs. Want dark-mode dim text to step further back? Lower it. The relationships become arithmetic you can read instead of hex you have to compare, and it made tuning the theme far less fiddly. I was reasoning about steps on a ladder, not guessing at hex values.
Every role stays above a 3:1 contrast floor against its background. Primary text sits well clear of it. The floor is really there for the quiet stuff, comments and dim labels and completion annotations, so they stay legible without shouting.
Light mode hugs pure white with faint gray surfaces. Dark mode needed a wider spread to keep from going flat, so its ladder runs from a bright foreground all the way down to some very dark surfaces.
Depending on your screen, pure black and pure white backgrounds can come off harsh. That’s what the softened background option is for. ivory-themes-soft-backgrounds nudges the base just off the end of the ladder, #f8f8f8 for light and #080808 for dark, and moves the neighboring surfaces to match. You can toggle it without a restart.
Where color is allowed back in
Ivory is near-monochromatic, not strict about it. In a few places, color is allowed back in: diffs, diagnostics, and version control.
Diffs keep red and green. We’re all used to red for removed and green for added, so Ivory leaves that be.

Diagnostics keep red, yellow, and green for error, warning, and note. The underlines are straight, though, since I prefer straight lines to squiggles.
Everything else that’s normally colorful goes gray. Magit’s log and Transient’s popup menus both ship full of color by default, and Ivory tones them down to match the rest of the editor.
Trying it
It’s on GitHub. With straight.el and use-package:
(use-package ivory-themes
:straight (:type git
:host github
:repo "khzaw/ivory-themes")
:custom
(ivory-themes-bold-constructs t)
(ivory-themes-italic-constructs nil)
:config
(load-theme 'ivory-dark t))
There are :vc and elpaca recipes in the README too, plus the options and the palette override system, if you want to push it toward more contrast, warmer comments, or stronger diffs.
I won’t pretend a monochrome theme is for everyone. If you love your rainbow, keep it. But if your editor has ever felt a little too busy, and you’ve quietly suspected the color wasn’t really helping you read, Ivory is that suspicion taken seriously.