Skip to content
ShineUI
Install

Bar chart

Columns, grouped, stacked, horizontal.

<sui-chart-bar>ChartsStable

About

Column and bar chart: grouped or stacked columns, or horizontal bars. Reach for it when categories compare against each other rather than over a continuous axis. Series draw from the brand ramp and one series, or one category, may take the accent. Hovering a column group or bar row dims the others and shows a tooltip; legend keys toggle series.

Usage

<sui-chart-bar heading="Revenue vs. spend" subtitle="Grouped columns · hover dims the other months" money></sui-chart-bar> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); const el = document.querySelector("sui-chart-bar"); el.labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"]; /* string[] */ el.series = [ { label: "Revenue", data: [148200, 152900, 161400, 155100, 166800, 168900, 166200] }, { label: "Spend", data: [136500, 139200, 147900, 143400, 151600, 153200, 154600], accent: true }, ]; /* SuiChartSeries[] */ el.addEventListener("suiHover", (e) => console.log(e.detail)); </script>
import { SuiChartBar } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { const labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"]; /* string[] */ const series = [ { label: "Revenue", data: [148200, 152900, 161400, 155100, 166800, 168900, 166200] }, { label: "Spend", data: [136500, 139200, 147900, 143400, 151600, 153200, 154600], accent: true }, ]; /* SuiChartSeries[] */ return <SuiChartBar heading="Revenue vs. spend" subtitle="Grouped columns · hover dims the other months" money labels={labels} series={series} onSuiHover={(e) => console.log(e.detail)} />; }
<script setup> import { SuiChartBar } from "@shineui/vue"; import "@shineui/core/shineui.css"; const labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"]; // string[] const series = [ { label: "Revenue", data: [148200, 152900, 161400, 155100, 166800, 168900, 166200] }, { label: "Spend", data: [136500, 139200, 147900, 143400, 151600, 153200, 154600], accent: true }, ]; // SuiChartSeries[] const onChange = (e) => console.log(e.detail); </script> <template> <SuiChartBar heading="Revenue vs. spend" subtitle="Grouped columns · hover dims the other months" money :labels="labels" :series="series" @sui-hover="onChange" /> </template>
import { Component } from "@angular/core"; import { SuiChartBar } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiChartBar], template: `<sui-chart-bar heading="Revenue vs. spend" subtitle="Grouped columns · hover dims the other months" money [labels]="labels" [series]="series" (suiHover)="onChange($event)"></sui-chart-bar>`, }) export class Example { labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"]; // string[] series = [ { label: "Revenue", data: [148200, 152900, 161400, 155100, 166800, 168900, 166200] }, { label: "Spend", data: [136500, 139200, 147900, 143400, 151600, 153200, 154600], accent: true }, ]; // SuiChartSeries[] onChange(e: CustomEvent) { console.log(e.detail); } }

Basic

Hovering a month dims the other groups and lists every visible series. Series take the ramp in order and the one marked accent takes --accent. The plot is one tab stop named by an aria-label on the host, else by the heading and the visible series, and the arrow keys reach every column. Legend keys toggle a series.

<sui-chart-bar heading="Revenue vs. spend" subtitle="Grouped columns · hover dims the other months" money></sui-chart-bar>
// setup(root): properties and events setup(root) { const el = root.querySelector('sui-chart-bar'); el.labels = M; el.series = [ { label: 'Revenue', data: REVENUE }, { label: 'Spend', data: SPEND, accent: true }, ]; }

In another currency

currency="EUR" prints euros on the axis, over the bars and in the tooltip. The page's locale places the symbol and sets the grouping, so a German page reads 150.000 € where this one reads €150k, and the value axis widens to fit the longer label.

<sui-chart-bar heading="Revenue, euro entity" subtitle="currency=&quot;EUR&quot;" money currency="EUR"></sui-chart-bar>
// setup(root): properties and events setup(root) { const el = root.querySelector('sui-chart-bar'); el.labels = M; el.series = [{ label: 'Revenue', data: REVENUE }]; }

Stacked columns

stacked piles the series in each column and the tooltip adds a Total row. Toggling a legend key off drops that bucket from the stack and the total.

<sui-chart-bar heading="A/R aging by month" subtitle="Stacked columns" stacked money></sui-chart-bar>
// setup(root): properties and events setup(root) { const el = root.querySelector('sui-chart-bar'); el.labels = ['Apr', 'May', 'Jun', 'Jul', 'Aug']; el.series = [ { label: 'Current', data: k([41, 44, 43, 46, 47.6]) }, { label: '1–30', data: k([18, 20, 19, 21, 22.5]) }, { label: '31–60', data: k([9, 10, 12, 11, 12.9]) }, { label: '61+', data: k([5, 6, 6.5, 7, 7.7]) }, ]; }

Horizontal bars

orientation="horizontal" lays the categories down the left with 26px per row. A single series prints its value at the end of each bar and draws no legend; hovering a row dims the others and shows the tooltip under the bar, and suiHover reports the row.

<sui-chart-bar heading="Top customers by open balance" subtitle="Horizontal bars with value labels · hover a row" orientation="horizontal" money></sui-chart-bar>
// setup(root): properties and events setup(root) { const el = root.querySelector('sui-chart-bar'); el.labels = ['Clark County Schools', 'Derby City Events', 'Riverbend Dental', 'New Albany Parks', 'Huber Winery']; el.series = [{ label: 'Open', data: [18940, 14300, 12480, 11050, 9480] }]; }

Flagged category

accentAt on a series names the one category drawn in orange, and refLine adds a dashed budget rule with a label. A single series prints its values above the columns.

<sui-chart-bar id="br-flag" heading="Monthly spend" subtitle="July flagged · dashed rule is the budget" money></sui-chart-bar>
// setup(root): properties and events setup(root) { const el = root.querySelector('#br-flag'); el.labels = M; el.series = [{ label: 'Spend', data: SPEND, accentAt: 'Jul' }]; el.refLine = { value: 150000, label: 'Budget' }; }

Fixed width

Every chart grows to the width its container gives it, so one card fills a column and two share it. fixed-size pins the plot to width instead, and the card is 34px wider, for an export or a row of charts that must measure the same whatever the space around them.

<div style="display:flex;flex-wrap:wrap;gap:14px"> <sui-chart-bar id="br-fixed-a" heading="Louisville" subtitle="Pinned to 300px" width="300" height="140" fixed-size money></sui-chart-bar> <sui-chart-bar id="br-fixed-b" heading="New Albany" subtitle="Pinned to 300px" width="300" height="140" fixed-size money></sui-chart-bar> </div>
// setup(root): properties and events setup(root) { const a = root.querySelector('#br-fixed-a'); a.labels = ['Q1', 'Q2', 'Q3', 'Q4']; a.series = [{ label: 'Revenue', data: k([412, 438, 451, 476]) }]; const b = root.querySelector('#br-fixed-b'); b.labels = ['Q1', 'Q2', 'Q3', 'Q4']; b.series = [{ label: 'Revenue', data: k([188, 194, 206, 215]) }]; }

Events

suiHover fires when the hovered month changes and reports { index }, with index -1 on leave. The pointer picks the nearest column band, so the log and the tooltip always agree.

Idle.
<div style="display:flex;flex-direction:column;gap:12px"> <sui-chart-bar id="br-events" heading="Revenue vs. spend" subtitle="Hover a month" money></sui-chart-bar> <span id="br-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('#br-events'); const log = root.querySelector('#br-log'); el.labels = M; el.series = [ { label: 'Revenue', data: REVENUE }, { label: 'Spend', data: SPEND, accent: true }, ]; el.addEventListener('suiHover', (e) => { const i = e.detail.index; log.textContent = i < 0 ? 'Idle.' : `${M[i]}: revenue $${REVENUE[i].toLocaleString('en-US')}, spend $${SPEND[i].toLocaleString('en-US')}.`; }); }

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-bar heading="Revenue vs. spend" subtitle="Fetching the quarter" height="150" loading></sui-chart-bar> <sui-chart-bar id="br-empty" heading="Tasks closed" subtitle="This quarter" height="150" empty-message="No tasks were closed this quarter."></sui-chart-bar> </div>
// setup(root): properties and events setup(root) { root.querySelector('#br-empty').labels = ['Jul', 'Aug', 'Sep']; }

Keyboard

KeyAction
TabMoves to the plot, then through the legend keys, one button per series.
ArrowRight ArrowLeftMoves to the next or the previous month and shows its tooltip; a reader hears the month, the series and the figure. On horizontal bars Up and Down move between rows and Left and Right between series.
ArrowDown ArrowUpMoves to the next or the previous series at the same month.
Home EndMoves to the first or the last month of the series.
Ctrl+Home Ctrl+EndMoves to the first point of the first series or the last point of the last.
EscapeLeaves the point and hides the tooltip; the plot keeps focus.
Enter SpaceOn a legend key, hides its series and redraws the chart without it; press again to bring it back.

API

<sui-chart-bar>

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 series or every value of every series is zero. 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 category label, so a dense axis stays legible. auto, the default, measures the widest label against the space one column is given; where they fit it draws all of them and the axis is what it always was, and where they would collide it sets each name on two lines under its column, broken at spaces, as long as every first word fits, and thins only past that. Set it to 1 to draw every label on one line 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. Vertical charts only: horizontal bars name every row beside it.
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 columns 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.
heightheightnumber200ViewBox height of the plot in px (vertical only; horizontal bars derive 26px per row).
hoverIndexhover-indexnumber-1Hovered category index; -1 when idle. The pointer and the arrow keys set it. A write from outside moves the highlight and the tooltip and emits nothing, which is how sui-chart-synced mirrors a sibling panel.
labelsproperty onlystring[][]Category labels. Property, not attribute.
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 and the labels ($120k), exact in the tooltip ($121,695). Off, the figures print plainly, which is what a count wants; set it for a balance. Reflected.
orientationorientation"horizontal" | "vertical"'vertical'Bar direction. horizontal lays the category label left and, for a single series, the value right; two or more series stack along each bar, and hovering a row dims the others and shows the tooltip under it, so hoverIndex and suiHover count rows. Under dir="rtl" horizontal bars mirror, the labels at the right and the bars growing left; columns keep their categories left to right and move the value axis to the right. Reflected, as on every other element that carries it.
refLineproperty onlySuiChartRefLineSuiChartRefLine | undefinedReference line: a dashed rule in the deep accent at value with an optional label, for a budget or a target. Vertical charts only. Property, not attribute.
seriesproperty onlySuiChartSeries[][]Series to plot. One may be accent, or accentAt on a series names the one category drawn in the accent. A single series prints its values as labels and draws no legend; two or more get legend keys that toggle them. Property, not attribute.
stackedstackedbooleanfalseStack the series in each column instead of grouping them side by side. The tooltip adds a Total row. Reflected.
subtitlesubtitlestringstring | undefinedOne-line context under the title.
widthwidthnumber440Width 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 category; { 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 bars. Does nothing while there is nothing to plot.

Slots

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

Types

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
SuiChartMark typePoint marker shape. dot is the default hollow circle, solid fills it, square and diamond mark a point measured differently (an estimate, a restated figure, a flagged event), and none hides it. Shapes carry the meaning so a marker never needs a hue of its own.
No members.
SuiChartRefLine interfaceReference line annotation: a dashed rule in the deep accent at value with an optional right-aligned label.
valuenumber
label?string
color?stringThe rule's own color, which its label takes too. Defaults to var(--accent-deep). Any CSS color or var(--token) works; use a token so the color follows the theme. The label is text, so pick a color that reads at 4.5:1 on the card.
SuiChartSeries interfaceOne plotted series. accent claims the single accent highlight; dashed marks a comparison (prior year); range draws a low/high envelope behind data; color overrides the ramp; accentAt (bar charts) names the one category drawn in accent; axis (line charts) puts the series on the end value axis; mark and marks (line charts) set the point markers.
labelstring
datanumber[]
accent?boolean
dashed?boolean
color?string
range?[number[], number[]]
accentAt?string
axis?'start' | 'end'Value axis this series is measured against, in reading order. start, the default, is the first axis; end gives the series its own scale and ticks on a second axis at the inline end, fitted to its own values rather than anchored on zero. Ignored when kind is stacked-area, where every series shares one scale. Any other value reads as start. Under dir="rtl" the start axis stands at the right edge beside the title and the end axis at the left.
mark?SuiChartMarkMarker drawn at every point of this series. Defaults to dot while markers are showing.
marks?(SuiChartMark | null | undefined)[]Markers per point, parallel to data; a gap falls back to mark. A point with a marker of its own always draws, whatever markers says, so a series can mark only what matters.

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