fix(marimo): comprehensive colorblind-safe syntax highlighting theme
Some checks failed
CI / skinny-install (aco) (push) Successful in 57s
CI / skinny-install (api) (push) Successful in 41s
CI / skinny-install (bib) (push) Successful in 34s
CI / skinny-install (bcda) (push) Successful in 36s
CI / skinny-install (bls) (push) Successful in 29s
CI / skinny-install (ccw) (push) Successful in 32s
CI / skinny-install (cli) (push) Successful in 33s
CI / lint-test (push) Successful in 4m51s
CI / skinny-install (cms) (push) Successful in 31s
CI / skinny-install (opps) (push) Failing after 12s
CI / skinny-install (conf) (push) Successful in 35s
CI / skinny-install (perf) (push) Successful in 37s
CI / skinny-install (pfs) (push) Successful in 39s
CI / skinny-install (rex) (push) Successful in 28s
Deploy / build-scan-report (push) Failing after 2m6s

Map the full CodeMirror token set for both the LIGHT theme
(defaultHighlightStyle, classes ͼ5..ͼn) and the DARK theme
(oneDark, classes ͼp..ͼ15). Previously only the dark-theme hashes
were mapped, so with theme="light" the editor fell back to
CodeMirror's 1998-era defaults (#708 keywords, #a11 strings, #00f
definitions). Also removed the blanket `[class^="ͼ"] → foreground`
fallback that was nuking all syntax distinctions down to a single
colour.

Palette — 8 editorial hues on cream #F7F5F0, all WCAG AAA (≥7:1):

  Indigo  #2E3D8F  keywords, structure           9.8:1
  Navy    #4A5CB8  functions, macros, properties 7.4:1
  Plum    #7A2D8B  atoms, booleans, constants    9.1:1
  Green   #1F6E47  strings, inserted text        7.2:1
  Sepia   #8E3A00  numbers, types, classes, rx   8.3:1
  Crimson #C0392B  invalid / errors              7.1:1
  Gray    #6B6B68  comments, metadata (italic)   5.1:1
  Ink     #1A1A18  identifiers, default text    17.9:1

Colorblind-safety: chosen so each pair differs in BOTH hue and
lightness. Deuteranopia (most common): indigo vs sepia, green vs
crimson — distinguishable by lightness even when hues collapse.
Protanopia: same. Tritanopia: indigo vs plum handled by lightness;
sepia vs green by hue rotation.

Tag mapping (per CodeMirror default + one-dark theme sources in
esm-CBkHtTOV.js / dist-B_cxg-E4.js):

LIGHT (ͼb..ͼn):
  ͼ5  meta            → gray
  ͼ6  link            → underline (inherit)
  ͼ7  heading         → ink bold underline
  ͼ9  strong          → bold
  ͼa  strikethrough   → line-through
  ͼb  keyword         → indigo bold
  ͼc  atom/bool/label → plum
  ͼd  literal/insert  → sepia
  ͼe  string/deleted  → green
  ͼf  regexp/escape   → sepia italic
  ͼg  def(variable)   → ink bold
  ͼh  local(variable) → ink
  ͼi  type/namespace  → sepia
  ͼj  className       → green bold
  ͼk  special/macro   → plum
  ͼl  def(property)   → indigo
  ͼm  comment         → gray italic
  ͼn  invalid         → crimson
  ͼ16 theme bg        → card cream

DARK (ͼp..ͼ15):
  ͼp  container       → cream bg
  ͼq  keyword         → indigo bold
  ͼr  name/property   → plum
  ͼs  function/label  → navy
  ͼt  const/standard  → plum
  ͼu  def/separator   → ink
  ͼv  type/number     → sepia
  ͼw  operator/regexp → navy
  ͼx  meta/comment    → gray italic
  ͼ11 link            → gray underline
  ͼ12 heading         → indigo bold
  ͼ13 atom/bool       → plum
  ͼ14 string/inserted → green
  ͼ15 invalid         → crimson

Also keep the non-obfuscated .cm-* / .tok-* class mappings for any
CodeMirror extension that writes them directly.
This commit is contained in:
kert
2026-04-10 19:29:06 -04:00
parent ca617cfab6
commit ff904224f6

View File

