Go to Uphill Solutions' GitHubSubscribe via RSS
Contact Us
Neovim12 min read

Syntax Highlighting as a Second Language


I can read a programming language I’ve never learned.

Not because I know its syntax, but because syntax highlighting exposes the roles beneath the surface. I can see what defines structure, what performs work, and where execution changes direction or fails.

Programming languages may differ in grammar, but they repeatedly express the same underlying ideas. Most colorschemes assign colors to those ideas without showing how they relate or which ones deserve attention first.

Oasis.nvim uses color to establish both relationships and priority. Routine syntax forms the pattern of the program, while meaningful output, changes in behavior, and interruptions to execution break through it.

The result is a visual language that can be learned.

Hierarchy directs attention

Color creates priority whether a colorscheme intends it to or not.

Brightness, contrast, saturation, frequency, and surrounding colors all influence what the eye notices first. Learned associations and cultural background add another layer. In software interfaces and warning systems, red commonly signals errors, danger, or stopping, so Oasis reserves it for moments when execution changes or fails.

A bright color used everywhere can disappear into the background, like a tree hidden in a forest. A rarer color can command more attention, like a red cardinal among green branches. Rarity makes it distinct; complementary contrast makes it difficult to ignore.

A bright red cardinal standing out against dense, dark-green evergreen branches

Oasis applies the same principle within a different landscape. Khaki yellow forms the desert floor of routine execution. Orange rises above it for values that influence behavior, while red is reserved for interruptions, failures, and early exits.

The greater the consequence, the stronger the emphasis. A routine declaration should settle into the surrounding terrain. An early return should break through it.

That hierarchy becomes easier to see when the same underlying roles are expressed through different syntax.

The same program in two languages

Consider the same program written in Ruby and Go. Both versions define a Person, validate its age, determine adulthood, and process a collection while handling invalid input.

Before reading either example closely, try to locate four things: where execution exits or fails, where function parameters are used, where a value is absent, and where classes or types appear.

Ruby and Go side by side using Oasis, demonstrating consistent colors for equivalent semantic roles

The syntax is different, but several roles are immediately comparable.

Red stands out first. In both examples, it marks returns, exceptions, errors, and early exits: places where execution changes direction or stops.

Rose isolates strings: messages and content that may reach a user. Orange identifies numbers, booleans, and constants: values that often govern how the program behaves.

Iris purple marking an absent value and palm green marking a function parameter in the same Ruby and Go example

At opposite boundaries of the warm and cool palette, iris purple and palm green break sharply from the dominant syntax. Iris marks the deliberate absence of a value, while palm draws attention to parameters as data enters and moves through a function.

Sky blue and muted teal highlighting properties and structural syntax in the same Ruby and Go example

Sky blue is quieter, marking properties and fields, while muted teal recedes further into structural context. Ruby’s class Person and Go’s type Person struct look different syntactically, but their shared teal treatment reveals the same underlying role.

These distinctions turn the palette into a search aid. An absent value can be found by scanning for iris. Parameters can be found by scanning for palm. Because each color is narrowly assigned, the reader can locate a role before reading every token around it.

Through color, contrast, and restraint, Oasis guides the reader through both languages in the same way.

OneDark: A hierarchy buried by noise

However, the same program produces a very different reading order in One Dark Pro, one of the most widely installed themes for VS Code.

The same Ruby and Go snippet from earlier rendered in OneDark

The same snippet in OneDark.

The first thing that stands out is red, but for the opposite reason it stands out in Oasis. Rather than marking a rare interruption, it becomes the most dominant color in the Go example, repeated across properties and parameters such as people, Name, Age, and err.

Red repeated across properties and parameters in OneDark’s Go example, dominating the syntax instead of marking rare interruptions

Red is an inherently forceful hue. Its brightness and contrast draw the eye, but its frequency turns that emphasis into the visual baseline. Once the strongest signal is spread across routine data, it no longer establishes priority; it only adds visual noise.

Purple and orange spread across unrelated semantic roles in OneDark

Red is the most obvious source of visual noise, but it is not the only one. Purple spans statements, conditionals, return, continue, rescue, and typedef annotations, leaving routine execution, changes in control flow, error handling, and structural context without meaningful visual separation.

Orange creates a similar problem by treating numbers, booleans, and nil alike. Ordinary values and the absence of a value become visually indistinguishable.

The palette also fails to preserve some relationships between closely related roles. A type is yellow while its typedef is purple, visually separating two forms of structural information that should remain connected.

These assignments are not arbitrary. Statements and control flow are purple, types are yellow, functions are blue, strings are green, and numbers are orange. OneDark contains an underlying semantic system, but overloaded colors and broken relationships obscure it. Its strongest signals compete so heavily that the hierarchy underneath them becomes difficult to see.

Catppuccin: Revealing the hierarchy

Catppuccin for Neovim begins with nearly the same semantic assignments as OneDark.

The same Ruby and Go snippet from earlier rendered in Catppuccin

The same snippet in Catppuccin.

The resemblance is not superficial. Both schemes assign the same broad color families to nearly every semantic role:

Semantic role OneDark Catppuccin
Statements / flow Purple Purple (mauve)
Types / classes Yellow Yellow
Functions / methods Blue Blue
Parameters Red Pink (flamingo)
Properties Red Light purple (lavender)
Strings Green Green
Numbers Orange Orange (peach)

That similarity makes the comparison more revealing. Catppuccin does not replace OneDark’s semantic model with a different one. It preserves most of the same assignments while correcting one of OneDark’s most disruptive collisions.

Parameters become pink. Properties become light purple.

This distinction does more than add another color. It prevents red from dominating the program, reduces competition across the palette, and gives the eye enough room to notice the organization that was already present underneath.

