I was reading an article by Nikita Prokopov about how “everyone is getting syntax highlighting wrong” [1]. It struck me that I never really thought about my code being highlighted. I’m pretty basic and have used the default VSCode Dark Theme since I switched away from JetBrains forever ago. I’ve gotten so accustomed to the same theme and colors that over the years I became blind to it.
This section in particular of Nikita’s post stood out to me:
Here’s another test. Close your eyes (not yet! Finish this sentence first) and try to remember what color your color theme uses for class names?
Can you?
If the answer for both questions is “no”, then your color theme is not functional. It might give you comfort (as in—I feel safe. If it’s highlighted, it’s probably code) but you can’t use it as a tool. It doesn’t help you.
What’s the solution? Have an absolute minimum of colors. So little that they all fit in your head at once.
I know theres some blue, other blue, and for sure some purple in there… not really sure which is which though. Ok, so they are saying that if I can’t remember, its because there’s too many colors in my theme. Well, lets just make a new one with fewer colors, how hard could it be?
I’m sure you can guess where this is already heading, but it turns out if you’re trying to do a light theme, it’s actually really hard! Basically, in order to have contrast with a white background, colors need to be darker than they would be on a dark theme. Darker colors are less vibrant, and thus offer less perceptual impact.
After messing around, I found that it was hard for me to make a theme that felt like it belonged on this site (mostly 1 bit aesthetic). It’s not that the colors didn’t work to make certain sections stand out, it was that they made things stand out too much.
That experince got me thinking…
Why should code even be highlighted in the first place?
So the obvious answer seems to be some combination of two things:
I want to be able to tell code apart(selective attention)
I want to find certain code by sight(quickly locate)
While I still agree with the premise, after a bunch of experimentation I concluded that in order to answer how I would make elemets stand out and locatable, I needed to first answer what elements should get that treatment. I let that question rattle around my head while also banging it against the wall desperately trying to get some color theme to work.
At this point in the process, I didn’t really like any light themes I saw—they were either too hard to read or too monotonous (at least compared to the absolute impact of dark themes).
This got me thinking even further…
Why does highlighting even need to be in color?
What if we just tried to see how far we could get without using any color contrast, and only using tones?
First step, remove all color syntax and rip it raw black on white.
Stripped Down to NothingStripped Down
/** Await all values in a property and preserve original types */
async function resolveProperties<
const T extends Record<string, unknown>,
>(properties: T): Promise<{ [K in keyof T]: Awaited<T[K]> }> {
const entries = await Promise.all(
Object.entries(properties).map(async ([key, value]) => [
key,
await value,
] as const),
);
return Object.fromEntries(entries) as {
[K in keyof T]: Awaited<T[K]>;
};
}I uhhhh… wow I really hate that. It’s so flat I feel like I have to read every character individually for my eyes to properly navigate! Unlike prose, where you usually read it linearly, with code you bounce around based on what connected elements you are exploring. This is why the quickly locatable aspect matters!
We are going to need to add back in some kind of contrast here.
So in order to prevent this post from going on forever, we are going to skip through the entire me playing around stage and just cover the end results.
Grayscale Highlighting ExampleWith Highlighting
/** Await all values in a property and preserve original types */
async function resolveProperties<
const T extends Record<string, unknown>,
>(properties: T): Promise<{ [K in keyof T]: Awaited<T[K]> }> {
const entries = await Promise.all(
Object.entries(properties).map(async ([key, value]) => [
key,
await value,
] as const),
);
return Object.fromEntries(entries) as {
[K in keyof T]: Awaited<T[K]>;
};
}So base text is still just black on white, because we need some mid point to center our focus. Variable and function references stay baseline, because thats just most of the code and you can’t pay special attention to most of anything. Things that differ are, in order of importance:
return, throw, yield are all branch terminators and mark where code in the function’s scope can stop or pause execution. These are structural and should be pretty quick to find by sight.Now on the flip side, it dawned on me that similar to how I want to pay more attention to some areas, I also wanted to pay less attention to others.
let, const: I basically never need to look at them after typing them out initially. These could be all but invisible and it would make no difference to me.Outside of the static highlighting, we have the dynamic click based highlighting for same-words and scope/bracket locating.
The idea here is that the highlighting should let your eyes skip between the pieces that matter the most when reading, so that you can focus on the semantic parts and skim over the purely syntactical. It should be distinct enough to offer real value, but subtle enough that parts I am not trying to look at don’t distract from what I am.
Comments and More Control FlowControl Flow
type Job = {
id: string;
label: string;
enabled: boolean;
};
async function* completedJobs(jobs: Job[]): AsyncGenerator<string> {
for (const job of jobs) {
if (!job.enabled) continue;
// Keep the request lazy so disabled jobs never wake the service.
const response = await fetch(`/api/jobs/${job.id}`);
if (!response.ok) continue;
const result = (await response.json()) as {
status: "complete" | "failed";
message: string;
};
if (result.status !== "complete") continue;
yield `${job.label} (${job.id}): ${result.message}`;
}
}Overall, optimizing highlighting based on what I wanted to pay attention to was a really interesting exercise in figuring out what exactly it was that I wanted to pay attention to.