Skip to main content

Bar

Info:Metrics
24

A grouped or stacked bar chart, colored from a theme-derived series palette, with an interactive value readout and opt-in clickable bars.

bar chart column chart histogram graph stacked bar

Live demo

live · @xtyle/astro

Bar

Grouped series

Two series across four quarters, colored from the accents scheme. Hover or focus a bar for its value.

WebMobile
Revenue by quarter
CategoryWebMobile
Q1128
Q21914
Q31518
Q42225

Stacked

stacked folds each day's tiers into one bar, here on the thermal scale.

CPUIONet
Load by tier
CategoryCPUIONet
Mon302012
Tue451822
Wed283416
Thu522228
Fri403018

Horizontal

orientation="horizontal" runs bars rightward with the categories down the side, better for long labels. A single series colors by category, so each bar takes its own skittles hue.

Lines by language
CategoryLines
Rust4200
TypeScript3800
Svelte1500
Astro900
CSS2100

Actionable bars

selectable makes each bar a button that fires select on click or Enter/Space, carrying { series, category, value, … } for a drill-in.

Runs by workflow
CategoryRuns
Alpha42
Bravo30
Charlie18
Delta9

Selected: (click a bar)

One scheme, four flavors

skittles

Skittles scheme
CategoryWebMobile
Q1128
Q21914
Q31518
Q42225

status

Status scheme
CategoryWebMobile
Q1128
Q21914
Q31518
Q42225

thermal, reversed

Thermal reversed
CategoryWebMobile
Q1128
Q21914
Q31518
Q42225

single series

Weekly visits
CategoryVisits
S40
M62
T55
W78
T90
F72
S60

Empty state

With no categories or series to plot, the chart shows a muted No data message in place of the axes, so a no-data period reads as intentional rather than broken.

No data

Bar plots one or more numeric series across a set of categories as an SVG chart that renders from data alone. Each series takes its color from a scheme resolved off the live theme (the accents fan, the skittles hue ring, a thermal cold-to-hot scale, or the status roster), so a chart is coherent with the rest of the UI out of the box; pass an explicit color array for full control, and reverse to flip any scheme.

Bars sit side by side by default or stack with stacked, and run vertically or horizontally (orientation) for long category labels. It's interactive: hovering or focusing a bar dims the rest and floats a value readout, and the whole chart is mirrored into a visually-hidden data table so assistive tech reads the numbers, not the pixels. Set selectable to make it a drill-in surface: each bar becomes a button that fires a select event with its series, category, and value, so a click filters, navigates, or drills into that data. A value axis with gridlines and category labels come derived; a legend appears for multi-series data. Set height for the plot area; the chart fills its container's width. With no categories or series to plot, it shows a muted No data message in place of the axes.

When to use

How this component composes with the rest of the set.

Colors come from the same register the rest of the UI does, so a chart matches its surrounding chrome without hand-picking colors. Pick a scheme that fits the data: accents for a branded set, skittles for many distinct categories, thermal or status for a scale.
Pair a Bar with Stat cards in the Metrics family for a headline number beside its breakdown.
For a single-series chart, drop the legend (legend={false}) and let the category labels carry the meaning.
Make a dashboard chart a drill-in with selectable and a select listener: click a bar to filter the table beside it, open that category's detail, or route to a filtered view. The event's detail names the exact series / category / value clicked, so the handler needs no lookup.

Props

12 props, straight from the manifest.