Statements, control flow, and functions grouped in cooler colors while types, parameters, and literals lean warmer in Catppuccin

Once the overloaded red signal is removed, the broader reading order becomes easier to see. Executable roles such as statements, control flow, and functions cluster within cooler families, while types, properties, parameters, and literals form a contrasting data-and-structure layer.

Cool Warm Neutral
Action and control Data and structure Syntax and support

The hierarchical system buried beneath OneDark’s visual noise becomes easier to see. Related roles remain connected through shared color families, while differences in hue separate broader categories.

Catppuccin also uses shade to distinguish roles that occupy nearby parts of the palette. Lavender properties remain lighter and less forceful than the darker purples used for statements and control flow, preventing the roles from collapsing into one another.

Types and typedefs receiving the same treatment in Catppuccin

Although the hierarchy is clearer, some finer distinctions remain unresolved. In the image above on line 15, the primary type Person and typedef annotations such as bool and error still receive the same treatment, making structure and descriptive context appear identical. Built-in constants such as nil also still share a color with booleans and numbers, making exceptional values harder to locate at a glance.

Comments, an imported namespace, types, and typedefs showing Catppuccin’s inconsistent use of italics

The palette also provides no universal visual signal for background context. Comments are italicized, but so are imported namespaces and conditionals, while other contextual elements such as LSP inlay hints and typedefs remain upright. Because the italicized roles share no clear semantic relationship, italics read as loosely applied emphasis rather than a meaningful distinction between supporting information and primary code.

Relationships create hierarchy

Catppuccin shows that a colorscheme can organize tokens into a coherent reading order rather than merely classify them.

Oasis preserves that relational structure, but reverses the temperature assignments and translates the palette into a desert landscape.

Warm Cool Neutral
Action and control Data and structure Syntax and support

Actions change state, redirect execution, and produce effects, so they receive the greater visual weight of the warm palette. Data and structure form the stable context around that activity and settle into cooler colors. Neutral elements remain visible without competing with either.

These assignments are organizing tendencies rather than rigid categories. The full palette shows both the dominant warm–cool split and the deliberate exceptions made for values that carry unusual behavioral or visual weight.

Statements, conditionals, types, typedefs, parameters, properties, literals, strings, nil, return, and LSP inlay hints showing Oasis’s semantic color families

Statements and conditionals occupy warm khakis and yellows, while types, typedefs, parameters, and properties move through cooler teals, greens, and blues. Numbers and booleans lean orange because they often influence behavior. Strings move toward rose as visible content. nil breaks into iris because the deliberate absence of a value deserves a distinct signal. Syntax, comments, and LSP inlay hints recede into neutral tones around them.

Temperature establishes the broad semantic domain. Hue identifies roles and orders them by visual priority. Shade creates finer hierarchy among closely related roles.

A darker khaki conditional contrasted with lighter khaki statements in Oasis

A conditional and a general statement both belong to the khaki family because both direct execution. The darker conditional signals a change in the program’s path, while the lighter statement marks routine execution.

Shade can also work alongside typography to distinguish primary syntax from supporting context. A type and a typedef both belong to the teal family because both describe structure, but the typedef uses a darker shade and italics to recede as supporting notation.

Comments, LSP inlay hints, preprocessor information, and typedefs rendered in italics to distinguish supporting context from primary code

Oasis treats typography as a semantic layer rather than decoration. Color identifies a token’s role and hierarchy, while italics mark supporting context that should remain visible without competing with executable code. Comments, LSP inlay hints, preprocessor information, typedefs, and similar annotations therefore recede from the primary reading order without disappearing from it.

Taken together, Oasis organizes these relationships across five layers of visual information:

Layer Communicates
Temperature Broad semantic domain
Hue Semantic role and visual priority
Shade Finer hierarchy within a color family
Typography Primary code versus supporting context
Frequency / contrast Interruption and visual prominence

The result communicates not only what a token is, but how it relates to the code around it.

A deliberate tradeoff

Oasis is intentionally denser than a minimal colorscheme. It asks the reader to learn more distinctions in exchange for making more relationships visible. That density only works when each signal remains narrow, stable, and consistent; otherwise, it becomes the same visual noise the system is designed to avoid.

The mappings are conventions, not universal truths. Red does not inherently mean failure, nor does teal inherently mean structure. Another coherent colorscheme could reverse those assignments, placing data in warm colors and statements in cool ones. The goal is not to discover the one correct mapping, but to build a system whose relationships remain predictable once learned.

Taste determines whether those mappings feel natural and how much visual differentiation a reader wants. Oasis favors warm action, cool structure, and visible hierarchy over visual restraint.

A second language

Syntax highlighting cannot teach you a programming language. It cannot explain an unfamiliar API, tell you what a function does, or replace an understanding of the code.

What it can do is give unfamiliar syntax a recognizable visual structure. It can help you see what is acting, what is being acted upon, what defines structure, where control flow changes, and what deserves attention first. It can also make specific roles easier to find without reading every token around them.

Traditional highlighting often stops at classification. Oasis gives those classifications a structure the reader can learn.

The reader does not need to understand color theory to use those relationships. They become useful through coherent assignments repeated consistently.

With enough exposure, the distinctions become instinctive. Actions feel different from data. Routine execution feels different from interruption. Related concepts begin to look related before the reader can explain why.

Intuitive systems are not self-explanatory by accident. They teach through consistency.

At first, you notice temperature.

Then you recognize hue, shade, and typography.

Eventually, you follow hierarchy and relationships.

That is when

Syntax highlighting stops behaving like decoration.
It becomes a second language.

Back to Insights

Good software makes its structure visible.

UpHill Solutions applies the same systems thinking to workflows, internal tools, and custom software.

Let's Talk