Status: parked / low priority. Scoped down from "ship OKLCH" to "explore colour holistically, later." See the post-mortem below for why the first attempt was abandoned.
What this is now
A standing, low-priority invitation to revisit how colour is rendered across the whole canvas — node fills, borders, backgrounds, card tints, edges, group labels, and gradients — rather than chasing one isolated effect. If we touch colour again, do it as a deliberate, holistic pass with a clear visual target, not a point fix.
Background: the OKLCH gradient investigation (closed without shipping)
The original ask was "can we use OKLCH for gradients?" We investigated thoroughly and shipped nothing. The reasoning, preserved so we don't re-tread it:
1. Native OKLCH gradient interpolation — unavailable
@shopify/react-native-skia (2.6.x) does not expose an interpolation colour space on MakeLinearGradient / <LinearGradient>. The CSS Color 4 Interpolation struct exists in Skia-the-C++-engine but isn't plumbed through to the JS binding. Skia interpolates gradient stops in sRGB, full stop.
- Even if it were exposed: the current
cc-card-gradient-Ndeg is a single-hue alpha fade (active → activeTransparent, same hue). Interpolation colour space is moot when both endpoints share a hue — OKLCH vs sRGB would be pixel-identical. It would only matter if gradients ever fade between different colours (a future design change).
2. OKLCH palette regen — tried, reverted
We then pivoted to "regenerate the preset palette in OKLCH for perceptually-uniform brightness." Two failure modes:
- Uniform-L OKLCH was actively wrong. Forcing all six presets to a common OKLCH lightness flattened the deliberately non-uniform Obsidian/hesprs palette (yellow & cyan are meant to be bright/poppy). It read as muted and underwhelming on real canvases — confirmed visually on
hesprs-demo.canvas.
- OKLCH anchored on the real vibrant hexes ≈ identical to the old HSL.
active / border / background / activeTransparent are just hex + alpha either way; OKLCH only changes the card-tint computation cosmetically. And its headline safety feature — automatic sRGB gamut mapping — buys nothing here, because every input (preset HSL, user #rrggbb) is already in-gamut sRGB.
Net: OKLCH never produced a user-visible win for this codebase. Continuing would have added a runtime dep (use-color) + import-path caveats to a published library for zero benefit. Abandoned.
Canonical palette (the "Grand" look — keep this)
Source of truth, verified from reference git history at packages/canvas-ui/src/theme.ts (recovered at 709086ec^, just before that package was retired). Byte-identical to our current src/renderer/theme.ts. HSL presets, "chosen to match the Obsidian/hesprs viewer aesthetic":
| Code |
Colour |
HSL |
Rendered hex |
| 0 |
neutral gray |
[0, 0, 40] |
— |
| 1 |
red |
[2, 78, 55] |
#e63933 |
| 2 |
orange |
[29, 90, 55] |
#f48925 |
| 3 |
yellow |
[48, 90, 55] |
#f4ca25 |
| 4 |
green |
[142, 60, 45] |
#2eb860 |
| 5 |
cyan |
[204, 80, 55] |
#309fe8 |
| 6 |
purple |
[270, 60, 55] |
#8c47d1 |
The non-uniform perceived brightness is the aesthetic — do not "correct" it.
Spec conformance note
JSON Canvas spec accepts exactly two color formats: hex (#RRGGBB) and preset codes "1"–"6". Exact preset shades are explicitly renderer-defined ("intentionally not defined so that applications can tailor the presets"). Spec labels code 5 "cyan"; ours leans blue (#309fe8) — conformant, but a faithful-cyan nudge is a candidate if we ever do this pass.
If/when we pick this up — possible scope
Decision
Not doing OKLCH now. Keep the canonical HSL palette. This issue stays open as a low-priority exploration of canvas colour as a whole.
What this is now
A standing, low-priority invitation to revisit how colour is rendered across the whole canvas — node fills, borders, backgrounds, card tints, edges, group labels, and gradients — rather than chasing one isolated effect. If we touch colour again, do it as a deliberate, holistic pass with a clear visual target, not a point fix.
Background: the OKLCH gradient investigation (closed without shipping)
The original ask was "can we use OKLCH for gradients?" We investigated thoroughly and shipped nothing. The reasoning, preserved so we don't re-tread it:
1. Native OKLCH gradient interpolation — unavailable
@shopify/react-native-skia(2.6.x) does not expose an interpolation colour space onMakeLinearGradient/<LinearGradient>. The CSS Color 4Interpolationstruct exists in Skia-the-C++-engine but isn't plumbed through to the JS binding. Skia interpolates gradient stops in sRGB, full stop.cc-card-gradient-Ndegis a single-hue alpha fade (active → activeTransparent, same hue). Interpolation colour space is moot when both endpoints share a hue — OKLCH vs sRGB would be pixel-identical. It would only matter if gradients ever fade between different colours (a future design change).2. OKLCH palette regen — tried, reverted
We then pivoted to "regenerate the preset palette in OKLCH for perceptually-uniform brightness." Two failure modes:
hesprs-demo.canvas.active/border/background/activeTransparentare justhex + alphaeither way; OKLCH only changes the card-tint computation cosmetically. And its headline safety feature — automatic sRGB gamut mapping — buys nothing here, because every input (preset HSL, user#rrggbb) is already in-gamut sRGB.Net: OKLCH never produced a user-visible win for this codebase. Continuing would have added a runtime dep (
use-color) + import-path caveats to a published library for zero benefit. Abandoned.Canonical palette (the "Grand" look — keep this)
Source of truth, verified from reference git history at
packages/canvas-ui/src/theme.ts(recovered at709086ec^, just before that package was retired). Byte-identical to our currentsrc/renderer/theme.ts. HSL presets, "chosen to match the Obsidian/hesprs viewer aesthetic":[0, 0, 40][2, 78, 55]#e63933[29, 90, 55]#f48925[48, 90, 55]#f4ca25[142, 60, 45]#2eb860[204, 80, 55]#309fe8[270, 60, 55]#8c47d1The non-uniform perceived brightness is the aesthetic — do not "correct" it.
Spec conformance note
JSON Canvas spec accepts exactly two
colorformats: hex (#RRGGBB) and preset codes"1"–"6". Exact preset shades are explicitly renderer-defined ("intentionally not defined so that applications can tailor the presets"). Spec labels code 5 "cyan"; ours leans blue (#309fe8) — conformant, but a faithful-cyan nudge is a candidate if we ever do this pass.If/when we pick this up — possible scope
palettecrate makes perceptual colour + gamut mapping cheap and shared across platforms) — then the architecture argument exists, even if the per-pixel one still doesn'tDecision
Not doing OKLCH now. Keep the canonical HSL palette. This issue stays open as a low-priority exploration of canvas colour as a whole.