Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
SVG chart component with progressive enhancement: semantic table → CSS chart → SVG chart. Supports bar, column, line, area, pie, ring, scatter, and bubble types.
An SVG chart web component with built-in charting engine. Supports eight chart types with five data source options. Progressive enhancement means a semantic <table> is visible without JavaScript — with JS, the SVG chart renders and the table becomes screen-reader only.
Set data-type to one of: bar, column, line, area, pie, ring, scatter, or bubble.
<chart-wc data-type="line" data-title="Weekly Users" data-label-x="Day of Week" data-label-y="Users" data-legend data-tooltip data-values='[ {"name": "Desktop", "values": {"Mon": 120, "Tue": 180, "Wed": 150, "Thu": 210, "Fri": 190, "Sat": 80, "Sun": 60}}, {"name": "Mobile", "values": {"Mon": 80, "Tue": 120, "Wed": 160, "Thu": 140, "Fri": 200, "Sat": 220, "Sun": 180}} ]'></chart-wc>
Pie charts accept a flat object (label → value) as data.
<chart-wc data-type="pie" data-title="Market Share" data-legend data-values='{"Chrome": 65, "Safari": 18, "Firefox": 8, "Edge": 5, "Other": 4}'></chart-wc>
A ring (donut) chart is a pie with a transparent center hole. Same data shape as pie: a flat object of label → value. Slices below an angular threshold render with external leader-line labels; the percentage labels inside the slices are suppressed in ring mode to keep the chart readable at small sizes.
<chart-wc data-type="ring" data-title="Storage Used" data-legend data-tooltip data-values='{"Documents": 42, "Photos": 28, "Music": 15, "Apps": 10, "Free": 5}'></chart-wc>
Tunables specific to ring (and pie) charts — pass via data-config:
| Path | Default | Purpose |
|---|---|---|
center.size | "40%" | Hole diameter as a percent of the chart's viewBox. Larger values produce a thinner ring. |
center.style.fill | var(--color-surface, #fff) | Hole fill. Follows the active VB theme by default; override with a custom --color-surface or an explicit fill value. |
plot.node.label.placement | "auto" | auto uses external labels for small slices and hides internal labels in ring mode. Force with "outside". |
plot.node.label.threshold | 12 | Slice angle (degrees) below which auto-placement switches to an external leader-line label. |
<chart-wc data-type="ring" data-title="Storage" data-legend data-config='{"center":{"size":"55%","style":{"fill":"transparent"}}}' data-values='{"Used": 78, "Free": 22}'></chart-wc>
Add data-size="sparkline" to render a minimal trend chart with no axes, gridlines, tick labels, axis labels, title, legend, or tooltip. The chart becomes a pure inline shape sized by its container — ideal for dashboard tiles, data tables, and the sparkline slot of <score-card>.
Sparkline mode is a deterministic opt-in: shrinking the container alone won't strip chrome. Add the attribute when you want a sparkline, omit it for the full chart.
<chart-wc data-type="line" data-size="sparkline" style="block-size: 40px;" data-values='[{"name":"r","values":{"1":120,"2":135,"3":128,"4":150,"5":162,"6":174,"7":190}}]'></chart-wc>
The line, area, and column/bar chart types are the recommended sparkline shapes. pie and ring still render but ignore the chrome-strip (their chrome is the chart). Pair with a CSS custom property override on --chart-series-1 to colour the trend by tone:
chart-wc[data-size="sparkline"].trend-up { --chart-series-1: var(--color-success); }chart-wc[data-size="sparkline"].trend-down { --chart-series-1: var(--color-error); }chart-wc[data-size="sparkline"].trend-flat { --chart-series-1: var(--color-text-muted); }
The component resolves data using this priority chain:
| Priority | Source | Description |
|---|---|---|
| 1 | JS property (.data) | Set data programmatically |
| 2 | data-values | Inline JSON attribute |
| 3 | <script type="application/json"> | JSON script block child |
| 4 | <template data-chart-data> | Template child with JSON |
| 5 | <table> | Child table (progressive enhancement) |
Wrap a semantic <table> inside <chart-wc>. The chart extracts data from the table and renders SVG. Without JavaScript, the table remains visible.
<chart-wc> <table class="vb-chart" data-type="column" data-chart="replace" data-tooltip> <caption>Quarterly Results</caption> <thead> <tr><th></th><th>Revenue</th><th>Expenses</th><th>Profit</th></tr> </thead> <tbody> <tr><th>Q1</th><td>45</td><td>32</td><td>13</td></tr> <tr><th>Q2</th><td>52</td><td>35</td><td>17</td></tr> <tr><th>Q3</th><td>48</td><td>30</td><td>18</td></tr> <tr><th>Q4</th><td>61</td><td>38</td><td>23</td></tr> </tbody> </table></chart-wc>
<chart-wc data-type="area" data-title="Traffic Sources" data-legend data-tooltip> <script type="application/json"> [ {"name": "Organic", "values": {"Jan": 3200, "Feb": 4100, "Mar": 3800, "Apr": 5200}}, {"name": "Referral", "values": {"Jan": 1200, "Feb": 1400, "Mar": 1100, "Apr": 1800}} ] </script></chart-wc>
<chart-wc data-type="bar" data-title="Team Velocity" data-tooltip> <template data-chart-data> [{"name": "Story Points", "values": {"Sprint 1": 21, "Sprint 2": 34, "Sprint 3": 29, "Sprint 4": 42}}] </template></chart-wc>
Use data-chart-label, data-chart-series, and data-chart-ignore on <th> elements to control how table columns are extracted.
<chart-wc> <table class="vb-chart" data-type="column" data-chart="replace" data-tooltip> <caption>Product Sales</caption> <thead> <tr> <th data-chart-label>Product</th> <th data-chart-ignore>ID</th> <th>Units Sold</th> <th>Revenue</th> </tr> </thead> <tbody> <tr><td>Alpha</td><td>P001</td><td>120</td><td>3600</td></tr> <tr><td>Beta</td><td>P002</td><td>85</td><td>4250</td></tr> <tr><td>Gamma</td><td>P003</td><td>200</td><td>6000</td></tr> </tbody> </table></chart-wc>
| Attribute | Values | Default | Description |
|---|---|---|---|
data-type | "bar", "column", "line", "area", "pie", "scatter", "bubble" | — | Chart type to render |
data-values | string | — | Chart data as JSON string |
data-config | string | — | SVC config overrides as JSON string |
data-title | string | — | Chart title text |
data-legend | boolean | — | Show legend (presence enables) |
data-tooltip | boolean | — | Enable tooltips (presence enables) |
data-palette | string | — | Custom color palette as JSON array |
data-chart | "replace", "enhance" | — | How to handle source table: replace hides table, enhance keeps both |
| Element | Required | Description |
|---|---|---|
<table> | yes | Optional source table — data is extracted and table becomes screen-reader accessible |
<script type='application/json'> | yes | Optional inline JSON data source |
<template data-chart-data> | yes | Optional template-based JSON data source |
| Event | Detail | Description |
|---|---|---|
chart-wc:render | { type, seriesCount } | Fired after SVG chart renders successfully |
chart-wc:error | { message } | Fired when chart rendering fails |
const chart = document.querySelector('chart-wc'); chart.addEventListener('chart-wc:render', (e) => { console.log('Rendered:', e.detail.type, e.detail.seriesCount, 'series');}); chart.addEventListener('chart-wc:error', (e) => { console.error('Chart error:', e.detail.message);});
| Property / Method | Type | Description |
|---|---|---|
.data | Array|Object | Get/set chart data. Triggers re-render on set. |
.config | Object | Get/set chart config overrides. Triggers re-render on set. |
.refresh() | void | Re-extract table data and re-render the chart. |
.toSVG() | string|null | Get the current SVG markup for export. |
const chart = document.querySelector('chart-wc'); // Set data via JS propertychart.data = [ {name: '2024', values: {Q1: 120, Q2: 180, Q3: 150, Q4: 210}}, {name: '2025', values: {Q1: 140, Q2: 200, Q3: 170, Q4: 240}},]; // Update configchart.config = { legend: { enabled: true } }; // Re-extract from table and re-renderchart.refresh(); // Get SVG markup for exportconst svg = chart.toSVG();
For frameworks pushing live data, wait for chart-wc:upgraded before the first assignment, then react to changes via chart-wc:data-changed. The source field lets consumers ignore echoes from their own assignments.
const chart = document.querySelector('chart-wc'); await new Promise(r => chart.addEventListener('chart-wc:upgraded', r, { once: true })); // Initial assignmentchart.data = visibleSeries.get(); // Re-assign whenever the framework's signal changeseffect(() => { chart.data = visibleSeries.get(); }); // Avoid feedback loops by filtering on source.chart.addEventListener('chart-wc:data-changed', (e) => { if (e.detail.source !== 'property') { // Internal change (e.g. table re-extracted) — push back to your store. syncStore(e.detail.data); }});
See the Data API concepts guide for the dual HTML-first / JS-first contract that <chart-wc> shares with <kanban-board>, <work-item>, and <user-story>.
| Event | Detail | When |
|---|---|---|
chart-wc:upgraded | — | Once after first connect; safe signal for the first .data assignment. |
chart-wc:data-changed | { data, config, source: 'property' } | Fires when .data or .config is assigned via the property API. |
Charts read VB design tokens via CSS custom properties. The theme bridge converts these into palette and axis configuration automatically.
| Custom Property | Purpose |
|---|---|
--chart-series-1 … --chart-series-6 | Chart color palette per series |
--chart-label-color | Axis tick label color |
--chart-axis-color | Axis line color |
--chart-grid-color | Grid line color |
| Layer | Environment | Experience |
|---|---|---|
| 1 | No CSS, no JS | Plain semantic table |
| 2 | CSS only | CSS chart via .vb-chart class |
| 3 | CSS + JS | Full SVG chart with interactivity |
<chart-wc> is part of the optional charts bundle (main-charts.js), not the core bundle. Include it separately:
<script type="module" src="/cdn/vanilla-breeze-charts.js"></script>