PropTypeDefaultBindingsDescription
series { name: string; values: number[] }[]
html svelte astro
The data: one entry per series, each a name and a value per category. JS property in html/svelte, JSON attribute or prop in astro.
categories string[]
html svelte astro
The category labels along the axis; one per index in each series' `values`.
scheme SeriesScheme | string[]
accents skittles statuses thermal status
accents
html svelte astro
How series are colored: a built-in scheme resolved off the theme, or an explicit color array.
reverse boolean false
html svelte astro
Flips the scheme end for end (a sequential scale runs the other way; a categorical set reverses order).
colorBy "series" | "category"
series category
auto
html svelte astro
Which axis drives the palette. Defaults to coloring by category for a single series (so the scheme still varies bar to bar) and by series once there's more than one; set explicitly to override. Kebab `color-by` on the element.
orientation "vertical" | "horizontal"
vertical horizontal
vertical
html svelte astro
Bars grow up from a category axis (`vertical`) or rightward from the categories down the side (`horizontal`, better for long labels or many categories).
stacked boolean false
html svelte astro
Stacks each category's series into one bar instead of placing them side by side.
showValues boolean false
html svelte astro
Draws each bar's value above it (grouped mode). Kebab `show-values` on the element.
legend boolean true
html svelte astro
Shows the series legend for multi-series charts; set false to hide it.
height number 320
html svelte astro
The plot height in pixels; the chart fills its container's width.
label string
html svelte astro
An accessible name for the chart, used as the data table's caption.
selectable boolean false
html svelte astro
Makes bars actionable: each bar becomes a `role="button"` with a pointer cursor, and clicking one (or pressing Enter/Space while it's focused) fires a `select` `CustomEvent` whose `detail` carries `{ series, category, value, seriesIndex, categoryIndex }`. `@xtyle/svelte` surfaces it as an `onselect` callback. Leave it off for a read-only chart.

Appearance

States

bar-hover

.xtyle-bar--hovering .xtyle-bar__bar:not(.is-active)

Pointer over or keyboard focus on a bar: the rest dim and a value readout floats above it.

bar-focus

.xtyle-bar__bar:focus-visible

Keyboard focus on a bar draws a token ring; each bar is a tab stop that announces its series, category, and value.

Anatomy

The named parts that make up the component, with their selectors.

chart

.xtyle-bar

The <figure> root holding the SVG plot, legend, tooltip, and the accessible data table.

--font-sans

bar

.xtyle-bar__bar

A single value bar, filled with its series color; the focus/hover target.

--ring --border-thick --duration-fast --ease-standard

grid

.xtyle-bar__grid

The value gridlines, axis baseline, and tick labels.

--line --line-2 --fg-3 --text-xs

legend

.xtyle-bar__legend

The series key, shown for multi-series charts; each item pairs a color dot with the series name.

--fg-1 --text-sm --space-1 --space-3 --radius-sm

tooltip

.xtyle-bar__tooltip

The floating value readout shown on hover or focus of a bar.

--surface-overlay --surface-overlay-border --elevation-3 --radius-md --fg-0

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 24/24 consumed tokens produced default register: 299 tokens

Live coverage check against the xtyle-default register (derive(xtyleDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--border-thick --border-thin --duration-fast --ease-standard --elevation-3 --fg-0 --fg-1 --fg-2 --fg-3 --font-sans --leading-normal --line --line-2 --radius-md --radius-sm --ring --space-1 --space-2 --space-3 --surface-overlay --surface-overlay-border --text-sm --text-xs --weight-semibold

Accessibility

The SVG is decorative (aria-hidden); the chart's data is mirrored into a visually-hidden <table> so assistive tech reads the actual numbers. label becomes the table's caption.
Each bar is a focusable tab stop with an aria-label naming its series, category, and value, so the chart is navigable by keyboard, and the value readout appears on focus as well as hover.
With selectable, each bar becomes a role="button" (announced as actionable) and activates on Enter/Space as well as click, so a keyboard user can drill in; a read-only chart keeps its bars as role="img" data points.
Color is never the only channel: the legend names each series in text, and the table repeats every value.

Code

Grouped series

Two series across four categories, colored from the accents scheme, with an interactive readout.

<xtyle-bar
	categories='["Q1","Q2","Q3","Q4"]'
	scheme="accents"
	label="Revenue by quarter"
	height="280"></xtyle-bar>
<script>
	document.querySelector("xtyle-bar").series = [
		{ name: "Web", values: [12, 19, 15, 22] },
		{ name: "Mobile", values: [8, 14, 18, 25] },
	];
</script>
<script lang="ts">
	import { Bar } from "@xtyle/svelte";

	const categories = ["Q1", "Q2", "Q3", "Q4"];
	const series = [
		{ name: "Web", values: [12, 19, 15, 22] },
		{ name: "Mobile", values: [8, 14, 18, 25] },
	];
</script>

<Bar {categories} {series} scheme="accents" label="Revenue by quarter" height={280} />
---
import { Bar } from "@xtyle/astro";
const categories = ["Q1", "Q2", "Q3", "Q4"];
const series = [
	{ name: "Web", values: [12, 19, 15, 22] },
	{ name: "Mobile", values: [8, 14, 18, 25] },
];
---

<Bar categories={categories} series={series} scheme="accents" label="Revenue by quarter" height={280} />

Stacked

stacked folds each category's series into one bar; here on the thermal scale.

<xtyle-bar
	categories='["Mon","Tue","Wed","Thu","Fri"]'
	scheme="thermal"
	stacked
	label="Load by tier"></xtyle-bar>
<script>
	document.querySelector("xtyle-bar").series = [
		{ name: "CPU", values: [30, 45, 28, 52, 40] },
		{ name: "IO", values: [20, 18, 34, 22, 30] },
		{ name: "Net", values: [12, 22, 16, 28, 18] },
	];
</script>
<script lang="ts">
	import { Bar } from "@xtyle/svelte";

	const categories = ["Mon", "Tue", "Wed", "Thu", "Fri"];
	const series = [
		{ name: "CPU", values: [30, 45, 28, 52, 40] },
		{ name: "IO", values: [20, 18, 34, 22, 30] },
		{ name: "Net", values: [12, 22, 16, 28, 18] },
	];
</script>

<Bar {categories} {series} scheme="thermal" stacked label="Load by tier" />
---
import { Bar } from "@xtyle/astro";
const categories = ["Mon", "Tue", "Wed", "Thu", "Fri"];
const series = [
	{ name: "CPU", values: [30, 45, 28, 52, 40] },
	{ name: "IO", values: [20, 18, 34, 22, 30] },
	{ name: "Net", values: [12, 22, 16, 28, 18] },
];
---

<Bar categories={categories} series={series} scheme="thermal" stacked label="Load by tier" />

Horizontal

orientation="horizontal" runs bars rightward with the categories down the side. A single series colors by category, so each bar takes its own skittles hue.

<xtyle-bar
	categories='["Rust","TypeScript","Svelte","Astro","CSS"]'
	orientation="horizontal"
	scheme="skittles"
	label="Lines by language"
	height="240"></xtyle-bar>
<script>
	document.querySelector("xtyle-bar").series = [
		{ name: "Lines", values: [4200, 3800, 1500, 900, 2100] },
	];
</script>
<script lang="ts">
	import { Bar } from "@xtyle/svelte";

	const categories = ["Rust", "TypeScript", "Svelte", "Astro", "CSS"];
	const series = [{ name: "Lines", values: [4200, 3800, 1500, 900, 2100] }];
</script>

<Bar {categories} {series} orientation="horizontal" scheme="skittles" label="Lines by language" height={240} />
---
import { Bar } from "@xtyle/astro";
const categories = ["Rust", "TypeScript", "Svelte", "Astro", "CSS"];
const series = [{ name: "Lines", values: [4200, 3800, 1500, 900, 2100] }];
---

<Bar categories={categories} series={series} orientation="horizontal" scheme="skittles" label="Lines by language" height={240} />

Actionable bars

selectable makes each bar a button that fires select on click or Enter/Space, so a chart can drive a drill-in. The detail carries the series, category, value, and both indices.

<xtyle-bar
	categories='["Alpha","Bravo","Charlie","Delta"]'
	label="Runs by workflow"
	selectable
	height="240"></xtyle-bar>
<script>
	const bar = document.querySelector("xtyle-bar");
	bar.series = [{ name: "Runs", values: [42, 30, 18, 9] }];
	bar.addEventListener("select", (e) => {
		openWorkflow(e.detail.category);
	});
</script>
<script lang="ts">
	import { Bar } from "@xtyle/svelte";

	const categories = ["Alpha", "Bravo", "Charlie", "Delta"];
	const series = [{ name: "Runs", values: [42, 30, 18, 9] }];
</script>

<Bar
	{categories}
	{series}
	label="Runs by workflow"
	selectable
	height={240}
	onselect={(e) => openWorkflow(e.detail.category)}
/>
---
import { Bar } from "@xtyle/astro";
const categories = ["Alpha", "Bravo", "Charlie", "Delta"];
const series = [{ name: "Runs", values: [42, 30, 18, 9] }];
---

<Bar categories={categories} series={series} label="Runs by workflow" selectable height={240} />
<script>
	document.querySelector("xtyle-bar").addEventListener("select", (e) => {
		openWorkflow(e.detail.category);
	});
</script>