A lightweight, zero-dependency SVG chart web component that renders line, bar, and area charts directly from data passed as a JSON attribute or a JS array property. All rendering is imperative SVG; no virtual DOM or framework runtime is involved.
<x-chart></x-chart>
| Attribute | Type | Default | Description |
|---|---|---|---|
type | enum string | "line" | Chart type: "line" | "bar" | "area" |
data | JSON string | — | Array of series objects (see Data Format section) |
height | number | 180 | Chart height in pixels |
padding | number | 12 | Plot area padding in pixels (applied to all four sides) |
grid | boolean | true | Show horizontal grid lines behind the plot |
axes | boolean | true | Show x-axis and y-axis tick labels |
tooltip | boolean | false | Show a floating tooltip on pointer hover |
cursor | enum string | "nearest" | Hit-test strategy: "nearest" | "x" | "none" |
disabled | boolean | false | Dims the chart and disables all pointer/keyboard interaction |
loading | boolean | false | Replaces chart content with an animated skeleton |
selected | string | — | Currently highlighted data point in "seriesId:index" format |
Boolean attributes follow the HTML boolean presence convention — the attribute being present (with any value, or empty) means true; absent means false.
All properties reflect to their corresponding attributes. Setting a property updates the attribute and triggers a re-render.
| Property | Type | Reflects attribute | Notes |
|---|---|---|---|
type | string | yes | Normalised to one of the allowed enum values |
height | number | yes | |
padding | number | yes | |
grid | boolean | yes | |
axes | boolean | yes | |
tooltip | boolean | yes | |
cursor | string | yes | |
disabled | boolean | yes | |
loading | boolean | yes | |
selected | string | yes | |
data | JS array | no | Alternative to the JSON data attribute. Setting this property directly accepts a plain JS array and bypasses JSON parsing. When both the attribute and the property are set, the property takes precedence. |
The x-format and y-format attributes accept a format spec string that controls how axis tick labels and tooltip values are rendered:
| Spec | Example output | Description |
|---|---|---|
"raw" | 42.5 | Raw value, no transformation (default) |
"int" | 42 | Integer, rounded |
"fixed:2" | 42.50 | Fixed decimal places (N after the colon) |
"pct" | 42.5% | Percentage suffix |
"abbr" | 42.5K | Abbreviated with K/M/B suffix |
The data attribute accepts a JSON string representing an array of series objects:
[
{
"id": "s1",
"label": "Revenue",
"color": "#0066cc",
"data": [
{ "x": 1, "y": 10 },
{ "x": 2, "y": 25 },
{ "x": 3, "y": 18 }
]
},
{
"id": "s2",
"label": "Costs",
"color": "#cc3300",
"data": [
{ "x": 1, "y": 5 },
{ "x": 2, "y": 12 },
{ "x": 3, "y": 9 }
]
}
]
| Field | Type | Required | Description |
|---|---|---|---|
id | string | no | Stable identifier used in selected attribute and events. Auto-generated if omitted. |
label | string | no | Human-readable series name shown in tooltips and screen-reader announcements. |
color | CSS color string | no | Stroke/fill color for this series. Falls back to --x-chart-series-N CSS custom properties in order. |
data | {x, y}[] | yes | Array of data points. x may be a number or a category string. y must be numeric. |
| Event | Bubbles | Composed | Cancelable | Detail |
|---|---|---|---|---|
x-chart-select | yes | yes | no | { seriesId, index, x, y, value } |
x-chart-hover | yes | yes | no | { seriesId, index, x, y, value } |
| Field | Type | Description |
|---|---|---|
seriesId | string | The id of the series that was interacted with |
index | number | Zero-based index of the data point within the series |
x | number | string | The x value of the data point |
y | number | The y value of the data point |
value | {x, y} | Full data point object (convenience alias) |
x-chart-select fires on pointer click or keyboard activation (Enter/Space) when cursor mode is not "none". It also updates the selected attribute on the element.
x-chart-hover fires on pointer move when tooltip mode is enabled and cursor mode is not "none".
| Part | Description |
|---|---|
container | Outermost div wrapping the SVG. Has tabindex="0" for keyboard focus. |
svg | The <svg> element that contains all chart drawing. |
sr-only | Visually-hidden region with aria-live="polite" for screen-reader announcements. |
tooltip | The floating tooltip card shown on hover when tooltip attribute is set. |
tooltip-header | The x-axis label row at the top of the tooltip card. |
tooltip-body | Container for the per-series rows. |
tooltip-row | One row per series (up to 8 pre-built; hidden rows have data-hidden="true"). |
tooltip-swatch | Colored dot matching the series color. |
tooltip-label | Series label text. |
tooltip-value | Formatted y-value, shown right-aligned. |
cursor="x" — tooltip shows one row per series sharing the nearest x position (multi-series mode).cursor="nearest" — tooltip shows one row for the single closest data point.cursor="none" — tooltip never appears even if the tooltip attribute is set.| Variable | Light default | Dark default |
|---|---|---|
--x-chart-series-1 | rgba(0,102,204,0.95) | rgba(120,190,255,0.95) |
--x-chart-series-2 | rgba(16,140,72,0.95) | rgba(80,230,150,0.92) |
--x-chart-series-3 | rgba(190,20,40,0.95) | rgba(255,100,110,0.93) |
--x-chart-series-4 | rgba(204,120,0,0.95) | rgba(255,190,60,0.93) |
--x-chart-radius | 0.75rem | same |
--x-chart-border | rgba(0,0,0,0.1) | rgba(255,255,255,0.1) |
--x-chart-grid | rgba(0,0,0,0.08) | rgba(255,255,255,0.08) |
--x-chart-axis-label | rgba(0,0,0,0.5) | rgba(255,255,255,0.45) |
--x-chart-focus-ring | rgba(0,102,204,0.55) | rgba(120,190,255,0.55) |
| Variable | Light default | Dark default |
|---|---|---|
--x-chart-tooltip-bg | rgba(255,255,255,0.96) | rgba(30,30,35,0.97) |
--x-chart-tooltip-border | rgba(0,0,0,0.12) | rgba(255,255,255,0.12) |
--x-chart-tooltip-shadow | 0 4px 16px … | 0 4px 20px … |
--x-chart-tooltip-radius | 0.5rem | same |
--x-chart-tooltip-padding | 0.45rem 0.7rem | same |
--x-chart-tooltip-font-size | 0.8125rem | same |
--x-chart-tooltip-header-color | rgba(0,0,0,0.5) | rgba(255,255,255,0.45) |
--x-chart-tooltip-label-color | rgba(0,0,0,0.65) | rgba(255,255,255,0.6) |
--x-chart-tooltip-value-color | rgba(0,0,0,0.9) | rgba(255,255,255,0.9) |
--x-chart-tooltip-swatch-size | 8px | same |
--x-chart-tooltip-gap | 0.35rem | same |
--x-chart-crosshair-color | rgba(0,0,0,0.18) | rgba(255,255,255,0.2) |
--x-chart-crosshair-width | 1 | same |
Dark mode values are applied automatically via @media (prefers-color-scheme: dark) inside the component's shadow styles. Use these custom properties on the host element to override.
[part=container] is a <div> with tabindex="0", making the chart keyboard-focusable.[part=sr-only] which has role="status" and aria-live="polite".<title> and <desc> children for basic SVG accessibility.@media (prefers-reduced-motion: reduce) — transitions and skeleton animations are disabled when the user has requested reduced motion.disabled attribute sets aria-disabled="true" on the container and removes tabindex.<!-- Line chart with two series -->
<x-chart
type="line"
height="240"
grid
axes
tooltip
data='[
{"id":"rev","label":"Revenue","color":"#0066cc","data":[
{"x":"Jan","y":12},{"x":"Feb","y":19},{"x":"Mar","y":15},
{"x":"Apr","y":28},{"x":"May","y":34}
]},
{"id":"cost","label":"Costs","color":"#cc2200","data":[
{"x":"Jan","y":8},{"x":"Feb","y":11},{"x":"Mar","y":9},
{"x":"Apr","y":14},{"x":"May","y":17}
]}
]'
></x-chart>
<!-- Bar chart with category x-axis -->
<x-chart
type="bar"
height="200"
grid
axes
data='[{"id":"s1","label":"Sales","data":[
{"x":"Q1","y":42},{"x":"Q2","y":61},{"x":"Q3","y":55},{"x":"Q4","y":78}
]}]'
></x-chart>
<!-- Area chart, loading state -->
<x-chart type="area" loading></x-chart>
<!-- Disabled chart -->
<x-chart type="line" disabled data='[{"id":"s1","data":[{"x":1,"y":5}]}]'></x-chart>
const chart = document.querySelector('x-chart');
chart.data = [
{
id: 'live',
label: 'Live feed',
color: '#0066cc',
data: Array.from({ length: 20 }, (_, i) => ({ x: i, y: Math.random() * 100 }))
}
];
const chart = document.querySelector('x-chart');
chart.addEventListener('x-chart-select', e => {
const { seriesId, index, x, y } = e.detail;
console.log(`Selected: series=${seriesId}, point=${index}, x=${x}, y=${y}`);
});
chart.addEventListener('x-chart-hover', e => {
const { seriesId, x, y } = e.detail;
console.log(`Hovering: series=${seriesId}, x=${x}, y=${y}`);
});
[:x-chart
{:type "line"
:height "240"
:grid true
:axes true
:tooltip true
:data (js/JSON.stringify
(clj->js [{:id "revenue"
:label "Revenue"
:color "#0066cc"
:data [{:x "Jan" :y 12}
{:x "Feb" :y 19}
{:x "Mar" :y 15}]}]))}]
x-chart {
--x-chart-series-1: #7c3aed;
--x-chart-series-2: #db2777;
--x-chart-radius: 6px;
--x-chart-line-width: 3;
}
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |