Yugen UI

BarChart

BarChart compares magnitudes across categories with vertical columns or horizontal bars, grouped or stacked, including negative values.

Hand-written SVG following the dataviz method: bars ≤ 24px thick, a 4px radius at the data end and square at the baseline, a 2px surface gap between bars / stacked segments, a single Y axis and a thin grid kept in the background. Hover the chart, or Tab into it and use the arrow keys, to see the tooltip; the “Data table” button switches to an equivalent table for screen readers.
Yugen Labs · experimentalThis component is outside SemVer: its API can change in a minor release, and What's new says so. Versioning policy
Revenue by channel
Last 6 months

Import

Install from npm, copy the source into your project with @yugen-ui/cli or the shadcn CLI, or import directly when working in this repo.

TSX
// npm install yugen-uiimport { BarChart } from "yugen-ui";

Variants

Each tile is a standalone example: hover to copy, or press Code to see the source.

Best sellers

Horizontal bars & value labels

Many categories or long names → horizontal bars. A single series doesn't need a legend; the value sits at the end of the bar.

Revenue by segment ($K)

Stacked columns

Part-to-whole by category; 2px gaps between segments, and only the outermost segment has rounded corners. The tooltip lists every segment and the total.

Net profit ($K)

Negative values (diverging)

Columns grow on both sides of the 0 baseline. diverging colors the two poles Sakura ↔ Ai; position already encodes the sign, color only adds emphasis.

Sales channel mix (%)

Texture

A fallback channel for full color blindness, print and forced-colors: 45° / 135° patterns. Turns on automatically in the high-contrast theme.

Usage

Do
  • Use columns to compare magnitudes; switch to horizontal bars when there are many categories or long names.
  • One series = one color (slot 1) for every bar. The title already names the series, so no legend is needed.
  • Turn on value labels only when there are few bars; otherwise leave it to the axis, the tooltip and the data table.
Don't
  • Don't use two Y axes. Split into two charts or index both to the same base (= 100).
  • Don't shade bars darker by value for categories with no order. The bar length already shows that.
  • Don't chart a single bar. Use StatTile.

Props

Prop · TypeDefaultDescription
dataChartDatum[]Default: —Array of data rows (objects).
indexstringDefault: —Category field.
series{ key, label, color? }[]Default: —Value series; color (1–8) pins a color slot, otherwise slots follow the order.
orientation"vertical" | "horizontal"Default: "vertical"Vertical columns or horizontal bars.
layout"grouped" | "stacked"Default: "grouped"Side by side or stacked.
valueFormat / axisFormatIntl.NumberFormatOptions | (v) => stringDefault: —Value format; the axis abbreviates automatically from 1,000.
showValuesbooleanDefault: falseLabels at the end of each bar, only when they fit.
divergingbooleanDefault: falseSingle series: negative / positive in a diverging color pair.
legendbooleanDefault: ≥ 2 seriesLegend that toggles series (colors stay the same when filtering).
barSizenumberDefault: 24Maximum bar thickness (px).
title / description / toolbarReactNodeDefault: —ChartContainer frame.
heightnumberDefault: 280 (vertical) · auto (horizontal)Height of the plot area, axis labels included.
localestringDefault: "vi-VN"Locale for Intl.
texturebooleanDefault: autoIdentifying patterns; turn on automatically in high contrast / forced-colors.
ariaLabel, indexLabel, hideTableToggle, defaultShowTable, animate…Default: —Accessible description, table column heading, table button, animation.

Messages

Text built into the component: labels, aria-labels, screen reader announcements.

English by default. Change it with the messages prop of YugenProvider, by the namespace below; messagesVi has the Vietnamese set. See Interface language.

messages.chart

Key · TypeDefault (English)messagesVi
roleDescriptionstring

aria-roledescription của vùng vẽ.

Default (English)"chart"messagesVi"biểu đồ"
heatmapRoleDescriptionstringDefault (English)"heat map"messagesVi"bản đồ nhiệt"
dataTablestring

Nút bật chế độ bảng của ChartContainer.

Default (English)"Data table"messagesVi"Bảng dữ liệu"
totalstring

Dòng tổng (tooltip, bảng) và nhãn giữa vòng DonutChart.

Default (English)"Total"messagesVi"Tổng"
valuestringDefault (English)"Value"messagesVi"Giá trị"
sharestringDefault (English)"Share"messagesVi"Tỉ lệ"
noDatastringDefault (English)"No data"messagesVi"Không có dữ liệu"
timestring

Tiêu đề cột đầu của bảng (khi không truyền indexLabel).

Default (English)"Time"messagesVi"Thời gian"
categorystringDefault (English)"Category"messagesVi"Danh mục"
groupstringDefault (English)"Group"messagesVi"Nhóm"
namestringDefault (English)"Name"messagesVi"Tên"
otherstring

Nhóm gộp (DonutChart vượt maxSlices, ScatterChart nhóm thứ 9+).

Default (English)"Other"messagesVi"Khác"
trendstring

Nút bật đường xu hướng của ScatterChart.

Default (English)"Trend"messagesVi"Xu hướng"
increasestringDefault (English)"Increase"messagesVi"Tăng"
decreasestringDefault (English)"Decrease"messagesVi"Giảm"
noChangestringDefault (English)"No change"messagesVi"Không đổi"
brushstring

ChartBrush: tên cửa sổ thu phóng, dòng "Đang xem … · n/N mốc", nút đặt lại.

Default (English)"Visible range"messagesVi"Vùng xem"
brushRoleDescriptionstringDefault (English)"zoom window"messagesVi"cửa sổ thu phóng"
brushViewingstringDefault (English)"Showing"messagesVi"Đang xem"
brushPoints(shown: number, total: number) => stringDefault (English)(shown, total) => `${shown}/${total} ${total === 1 ? "point" : "points"}`messagesVi(shown, total) => `${shown}/${total} mốc`
brushResetstringDefault (English)"Reset"messagesVi"Đặt lại"
lineSummary(info: { kind: "line" | "area" | "stacked" | "expand"; points: number; /** Mốc đầu / cuối (chỉ có khi points > 0). */ from?: string; to?: string; series: string[]; }) => string

Mô tả truy cập tự sinh (khi không truyền ariaLabel).

Default (English)Function, see messages.tsmessagesViFunction, see messages.ts
barSummary(info: { horizontal: boolean; stacked: boolean; categories: number; series: string[] }) => stringDefault (English)Function, see messages.tsmessagesViFunction, see messages.ts
donutSummary(info: { groups: number; total: string; largest?: { label: string; share: string } }) => stringDefault (English)Function, see messages.tsmessagesViFunction, see messages.ts
scatterSummary(info: { points: number; x: string; y: string; /** Tên đại lượng kích thước (biểu đồ bong bóng). */ size?: string; /** Tên các nhóm (khi có nhiều nhóm). */ groups?: string[]; trends: { group?: string; slope: string; r2: string }[]; }) => stringDefault (English)Function, see messages.tsmessagesViFunction, see messages.ts
heatmapSummary(info: { rows: number; columns: number; min?: string; max?: string; hasEmpty: boolean }) => stringDefault (English)Function, see messages.tsmessagesViFunction, see messages.ts
sparklineSummary(info: { points: number; first: string; last: string; min: string; max: string }) => stringDefault (English)Function, see messages.tsmessagesVi({ points, first, last, min, max }) => `Xu hướng ${points} điểm: từ ${first} đến ${last}, thấp nhất ${min}, cao nhất ${max}`