Repository navigation
feat(frontend): /chart/[index] candle page + ECharts swap + dark/light theme - #68
Merged
Merged
Conversation
lightweight-charts ships a mandatory TradingView attribution that the license forbids removing. Replace with echarts (MIT, tree-shakable, native candlestick + line + bar). Bundle weight grows from ~52 kB to ~200 kB on the chart route only — the trade for richer chart chrome and no third-party branding sitting on every screenshot.
- CSS variable palette per [data-theme="dark|light"] (background, surface, border, up/down, soft/muted text). Tailwind v4 @theme inline binds them so utilities like bg-surface, text-muted, text-up resolve via the active theme. - ThemeProvider holds the React state, syncs to localStorage and writes data-theme on <html>; an inline pre-paint script in <head> reads the stored / system value before hydration so the page never flashes the wrong palette. - ThemeToggle button sits in the corner of both landing and chart pages. - Landing: gradient hero, badge above title, link-card per index that routes to /chart/{id} on click with a "chart →" hover affordance. - Sparkline strokes via var(--up) / var(--down) so it flips with the theme automatically.
Full-screen detail page per issue #27. Renders a candle + volume chart with a CoinGecko-style hero (6xl value, delta pill, live indicator), 6-stat strip (open/close/high/low/ticks/avg conf), and a 12-col grid with chart in col-span-9 and a right rail (about / live snapshot / window) that collapses below the chart at <xl. Chart chrome: - timeframe pill (1h / 1d / 7d / 30d / 90d / all) maps to (interval, limit) and refetches history; auto-refresh every 60 s matches the engine cadence so bar rolls catch up without a manual switch - area-gradient under closes for a "filled" feel without hiding candles - adaptive EMA overlay (period scales with bar count) - mark-line at last close with a pill label - crosshair + tooltip pills with backdrop blur and OHLC + range + Δ% - axis labels rendered at 12 px / 0.9 opacity (was 10 px / 0.35) so the price ladder is actually readable - ECharts palette keyed off the React-state theme rather than CSS-var reads, sidestepping the race with ThemeProvider's data-theme write on toggle Page validates {bvol|evol} via notFound() so /chart/foo 404s. REST history extended to interval="1m" for the 1 h timeframe.
- HIGH-1: live tick redraws via the full draw() path so the candle,
area-gradient line, EMA overlay, and mark-line all stay in sync.
The prior partial setOption([{ data: ohlc }]) targeted series[0]
by position, which is the close-area line — the candle (series[2])
silently kept stale data and the line series was corrupted with
OHLC arrays.
- HIGH-2: introduce latestTickRef and apply it to the last bar after
every history refetch. Previously the 60 s refetch race condition
overwrote tick patches with the server snapshot (which lacked the
in-flight tick), erasing the live candle for the next minute.
- MED-1: hoist draw into drawRef so the history-effect deps shrink
to [id, timeframe]. A theme flip no longer cancels and restarts the
history interval, eliminating the redundant API round-trip on every
toggle. Theme-redraw effect reads drawRef.current directly so it
still picks up the fresh palette.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the public frontend surface of the index. Issue #27 (the
/chart/[index]candle chart) is the visible deliverable; the PR also bundles two adjacent scope-creep items the chart work surfaced and demanded:lightweight-chartsships a non-removable TradingView attribution under its license. Replaced with ECharts (MIT, tree-shakable, native candlestick + line + bar).Links into the new chart page (hover state, "chart →" affordance) and the hero/typography were tightened.Three atomic commits, in dependency order.
Commits
4ce4bdf9b374d575ad6b1Routes after this PR
//chart/bvol/chart/evol/chart/foonotFound())bvol/evolallowed/methodology,/docs,_not-foundChart page layout
Inspired by CoinGecko + Deribit DVOL pages, condensed into a 12-col grid:
Below
xlthe rail drops under the chart, the stat strip wraps to 2 / 3 columns, the chart aspect tightens to 16:9.Chart visuals
min(20, max(4, bars/6))), orange overlay.Live tick updates the open candle in place (high/low/close); periodic 60 s history refresh handles bar rolls so client-side
toStartOfIntervalmirroring is not required.Timeframe → REST interval map
fetchHistorywidened to accept"1m"as an interval literal (was"5m" | "1h" | "1d").Theme system
globals.cssdeclares two palettes via:root, [data-theme="dark"]and[data-theme="light"]— background, surface, surface-2, border-subtle, border, border-strong, muted, soft, soft-2, up/down, up-soft/down-soft, grid, tooltip-bg, bg-radial-1/2.@theme inlineexports these as Tailwind color tokens (bg-background,text-foreground,bg-surface,text-muted,text-up, etc.).@custom-variant darkand@custom-variant light(Tailwind v4) target the[data-theme]attribute so explicitdark:/light:prefixes are available if needed.lib/theme.tsxprovides<ThemeProvider>(context + localStorage sync) andthemeInitScript(inline pre-paint script that runs before React hydrates, killing the "flash of wrong theme" effect).<ThemeToggle>placed top-right on landing + chart page.theme, not CSS-var reads — this sidesteps a race with theThemeProvideruseEffectthat writesdata-themeon<html>.CORS / proxy refresher
REST stays same-origin via the Next rewrite (
/v1/:path*→API_PROXY_TARGET); WS connects direct viaNEXT_PUBLIC_API_BASE. Both default tohttp://localhost:8080. No backend change here.Verification
pnpm installpnpm typecheckpnpm lintpnpm build/7.02 kB / 112 kB FL;/chart/[index]200 kB / 305 kB FL/renders both cards, sparkline, link to chart/chart/bvol,/chart/evol/chart/foonotFound()curl localhost:3000/v1/index/bvol/history?interval=1m&limit=60→ 200ws://localhost:8080/v1/stream, subscribes to single channel, redraws last candle on tickprefers-color-schemeon first visit; chart redraws automaticallyOut of scope
Notes
experimental.typedRoutes: truevalidates the<Link href="/chart/bvol">etc. paths at build time — caught one wrong path during the refactor./chart/[index]route (use client). Landing stays at 7 kB.LineChartandMarkLineComponentare imported explicitly (tree-shake friendly).useTheme()must be inside<ThemeProvider>—Chart,Dashboard, andIndexCardall are.Test plan
pnpm typecheckcleanpnpm lintcleanpnpm buildclean/,/chart/bvol,/chart/evol,/chart/foo(404)intervalliteralsFixes #27