4.6 KiB
Light and dark themes
Status: tracked, not started. A direction to refactor toward, not a project with a date. Recorded 2026-08-14 from a contrast pass over the colours that are actually in the tree.
The page is a warm paper theme. A dark theme that just inverts it will look broken. Chrome is cheap. The chart colours were picked for cream, and many of them are stored as hex on drawings, so a CSS flip does not restyle lines already placed.
What is already factored
:root in static/style.css owns the chrome:
:root {
color-scheme: light;
--bg: #e8dfcf;
--panel: #f7f1e6;
--chart-bg: #fbf7ef;
--fg: #2c2924;
--muted: #746c60;
--line: #d2c5b2;
--accent: #b7771d;
--green: #27825c;
--red: #bd4545;
}
Lightweight Charts already reads --chart-bg and --muted at create().
Sidebar, comments, tooltips and most chrome follow the tokens. Flipping
those variables restyles the frame. It does not restyle the series.
What is hardcoded for cream paper
Candles — #20b8a6 / #ef5b3f with borders and wicks #087f76 /
#b9342d. Mid-luminance, already the usual trading pair. Fine on dark.
Timeframe and kind colours (ConfluenceChart.tfColors,
kindColors, and the layer swatches):
| Role | Hex | On dark |
|---|---|---|
| 1m | #82909f |
usable |
| 5m / manual | #65b7cf |
fine |
| 1h | #efb643 |
fine |
| 1d MA | #d96073 |
fine |
| VWAP | #b07ad6 |
fine |
| prior-day H/L/C | #9fb0c4 |
fine |
Grid — rgba(128,128,128,.10). Invisible on a dark plot. Needs a
higher alpha, or a token.
Accent — #b7771d is a mid amber. On cream it reads as gold. On dark
it goes muddy/brown. Dark chrome wants a brighter accent, not the same hex.
Price tag — #e0a34a on #1a1206. Fine on both.
Snap support/resistance — #27825c / #bd4545. Fine.
Current-price pulse — color-mix(..., var(--accent), white). On dark,
mix toward the chart background or a lighter accent, not white.
Shadows — rgba(0,0,0,.16). Vanish on dark. Drop them or invert.
button.active — color: var(--bg) on background: var(--accent).
Survives a token flip (dark text on gold instead of cream text on gold).
The drawing palette is the real problem
Four stops per family, named green1–green4 and so on, persisted as hex
on each drawing:
| 1 (pastel) | 2 | 3 | 4 (dark) | |
|---|---|---|---|---|
| green | #A6D8AA |
#4DB155 |
#258F33 |
#006D09 |
| red | #FAADBC |
#F45B78 |
#D13F62 |
#AF2850 |
| blue | #A3CCFF |
#4699FE |
#1E76D8 |
#0054B3 |
| orange | #F8C592 |
#F08A24 |
#D66B12 |
#B94E08 |
| teal | #80D8D8 |
#00B0B1 |
#008E8F |
#006C6E |
| purple | #DDBCEB |
#BB79D7 |
#9858B3 |
#763790 |
Stop 1 pops on dark and fades into cream. Stop 4 reads on cream and
disappears on dark. A theme switch does not rewrite stored #006D09
lines. Invert-and-ship leaves those lines looking like they vanished.
Colours that work on both
Stay in the middle of each row. Mid-luminance, reasonably saturated hues
contrast with both #fbf7ef and a warm dark plot (roughly OKLCH lightness
0.55–0.70). That is why the candles already work, and why stops 2–3 work:
#4DB155 #F45B78 #4699FE #F08A24 #00B0B1 #BB79D7
You cannot keep a four-stop range that is readable on both backgrounds. Two ways out, pick one when this is built:
- Store the name, resolve the hex. The labels already exist
(
green2). Light and dark each map the name to a hex. Identity survives; appearance follows the theme. New drawings and a one-shot migration of known palette hexes onto names. Custom hexes stay hexes. - A 1px opposite-luma hairline on every series. The fill can then be
anything, including saved
#006D09. Escape hatch for lines that never get a name.
Do not invent a third palette that claims all eight extremes work on both. They do not.
What a dark theme is, and is not
It is a contrast pass: chrome tokens, grid alpha, accent, pulse mix, shadows, and a decision about named vs hex drawings. It is not a restyle of the product and not a reason to rebuild the chart.
Do not invert the cream hexes and ship. Do not delete stop 1 and stop 4
from the picker without deciding how existing drawings render. Do not
treat prefers-color-scheme as enough on its own — this is a trading
desk, not a marketing page; the choice has to be sticky and explicit,
with the system preference as a default only.
When this is built
Change docs/plan.md in the same commit if any colour table or “paper
theme” description becomes false. This file then records the reasoning,
not the live tokens.