Liveline
A real-time Canvas price chart with event markers (buys, trims, thesis updates). Smooth lerp animation, theme-aware tokens, and a pluggable data adapter that works with CoinGecko today and any OHLC source tomorrow.
Live — real-time coin data
Pick a coin. Prices stream from CoinGecko through our /api/prices proxy (60-second cache, works identically in dev and on Vercel). The chart re-fetches every minute
and on range change.
Basic
With no data prop, Liveline generates deterministic mock data so the component always has something to render — useful for empty states and storyboards.
<script lang="ts">
import { Liveline } from '$lib/components/ui/liveline-x';
</script>
<Liveline variant="detail" height={260} showHeading price="$2,119" delta="+4.2%" tokenName="Ethereum" tokenTicker="ETH" />With thesis markers
Hover any marker to inspect. Two or more markers within 16px cluster into a stack with a multi-row tooltip; thesis posts carry the full body text below their row.
<script lang="ts">
import { Liveline } from '$lib/components/ui/liveline-x';
import type { ChartMarker } from '$lib/components/ui/liveline-x';
const now = Math.floor(Date.now() / 1000);
const HOUR = 3600;
const markers: ChartMarker[] = [
{ time: now - 144 * HOUR, value: 0, type: 'entry', meta: { label: 'Opened $500' } },
{ time: now - 96 * HOUR, value: 0, type: 'buy', meta: { label: 'Added $200' } },
{
time: now - 72 * HOUR,
value: 0,
type: 'post',
meta: {
label: 'L2 thesis update',
thesis: 'ETH L2 ecosystem growing rapidly — bullish.'
}
},
{ time: now - 48 * HOUR, value: 0, type: 'sell', meta: { label: 'Trimmed $150' } },
{ time: now - 24 * HOUR, value: 0, type: 'buy', meta: { label: 'Added $300' } }
];
</script>
<Liveline variant="detail" showHeading showMarkers {markers} price="$2,119" delta="+4.2%" tokenName="Ethereum" tokenTicker="ETH" />Variants
Three layout presets: full (token page), detail (thesis page), and compact (feed card).
variant="full"
variant="detail"
variant="compact"
Marker states on the thesis page
Each marker type encodes a different action from the position timeline. Cluster + hover are derived visual states, not separate types.
Marker types
| Prop | Type | Default | Description |
|---|---|---|---|
green circle, plus icon | — | Adding capital to a position. Uses the semantic success color. | |
amber circle, minus icon | — | Trimming without fully exiting. | |
red circle, minus icon | — | Fully closed position. | |
purple circle, pen stroke + glow | — | Thesis update or commentary. Uses bloom-nx brand purple with a soft outer glow so it reads as a content event, not a trade. | |
overlapping markers + ×N badge | — | Markers within 16px cluster into a single hit target. Hovering scales the cluster 1.5× and opens a multi-row tooltip listing all contained events. | |
marker scales 1.5×, halo ring | — | On pointer move within HIT_RADIUS the marker scales up and a faint halo ring appears. The cluster's tooltip is positioned above the marker and clamped to the viewport. |
Wiring live data
Liveline itself is data-agnostic. It accepts a ChartPoint[] and re-renders when it changes. The coingecko.ts adapter routes
through our /api/prices proxy — a thin
SvelteKit server route that caches CoinGecko for 60 seconds. Point baseUrl at your own backend to swap
the source (Binance WebSocket, TradingView UDF, your own warehouse).
<script lang="ts">
import { Liveline } from '$lib/components/ui/liveline-x';
import {
fetchChart, formatHeaderPrice, formatDelta, type CoinKey
} from '$lib/components/ui/liveline-x/adapters/coingecko';
import type { ChartPoint, TimeRange } from '$lib/components/ui/liveline-x';
import { onMount } from 'svelte';
let coin = $state<CoinKey>('eth');
let range = $state<TimeRange>('1W');
let data = $state<ChartPoint[]>([]);
let price = $state(''), delta = $state(''), dir = $state<'up'|'down'>('up');
async function refresh() {
const r = await fetchChart(coin, range);
data = r.data;
price = formatHeaderPrice(r.lastPrice);
delta = formatDelta(r.deltaPct);
dir = r.deltaDirection;
}
onMount(() => {
refresh();
const id = setInterval(refresh, 60_000);
return () => clearInterval(id);
});
</script>
<Liveline
variant="detail"
showHeading
{data}
activeRange={range}
onRangeChange={(r) => { range = r; refresh(); }}
{price}
delta={delta}
deltaDirection={dir}
tokenName="Ethereum"
tokenTicker="ETH"
/>API reference
| Prop | Type | Default | Description |
|---|---|---|---|
ChartPoint[] | [] | Price time series as { time: seconds, value: number }[]. When empty, the component falls back to deterministic mock data keyed to activeRange. | |
ChartMarker[] | [] | Event markers (buy / sell / post / entry / exit). Markers are filtered to the visible time window and snapped to the nearest data point. | |
boolean | false | Gate for rendering the markers prop. | |
string | '#22c55e' | Hex line color. Drives gradient fill, pill background, dashed price line, glow, and the pulse dot. Pass a red hex when delta is negative. | |
'full' | 'detail' | 'compact' | 'full' | 'full' shows price header + metric toggle; 'detail' shows token identity row + metric toggle (thesis-page layout); 'compact' hides heading and bottom controls. | |
'1H' | '1D' | '1W' | '1Y' | 'All' | '1W' | Current time range tab. Bindable. | |
number | 280 | Canvas height in logical CSS pixels. | |
boolean | false | Toggle for the header row (price + metric tabs). | |
string | '' | Formatted price string for the header display. | |
string | '' | Formatted delta string (e.g. "+4.2%") next to price. | |
'up' | 'down' | 'up' | Controls the chevron direction and text color (success / destructive). | |
string | '' | Token display name in the detail variant header. | |
string | '' | Token ticker in the detail variant header. | |
string | '' | URL of a square token icon rendered beside the name. | |
boolean | false | Suppresses the pulse animation and price pill overlay. Use for feed cards where the chart must read as a snapshot, not a live surface. | |
'price' | 'mcap' | 'price' | Initial metric tab. | |
(range: TimeRange) => void | — | Fires when the user picks a new range tab. Useful for external data re-fetching. | |
(marker: ChartMarker | null) => void | — | Fires with the hovered marker (or null when leaving). Use to sync other UI (e.g. scroll a timeline to the same event). |