Skip to content
ShineUI
Install

Candlestick chart

Candles, OHLC and hollow.

<sui-chart-candlestick>ChartsStable

About

Candlestick, OHLC-tick or hollow-candle chart for a price or balance over periods, when the high and low of a period matter as much as its close. Up periods take the first ramp step and down periods the accent, never a green and a red. Hovering a period dims the others and shows open, high, low and close.

Usage

<sui-chart-candlestick heading="Daily cash position" subtitle="Candles · up is navy, down is orange" caption="Body is open to close, wick is high to low · hover for the four figures" money></sui-chart-candlestick> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); const el = document.querySelector("sui-chart-candlestick"); el.items = [ { label: "Aug 1", o: 96000, h: 102100, l: 93000, c: 99100 }, { label: "Aug 2", o: 100045, h: 103045, l: 94528, c: 97528 }, { label: "Aug 3", o: 103765, h: 111684, l: 100765, c: 108684 }, { label: "Aug 4", o: 106873, h: 113255, l: 103873, c: 110255 }, { label: "Aug 5", o: 109147, h: 112147, l: 100434, c: 103434 }, { label: "Aug 6", o: 110459, h: 114641, l: 107459, c: 111641 }, { label: "Aug 7", o: 110784, h: 116325, l: 107784, c: 113325 }, { label: "Aug 8", o: 110208, h: 113208, l: 104322, c: 107322 }, { label: "Aug 9", o: 108915, h: 116994, l: 105915, c: 113994 }, { label: "Aug 10", o: 107170, h: 114094, l: 104170, c: 111094 }, { label: "Aug 11", o: 105285, h: 108285, l: 96997, c: 99997 }, { label: "Aug 12", o: 103589, h: 107689, l: 100589, c: 104689 }, ]; /* SuiCandle[] */ el.addEventListener("suiHover", (e) => console.log(e.detail)); </script>
import { SuiChartCandlestick } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { const items = [ { label: "Aug 1", o: 96000, h: 102100, l: 93000, c: 99100 }, { label: "Aug 2", o: 100045, h: 103045, l: 94528, c: 97528 }, { label: "Aug 3", o: 103765, h: 111684, l: 100765, c: 108684 }, { label: "Aug 4", o: 106873, h: 113255, l: 103873, c: 110255 }, { label: "Aug 5", o: 109147, h: 112147, l: 100434, c: 103434 }, { label: "Aug 6", o: 110459, h: 114641, l: 107459, c: 111641 }, { label: "Aug 7", o: 110784, h: 116325, l: 107784, c: 113325 }, { label: "Aug 8", o: 110208, h: 113208, l: 104322, c: 107322 }, { label: "Aug 9", o: 108915, h: 116994, l: 105915, c: 113994 }, { label: "Aug 10", o: 107170, h: 114094, l: 104170, c: 111094 }, { label: "Aug 11", o: 105285, h: 108285, l: 96997, c: 99997 }, { label: "Aug 12", o: 103589, h: 107689, l: 100589, c: 104689 }, ]; /* SuiCandle[] */ return <SuiChartCandlestick heading="Daily cash position" subtitle="Candles · up is navy, down is orange" caption="Body is open to close, wick is high to low · hover for the four figures" money items={items} onSuiHover={(e) => console.log(e.detail)} />; }
<script setup> import { SuiChartCandlestick } from "@shineui/vue"; import "@shineui/core/shineui.css"; const items = [ { label: "Aug 1", o: 96000, h: 102100, l: 93000, c: 99100 }, { label: "Aug 2", o: 100045, h: 103045, l: 94528, c: 97528 }, { label: "Aug 3", o: 103765, h: 111684, l: 100765, c: 108684 }, { label: "Aug 4", o: 106873, h: 113255, l: 103873, c: 110255 }, { label: "Aug 5", o: 109147, h: 112147, l: 100434, c: 103434 }, { label: "Aug 6", o: 110459, h: 114641, l: 107459, c: 111641 }, { label: "Aug 7", o: 110784, h: 116325, l: 107784, c: 113325 }, { label: "Aug 8", o: 110208, h: 113208, l: 104322, c: 107322 }, { label: "Aug 9", o: 108915, h: 116994, l: 105915, c: 113994 }, { label: "Aug 10", o: 107170, h: 114094, l: 104170, c: 111094 }, { label: "Aug 11", o: 105285, h: 108285, l: 96997, c: 99997 }, { label: "Aug 12", o: 103589, h: 107689, l: 100589, c: 104689 }, ]; // SuiCandle[] const onChange = (e) => console.log(e.detail); </script> <template> <SuiChartCandlestick heading="Daily cash position" subtitle="Candles · up is navy, down is orange" caption="Body is open to close, wick is high to low · hover for the four figures" money :items="items" @sui-hover="onChange" /> </template>
import { Component } from "@angular/core"; import { SuiChartCandlestick } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiChartCandlestick], template: `<sui-chart-candlestick heading="Daily cash position" subtitle="Candles · up is navy, down is orange" caption="Body is open to close, wick is high to low · hover for the four figures" money [items]="items" (suiHover)="onChange($event)"></sui-chart-candlestick>`, }) export class Example { items = [ { label: "Aug 1", o: 96000, h: 102100, l: 93000, c: 99100 }, { label: "Aug 2", o: 100045, h: 103045, l: 94528, c: 97528 }, { label: "Aug 3", o: 103765, h: 111684, l: 100765, c: 108684 }, { label: "Aug 4", o: 106873, h: 113255, l: 103873, c: 110255 }, { label: "Aug 5", o: 109147, h: 112147, l: 100434, c: 103434 }, { label: "Aug 6", o: 110459, h: 114641, l: 107459, c: 111641 }, { label: "Aug 7", o: 110784, h: 116325, l: 107784, c: 113325 }, { label: "Aug 8", o: 110208, h: 113208, l: 104322, c: 107322 }, { label: "Aug 9", o: 108915, h: 116994, l: 105915, c: 113994 }, { label: "Aug 10", o: 107170, h: 114094, l: 104170, c: 111094 }, { label: "Aug 11", o: 105285, h: 108285, l: 96997, c: 99997 }, { label: "Aug 12", o: 103589, h: 107689, l: 100589, c: 104689 }, ]; // SuiCandle[] onChange(e: CustomEvent) { console.log(e.detail); } }

Candles

The body runs open to close, the wick high to low; up periods take --viz-1, down periods --accent, and the key names the two. The plot is one tab stop named by an aria-label on the host, else by the heading and the span of periods, and the arrow keys reach every period. every thins colliding labels.

<sui-chart-candlestick heading="Daily cash position" subtitle="Candles · up is navy, down is orange" caption="Body is open to close, wick is high to low · hover for the four figures" money></sui-chart-candlestick>
// setup(root): properties and events setup(root) { root.querySelector('sui-chart-candlestick').items = days.slice(0, 12); }

OHLC ticks

kind="ohlc" draws each period as a vertical line from high to low with a left tick at the open and a right tick at the close.

<sui-chart-candlestick heading="Daily cash position" subtitle="Same series as OHLC ticks" kind="ohlc" money></sui-chart-candlestick>
// setup(root): properties and events setup(root) { root.querySelector('sui-chart-candlestick').items = days.slice(0, 12); }

Hollow candles

kind="hollow" leaves the body of an up period unfilled, so direction reads from the fill as well as the color.

<sui-chart-candlestick heading="Daily cash position" subtitle="Up days render hollow" kind="hollow" money></sui-chart-candlestick>
// setup(root): properties and events setup(root) { root.querySelector('sui-chart-candlestick').items = days.slice(10, 22); }

Events

suiHover fires when the hovered period changes and reports { index }, with index -1 on leave. The tooltip lists open, high, low and close, with the close row tinted by direction.

Idle.
<div style="display:flex;flex-direction:column;gap:12px"> <sui-chart-candlestick id="cs-events" heading="Daily cash position" subtitle="Hover a period" money></sui-chart-candlestick> <span id="cs-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Idle.</span> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#cs-events'); const log = root.querySelector('#cs-log'); const items = days.slice(0, 12); el.items = items; el.addEventListener('suiHover', (e) => { const i = e.detail.index; if (i < 0) { log.textContent = 'Idle.'; return; } const d = items[i]; log.textContent = `${d.label}: open ${money(d.o)}, close ${money(d.c)}, ${d.c >= d.o ? 'up' : 'down'}.`; }); }

Empty and loading

loading stands a shimmer in for the plot and marks the host aria-busy while the figures are on their way; the frame and its heading stay. A chart with nothing to plot keeps its frame and says so, and emptyMessage is the sentence. Both are the same on every chart.

<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px"> <sui-chart-candlestick heading="Daily cash position" subtitle="Fetching the month" height="150" loading></sui-chart-candlestick> <sui-chart-candlestick heading="Daily cash position" subtitle="This month" height="150" empty-message="No days have closed this month."></sui-chart-candlestick> </div>

Keyboard

KeyAction
TabMoves to the plot. The color key under it does not toggle, so it takes no stop.
ArrowRight ArrowLeftMoves to the next or the previous period and shows its tooltip; a reader hears its open, high, low and close.
Home EndMoves to the first or the last period.
EscapeLeaves the point and hides the tooltip; the plot keeps focus.

API

<sui-chart-candlestick>

Properties

PropertyAttributeTypeDefaultDescription
captioncaptionstringstring | undefinedFootnote under the legend.
currencycurrencystring'USD'The ISO 4217 code money prints in: USD by default, EUR, JPY. The page's locale (setLocale(), else <html lang>) places the symbol and sets the grouping and the decimal mark, so EUR on a German page reads 1,2 Mio. € on the axis and 121.695 € in the tooltip. An unknown code prints the bare figure. Ignored without money.
emptyMessageempty-messagestringstring | undefinedSentence drawn over the plot when there are no periods. Defaults to "Nothing to plot yet."; the frame, the axis and the legend stay. The chart filters nothing, so this is its only empty string.
everyeverySuiChartEvery"auto" | number'auto'Draw every nth period label, so a dense axis stays legible. auto, the default, measures the widest label against the space one period is given and thins only where the labels would collide; where they fit it draws all of them and the axis is what it always was. Set it to 1 to draw every label at any width, or to a larger number for the stride to start from: where its labels would run into each other it widens to the next multiple that clears them.
fixedSizefixed-sizebooleanfalsePin the plot to width, which is what a fixed export wants, or a row of charts that have to measure the same whatever the space around them. Without it the plot grows to the width the container gives it and the coordinate space tracks the pixel width, so one unit stays one pixel: the periods spread out and the axis text keeps the size it was drawn at. Reflected.
headingheadingstringstring | undefinedChart heading (the reference title prop, renamed because title is a native attribute). With what the chart shows, it names the plot for a reader; an aria-label or aria-labelledby on the host names the plot instead, and this summary then describes it.
heightheightnumber210ViewBox height of the plot in px.
hoverIndexhover-indexnumber-1Hovered period index; -1 when idle. The pointer and the arrow keys set it. The tooltip lists open, high, low and close, with close tinted by direction. A write from outside moves it and emits nothing, which is how sui-chart-synced mirrors a sibling panel.
itemsproperty onlySuiCandle[][]Periods in order. A period closing at or above its open is up and paints --sui-chart-candlestick-up, the first ramp step by default; one closing below is down and paints --sui-chart-candlestick-down, the accent by default. The legend and the tooltip's close key follow. Set them on the element or any ancestor. Any CSS color or var(--token) works; use a token so the color follows the theme. Property, not attribute.
kindkind"candles" | "hollow" | "ohlc"'candles'Mark style: filled candles, OHLC ticks, or candles that render hollow on up periods. Reflected.
loadingloadingbooleanfalseThe figures are still on their way: a shimmer stands in for the plot, the frame and its heading stay, and the host is aria-busy. Reflected, so a page can style the wait.
moneymoneybooleanfalseMoney formatting in currency: compact on the axis ($120k), exact in whole units in the tooltip ($121,695). Off, the figures print plainly, which is what a count or an index wants; set it for a price or a balance. Reflected.
subtitlesubtitlestringstring | undefinedOne-line context under the title.
widthwidthnumber460Width of the plot in px. The plot grows to the width its container gives it, and this is the starting figure, used until the container has been measured and wherever it measures zero; with fixedSize it is the plot's exact width. The card adds 34px of padding and border on top of it.

Events

EventDetailReactVueDescription
suiHoverSuiChartHoverDetailonSuiHover@sui-hoverFires when the pointer or the keyboard moves the hover to another period; { index: -1 } when the pointer leaves, or Escape or a blur takes the keyboard's point away.

Methods

MethodSignatureDescription
setFocus()setFocus() => Promise<void>Moves focus to the plot, the chart's one tab stop, from which the arrow keys reach the periods. Does nothing while there is nothing to plot.

Slots

SlotDescription
header-endControls at the end of the head row, after the title.

Types

SuiCandle interfaceOne period: label plus open, high, low and close.
labelstring
onumber
hnumber
lnumber
cnumber
SuiChartEvery typeHow the x labels thin: a fixed stride, or auto to measure the widest label against the room one point is given and draw only as many as fit.
No members.
SuiChartHoverDetail interfaceDetail of suiHover: the hovered index, or -1 when the pointer leaves the plot. What the index counts is each chart's own: an x position here, a point on a scatter, an axis on a radar.
indexnumber

Theming

Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.

See also