Files
apex/apex-neon.md
mpuchstein 43dcb71d15 docs: promote apex-neon.md/apex-aeon.md to canonical specs
Rename the v2 spec docs to drop the -v2 suffix and make them the authoritative source of truth. Slim GEMINI.md to a pointer and update CLAUDE.md, AGENTS.md, README.md, INSTALL.md to v2 semantics and the current 15-app target list.
2026-06-03 02:08:47 +02:00

13 KiB
Raw Permalink Blame History

Apex Neon — Dark Theme Specification

Apex Neon is a high-contrast dark theme for terminals, editors, and system UI. It is a rule system, not a palette. Color carries meaning, not decoration.

Thesis. A cold neon HUD on deep black. Hue carries meaning, sparsely. Red = danger. Violet = attention. Both are absent from code, so they always strike. The forest (syntax) is loud and typed by grammatical role so the hunter navigates fast — but the predator and the crosshair stay the only red and violet things in it.


0. What changed from v1

v1 problem v2 fix
Red overloaded — meant self/cursor and danger simultaneously Hue split: red = danger only, violet = attention (cursor/search/pairs)
color4 ≡ color6 and color12 ≡ color14 (cyan/blue collision) Blue split into a real channel (#2e6bff / #5b8cff)
Comments double-defined (Stealth and Muted) Comments = Ghost; Stealth = ignored text only
Backwards bright-red (#ff8899 softer than base — "escalation" read calmer) #ff3b3b — hotter and truer red
No diff/git colors Figure/ground diff tints added
No syntax model Role-typed syntax across all languages
#050505 near-pure black (halation, crushed surfaces) #070709 — deep, cool-tinted, halation-safe

1. Semantic Spine — five hues, zero overlap

The core invariant. Every color does exactly one job.

Hue Meaning In code buffer? Temp
Red Danger — error, critical, destructive never warm
Violet Attention — cursor, search, matched pairs never cool
Cyan Information / actions (functions, bang-macros) cool
Blue Kinds (types, storage, attributes) cool
Green Success / values (strings, numbers) cool-warm
Amber Warning — UI only never warm

Keystone rule. Red, violet, and amber never appear inside a code buffer. When an error (red), the cursor/search (violet), or a warning (amber) fires, it pops against a field that contains none of those hues — however loud the syntax is.


2. Operating Principles

  1. Saturation budget. Neon is for signal. Body text is near-neutral. Striking comes from restraint against contrast — not coverage.
  2. Figure / ground. A bright hue marks the one thing (cursor, error text); a dim hue field marks the set (search hits, error region, diff block). Applied to both red and violet.
  3. Hue rotates for severity. info → warn → error → critical rotates hue; it never ramps lightness within a single hue (pre-attentively unreadable).
  4. Loudness is allocated by grammatical role, not sprayed. Code can be vivid; uniform saturation is noise. Each grammatical class owns one hue family.
  5. Pre-attentive bandwidth is ~57 categories. The limit is the feature: scarcity is what makes a signal pop. The display is not the constraint; the visual cortex is.

3. Neutrals — the readable 80%

Anchored at #070709. Faint cool bias (B ≳ R) reinforces the cold scan and makes warm signals pop by complementary contrast.

Hex Name Role
#070709 Void Main background, terminal background
#101218 Hull Inputs, inactive tabs, widgets
#181b22 Plating Panels, separators, scroll track
#262a34 Seam Visible borders, line-highlight background
#383d49 Stealth Ignored / suppressed text (sub-threshold by design)
#8b909c Ghost Comments, subtitles, muted labels
#e6e8ec Stark Default body text (not pure white — fatigue / halation)
#ffffff Flare Extreme highlight only

Rule. Depth comes from contrast, not brightness. Nothing load-bearing ever sits at Stealth.


4. Signal Layer — figure / ground

DANGER  (red — warm, alarm)
#ff0044  Razor Red     error figure, destructive action, active alarm
#1f0a12  Ember Field    error region background (+ diff-delete background)
#ff3b3b  Bright Red     ANSI color9 / escalation

ATTENTION  (violet — cool, scanner; underline-shaped)
#d68fff  Focus Max      cursor — the crosshair (the single hottest mark)
#b14dff  Focus Active   current search match, matched bracket / pair
#3a1a4d  Focus Field    other search matches, selection background

INFORMATION
#00eaff  Electric Cyan  info / links
#00ff99  Acid Green     success / diff-add
#ffb700  Amber          warning / diff-change  (UI only — never in buffer)
#9d00ff  Violet (deep)  reserved special / root (ANSI color5)

5. Interaction & State

Element Color Form Notes
Cursor #d68fff Focus Max underline Sits below a search match's bg+fg — all three stay visible.
Current search match #b14dff Focus Active fg The one match you are on.
Other search matches #e6e8ec on #3a1a4d field The set; dim violet ground.
Selection #e6e8ec on #3a1a4d field "Region scanned." fg preserved (no black-on-red).
Matched bracket / pair #b14dff fg Attention, not syntax — same act as search.
Active line neutral bg #101218 Line number in #d68fff is the crosshair; near-zero red coverage.
Active border #ff0044 edge Focused window/element.

Known overlap. Cursor underline (violet, straight) and error undercurl (red, wavy) share the bottom-line layer. On an error token under the cursor they coexist — distinguishable by shape and hue, but visually busy. Accepted, not a surprise.


6. Severity Ladder — rotates hue, never a red ramp

info        warn        error                    critical
#00eaff  →  #ffb700  →  #ff0044  →  figure on ground: #ff0044 on #1f0a12
 cyan       amber       red          the whole region lights red

Critical does not get a new hue (rotating past red collides with amber or violet). It escalates via field — error-red figure on a dark-red ground. This is the only place red becomes a background. Maximum alarm, maximally rare.


7. Syntax — loud, typed by grammatical role

Model: blue = kinds · cyan = actions · green = values · neutral = connective tissue · bold/bright = navigation. Roles are language-invariant (see §10).

Rust reference map

Token Hex Family / weight
Comment #8b909c italic neutral — not a target
Punctuation / delimiter #aab0bc neutral, lifted
Operator #c2c8d4 neutral
Variable / param #e6e8ec Stark body (everywhere = calm)
Property / field #cdd4e3 dim Stark
Type / struct / enum / trait #7db4ff blue — kinds
Storage / modifier (pub mut async) #2e6bff bold blue, heavier
Attribute (#[derive]) #2e6bff italic blue — declarative
Function def / call #5af3ff bold cyan — actions
Bang-macro (println!) #5af3ff bold italic cyan — action + ceremony
self / builtin #8fd6ff italic cyan, dim
String #2bffb2 green — values
Char / escape #7dffd0 green, brighter
Number / bool / const #37dba0 green, cooler

Navigation — split static vs dynamic

Kind Examples Color
Static control flow if return break continue match, \begin \end, goto #5b8cff bold bright-blue — trail markers
Dynamic pairing bracket-under-cursor partner, \begin\end, jump target #b14dff violet — this is attention, not syntax

8. Diff / Git

Dark tinted grounds, neon figures. Never full-saturation blocks.

State fg bg
Add #00ff99 #0a1f16
Change #ffb700 #1f1a0a
Delete #ff0044 #1f0a12 ← reused Ember Field

9. Terminal ANSI Table (16-color fallback bank)

Truecolor-first: design in exact hex everywhere the app emits 24-bit. This bank is the floor for anything emitting raw 16-color codes. Skip the 256-color cube entirely.

Normal Bank (07)

Slot Name Hex Meaning
color0 Black #070709 Background
color1 Red #ff0044 Danger / error / destructive
color2 Green #00ff99 Success
color3 Yellow #ffb700 Warning
color4 Blue #2e6bff Kinds / structural (real blue)
color5 Magenta #9d00ff Special / root
color6 Cyan #00eaff Info / actions
color7 White #e6e8ec Default text

Bright Bank (815)

Slot Name Hex Meaning
color8 BrBlack #383d49 Muted / suggestions
color9 BrRed #ff3b3b Escalation / alert (hotter than base)
color10 BrGreen #2bffb2 Active success / values
color11 BrYellow #ffd24d Urgent warning
color12 BrBlue #5b8cff Active flow nav / focus highlight
color13 BrMagenta #c84dff Elevated attention state
color14 BrCyan #5af3ff Active tech signal / functions
color15 BrWhite #ffffff Extreme highlight only

Multiplexer warning. tmux silently downsamples truecolor to the 256-cube if misconfigured — which collapses the figure/ground tints. Assert passthrough:

set -g default-terminal "tmux-256color"
set -ga terminal-overrides ",*:RGB"
# verify the chain carries 24-bit (run inside and outside tmux — should not shift)
printf '\033[38;2;255;0;68mtruecolor\033[0m\n'

Neovim additionally requires vim.o.termguicolors = true.


10. Cross-Language Role Mapping

Color follows roles, not tokens. Surface syntax differs across languages; the deep grammar does not. One palette, every grammar — more languages means more query mappings to maintain, not more colors.

Role → hue Rust Python TypeScript Go Java Bash/sh Nushell
Kinds (blue) struct trait class, hints interface type struct interface class enum — (untyped) int record table
Actions (cyan) fn / m! def, methods fn, arrow, methods func methods commands, echo def, commands
Values (green) str / num str / f-str / num str / template / num literals literals quoted str literals / records
Declarative (blue italic) #[attr] @decorator @Decorator struct tags @Override
Flow nav (bold blue) if match if for try if switch if for go defer if switch if/fi for/done case if/else for
Connective (neutral) + | :: + . + . => + . := + . | && $ > | $ ;

Per-language judgment calls (handle with weight/style, never a new hue)

  • Family skew. TypeScript is type-saturated (blue-heavy); bash is command-saturated (cyan-heavy). Resolve with brightness/weight tiers inside the family (e.g. dim built-in types, bright user types).
  • Role-ambiguous constructs. Bash $VAR / ${...} / $(...) expansion, Python f-strings / TS template literals (green string containing live code), Nushell pipelines | (connective but navigationally load-bearing). Map each onto an existing family with a weight/style tweak per-language.
  • Identity pronouns. self / this / $ → dim cyan italic everywhere.

11. Accessibility Contract

  • Body text #e6e8ec on Void #070709 — high contrast, low saturation. The resting surface. (≈ 16:1, well above WCAG AAA.)
  • Comments #8b909c on Void — readable (≈ 6.5:1, AA for normal text).
  • Stealth #383d49 on Void — sub-threshold by design; ignored text only, never load-bearing.
  • No neon on neon. Saturated signal colors are never placed on saturated backgrounds; signal always sits on a neutral or dim field.
  • Verify exact ratios per render target; near-black + neon halation is panel-dependent and only confirmable on the actual display.

12. Rules with Teeth

  • Red is danger. Violet is attention. Neither ever appears inside code.
  • Cyan is information and action. Never errors, never destructive.
  • Amber is warning — UI only. It never enters a buffer.
  • Bright = escalation. If nothing changed, don't use a bright variant.
  • Backgrounds stay dark. Text and state carry the signal.
  • One color, one meaning. If a color has no semantic reason, it's wrong.
  • Loud is earned by role. Vivid syntax is fine; uniform saturation is noise.
  • Scarcity is the weapon. A signal only carries information when it is rare.

Apex Neon is not a palette. It's a rule system with teeth.


Open calibrations (verify on a real buffer — not on paper)

  1. Cyan budget in dense code. Functions, bang-macros, self, and info/links all live in cyan. A function-heavy file may read as a cyan wash. If so, drop functions to mid-cyan and reserve bright #5af3ff for sparse use.
  2. Blue vs cyan separability. #2e6bff (kinds) vs #5af3ff (actions) are both cool and adjacent. Types-vs-functions is a constant distinction; confirm they split cleanly on #070709 first.
  3. Green double duty. Values (#2bffb2) and success (#00ff99) are close. Low collision risk (success rarely renders inside code), but noted.