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 | |
xFormat | string | yes (x-format) | |
yFormat | string | yes (y-format) | |
data | array or object | yes | Reflects the JSON data attribute. Writing chart data (an array of series, or one series), or JSON text that holds it, writes its JSON. Anything else removes the attribute. Reading parses the attribute into a new value, or gives null when it is absent or not chart data. |
The x-format and y-format attributes accept a format spec string that controls how axis tick labels and tooltip values are rendered:
When the attribute is omitted, values are auto-formatted: integers are shown as-is, values ≥ 1 000 are abbreviated with K/M suffixes, and decimals are shown to one decimal place.
| Spec | Example output | Description |
|---|---|---|
"raw" | 42.5 | Raw value converted to string, no transformation |
"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 role="status" and 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 | var(--x-radius-md,0.75rem) | same |
--x-chart-border | var(--x-color-border,rgba(0,0,0,0.1)) | rgba(255,255,255,0.1) |
--x-chart-grid | var(--x-color-border,rgba(0,0,0,0.08)) | rgba(255,255,255,0.08) |
--x-chart-axis-label | var(--x-color-text-muted,rgba(0,0,0,0.5)) | rgba(255,255,255,0.45) |
--x-chart-focus-ring | var(--x-color-focus-ring,rgba(0,102,204,0.55)) | rgba(120,190,255,0.55) |
| Variable | Light default | Dark default |
|---|---|---|
--x-chart-tooltip-bg | var(--x-color-bg,rgba(255,255,255,0.96)) | rgba(30,30,35,0.97) |
--x-chart-tooltip-border | var(--x-color-border,rgba(0,0,0,0.12)) | rgba(255,255,255,0.12) |
--x-chart-tooltip-shadow | var(--x-shadow-md,0 4px 16px rgba(0,0,0,0.14)) | 0 4px 20px … |
--x-chart-tooltip-radius | var(--x-radius-sm,0.5rem) | same |
--x-chart-tooltip-padding | 0.45rem 0.7rem | same |
--x-chart-tooltip-font-size | var(--x-font-size-sm,0.8125rem) | same |
--x-chart-tooltip-header-color | var(--x-color-text-muted,rgba(0,0,0,0.5)) | rgba(255,255,255,0.45) |
--x-chart-tooltip-label-color | var(--x-color-text-muted,rgba(0,0,0,0.65)) | rgba(255,255,255,0.6) |
--x-chart-tooltip-value-color | var(--x-color-text,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 | var(--x-color-border,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.x-chart-select for the current selection.[part=sr-only] which has role="status" and aria-live="polite".aria-hidden="true"; all screen-reader content is conveyed through [part=sr-only].@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;
}
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 |