Chart
Value-over-time charts with a headline figure, change readout, and range control.
Total value
€93,300.00
up €1,800.00 (1.97%)Usage
import {
ChartArea,
ChartCandlestick,
ChartPanel,
ChartRangeControl,
ChartValueDisplay,
computeAreaRangeStats,
} from "@sophic/portal-ui/components/ui/charts";
import { SpringReveal } from "@sophic/portal-ui/components/ui/spring-reveal";ChartPanel owns layout only — swap the plot primitive (ChartArea,
ChartCandlestick, ChartValuation, …) without changing the frame. Use
layout="responsive" to put the range in the header from sm up and in the
footer below.
Named motion for settings reveals is on Animations.
Chart types
| Primitive | Use case |
|---|---|
ChartArea | Area sparkline — portfolio / instrument price; optional axes + price guide |
ChartArea + dials | Instrument detail: volume, dotted refs, expandable ranges, gear settings |
ChartCandlestick | OHLC candles for the same dials series (settings toggle); same axis style |
ChartValuation | Value vs contributions with gain/loss bands |
ChartBar | Signed P&L bars (green/red per period) |
ChartStackedBar | Multi-series stacked bars — e.g. contributions + growth |
ChartLine | Line chart with axes and grid |
Instrument dials
Canonical instrument / fund price chart (Option B):
- Expandable ranges —
ChartRangeControlwithextraOptions(trailing …) - Gear settings — type (line / candles icons), volume, details panel;
open/close with
SpringReveal(spring-pop → soft-dismiss). Defaults: Y axis off, details panel hidden; volume + price guide on. - Plot —
ChartAreaorChartCandlestickfrom the same OHLCV points - Details strip —
computeAreaRangeStats; four columns when wide, two when narrow (right column end-aligned); show/hide withSpringReveal - Change readout — amount and parenthesized percent on
ChangeIndicator(comma decimal on the percent)
Scrub tooltip date
The scrub chip’s date header is owned by formatLabel on ChartArea,
ChartCandlestick, and ChartValuation (default: locale date and
time). Product charts pass a label that depends on the series bucket
from the API — not the range chip alone.
| API field | Endpoint | Finer than a day | Daily or coarser |
|---|---|---|---|
Series.resolution | Account valuation / earnings series | 1m · 5m · 15m · 30m · 1h | 1d |
Bar interval | Instrument /series/ohlc | 5min · 1h | 1d · 1w |
Show hours when the bucket is finer than 1d. Date only when the
bucket is 1d or coarser (1w). Prefer the live response value; portal
keeps period/range → default tables as fallback while data loads
(ACCOUNT_SERIES_DEFAULT_RESOLUTION, INSTRUMENT_PRICE_DEFAULT_INTERVAL
in the home chart-ranges module).
<ChartArea
data={series}
formatLabel={(timestamp) =>
formatChartBucketTooltipLabel(timestamp, resolution)
}
/>Apply the same rule on every product chart unless a surface opts out.
Playground
import { ChartPanel, ChartArea, ChartRangeControl, ChartValueDisplay,} from "@sophic/portal-ui/components/ui/charts";<ChartPanel layout="responsive" label="Total value" plotBackground={true} edgeFade={true} skeletonVariant="area" range={<ChartRangeControl value="6m" options={...} />} headline={<ChartValueDisplay currency="EUR" value={92400} />}> <ChartArea data={areaData} /></ChartPanel>ChartPanel
Card shell with slots for label, headline, range, footer, and the plot.
loading swaps the plot for a ChartSkeleton. For range switches, keep the
previous series mounted (placeholderData) with a soft opacity pulse and a
translucent skeleton blend (refreshing), then ChartArea morphs once the
new points arrive.
Prop
Type
Default
Slot props accept ChartPanelSlot — expand a row in the table above to see the
full ReactNode union.
ChartArea
Area sparkline with scrub support and a gradient fill clipped under the line.
Pass edgeFade when rendering without ChartPanel (the panel applies the fade
to the whole plot region by default).
When onScrub is set (or scrubTint is true), scrubbing tints the prefix
green/red vs series open, with a dotted vertical cursor tinted to match and no
floating Chart Scrub Tooltip — unless priceGuide / axes turn the chip on.
Idle fill uses the color prop (neutral by default; pass positive /
negative for at-rest tint, e.g. instrument charts). Pass scrubTint={false}
to keep a static color tint with no prefix reveal. On coarse pointers the
date-only chip sits on the vertical crosshair above the high price guide.
Optional style (off by default so existing call sites stay sparklines):
xAxis/yAxis— labelled ticks (Y reserves width so labels never clip)priceGuide— muted level lines on the plotted series low/high; corner labels on hover- Scrub crosshair — vertical + horizontal arms; vertical tinted +/-; Chart Scrub Tooltip chip when style is on (desktop); touch uses on-plot date only
tooltip— force the Chart Scrub Tooltip on/off (defaults on with frame)formatLabel— Chart Scrub Tooltip date (see Scrub tooltip date)showVolume— plotvolumeas a low subplot when points include itreferenceLines— unlabeled dotted levels whenpriceGuideis offanimateData— ease the path whendatachanges (range switches); default on. Hold the previous series until the fetch completes, then morph toward the new points (same vertex count for the whole animation — no end snap). Snaps underprefers-reduced-motion.- richer
ChartAreaPointfields (open/high/low/volume) for domain padding and scrub tooltips computeAreaRangeStats(data)— helper for a compact OHLC strip beside the panel
Prop
Type
Default
ChartCandlestick
OHLC candlesticks for instrument dials. Same point shape as ChartArea
(value = close). Optional volume subplot and reference lines.
Scrubbing a candle opens the same tinted mono chip as the line chart, with
the date on top and Close / Open / High / Low (and Vol when volume
is on). Pass formatLabel for the date header — same
under-1d time rule as area charts.
<ChartCandlestick
data={series}
showVolume
referenceLines={[rangeOpen]}
formatValue={(v) => `$${v.toFixed(2)}`}
/>Prop
Type
Default
ChartValuation
Value vs contributions with gain/loss bands and an optional dashed contributions baseline.
Optional style (off by default):
priceGuide— muted series low/high level lines; corner labels on hovertooltip— tinted date + amount scrub chip with H+V crosshair (defaults on withpriceGuide). WhenshowContributionsis on, the chip also shows the contributions value.referenceLines— unlabeled dotted levels whenpriceGuideis offformatLabel/formatAxisValue— scrub chip date and guide labels (see Scrub tooltip date)
Total value
Prop
Type
Default
ChartBar
Signed period P&L bars — green when positive, red when negative.
Playground
import { ChartPanel, ChartBar, ChartRangeControl, ChartValueDisplay,} from "@sophic/portal-ui/components/ui/charts";<ChartPanel layout="responsive" label="Earnings" plotBackground={false} edgeFade={false} skeletonVariant="bar" range={<ChartRangeControl value="6m" options={...} />} headline={<ChartValueDisplay currency="EUR" value={92400} />}> <ChartBar data={barData} /></ChartPanel>Prop
Type
Default
ChartStackedBar
Multi-series bars stacked on a shared baseline — e.g. contributions plus growth to total value.
Playground
import { ChartPanel, ChartStackedBar, ChartRangeControl, ChartValueDisplay,} from "@sophic/portal-ui/components/ui/charts";<ChartPanel layout="responsive" label="Total value" plotBackground={false} edgeFade={false} skeletonVariant="stacked-bar" range={<ChartRangeControl value="6m" options={...} />} headline={<ChartValueDisplay currency="EUR" value={92400} />}> <ChartStackedBar data={stackedData} series={series} /></ChartPanel>Prop
Type
Default
ChartLine
Line chart with labelled axes and optional horizontal grid.
Playground
import { ChartPanel, ChartLine, ChartRangeControl, ChartValueDisplay,} from "@sophic/portal-ui/components/ui/charts";<ChartPanel layout="responsive" label="Total value" plotBackground={false} edgeFade={false} skeletonVariant="line" range={<ChartRangeControl value="6m" options={...} />} headline={<ChartValueDisplay currency="EUR" value={92400} />}> <ChartLine data={lineData} /></ChartPanel>Prop
Type
Default
ChartRangeControl
Segmented pill range selector. Pass layout="responsive" when the control
sits in a ChartPanel with layout="responsive".
Pass extraOptions to add a trailing … segment that expands the same
track with more ranges (height morph, fixed radius when expandable, same
sliding pill and padding).
Expandable
<ChartRangeControl
value={range}
onValueChange={setRange}
options={primaryOptions}
extraOptions={extraOptions}
layout="stacked"
/>Prop
Type
Default
ChartValueDisplay
Fiat headline with dimmed currency symbol and decimals. Size ladder
(xs → xl) keeps relative sizes; default md. Digits roll on value change
by default (animated, Balanced motion) — enter scales up with a 1.1 magnify
settle, exit scales down. Pass animated={false} for a static render; respects
reduced motion. While scrubbing a chart, pass scrubIndex so roll speed tracks
pointer motion and stagger collapses under fast scrub. Pass loading for a
same-footprint skeleton so surrounding style does not jump.
€93,300.00
€93,300.00
€93,300.00
€93,300.00
€93,300.00
Prop
Type
Default
ChartLegend
Line-swatch legend for valuation charts — solid for value, dashed for contributions.
Prop
Type
Default
ChartSkeleton
Loading placeholder for the plot region. Set loading on ChartPanel to
render it automatically.
Prop
Type
Default
ChartPlotBackground
Dot grid behind the plot. Rendered by ChartPanel when plotBackground is
true (default). No props beyond className.
Changelog
- 1 October 2026 — Valuation scrub chip includes contributions when that overlay is on. Full notes
- 24 September 2026 — Price-guide low and high sit on the series (tick + figure above the peak, below the trough). Visible at rest; they fade while the pointer is on the chart. Full notes
- 8 September 2026 — Panel corners use 16px (
--radius-2xl) on product tokens. Expandable range track matches (--chart-range-expandable-radius). Full notes - 2 September 2026 — Instrument dials: YTD on the primary row; 3Y and 5Y restored in extra. Full notes
- 25 August 2026 — Shell styles (range, panel, skeleton, value display, scrub chips) in CSS modules; look unchanged. Full notes
- 25 August 2026 — Candlestick scrub crosshair sits on the wick; volume aligns under the same bucket. Full notes
- 11 August 2026 — Touch scrub: sticky scrub index; Chart Scrub Tooltip
date on the vertical crosshair (touch only); vertical cursor tinted +/-;
idle clear 10s.
chartScrubTooltiprename. Full notes - 11 August 2026 —
ChartAreaidlecolortint; scrub still reveals origin → active point. Full notes - 11 August 2026 — Scrub tooltip date: show time only when the API
bucket is under
1d(passformatLabelfrom resolution / bar interval). Full notes - 3 August 2026 —
ChartBarhover soft-dims sibling bars (150ms ease) so the active period stays obvious. Full notes - 3 August 2026 —
ChartBarall-positive series floors at zero (no padded negative Y tick). Full notes - 3 August 2026 —
ChartBarscrub chip + axes match the line chart (mono tooltip with bold period label; mono tabular ticks). Bars stay green/red; tooltip is foreground when all-positive, signed tint when negatives exist. Full notes - 3 August 2026 —
ChartBarstronger zero baseline when any bar is negative (centered on zero, slightly past plot edges). Full notes - 3 August 2026 —
ChartValueDisplaysize ladder (xs–xl); total value usesxl+ChangeIndicatorlg, other charts defaultmd(instrument change rows useChangeIndicatorxs). Full notes - 3 August 2026 — Sparkline plot margins: more vertical inset (top 16 / bottom 16) between change row and range control. Full notes
- 3 August 2026 — Instrument dials default Y axis + details off; change
readout is amount + parenthesized comma percent on
ChangeIndicator. Full notes - 3 August 2026 — Total-value delta: percent joins the amount on
ChangeIndicator(shared color, comma decimal). Full notes - 3 August 2026 — Candles scrub with tinted OHLCV chip (date header + Close / O / H / L) and denser-series scrub optimizations. Full notes
- 3 August 2026 — Scrub vertical crosshair slightly stronger (opacity / weight) so it stays readable in light and dark. Full notes
- 3 August 2026 —
ChartCandlestickscrub uses the same rAF coalesce + memo path asChartArea(smoother dense OHLC hover). Full notes - 3 August 2026 — Total value uses instrument
priceGuidestyle (low/high guides + corner labels on scrub, tinted date chip);ChartValuationgainspriceGuide/tooltip/referenceLines. Full notes - 1 August 2026 — Details strip toggles with
SpringReveal(same enter / exit as gear settings; already-open skips the enter pop). Full notes - 31 July 2026 — Range switches hold the prior series with a soft plot
pulse, then
ChartAreamorphs into the fetched points. Full notes - 31 July 2026 —
ChartAreapath morph ondataswap (~350ms; opt out withanimateData={false}). Full notes - 31 July 2026 — Optional
xAxis/yAxis/priceGuideplus tinted scrub chip; instrument gear Axes + Price guide dials. Full notes - 31 July 2026 — Dense scrub: clip-reveal tint (no per-point remap),
onScrubindex arg,ChartValueDisplayscrubIndexfor roll velocity. Full notes - 30 July 2026 — Responsive
ChartPanelfooter / mobile range keep the same bottom inset as the header bar (fixes Contributions flush to the card corner). Full notes - 30 July 2026 — Instrument dials (candles, expandable ranges, gear
settings with line/candles icons, OHLC scrub card). Currency headlines
always use glyphs (
$,€, …). Full notes - 29 July 2026 — Dark
ChartPanelrestores a readable edge hairline. Full notes - 22 July 2026 — Headline tracking tightened; active range is a flat white pill with a 0.5px edge. Full notes
- 17 July 2026 — Added (
ChartPanel,ChartArea,ChartValuation, range control, value display, plot grid). Full notes