Charts
One block, thirty-plus chart types. Bars and lines render in plain HTML + CSS. The browser reflows them, no ResizeObserver, no redraw loop. Everything else is a single scaling SVG. Zero dependencies, no build step, and every prop is reactive: change it, and only the dependent bindings re-render.
npm install @lemonadejs/charts@beta
import Charts from '@lemonadejs/charts';
const series = [
{ name: 'Product A', data: [120, 190, 80, 220] },
{ name: 'Product B', data: [80, 105, 130, 140] },
];
const App = () => html`<${Charts} type="bar"
categories="${['Q1', 'Q2', 'Q3', 'Q4']}"
series="${series}" legend labels />`;One data definition, every chart
The same series + categories pair drives every type, so switching charts
is changing one word, and because type is a live prop, you can switch at
runtime too:
import Charts from '@lemonadejs/charts';
const series = [
{ name: 'Users', data: [42, 51, 60, 55, 71, 68, 84, 90, 82, 95, 102, 110] },
{ name: 'Sessions', data: [30, 34, 41, 38, 45, 52, 58, 55, 62, 60, 71, 75] },
];
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const App = (props, { state }) => {
const type = state('line');
return html`<div>
<select onchange="${(e) => (type.value = e.target.value)}">
<option value="line">line</option>
<option value="bar">bar</option>
<option value="stackedarea">stacked area</option>
<option value="heatmap">heatmap</option>
</select>
<${Charts} type="${type}" categories="${months}" series="${series}"
smooth markers="${false}" legend />
</div>`;
};Live by construction
Props are states. Assign to a state and the chart updates. There is no
chart.update(), no imperative object to keep in sync:
import Charts from '@lemonadejs/charts';
const App = (props, { state }) => {
const series = state([{ name: 'Live', data: [30, 45, 38, 52, 44] }]);
const push = () => {
const data = series.value[0].data.slice(-19);
data.push(Math.round(30 + Math.random() * 40));
series.value = [{ name: 'Live', data }];
};
return html`<div>
<button onclick="${push}">Add point</button>
<${Charts} type="line" series="${series}" markers="${false}"
smooth legend="${false}" height="${240}" />
</div>`;
};Every type, one prop away
| Family | Types |
|---|---|
| Bars, lines & areas | bar, stackedbar (100%-stack), line, stackedarea, streamgraph, scatter, bubble, lollipop, dumbbell, waterfall, histogram, pareto, candlestick, ohlc, boxplot, arearange, columnrange |
| Pies & radial | pie, donut, radar, radialbar, polararea, gauge, funnel, pyramid |
| Hierarchy & flow | treemap, sunburst, icicle, sankey, chord, arcdiagram |
| Heatmap, bullet & more | heatmap, bullet, sparkline, packedbubble, pictogram, waffle, wordcloud |
Plus the cross-cutting features every type shares: palettes & colors, annotations, reference lines & axes, and events, drilldown & zoom.
Why it is this small
- Bars are flexbox. Bar and stacked charts are plain HTML + CSS (percentage heights in a flex row). Responsive on both axes with zero JavaScript. The browser does the layout, the chart never recomputes.
- SVG scales itself. Radial, hierarchy and flow types render one
<svg viewBox>; the viewBox scales the scene natively, so there is no resize handling anywhere in the package. - No canvas. Nothing rasterizes; everything is inspectable DOM you can style with CSS, test with selectors, and screen-read.
- The reactive model is the engine’s. The chart is a regular LemonadeJS component: its scene is a computed model over live props. That is the whole architecture.
Works everywhere
import { html, mount, setComponents, createWebComponent } from 'lemonadejs';
import Charts from '@lemonadejs/charts';
import '@lemonadejs/charts/style.css';
html`<${Charts} />` // by value, no registration
setComponents({ Charts }); // <Charts /> by name anywhere
createWebComponent(Charts); // <lm-charts> in plain HTML or any framework
React users: the React adapter wraps any LemonadeJS block, charts included.