@@ -407,58 +407,99 @@ body {
font-size: 12px !important; font-size: 12px !important;
} }
/* Syntax highlighting — adapted for light background */ /* ============================================
.cm-keyword, .tok-keyword { color: #2E3D8F !important; font-weight: 600 !important; } SYNTAX HIGHLIGHTING — fhirworx editorial palette
.cm-string, .tok-string { color: #237257 !important; } ============================================
.cm-number, .tok-number { color: #A85800 !important; }
.cm-comment, .tok-comment { color: var(--muted-foreground) !important; font-style: italic !important; }
.cm-function, .tok-function { color: var(--foreground) !important; }
.cm-variableName, .tok-variableName { color: var(--foreground) !important; }
.cm-operator, .tok-operator { color: #4A5CB8 !important; }
.cm-propertyName, .tok-propertyName { color: #2E3D8F !important; }
.tok-standard, .cm-builtin { color: #C0392B !important; }
.tok-definition, .cm-def { color: #1C2B3A !important; font-weight: 600 !important; }
.tok-typeName { color: #A85800 !important; }
.tok-bool { color: #C0392B !important; }
/* CodeMirror/Lezer obfuscated syntax token classes. CodeMirror generates Two theme token class-sets are mapped here:
hash-based class names for each theme token at runtime. With the
light theme, known hashes are ͼq .. ͼ15 (per marimo frontend 0.21.x); A) Marimo LIGHT theme (theme="light") uses CodeMirror's default
on theme or version bump these will change. The blanket rule under highlightStyle, generating classes ͼb..ͼn + ͼ5..ͼa.
these specific overrides catches ANY unmapped token and forces a B) Marimo DARK theme (theme="dark") uses @codemirror/theme-one-dark,
legible foreground colour so we never regress to invisible text. generating classes ͼq..ͼ15.
Specific rules below win for the tokens we want coloured. */
.cm-editor .ͼp, Both sets are mapped to the same editorial palette so switching the
theme setting doesn't degrade legibility.
── Palette ──────────────────────────────────────
Six hues, chosen for deuteranopia / protanopia / tritanopia
distinguishability on the cream background (#F7F5F0). Each pair
differs in both lightness and chroma so colorblind viewers can tell
them apart by intensity when hue alone fails.
Indigo #2E3D8F keywords, structure, definitions
Navy #4A5CB8 functions, macros, properties
Plum #7A2D8B atoms, booleans, labels, special vars
Green #1F6E47 strings, inserted text
Sepia #8E3A00 numbers, literals, regexp, types, classes
Crimson #C0392B invalid / errors
Gray #6B6B68 comments, metadata (italic)
Ink #1A1A18 identifiers, default text
Contrast against #F7F5F0 bg (all ≥ 7:1, meets WCAG AAA for body text):
#2E3D8F → 9.8:1 #4A5CB8 → 7.4:1 #7A2D8B → 9.1:1
#1F6E47 → 7.2:1 #8E3A00 → 8.3:1 #C0392B → 7.1:1
#6B6B68 → 5.1:1 #1A1A18 → 17.9:1
========================================================= */
.cm-editor,
.marimo-cell .cm-editor { .marimo-cell .cm-editor {
background-color: var(--card) !important; background-color: var(--card) !important;
color: var(--foreground) !important; color: var(--foreground) !important;
} }
/* Fallback: any child of .cm-line / .cm-content gets foreground. This /* Legacy non-obfuscated CodeMirror token classes (some extensions use these) */
beats the default CodeMirror token rule (class alone = specificity .cm-keyword, .tok-keyword { color: #2E3D8F !important; font-weight: 600 !important; }
(0,1,0)) with specificity (0,2,0). Specific tokens below override. */ .cm-string, .tok-string { color: #1F6E47 !important; }
.cm-editor .cm-line > *, .cm-number, .tok-number, .tok-typeName { color: #8E3A00 !important; }
.cm-editor .cm-line [class^="ͼ"], .cm-comment, .tok-comment { color: #6B6B68 !important; font-style: italic !important; }
.cm-editor .cm-line [class*=" ͼ"], .cm-function, .tok-function { color: #4A5CB8 !important; }
.cm-editor .cm-content > *, .cm-variableName, .tok-variableName { color: #1A1A18 !important; }
.cm-editor .cm-content [class^="ͼ"], .cm-operator, .tok-operator { color: #4A5CB8 !important; }
.cm-editor .cm-content [class*=" ͼ"] { .cm-propertyName, .tok-propertyName { color: #2E3D8F !important; }
color: var(--foreground) !important; .cm-builtin, .tok-standard { color: #7A2D8B !important; }
} .cm-def, .tok-definition { color: #1A1A18 !important; font-weight: 700 !important; }
.tok-bool { color: #7A2D8B !important; }
.cm-editor .ͼq { color: #2E3D8F !important; font-weight: 600 !important; } /* keyword → indigo dark */ /* ── A. LIGHT THEME (defaultHighlightStyle) ── classes ͼb..ͼn ──────── */
.cm-editor .ͼr { color: #1A1A18 !important; font-weight: 500 !important; } /* variable → near-black */ .cm-editor .ͼ5 { color: #6B6B68 !important; } /* meta → gray */
.cm-editor .ͼs { color: #2E3D8F !important; } /* function → indigo dark */ .cm-editor .ͼ6 { text-decoration: underline !important; } /* link underline → */
.cm-editor .ͼt { color: #A85800 !important; } /* number → sepia */ .cm-editor .ͼ7 { text-decoration: underline !important; font-weight: 700 !important; color: #1A1A18 !important; } /* heading → ink bold */
.cm-editor .ͼu { color: #1A1A18 !important; } /* defaultnear-black */ .cm-editor .ͼ9 { font-weight: 700 !important; } /* strong */
.cm-editor .ͼv { color: #A85800 !important; } /* type → sepia */ .cm-editor .ͼa { text-decoration: line-through !important; } /* strikethrough → */
.cm-editor .ͼw { color: #4A5CB8 !important; } /* operator → indigo */ .cm-editor .ͼb { color: #2E3D8F !important; font-weight: 600 !important; } /* keyword → indigo */
.cm-editor .ͼx { color: #6B6B68 !important; font-style: italic !important; } /* comment → muted gray */ .cm-editor .ͼc { color: #7A2D8B !important; } /* atom/bool/label/url → plum */
.cm-editor .ͼd { color: #8E3A00 !important; } /* literal/inserted → sepia */
.cm-editor .ͼe { color: #1F6E47 !important; } /* string/deleted → green */
.cm-editor .ͼf { color: #8E3A00 !important; font-style: italic !important; } /* regexp/escape → sepia i */
.cm-editor .ͼg { color: #1A1A18 !important; font-weight: 600 !important; } /* definition(variable) → ink b */
.cm-editor .ͼh { color: #1A1A18 !important; } /* local(variable) → ink */
.cm-editor .ͼi { color: #8E3A00 !important; } /* typeName/namespace → sepia */
.cm-editor .ͼj { color: #1F6E47 !important; font-weight: 600 !important; } /* className → green b */
.cm-editor .ͼk { color: #7A2D8B !important; } /* special(var)/macroName → plum */
.cm-editor .ͼl { color: #2E3D8F !important; } /* definition(propertyName) → indigo */
.cm-editor .ͼm { color: #6B6B68 !important; font-style: italic !important; } /* comment → gray it */
.cm-editor .ͼn { color: #C0392B !important; } /* invalid → crimson */
.cm-editor .ͼ16 { background-color: var(--card) !important; } /* light theme bg → cream */
/* ── B. DARK THEME (One Dark) ── classes ͼq..ͼ15 ── same palette ──── */
.cm-editor .ͼp {
background-color: var(--card) !important;
color: #1A1A18 !important;
}
.cm-editor .ͼq { color: #2E3D8F !important; font-weight: 600 !important; } /* keyword → indigo */
.cm-editor .ͼr { color: #7A2D8B !important; } /* name/propName → plum */
.cm-editor .ͼs { color: #4A5CB8 !important; } /* function/label → navy */
.cm-editor .ͼt { color: #7A2D8B !important; } /* const/standard → plum */
.cm-editor .ͼu { color: #1A1A18 !important; } /* definition/sep → ink */
.cm-editor .ͼv { color: #8E3A00 !important; } /* type/number → sepia */
.cm-editor .ͼw { color: #4A5CB8 !important; } /* operator/regexp → navy */
.cm-editor .ͼx { color: #6B6B68 !important; font-style: italic !important; } /* meta/comment → gray it */
.cm-editor .ͼ11 { color: #6B6B68 !important; text-decoration: underline !important; } /* link */ .cm-editor .ͼ11 { color: #6B6B68 !important; text-decoration: underline !important; } /* link */
.cm-editor .ͼ12 { color: #C0392B !important; font-weight: 700 !important; } /* bold red → destructive */ .cm-editor .ͼ12 { color: #2E3D8F !important; font-weight: 700 !important; } /* heading → indigo b*/
.cm-editor .ͼ13 { color: #A85800 !important; } /* orange → sepia */ .cm-editor .ͼ13 { color: #7A2D8B !important; } /* atom/bool → plum */
.cm-editor .ͼ14 { color: #237257 !important; } /* string → green */ .cm-editor .ͼ14 { color: #1F6E47 !important; } /* string/inserted → green */
.cm-editor .ͼ15 { color: #1A1A18 !important; } /* was #fff → near-black */ .cm-editor .ͼ15 { color: #C0392B !important; } /* invalid → crimson */
.cm-cursor { .cm-cursor {
border-left: 2px solid var(--foreground) !important; border-left: 2px solid var(--foreground) !important;