diff --git a/app/app/globals.css b/app/app/globals.css index 766be9e9..2cc66cce 100644 --- a/app/app/globals.css +++ b/app/app/globals.css @@ -186,7 +186,23 @@ --accent-text: var(--accent-muted); --hud-line: rgba(153, 69, 255, 0.15); --hud-line-hover: rgba(153, 69, 255, 0.35); - --cyan-muted: rgba(20, 241, 149, 0.10); + + /* Semantic colors — the dark-theme values are neons tuned for dark + surfaces and collapse on white (#14F195 lands at ~1.4:1 — every + positive-PnL numeral, Long fill, and APR figure was near-invisible in + light mode). These darker tones stay legible as TEXT on the light bg + (emerald-600 3.9:1, red-600 4.5:1, amber-700 4.6:1, teal-700 4.9:1) + while keeping the two FILL pairings readable: Long fills pair with + black text (black on #059669 ≈ 5.9:1), Short fills with white text + (white on #DC2626 ≈ 4.6:1). Chart candles use the same values via + useChartTheme's LIGHT_THEME — keep the two in sync. */ + --long: #059669; + --short: #DC2626; + --good: #059669; + --critical: #DC2626; + --warning: #B45309; + --cyan: #0F766E; + --cyan-muted: rgba(5, 150, 105, 0.10); /* Elevation — lighter neutral shadows read correctly on a white surface */ --shadow-xs: 0 1px 2px rgba(13, 14, 21, 0.06); diff --git a/app/components/trade/TradingChart.tsx b/app/components/trade/TradingChart.tsx index cfd09c7d..e5d4d955 100644 --- a/app/components/trade/TradingChart.tsx +++ b/app/components/trade/TradingChart.tsx @@ -273,6 +273,12 @@ const TradingChartInner: FC<{ slabAddress: string; mintAddress?: string }> = ({ // isolated ~10-line leaf (`LiveMarkPriceLabel`, defined above) that // subscribes to useLivePrice() itself so only IT re-renders on a tick. const chartTheme = useChartTheme(); + // Ref mirror for the tick handler below — that effect deliberately deps + // only on [slabAddress] (rebuilding the subscription on theme change would + // be wasteful), so it must read the CURRENT theme through a ref or a theme + // flip would leave its closure coloring ticks with the old palette. + const chartThemeRef = useRef(chartTheme); + chartThemeRef.current = chartTheme; const [chartStyle, setChartStyle] = useChartStylePref(); const [overlayPrefs, setOverlayPref] = useChartOverlayPrefs(); const { @@ -716,6 +722,12 @@ const TradingChartInner: FC<{ slabAddress: string; mintAddress?: string }> = ({ rightPriceScale: { borderColor: chartTheme.borderColor }, timeScale: { borderColor: chartTheme.borderColor }, }); + // Overlay price lines were created with the previous theme's palette — + // repaint them too (the tick handler recolors the Mark line on the next + // tick anyway, but a flat market shouldn't keep stale-theme lines). + priceLineRef.current?.applyOptions({ color: chartTheme.neutralLine }); + liqLineRef.current?.applyOptions({ color: chartTheme.downColor }); + entryLineRef.current?.applyOptions({ color: chartTheme.entryLine }); }, [chartTheme]); // Derive entry price from user account @@ -791,7 +803,7 @@ const TradingChartInner: FC<{ slabAddress: string; mintAddress?: string }> = ({ if (initialPriceUsd != null && Number.isFinite(initialPriceUsd)) { priceLineRef.current = s.createPriceLine({ price: initialPriceUsd, - color: "rgba(255,255,255,0.6)", + color: chartThemeRef.current.neutralLine, lineWidth: 1, lineStyle: LineStyle.Dashed, axisLabelVisible: true, @@ -803,7 +815,8 @@ const TradingChartInner: FC<{ slabAddress: string; mintAddress?: string }> = ({ if (overlayPrefs.liq && liqPriceNum != null && liqPriceNum > 0) { liqLineRef.current = s.createPriceLine({ price: liqPriceNum, - color: "#FF3B5C", // matches --short (canvas can't read CSS vars) + // Per-theme --short equivalent (canvas can't read CSS vars) + color: chartThemeRef.current.downColor, lineWidth: 2, lineStyle: LineStyle.Solid, axisLabelVisible: true, @@ -814,7 +827,7 @@ const TradingChartInner: FC<{ slabAddress: string; mintAddress?: string }> = ({ if (overlayPrefs.entry && entryPriceNum != null && entryPriceNum > 0) { entryLineRef.current = s.createPriceLine({ price: entryPriceNum, - color: "#22d3ee", + color: chartThemeRef.current.entryLine, lineWidth: 1, lineStyle: LineStyle.Dashed, axisLabelVisible: true, @@ -1074,10 +1087,12 @@ const TradingChartInner: FC<{ slabAddress: string; mintAddress?: string }> = ({ // long-green/short-red on each tick instead of sitting flat gray — // same semantic tokens as MarketInfoBar's MarkPrice flash, applied // here via the price line's own color (canvas can't read CSS vars, - // so these are the literal --long/--short hex values). Holds its - // last color on an exact-equal tick rather than resetting to gray. + // so ChartTheme carries the per-theme --long/--short equivalents; + // read through the ref, this effect deps only on slabAddress). Holds + // its last color on an exact-equal tick rather than resetting to gray. const prev = prevTickPriceRef.current; - const color = prev == null ? "rgba(255,255,255,0.6)" : usd > prev ? "#14F195" : usd < prev ? "#FF3B5C" : undefined; + const t = chartThemeRef.current; + const color = prev == null ? t.neutralLine : usd > prev ? t.upColor : usd < prev ? t.downColor : undefined; prevTickPriceRef.current = usd; priceLineRef.current.applyOptions(color ? { price: usd, color } : { price: usd }); } diff --git a/app/hooks/useChartTheme.ts b/app/hooks/useChartTheme.ts index c1f7168a..ca5a06b4 100644 --- a/app/hooks/useChartTheme.ts +++ b/app/hooks/useChartTheme.ts @@ -11,6 +11,10 @@ export interface ChartTheme { downColor: string; volUpColor: string; volDownColor: string; + /** Neutral overlay-line color (the Mark price line before any tick). */ + neutralLine: string; + /** Entry price overlay line — cyan family, per-theme so it stays legible. */ + entryLine: string; } const DARK_THEME: ChartTheme = { @@ -36,6 +40,8 @@ const DARK_THEME: ChartTheme = { downColor: "#FF3B5C", volUpColor: "rgba(20,241,149,0.6)", volDownColor: "rgba(255,59,92,0.6)", + neutralLine: "rgba(255,255,255,0.6)", + entryLine: "#22d3ee", }; const LIGHT_THEME: ChartTheme = { @@ -43,10 +49,14 @@ const LIGHT_THEME: ChartTheme = { textColor: "rgba(13,14,21,0.65)", gridColor: "rgba(0,0,0,0.05)", borderColor: "rgba(0,0,0,0.10)", - upColor: "#16a34a", - downColor: "#dc2626", - volUpColor: "rgba(22,163,74,0.5)", + // Same values as the light-theme --long/--short tokens in globals.css — + // candles and UI numerals must agree on what "up" green looks like. + upColor: "#059669", + downColor: "#DC2626", + volUpColor: "rgba(5,150,105,0.5)", volDownColor: "rgba(220,38,38,0.5)", + neutralLine: "rgba(13,14,21,0.55)", + entryLine: "#0F766E", }; function getThemeFromDOM(): "dark" | "light" {