<tc-chart>
Pure-SVG, themeable, responsive charts in five flavors — line, area, bar, sparkline, donut. ~11 KB minified; every element is a real DOM node screen readers can reach.
Props
| Name | Type | Default | Description |
|---|---|---|---|
type | "line" | "area" | "bar" | "sparkline" | "donut" | "line" | Chart type. Unknown values fall back to `line`. |
data | ChartData | { series: [] } | Data to plot. For line/area/bar/sparkline pass `{ labels, series:[{name, values}] }`; for donut pass `{ series:[{name, value}] }`. |
height | string | "240px" | Any CSS length. The chart fills the host width and uses this for its viewport height. |
smooth | boolean | true | Catmull-Rom curves for line and area. Set to `false` for jagged polylines. |
stacked | boolean | false | Stack series vertically. Applies to `area` and `bar`. |
showLegend | boolean | true | Series legend below the chart. |
showAxes | boolean | true | Render axis chrome (Y labels, baseline). Ignored for sparkline / donut. |
showGrid | boolean | true | Horizontal grid lines at Y-axis ticks. |
showLabels | boolean | true | Show X-axis tick labels. Auto-strides when there are too many. |
showValues | boolean | false | Render the value next to each data point / bar / segment. |
innerRadius | number | 0.6 | Donut only. `0` = pie. `0.9` = thin ring. |
yMin | number? | null | Force the y-axis minimum. Auto when null. |
yMax | number? | null | Force the y-axis maximum. Auto when null. |
ariaLabel | string | "Chart" | Accessible name for the chart. |
colors | string[] | null | null | Override the default series palette. Pass any valid CSS color (hex, `rgb`, `var(--token)`, etc.). |
CSS variables
| Variable | Default | Description |
|---|---|---|
--tc-chart-bg | transparent | Chart background. |
--tc-chart-fg | var(--tc-color-ink) | Foreground text color. |
--tc-chart-axis | var(--tc-color-rule-strong) | Baseline / axis stroke. |
--tc-chart-grid | var(--tc-color-rule) | Gridline stroke. |
--tc-chart-label | var(--tc-color-ink-muted) | Axis and value-label fill. |
--tc-chart-color-1 | var(--tc-color-accent) | First series color. |
--tc-chart-color-2 | var(--tc-color-info) | Second series color. |
--tc-chart-color-3..8 | — | Up to 8 series colors, falling back to semantic tokens. Override per host to brand. |
Examples
Why a custom chart component
Chart.js, Recharts, Apex, and ECharts all render to <canvas>, which means:
- Series colors live in JS config, not CSS tokens — you can't theme them by importing your dark preset.
- Canvas elements expose nothing to assistive tech beyond an alt-text guess. A screen reader gets
"chart". - Each library is 100–400 KB before plugins.
tc-chart renders pure SVG, themes via CSS custom properties, exposes every point as a real DOM element with a <title> and structured aria description, and weighs ~11 KB minified.
Line — multi-series
Area — stacked
Bar — grouped
Sparkline — inline mini-trend
Sparklines drop the axes, grid, and legend so they sit inline with text.
Donut — composition
Pie (innerRadius=0)
Driven from JavaScript
The data prop is just JSON — set the property directly for live updates.
Brand colors
Three layers to bring the chart in line with brand:
- Override series colors per chart via the
colorsprop. - Override the series palette tokens (
--tc-chart-color-1…8) on the host so every chart that doesn't passcolorspicks them up. - Restyle the chart chrome with
--tc-chart-axis,--tc-chart-grid,--tc-chart-label.
…or pass a specific palette inline:
Manual y-axis bounds
Force the y-axis to a known range with yMin and yMax — useful for KPI dashboards where the relative magnitude matters more than the auto-fit.
Accessibility
- The chart container has
role="img"and theariaLabelprop becomes its accessible name. Pass something meaningful like"Weekly request volume by region". - A visually-hidden description below the chart enumerates the series and point count for screen readers (
"Line chart with 2 series (Sessions, Signups) and 8 data points."). - Every data point, bar, and donut segment is a real SVG element with a
<title>child — desktop screen readers and tooltips both pick this up. prefers-reduced-motion: reducedisables the hover scale on segments and points.
Responsiveness
The host is display: block; width: 100% by default, so the chart fills its container. The SVG uses a fixed internal viewBox (0 0 800 400 for cartesian, 0 0 320 320 for donut) and scales to the host. Set the visual height with the height prop or a wrapper.
For a chart inside a grid card:
Theming
The default palette resolves through semantic tokens, so loading a theme preset (light, dark, bootstrap, tailwind, material, shadcn) re-skins every chart automatically. To bake a chart-specific palette into your design system, set the chart tokens at :root:
Limits, by design
- One chart type per element. Multi-axis / combined line+bar isn't supported; compose two charts side-by-side instead.
- No hover crosshair / shared tooltip yet — the per-element
<title>covers most use cases. - No animations on data swap (yet). A
datachange re-renders immediately; users get an instant update rather than a transition. - Donut data shape is single-series. For nested donuts, stack two
<tc-chart>instances.