# 82chart documentation (v0.2.0) > A free (MIT), zero-dependency SVG chart component for Korean sites: Korean number units, zoom, export, keyboard access and server rendering included. Summary: https://82chart.com/llms.txt --- # Getting started > Draw your first chart in five minutes. Source: https://82chart.com/en/docs/getting-started/ 82chart is a dependency-free SVG chart component. A page without a build step needs two CDN lines; an npm project needs one package. No license key, no watermark, no sign-up, no payment. ```js createChart('#chart', { type: 'bar', title: 'Monthly sales', prefix: '₩', data: { labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], series: [{ name: 'Sales', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }, }); ``` Axis labels are short (**32M, 52M**) and the tooltip shows the exact number (**₩32,000,000**). On Korean pages (``) the axis uses 만·억 units instead. ## From the CDN The quickest path. The script defines the global `Chart82` and registers the `` tag. ```html
``` ## From npm ```sh npm i @82chart/core ``` ```js import { createChart } from '@82chart/core'; import '@82chart/core/styles.css'; const chart = createChart('#chart', { type: 'bar', title: 'Monthly sales', prefix: '₩', data: { labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], series: [{ name: 'Sales', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }, }); chart.on('click', (point) => console.log(point.x, point.y)); ``` The first argument of `createChart` is an element or a selector; whatever it holds is replaced by the chart. The chart takes its parent's width and redraws when that changes. ## Changing the data ```js chart.update({ labels: ['Jul', 'Aug'], series: [{ name: 'Sales', data: [64000000, 70500000] }], }); chart.setOptions({ type: 'line', smooth: true }); // change some options chart.destroy(); // remove it ``` ## Next - [Data format](https://82chart.com/en/docs/data-format/): time axes, gaps, point lists - [Numbers and dates](https://82chart.com/en/docs/numbers-dates/): K/M, 만·억, units, date patterns - [Bar](https://82chart.com/en/docs/bar/) · [Line](https://82chart.com/en/docs/line/) · [Pie and donut](https://82chart.com/en/docs/pie-donut/) · [KPI cards](https://82chart.com/en/docs/sparkline-kpi/) - [React](https://82chart.com/en/docs/react/) · [Vue](https://82chart.com/en/docs/vue/) · [Svelte](https://82chart.com/en/docs/svelte/) · [Angular](https://82chart.com/en/docs/angular/) --- # CDN files > Pinned and latest URLs, and what each file is. Source: https://82chart.com/en/docs/cdn/ 82chart.com serves the files itself. Versioned URLs never change, so browsers cache them for a year. Use them on production sites. | File | What it is | | --- | --- | | `/cdn/v0.2.0/82chart.min.js` | For a script tag. Defines the global `Chart82` and registers ``. | | `/cdn/v0.2.0/82chart.esm.js` | A single ES module for ` ``` ## What the Chart82 global holds `createChart`, `renderToSVG`, `defineChartElement`, the number helpers `korean`, `compact` and `comma`, `registerLocale`, the palettes `PALETTE_LIGHT` and `PALETTE_DARK`, and `VERSION`: the same as the npm package exports. > You may also download the files and host them yourself. It is MIT licensed: change and redistribute freely. --- # Data format > labels and series, pairs, point objects, gaps and time axes. Source: https://82chart.com/en/docs/data-format/ Every chart takes one `data: { labels?, series }`. A series' `data` comes in one of three shapes. | Shape | Example | Used for | | --- | --- | --- | | Numbers | `[120, 98, null, 143]` | `labels[i]` is the x. Most lines, bars and areas | | [x, y] pairs | `[['2026-10-01', 120], ['2026-10-02', 98]]` | Time axes, series with different x values | | Point objects | `[{ x: 3, y: 12, r: 8 }]` | Scatter plots, bubbles (`r`) | ## Series options | Property | What it does | | --- | --- | | `name` | Name in the legend, tooltip and table | | `data` | One of the shapes above | | `type` | `'line' \| 'area' \| 'bar' \| 'scatter'`; mixing them makes a [combo](https://82chart.com/en/docs/combo/) | | `color` | This series' color instead of the palette | | `stack` | Series with the same name pile up (`stacked: true` puts every bar or area series in one stack) | | `smooth` / `dashed` | Curve or dash just this series | | `hidden` | Hidden at first (the legend shows it again) | | `axis` | `'right'` puts it on a right y axis ([combo](https://82chart.com/en/docs/combo/), opt-in) | ## Gaps `null` means "no value": the line breaks instead of dropping to zero. Change that with `nulls`: `'gap'` (default), `'connect'` (a dashed bridge) or `'zero'`. ```js createChart('#chart', { type: 'line', title: 'Daily visitors', nulls: 'connect', data: { labels: ['Oct 14', 'Oct 15', 'Oct 16', 'Oct 17', 'Oct 18', 'Oct 19', 'Oct 20'], series: [{ name: 'Visitors', data: [1820, 2140, null, 2410, 2290, 1650, 1980] }], }, }); ``` ## Time axes When labels or x values are dates (`Date`, `'2026-10'`, `'2026-10-21'`, `'2026-10-21T09:00'`) 82chart labels them to fit the spacing: `Oct`, `Oct 21`, `09:00`. Given as [x, y] pairs they make a real time axis, so missing days leave a gap of their width. Date-only strings are read as local midnight, not UTC. ```js createChart('#chart', { type: 'area', title: 'Orders', data: { series: [{ name: 'Orders', data: [['2026-10-01', 412], ['2026-10-02', 455], ['2026-10-05', 520], ['2026-10-06', 498], ['2026-10-07', 610], ['2026-10-08', 588]] }], }, }); ``` Pick the axis kind yourself with `xAxis: { type: 'category' | 'linear' | 'time' }`; `'category'` spaces date labels evenly. ## From a server or a web component The data is plain JSON. If your server returns `{"labels": [...], "series": [...]}`, it is one `chart.update(json)`; for `` it goes in the attribute as a string. --- # Line chart > Change over time: curves, steps, points and the last value. Source: https://82chart.com/en/docs/line/ `type: 'line'` is the default. Hovering shows a vertical crosshair and one tooltip with every series at that x. A single line gets its last value labelled (`lastPoint`). ```js createChart('#chart', { type: 'line', title: 'Monthly visitors', smooth: true, data: { labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct'], series: [ { name: 'This year', data: [41200, 43800, 52100, 49800, 61200, 66400, 71800, 69500, 74300, 80100] }, { name: 'Last year', data: [36500, 35200, 40100, 42800, 47900, 51300, 55100, 53800, 58200, 60400], dashed: true }, ], }, }); ``` ## Options | Option | Default | What it does | | --- | --- | --- | | `curve` | 'straight' | `'straight' \| 'smooth' \| 'step'`; smooth is monotone and never overshoots the data | | `smooth` | false | Same as `curve: 'smooth'`; on a series, just that series | | `points` | 'auto' | Show points; auto shows them up to 30 points | | `pointShape` | 'circle' | `'circle' \| 'square' \| 'diamond'` | | `lastPoint` | true for one line | Highlight the last point | | `nulls` | 'gap' | Missing values: `'gap' \| 'connect' \| 'zero'` | | `dataLabels` | false | `true` or `'end'`: the value at the end of each line only | ## Step lines Data that holds until it changes (prices, stock levels) reads correctly with `curve: 'step'`. ```js createChart('#chart', { type: 'line', title: 'Stock on hand', curve: 'step', points: true, data: { labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], series: [{ name: 'Stock', data: [120, 120, 85, 85, 140, 132, 132] }], }, }); ``` --- # Area chart > Volume and totals: areas, stacked and 100% stacked. Source: https://82chart.com/en/docs/area/ `type: 'area'` fills under the line with a soft gradient (`gradient: false` turns it off), and its y axis always starts at zero. Use `stacked: true` when the total matters and `stacked: 'percent'` when the shares do. ```js createChart('#chart', { type: 'area', title: 'Visits by source', stacked: true, data: { labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], series: [ { name: 'Search', data: [5200, 5600, 5900, 6100, 5800, 4200, 3900] }, { name: 'Social', data: [2100, 2300, 2600, 2400, 2900, 3800, 4100] }, { name: 'Direct', data: [1400, 1500, 1450, 1600, 1550, 1100, 980] }, ], }, }); ``` ## 100% stacked Each x adds up to 100. The tooltip shows the share and the original value. ```js createChart('#chart', { type: 'area', title: 'Device share', stacked: 'percent', data: { labels: ['2026-05', '2026-06', '2026-07', '2026-08', '2026-09', '2026-10'], series: [ { name: 'Phone', data: [61, 63, 64, 66, 67, 69] }, { name: 'Desktop', data: [33, 31, 31, 29, 28, 26] }, { name: 'Tablet', data: [6, 6, 5, 5, 5, 5] }, ], }, }); ``` > Avoid stacking more than three areas: the upper bands inherit the shape of the ones below and become hard to read. --- # Bar chart > Grouped, stacked, 100% stacked, horizontal and negative bars. Source: https://82chart.com/en/docs/bar/ `type: 'bar'` puts several series side by side. Bars are rounded at the value end (`barRadius`, default 6) and square at the baseline. Hovering highlights the whole band and lists every value in it. ```js createChart('#chart', { type: 'bar', title: 'Quarterly revenue', prefix: '$', data: { labels: ['Q1', 'Q2', 'Q3', 'Q4'], series: [ { name: '2025', data: [182000, 205000, 198000, 241000] }, { name: '2026', data: [214000, 236000, 251000, null] }, ], }, }); ``` ## Stacks and totals `stacked: true` piles every bar series into one stack and `stackTotals: true` writes the total on top. Give series a `stack: 'name'` to make several stacks. ```js createChart('#chart', { type: 'bar', title: 'Sign-ups by age', stacked: true, stackTotals: true, data: { labels: ['Teens', '20s', '30s', '40s', '50+'], series: [ { name: 'Women', data: [820, 4210, 3880, 2140, 960] }, { name: 'Men', data: [640, 3650, 4120, 2530, 1180] }, ], }, }); ``` ## Horizontal bars (rankings) Long names and rankings (top 10 products) read better with `horizontal: true`. Sort largest first. `dataLabels: true` writes the value at the end of each bar. ```js createChart('#chart', { type: 'bar', horizontal: true, title: 'Best sellers', dataLabels: true, data: { labels: ['Wireless earbuds', 'Power bank 10000mAh', 'USB-C cable', 'Phone stand', 'Laptop sleeve'], series: [{ name: 'Units', data: [1840, 1520, 1310, 980, 760] }], }, }); ``` ## 100% stacked `stacked: 'percent'` makes every band add up to 100%, for survey answers and other shares. ## Negative values Negative bars go below the zero line. `negativeColor` colors them, or `palette: 'korea'` uses red for up and blue for down, as Korean stock apps do. ```js createChart('#chart', { type: 'bar', title: 'Change from last month', unit: '%', palette: 'korea', data: { labels: ['May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct'], series: [{ name: 'Change', data: [4.2, -1.8, 6.5, -3.1, 2.4, 8.9] }], }, }); ``` | Option | Default | What it does | | --- | --- | --- | | `horizontal` | false | Horizontal bars | | `stacked` | false | `true` or `'percent'` | | `stackTotals` | false | Total above stacked bars | | `dataLabels` | false | Value at the bar end (`true` or `'end'`) | | `barRadius` | 6 | Corner radius at the value end (px) | | `barWidth` | 0.82 (0.78 grouped) | Share of the band a bar group fills (0–1) | | `negativeColor` | - | Color of bars below zero | | `gradient` | true | Soft vertical gradient | --- # Pie and donut > Shares of a whole, with the total in the middle and small slices folded into Other. Source: https://82chart.com/en/docs/pie-donut/ `type: 'pie'` and `'donut'` use `labels` and the first series. The legend sits on the right; clicking an item takes that slice out and recomputes the rest. The donut hole shows the total (change it with `donutLabel`, or `false` to hide it). ```js createChart('#chart', { type: 'donut', title: 'Payment methods', prefix: '$', data: { labels: ['Card', 'Wallet', 'Bank transfer', 'Carrier billing', 'Other'], series: [{ name: 'Amount', data: [48200, 31500, 9800, 4100, 600] }], }, }); ``` | Option | Default | What it does | | --- | --- | --- | | `donutHole` | 0.62 | Hole size as a share of the radius (0–1) | | `donutLabel` | the total | Text in the hole; `false` hides it | | `otherThreshold` | 0.02 | Slices under this share (2%) fold into "Other"; 0 keeps every slice | | `startAngle` | 0 | Start angle in degrees (0 = 12 o'clock) | | `legend` | 'right' | `'top' \| 'bottom' \| 'right' \| false` | > Past six slices a bar chart reads more accurately: people compare lengths better than angles. --- # Scatter and bubble > The relation between two values, with a third as the bubble size. Source: https://82chart.com/en/docs/scatter/ `type: 'scatter'` takes `[x, y]` pairs or `{ x, y, r }` points; numeric x makes a numeric axis. With `r` it becomes a bubble chart (the largest r gets about 48px across; areas follow the values). ```js createChart('#chart', { type: 'scatter', title: 'Ad spend and sales', xAxis: { title: 'Ad spend ($)' }, yAxis: { title: 'Sales ($)' }, numberFormat: 'comma', data: { series: [ { name: 'Search ads', data: [{ x: 1200, y: 9800, r: 14 }, { x: 1800, y: 13200, r: 22 }, { x: 2600, y: 16100, r: 30 }, { x: 3400, y: 17900, r: 26 }] }, { name: 'Social ads', data: [{ x: 900, y: 5200, r: 10 }, { x: 1500, y: 7600, r: 16 }, { x: 2300, y: 10100, r: 18 }, { x: 3100, y: 11500, r: 20 }] }, ], }, }); ``` - Up to three series stay easy to tell apart by color; split the chart beyond that. - Marker shape: `pointShape`, `'circle' | 'square' | 'diamond'`. - Dates on x make a time axis. --- # Radar chart > Several scores at a glance. Source: https://82chart.com/en/docs/radar/ `type: 'radar'` turns `labels` into spokes and draws one polygon per series. Every spoke starts at zero and shares one scale, so use it only for values with the same unit (scores, ratings). ```js createChart('#chart', { type: 'radar', title: 'Customer ratings', data: { labels: ['Speed', 'Design', 'Price', 'Support', 'Delivery'], series: [ { name: 'Us', data: [4.6, 4.1, 3.8, 4.4, 4.0] }, { name: 'Industry', data: [3.9, 3.7, 3.6, 3.5, 3.8] }, ], }, }); ``` --- # Gauge > A single value against a range, such as progress to a target. Source: https://82chart.com/en/docs/gauge/ `type: 'gauge'` draws one value, `data.series[0].data[0]`. Set the range with `gauge: { min, max }` (default 0–100); with `bands` the arc takes the color of the band the value falls in. ```js const chart = createChart('#chart', { type: 'gauge', title: 'Progress to target', unit: '%', gauge: { min: 0, max: 100, label: 'Target $500k', bands: [{ to: 50, color: '#e34948' }, { to: 80, color: '#eda100' }, { to: 100, color: '#1baf7a' }], }, data: { series: [{ name: 'Progress', data: [72.4] }] }, }); setTimeout(() => chart.update({ series: [{ name: 'Progress', data: [86.1] }] }), 2500); ``` | gauge property | What it does | | --- | --- | | `min` / `max` | Range (default 0 / 100) | | `bands` | `[{ to, color }]`: the first band whose `to` is at or above the value | | `label` | Small text under the number | --- # Combo (bars and a line) > Mix series types in one chart, with an opt-in right y axis for a second unit. Source: https://82chart.com/en/docs/combo/ Give each series its own `type` to mix them: bars with a line on top, like the common "daily sales with a 7-day average" in admin dashboards. When both series share a unit, one y axis is enough. ```js createChart('#chart', { type: 'bar', title: 'Daily sales and 7-day average', prefix: '$', data: { labels: ['Oct 8', 'Oct 9', 'Oct 10', 'Oct 11', 'Oct 12', 'Oct 13', 'Oct 14', 'Oct 15', 'Oct 16', 'Oct 17'], series: [ { name: 'Sales', data: [3120, 2870, 3540, 4210, 3980, 2650, 2410, 3390, 3710, 3060] }, { name: '7-day average', type: 'line', smooth: true, data: [3010, 3050, 3110, 3240, 3330, 3310, 3250, 3290, 3310, 3260] }, ], }, }); ``` ## Dual y axis (opt-in) For two values with different units, such as "monthly revenue bars + year-over-year change (%) line", give a series `axis: 'right'` and a second axis appears on the right. Its ticks, unit and title come from `y2Axis`. If no series uses `axis: 'right'`, there is one axis exactly as before. ```js createChart('#chart', { type: 'bar', title: 'Monthly revenue and YoY change', prefix: '$', data: { labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], series: [ { name: 'Revenue', data: [182000, 164000, 201000, 223000, 248000, 236000] }, { name: 'YoY change', type: 'line', axis: 'right', data: [4.2, -2.1, 6.8, 9.5, 12.3, 7.4] }, ], }, y2Axis: { unit: '%', title: 'YoY change' }, }); ``` - The right axis gets as many intervals as the left one, so both sets of numbers sit on the same grid lines. With `min`, `max`, `step` or `ticks` in `y2Axis` it follows those instead, and the grid follows the left axis. - The right axis does not inherit the chart's `unit`, `prefix` or `scale` (it measures something else). It inherits only the number style (K/M, 만·억) and `decimals`; set its unit with `y2Axis.unit` / `y2Axis.prefix` and its style with `y2Axis.format` (`'percent'`, `'comma'` or a function). - Tooltips and the table write each series in its own axis' format, and the legend marks right-axis series with **(R)** (Korean (우); change it with `locale.rightAxis`). - Right-axis series never stack onto left ones. Horizontal bars do not support it. > **Use a dual axis with care.** Each axis picks its own range, so the same data can look correlated or opposed depending on those ranges; where the two lines cross and how steep they look mean nothing. That is why it is off by default and opted into per series. When the point is comparison, [two stacked charts with a linked crosshair](https://82chart.com/en/docs/group-sync/) are more honest. --- # Sparklines and KPI cards > A big number, a change badge and a small trend line in one card. Source: https://82chart.com/en/docs/sparkline-kpi/ The default look (`skin: 'card'`) is a card: a small `eyebrow`, the title, a subtitle, a big `value`, a change badge (`delta`) top right, the chart, and a `footnote`. ```js createChart('#chart', { type: 'area', eyebrow: 'REVENUE', title: 'Revenue this month', subtitle: 'October 1–21', value: 48250, prefix: '$', delta: 18.2, deltaLabel: 'vs last month', footnote: 'As of Oct 21, 2026 09:00', height: 140, data: { labels: ['Oct 15', 'Oct 16', 'Oct 17', 'Oct 18', 'Oct 19', 'Oct 20', 'Oct 21'], series: [{ name: 'Revenue', data: [2010, 2240, 2180, 2650, 2420, 2890, 3120] }], }, }); ``` ## The change badge | Option | What it does | | --- | --- | | `delta` | A number is a percent (`18.2` → +18.2%); a string is shown as is | | `deltaUpIsGood` | Default true (up is green). Set `false` for costs and errors | | `deltaColors` | `'semantic'` (good green, bad red; default) or `'korea'` (up red ▲, down blue ▼, as in Korean stock apps) | | `deltaLabel` | Small text after the badge, e.g. `'vs last month'` | ## Sparklines `skin: 'sparkline'` draws just the line, without axes, grid or legend: a small trend for table cells and card corners. Its height follows the width, between 32 and 80px. ```js createChart('#chart', { type: 'line', skin: 'sparkline', data: { series: [{ name: 'Visitors', data: [12, 15, 14, 18, 17, 21, 19, 24, 26, 25, 29] }] }, }); ``` For the chart without the card frame, use `skin: 'plain'`. --- # Numbers and dates > Korean units, K/M, "in millions", currency, decimals, date patterns and time zones. Source: https://82chart.com/en/docs/numbers-dates/ Axis ticks are short; tooltips and tables are exact. On English pages the axis reads **12.3K · 340M** and the tooltip **123,456,789**; on Korean pages the axis uses 만·억. ## numberFormat | Value | 12,345 → | 340,000,000 → | Used for | | --- | --- | --- | --- | | `'korean'` | 1.2만 | 3.4억 | Default on Korean pages | | `'compact'` | 12.3K | 340M | Default in other languages | | `'comma'` | 12,345 | 340,000,000 | Reports that need exact numbers | | `'percent'` | 12,345% | - | Values that already are percents (pass 50, not 0.5) | | a function | `(v) => string` | | Used for the axis and the tooltip | To change only the axis ticks, give the same value to `yAxis: { format }` (or `xAxis`). The helpers are exported too. ```js import { korean, compact, comma } from '@82chart/core'; korean(123456789); // '1.2억' compact(123456789); // '123.5M' comma(1234.5); // '1,234.5' ``` ## "In millions" reports For report-style axes in millions with commas, divide with `scale` and add a `unit`. With a `scale`, the axis, tooltip and table all show the divided number with the unit, while the headline `value` stays the full amount. State the unit in a `footnote`. ```js createChart('#chart', { type: 'bar', title: 'Revenue by division', scale: 1000000, unit: 'M', footnote: 'In millions of won', data: { labels: ['Domestic', 'Overseas', 'Online', 'Wholesale'], series: [{ name: 'Revenue', data: [1820000000, 940000000, 2310000000, 610000000] }], }, }); ``` | Option | What it does | | --- | --- | | `unit` | After the value: `'%'`, `' users'`, `'M'` | | `prefix` | Before the value: `'$'`, `'₩ '`. With `scale`, the tooltip shows the full amount ($1,820,000,000) | | `scale` | Divide axis ticks by this (1000000 → millions) | | `decimals` | Decimals in tooltips and labels (default: up to 2, trailing zeros trimmed) | ## Dates Time axis labels follow the spacing: months as `Jan · Feb` (with the year when it changes), days as `Oct 21`, hours as `09:00`. Tooltips show the full date. Pass a pattern to `dateFormat` to choose yourself. | Token | Meaning | Example | | --- | --- | --- | | `YYYY` / `YY` | Year | 2026 / 26 | | `M` / `MM` | Month | 3 / 03 | | `MMM` | Month name (languages that have them) | Mar | | `D` / `DD` | Day | 7 / 07 | | `ddd` | Weekday | Tue | | `H` / `HH` | Hour | 9 / 09 | | `mm` / `ss` | Minutes / seconds | 05 / 30 | ```js createChart('#chart', { type: 'line', dateFormat: 'MMM D (ddd)', // Oct 21 (Tue) data: { series: [{ name: 'Visitors', data: [['2026-10-20', 1820], ['2026-10-21', 2140]] }] }, }); ``` `dateFormat` is used for both the axis and the tooltip; `'auto'` is the default behaviour. ## Time zone (timeZone) Dates are read and written in the runtime's zone by default. That is fine in browsers, but servers (Cloudflare Workers, most Node hosts) run on UTC, so `renderToSVG` there puts Korean midnight data on the previous day. Give `timeZone` an IANA name and the axis, tooltip, table and CSV all use that zone. Date-only strings such as `'2026-10-21'` are that day in the zone, and `'2026-10-21T09:00'` without an offset is 9 o'clock there. ```js createChart('#chart', { type: 'line', timeZone: 'America/New_York', // the same labels wherever it runs data: { series: [{ name: 'Orders', data: [['2026-10-21T04:00:00Z', 120], ['2026-10-21T10:00:00Z', 180]] }] }, }); ``` An unknown zone name logs one warning and falls back to the runtime zone. The exported helper `formatDate(date, pattern, locale, timeZone)` takes the zone as its fourth argument. --- # Axes > Whole-number ticks, tick step, reversed axes for ranks, two-line labels and a right y axis. Source: https://82chart.com/en/docs/axes/ Axis options go in `xAxis` and `yAxis` (`y2Axis` for the right axis). For horizontal bars the value axis is still `yAxis`. ## Whole-number ticks and a fixed step For counts and people, `integer: true` rules out ticks such as 0.5. `step` fixes the distance between ticks (every 10, say). A step so small it would draw more than 200 ticks is ignored. ```js createChart('#chart', { type: 'bar', title: 'Support tickets by day', yAxis: { integer: true }, data: { labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], series: [{ name: 'Tickets', data: [2, 3, 1, 3, 2] }], }, }); ``` ## Reversed axes (ranks) `yAxis: { reverse: true }` flips the value axis so small values are on top, for rank charts where #1 belongs at the top. On horizontal bars it flips the category order, putting the first item at the bottom. ```js createChart('#chart', { type: 'line', title: 'Search rank', yAxis: { reverse: true, integer: true, min: 1, title: 'Rank' }, data: { labels: ['W1', 'W2', 'W3', 'W4', 'W5'], series: [ { name: 'Our product', data: [8, 5, 3, 2, 1] }, { name: 'Competitor', data: [1, 2, 2, 4, 6] }, ], }, }); ``` ## Two-line labels A `'\n'` in a category label writes it on two lines, handy for long names. Tooltips and the table join the lines with a space. ```js createChart('#chart', { type: 'bar', title: 'Stores by district', data: { labels: ['Seoul\nGangnam-gu', 'Seoul\nSongpa-gu', 'Busan\nHaeundae-gu', 'Daegu\nSuseong-gu'], series: [{ name: 'Stores', data: [42, 35, 28, 19] }], }, }); ``` ## A right y axis Series in a different unit can sit on a right axis with `axis: 'right'`. The caveats and an example are on the [combo chart](https://82chart.com/en/docs/combo/) page. | Option | What it does | | --- | --- | | `integer` | Whole-number ticks only | | `step` | Fixed tick step | | `reverse` | Flip the value axis (horizontal bars: the category order) | | `unit` / `prefix` | `y2Axis` only: unit of the right axis and its series | --- # Theme and colors > Light and dark, CSS variables, palettes and series colors. Source: https://82chart.com/en/docs/theme-colors/ The default `theme: 'auto'` follows the page: an ancestor's `data-theme="dark|light"`, then ``, then the OS setting, and redraws when they change. Try the theme button at the top of this site. ```js chart.setTheme('dark'); // 'light' | 'dark' | 'auto' ``` ## Palettes The default palette has eight colors checked so that neighbours stay distinct for color-blind readers (blue, orange, teal, yellow, pink, green, purple, red). From the ninth series on it uses grays instead of repeating a hue, so one color never means two things. | palette | What it is | | --- | --- | | `'default'` | Eight colors starting with blue | | `'korea'` | Starts with red (up) and blue (down); negative bars are blue | | `'mono'` | Shades of the first color (`colors[0]` or blue) | ```js createChart('#chart', { type: 'bar', title: 'Sales by category', palette: 'mono', colors: ['#6250d6'], stacked: true, data: { labels: ['Q1', 'Q2', 'Q3', 'Q4'], series: [ { name: 'Clothing', data: [42, 48, 51, 63] }, { name: 'Accessories', data: [28, 31, 30, 38] }, { name: 'Food', data: [18, 22, 25, 24] }, ], }, }); ``` ## Series colors `colors: ['#3182f6', '#eb6834']` pins colors in series order; a series' own `color` changes just that one. Colors belong to series, not ranks, so "Sales" keeps its color when the data changes. ## CSS variables Set `--c82-*` variables on the chart or any ancestor to match your site. Colors are resolved into the SVG attributes, so exported PNG and SVG files look the same as the screen. ```css /* light theme */ .my-dashboard { --c82-bg: #fffdf8; --c82-ink: #1c1917; --c82-grid: #ece7df; --c82-s1: #e8590c; /* first series */ --c82-font: 'Inter', sans-serif; } /* only in the dark theme */ .my-dashboard .c82[data-theme='dark'] { --c82-bg: #1c1917; --c82-ink: #f5f5f4; --c82-grid: #2e2a27; } ``` | Variable | Used for | | --- | --- | | `--c82-bg` | Background (card, point outlines) | | `--c82-ink` / `--c82-ink-2` / `--c82-muted` | Text (titles / body / secondary) | | `--c82-grid` / `--c82-axis` | Grid lines / axis line | | `--c82-hover` | Hovered band | | `--c82-good` / `--c82-bad` | Change badge good / bad | | `--c82-up` / `--c82-down` | Up / down for `deltaColors: 'korea'` and `palette: 'korea'` | | `--c82-s1` … `--c82-s8` | Series colors | | `--c82-font` | Font | | `--c82-border`, `--c82-radius`, `--c82-shadow`, `--c82-tip-bg`, `--c82-tip-border` | Card border, radius and shadow, tooltip (CSS only) | --- # Tooltip and legend > Crosshair tooltips, custom values, and legend toggles. Source: https://82chart.com/en/docs/tooltip-legend/ Lines and areas show a vertical crosshair with one tooltip for every series at that x; bars highlight the band and list its values; pies, scatter plots and gauges show the item under the pointer. Stacked bars add a total row. Near the right edge the tooltip flips left instead of being cut off. ```js const chart = createChart('#chart', { type: 'line', title: 'Visits and sign-ups', tooltip: { format: (v, ctx) => ctx.seriesName === 'Sign-ups' ? v + ' (' + (v / 20).toFixed(1) + '%)' : v.toLocaleString('en-US') }, legend: 'bottom', data: { labels: ['Oct 1', 'Oct 2', 'Oct 3', 'Oct 4', 'Oct 5', 'Oct 6', 'Oct 7', 'Oct 8', 'Oct 9', 'Oct 10', 'Oct 11', 'Oct 12', 'Oct 13', 'Oct 14', 'Oct 15', 'Oct 16', 'Oct 17', 'Oct 18', 'Oct 19', 'Oct 20', 'Oct 21'], series: [ { name: 'Visitors', data: [1820, 2140, 1990, 2410, 2290, 1650, 1580, 2010, 2380, 2550, 2470, 2620, 1910, 1840, 2230, 2680, 2810, 2740, 2950, 2100, 2060] }, { name: 'Sign-ups', data: [120, 156, 131, 188, 171, 98, 92, 140, 176, 205, 190, 214, 121, 117, 163, 221, 240, 228, 262, 141, 136] }, ], }, }); chart.on('legendclick', (e) => log(e)); ``` ## tooltip | Value | What it does | | --- | --- | | `true` | Default | | `false` | No tooltip (with `group` the crosshair stays) | | `{ format(value, ctx) }` | Write the value text yourself; `ctx` is `{ seriesName, seriesIndex, index, x }` | Tooltip text is set with `textContent`, so HTML in a series name never runs. Show the tooltip from code with `chart.showTooltip(index, seriesIndex?)`; `chart.showTooltip(null)` hides it. ## legend | Value | What it does | | --- | --- | | default | Top with two or more series, none for one (the title names it); right for pies and donuts | | `'top' \| 'bottom' \| 'right'` | Placement | | `true` / `false` | Always show / hide | Legend items are buttons: clicking hides the series and refits the axis (the last visible series stays), hovering dims the others. From code: `chart.toggleSeries(index, visible?)`. --- # Zoom and pan > Drag, wheel or pinch to zoom; double-click to reset. Free. Source: https://82chart.com/en/docs/zoom/ `zoom: true` lets readers zoom the x range of line, area, vertical bar and scatter charts, a paid-tier feature elsewhere. Drag across the chart below. ```js const chart = createChart('#chart', { type: 'area', title: 'October visits', zoom: true, data: { labels: ['Oct 1', 'Oct 2', 'Oct 3', 'Oct 4', 'Oct 5', 'Oct 6', 'Oct 7', 'Oct 8', 'Oct 9', 'Oct 10', 'Oct 11', 'Oct 12', 'Oct 13', 'Oct 14', 'Oct 15', 'Oct 16', 'Oct 17', 'Oct 18', 'Oct 19', 'Oct 20', 'Oct 21'], series: [ { name: 'Visitors', data: [1820, 2140, 1990, 2410, 2290, 1650, 1580, 2010, 2380, 2550, 2470, 2620, 1910, 1840, 2230, 2680, 2810, 2740, 2950, 2100, 2060] }, { name: 'Sign-ups', data: [120, 156, 131, 188, 171, 98, 92, 140, 176, 205, 190, 214, 121, 117, 163, 221, 240, 228, 262, 141, 136] }, ], }, }); chart.on('zoom', (e) => log(e ?? 'reset')); ``` | Action | Mouse | Touch | Keyboard | | --- | --- | --- | --- | | Zoom in | Drag to select a range | Pinch out | `+` | | Zoom out | Ctrl (⌘) + wheel | Pinch in | `-` | | Pan | Shift-drag, horizontal wheel, Shift + wheel | Drag with one finger once zoomed | `←`/`→` at the edge | | Reset | Double-click, the Reset zoom button, the ⋯ menu | Reset zoom button | `0` | - A plain wheel never blocks page scrolling; zooming needs Ctrl (⌘). - The `zoom` event carries `{ start, end }` (x indexes), or `null` on reset. - Reset from code with `chart.resetZoom()`. New data or a new type resets the zoom. - It needs at least three x values and is off for horizontal bars. --- # Annotations: lines and bands > Target lines, averages and shaded periods. Source: https://82chart.com/en/docs/annotations/ `annotations` draws target lines (`y`), lines at an x value (`x`) and shaded periods or value ranges (`bands`). Lines sit on top of the data, bands underneath. A y line is included in the axis range so it is always visible. ```js createChart('#chart', { type: 'line', title: 'Daily sales', prefix: '$', annotations: { y: [{ value: 3000, label: 'Target $3k', color: '#1baf7a' }], x: [{ value: 'Oct 16', label: 'App update' }], bands: [{ from: 'Oct 18', to: 'Oct 20', label: 'Weekend sale' }], }, data: { labels: ['Oct 14', 'Oct 15', 'Oct 16', 'Oct 17', 'Oct 18', 'Oct 19', 'Oct 20', 'Oct 21'], series: [{ name: 'Sales', data: [2410, 2650, 2580, 2990, 3420, 3610, 3380, 2870] }], }, }); ``` | Property | What it does | | --- | --- | | `y: [{ value, label?, color?, dashed? }]` | Horizontal reference line; dashed by default, `dashed: false` for solid | | `x: [{ value, label?, color?, dashed? }]` | Vertical line; `value` is a label, date or number matching the x axis | | `bands: [{ from, to, label?, color?, axis? }]` | Shading; `axis` defaults to `'x'` (a period), `'y'` shades a value range | --- # Export > PNG, SVG and CSV downloads and a data table, with no key or watermark. Source: https://82chart.com/en/docs/export/ The download button at the top right of the card saves a PNG; the ⋯ menu offers PNG, SVG, CSV and the data table (`exporting: false` hides them). The same is available from code. ```js const chart = createChart('#chart', { type: 'bar', title: 'Orders by region', data: { labels: ['Seoul', 'Gyeonggi', 'Busan', 'Daegu', 'Incheon', 'Gwangju'], series: [{ name: 'Orders', data: [4820, 3910, 1640, 1120, 1080, 760] }], }, }); log(chart.toCSV()); ``` | Method | Returns | | --- | --- | | `chart.toPNG(scale = 2)` | `Promise`, a 2× PNG for sharp screens | | `chart.toSVG()` | A self-contained SVG string with colors resolved (title, legend and annotations included) | | `chart.toCSV()` | A CSV string with raw numbers (no units), ready to sum in a spreadsheet | | `chart.download('png' \| 'svg' \| 'csv', filename?)` | Save a file; the title is the default name | - The CSV opens straight in Excel: a BOM so Korean text reads correctly, a header row (the first column is `xAxis.title`, or "Label"), dates as local `2026-10-21` (`2026-10-21 09:30` when there are times of day) in `timeZone`, and raw numbers without units, ready to sum. - Cells starting with `=`, `+` or `@` are escaped so they never run as formulas. - `table: true` shows the data table under the chart from the start; it is always there for screen readers. - Buttons and tooltips are hidden when printing. ```js // a PNG for a report e-mail const blob = await chart.toPNG(2); const form = new FormData(); form.append('file', blob, 'sales.png'); await fetch('/api/report', { method: 'POST', body: form }); ``` --- # Large data (downsampling) > Draw lines and areas with tens of thousands of points, thinned with LTTB; CSV and table keep everything. Source: https://82chart.com/en/docs/large-data/ When a line or area chart has more than 2,000 points, 82chart draws about one point per pixel of plot width. It picks them with LTTB (largest-triangle-three-buckets), which keeps the point that changes the shape most in each bucket, so peaks and dips survive. The SVG never holds tens of thousands of points, so the screen, server rendering and exports all stay light. ```js createChart('#chart', { type: 'line', title: 'Sensor readings (20,000)', data: { series: [{ name: 'Temperature', data: Array.from({ length: 20000 }, (_, i) => [Date.UTC(2026, 9, 1) + i * 60000, 20 + Math.sin(i / 300) * 6 + (i % 1733 === 0 ? 9 : 0)]).map(([t, v]) => [new Date(t), +v.toFixed(2)]), }], }, }); ``` | downsample | Behaviour | | --- | --- | | (default) | Lines and areas only: above 2,000 visible points, thin to about the plot width | | a number, e.g. `500` | Thin to this many when more are visible | | `false` | Draw every point | - Tooltips, the crosshair and the keyboard move over the kept points. Zooming in (`zoom`) samples just the visible range again, so more of the original points show. - `toCSV()`, the downloaded CSV and the data table hold every value from before thinning. - The edges of gaps (`null`) are kept, so lines still break where they did. With several series the points picked for each are merged so they all share the same x. - Bars and scatter plots are not thinned; aggregate thousands of bars first (daily or weekly totals). --- # Loading, errors and no data > setLoading, setError with retry, and the noData message. Source: https://82chart.com/en/docs/loading-errors/ Show a bar-shaped placeholder while data loads (`loading: true` or `chart.setLoading(true)`) and report failures with `setError(message, retry)`. ```js const chart = createChart('#chart', { type: 'bar', title: 'Weekly sign-ups', loading: true, data: { labels: [], series: [] }, }); let tries = 0; const load = () => { chart.setError(null); chart.setLoading(true); setTimeout(() => { chart.setLoading(false); if (++tries === 1) return chart.setError('Could not load the stats.', load); chart.update({ labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], series: [{ name: 'Sign-ups', data: [42, 51, 38, 64, 57] }] }); }, 1200); }; load(); ``` | API | What it does | | --- | --- | | `loading: true` / `chart.setLoading(bool)` | Shows the placeholder and sets `aria-busy` | | `chart.setError(message, retry?)` | An error message with a Try again button; `setError(null)` clears it | | `noData` | Message when there is nothing to draw (default 'No data to display') | --- # Linked charts (shared crosshair) > Different units: two charts with one crosshair instead of a dual axis. Source: https://82chart.com/en/docs/group-sync/ Charts with the same `group` name share the crosshair: hover one and the others point at the same x. They match by label value, so give them the same `labels`. It is how to read values with different units together without a dual axis. ```js const labels = ['Oct 15', 'Oct 16', 'Oct 17', 'Oct 18', 'Oct 19', 'Oct 20', 'Oct 21']; createChart('#sales', { type: 'bar', title: 'Sales', prefix: '$', group: 'daily', height: 180, data: { labels, series: [{ name: 'Sales', data: [2410, 2650, 2580, 2990, 3420, 3610, 3380] }] }, }); createChart('#orders', { type: 'line', title: 'Orders', group: 'daily', height: 180, data: { labels, series: [{ name: 'Orders', data: [61, 70, 66, 74, 92, 98, 85] }] }, }); ``` Move a chart to another group with `setOptions({ group: 'other' })`; `destroy()` takes it out. --- # Accessibility > Keyboard reading, an automatic summary, a hidden table and reduced motion. Source: https://82chart.com/en/docs/accessibility/ 82chart can be read with a screen reader and a keyboard without any setup, which is rare among free libraries. ## Automatic summary The outer element is `role="figure"` with a summary in `aria-label`, e.g. "Line chart, Monthly visitors. 2 series. Jan to Oct. This year: highest 80,100 (Oct), lowest 41,200 (Jan) …". The `description` option is read first. ## Keyboard | Key | Action | | --- | --- | | `Tab` | Entering the chart shows the first value | | `←` / `→` | Previous / next x (`↑`/`↓` for horizontal bars) | | `↑` / `↓` | Another series | | `Home` / `End` | First / last value | | `Enter` / `Space` | Same as clicking the point (`click` event) | | `Esc` | Close the tooltip | | `+` / `-` / `0` | Zoom in / out / reset (`zoom: true`) | Tooltip text is also announced through an `aria-live` region. Legend items are buttons with `aria-pressed`; the ⋯ menu works with the arrow keys and Esc. ## Table and motion - A visually hidden `` with every value is always present; `table: true` shows it. - With the OS "reduce motion" setting (`prefers-reduced-motion`) the first-draw animation is skipped; `animation: false` turns it off too. - In high-contrast mode (`forced-colors`) lines use the system text color. - Do not rely on color alone: combine dashed series (`dashed`) and marker shapes (`pointShape`). --- # Web component > One HTML tag, no framework. Source: https://82chart.com/en/docs/web-component/ The CDN scripts (`82chart.min.js`, `82chart.esm.js`) register `` on their own; with the npm package call `defineChartElement()` once. Server templates (PHP, JSP, Django) only need to print JSON. ```html ``` ```js import { defineChartElement } from '@82chart/core'; import '@82chart/core/styles.css'; defineChartElement(); // // defineChartElement('my-chart'); // under another tag name const el = document.querySelector('chart-82'); el.options = { zoom: true, annotations: { y: [{ value: 40000, label: 'Target' }] } }; // options as an object el.chart.download('png'); // the chart instance inside ``` | Attribute | What it takes | | --- | --- | | `type`, `theme`, `skin` | Strings | | `data` | A JSON string (`{"labels": …, "series": …}`) | | `options` | Other options as JSON; options that need functions go through the `el.options` property | | `height` | A number or `auto` | | `title`, `subtitle`, `eyebrow`, `footnote`, `unit` | Card header and unit | | `value`, `delta` | Read as numbers when they look like numbers | Changing an attribute redraws the chart; removing the element cleans the chart up. --- # React > Use 82chart as a React component. Source: https://82chart.com/en/docs/react/ ```sh npm i @82chart/core @82chart/react ``` ```js import { Chart82 } from '@82chart/react'; import '@82chart/core/styles.css'; const data = { labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], series: [{ name: 'Sales', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }; export function SalesChart() { return ( console.log(point)} /> ); } ``` - ``; `options` holds every other [option](https://82chart.com/en/docs/options/). - `data` redraws when it is a new object; build a new one rather than mutating the array. - `options` is compared key by key and changed keys go through `setOptions`. - `ref` gives you the chart instance. It is created after mount, so it is safe with SSR and Next.js. ## Export through ref ```js import { useRef } from 'react'; import { Chart82, type Chart } from '@82chart/react'; function Report({ data }) { const ref = useRef(null); return ( <> ); } ``` --- # Vue > Use 82chart as a Vue component. Source: https://82chart.com/en/docs/vue/ ```sh npm i @82chart/core @82chart/vue ``` ```html ``` - `` (Vue 3). - `data` is watched deeply, so changing values inside the arrays redraws. - Take the chart instance from `@ready` to call methods such as `toPNG()`. - In Nuxt, wrap it in ``. --- # Svelte > Use 82chart as a Svelte component. Source: https://82chart.com/en/docs/svelte/ ```sh npm i @82chart/core @82chart/svelte ``` ```html ``` - A Svelte 5 component: ``. - There is also a `use:chart82` action for any element; it takes one options object. - `data` redraws when you pass a new object. ## As an action ```html
console.log(p) }}>
``` --- # Angular > Use 82chart as a Angular component. Source: https://82chart.com/en/docs/angular/ ```sh npm i @82chart/core @82chart/angular ``` ```js import { Component } from '@angular/core'; import { Chart82Component } from '@82chart/angular'; import type { ChartData, ChartOptions, ChartPoint } from '@82chart/core'; @Component({ selector: 'app-sales', standalone: true, imports: [Chart82Component], template: ``, }) export class SalesComponent { data: ChartData = { labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], series: [{ name: 'Sales', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }; options: Partial = { title: 'Monthly sales', prefix: '₩' }; onPoint(p: ChartPoint) { console.log(p); } } // angular.json → "styles": ["node_modules/@82chart/core/dist/styles.css"] ``` - A standalone component: ``. - `(pointClick)` fires on a point, bar or slice; `(ready)` hands you the chart instance. - `data` redraws when you pass a new object. - Angular 17 or newer. --- # Server rendering (SSR) > renderToSVG makes an SVG string without a DOM: Node, Workers, Astro, e-mail and OG images. Source: https://82chart.com/en/docs/ssr/ The function that draws on screen also produces the SVG string, so a chart rendered on the server with `renderToSVG`, the one drawn in the browser and the file from `toSVG()` are identical. No DOM is needed: Node, Cloudflare Workers, Deno and Bun all work. ```js import { renderToSVG } from '@82chart/core'; const svg = renderToSVG( { type: 'line', language: 'en', // there is no document language on a server timeZone: 'Asia/Seoul', // servers mostly run on UTC: keep dates on the right day title: 'Weekly visitors', data: { labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], series: [{ name: 'Visitors', data: [1820, 2140, 1990, 2410, 2290] }] }, }, { width: 640, height: 360, theme: 'light' }, ); // '…' ``` | Second argument | Default | What it does | | --- | --- | --- | | `width` | 640 | Width (px) | | `height` | automatic | Total height (px); without it the plot is width × 0.5625 (`aspectRatio`) | | `theme` | 'light' | `'light' \| 'dark'` | | `header` | true | Include the card header, legend and footnote | | `idPrefix` | automatic | Avoid id clashes with several SVGs on one page | ## Cloudflare Workers ```js import { renderToSVG } from '@82chart/core'; export default { async fetch(req, env) { const rows = await env.DB.prepare('SELECT day, visits FROM stats ORDER BY day DESC LIMIT 14').all(); const list = rows.results.reverse(); const svg = renderToSVG({ type: 'area', language: 'en', title: 'Visits, last 14 days', data: { series: [{ name: 'Visits', data: list.map((r) => [r.day, r.visits]) }] }, }, { width: 800, height: 400 }); return new Response(svg, { headers: { 'content-type': 'image/svg+xml; charset=utf-8', 'cache-control': 'max-age=300' } }); }, }; ``` ## Static sites such as Astro ```html --- import { renderToSVG } from '@82chart/core'; const svg = renderToSVG({ type: 'bar', language: 'en', data: { labels: ['A', 'B'], series: [{ data: [3, 5] }] } }, { width: 560 }); ---
``` ## Idea: share images (OG) A post or report page can use a chart with real numbers as its `og:image`: render a 1200×630 SVG with `renderToSVG` in a Worker, convert it with an SVG-to-PNG library (resvg-wasm, for example) and cache the result. Convert to PNG for e-mail too; most mail clients do not show SVG. > Servers mostly run on UTC. For charts with dates, set a zone such as `timeZone: 'Asia/Seoul'` so the days match what the browser draws. See [Numbers and dates](https://82chart.com/en/docs/numbers-dates/). > A server-rendered SVG is a picture. For tooltips and zoom, draw it again in the browser with `createChart`. The screen-reader summary is in its `aria-label` and ``. --- # API > createChart, chart instance methods and other exports. Source: https://82chart.com/en/docs/api/ ```js import { createChart } from '@82chart/core'; const chart = createChart(target, options); // target: an element or a selector ``` ## Instance methods | Method | What it does | | --- | --- | | `update(data)` | Replace the data and redraw | | `setOptions(partial)` | Change some options (a new `type` or `data` resets the zoom) | | `getOptions()` | A copy of the current options | | `setTheme(theme)` | `'light' \| 'dark' \| 'auto'` | | `resize()` | Measure and redraw (usually automatic) | | `toggleSeries(index, visible?)` | Show or hide a series (a slice for pies) | | `resetZoom()` | Undo the zoom | | `setLoading(bool)` | Loading placeholder | | `setError(message \| null, retry?)` | Error message with a retry button | | `showTooltip(index \| null, seriesIndex?)` | Show or hide the tooltip from code | | `toSVG()` | An SVG string | | `toPNG(scale = 2)` | `Promise<Blob>` | | `toCSV()` | A CSV string | | `download('png' \| 'svg' \| 'csv', filename?)` | Save a file | | `on(event, fn)` / `off(event, fn)` | [Events](https://82chart.com/en/docs/events/) | | `destroy()` | Remove the chart and its listeners | | `root` / `svg` | The outer element / the current SVG element | ## Other exports | Name | What it is | | --- | --- | | `renderToSVG(options, { width, height, theme, header, idPrefix })` | An SVG string without a DOM ([SSR](https://82chart.com/en/docs/ssr/)) | | `defineChartElement(tag = 'chart-82')` | Register the [web component](https://82chart.com/en/docs/web-component/) | | `korean(v)` / `koreanLong(v)` | '1.2억' / '1억 2,345만 6,789' | | `compact(v)` / `comma(v)` | '123.5M' / '123,456,789' | | `formatTick(v, style)` / `formatValue(v, style)` / `formatDate(d, pattern, locale)` | Text by the same rules as the axis and tooltip | | `niceStep` / `linearTicks` | Round tick steps | | `PALETTE_LIGHT` / `PALETTE_DARK` | The eight default colors | | `LIGHT_THEME` / `DARK_THEME` | Default theme colors | | `LOCALES` / `registerLocale(lang, strings)` | Interface strings / add a language | | `VERSION` | The version string, e.g. '0.1.0' | All types (`ChartOptions`, `ChartData`, `Series`, `ChartPoint`, `Chart` …) are exported too. --- # Events > click, hover, legendclick, zoom, ready, render and destroy. Source: https://82chart.com/en/docs/events/ Listen with `chart.on(name, fn)` and stop with `chart.off(name, fn)`. Hover or click the chart below. ```js const chart = createChart('#chart', { type: 'bar', title: 'Orders by weekday', data: { labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], series: [{ name: 'Orders', data: [61, 70, 66, 74, 92] }], }, }); chart.on('click', (p) => log({ click: p.x, y: p.y, series: p.seriesName })); chart.on('hover', (p) => p && log('hover ' + p.x)); ``` | Event | Payload | When | | --- | --- | --- | | `click` | `ChartPoint` | A point, bar or slice is clicked or Enter is pressed (same as the `onClick` option) | | `hover` | `ChartPoint \| null` | The pointer moves to another item; null when it leaves | | `legendclick` | `{ seriesIndex, visible }` | A legend item toggles a series | | `zoom` | `{ start, end } \| null` | The zoom range changes (x indexes) | | `ready` | `Chart` | Once after creation (same as `onReady`) | | `render` | `Chart` | Every redraw | | `destroy` | - | On `destroy()` | ## ChartPoint ```js { seriesIndex: 0, seriesName: 'Orders', index: 4, // position on x x: 'Fri', // label, number or date y: 92, percent?: 31.5, // share for pies, donuts and 100% stacks color: '#3182f6', } ``` --- # Options > Every option createChart accepts. Source: https://82chart.com/en/docs/options/ Everything but `data` is optional. With nothing else you get a line chart in a card, in the document language (Korean if unsupported), following the page theme. ## Type and data | Option | Default | What it does | | --- | --- | --- | | `type` | 'line' | `'line' \| 'area' \| 'bar' \| 'pie' \| 'donut' \| 'scatter' \| 'radar' \| 'gauge'` | | `data` | (required) | `{ labels?, series }`; see [Data format](https://82chart.com/en/docs/data-format/) | ## Card header | Option | Default | What it does | | --- | --- | --- | | `eyebrow` | - | Small text above the title (shown in capitals) | | `title` | - | Title; also used in the summary, table caption and file name | | `subtitle` | - | Grey line under the title | | `value` | - | Headline figure; numbers get commas, `prefix` and `unit` | | `delta` | - | Change badge: a number is a percent, a string is shown as is | | `deltaUpIsGood` | true | Is a rise good? | | `deltaColors` | 'semantic' | `'semantic'` (good green, bad red) or `'korea'` (up red ▲, down blue ▼) | | `deltaLabel` | - | Text after the badge, e.g. 'vs last month' | | `footnote` | - | Small text under the chart, e.g. 'In millions' | | `description` | - | Read by screen readers before the automatic summary | | `skin` | 'card' | `'card' \| 'plain' \| 'sparkline'` | ## Size | Option | Default | What it does | | --- | --- | --- | | `width` | parent width | Width (px) used when the parent has no width | | `height` | per type | Plot height (px) or `'auto'` (width × `aspectRatio`) | | `aspectRatio` | 0.5625 | Height ÷ width when height is `'auto'` | ## Shape | Option | Default | What it does | | --- | --- | --- | | `horizontal` | false | Horizontal bars | | `stacked` | false | `true` or `'percent'` (100% stacked) | | `smooth` | false | Same as `curve: 'smooth'` | | `curve` | 'straight' | `'straight' \| 'smooth' \| 'step'` | | `nulls` | 'gap' | `'gap' \| 'connect' \| 'zero'` | | `points` | 'auto' | Show points (auto: up to 30) | | `pointShape` | 'circle' | `'circle' \| 'square' \| 'diamond'` | | `lastPoint` | true for one line | Highlight the last point | | `barRadius` | 6 | Bar corner radius at the value end (px) | | `barWidth` | 0.82 / 0.78 | Share of the band a bar group fills | | `gradient` | true | Soft gradient on bars and areas | | `stackTotals` | false | Totals above stacked bars | | `dataLabels` | false | `true \| 'end'`: values at bar and line ends | | `otherThreshold` | 0.02 | Pie and donut slices below this share fold into "Other" | | `startAngle` | 0 | Pie and donut start angle (degrees, 0 = top) | | `donutHole` | 0.62 | Donut hole size (0–1) | | `donutLabel` | the total | Text in the hole; `false` hides it | | `gauge` | - | `{ min, max, bands: [{ to, color }], label }` | ## Axes, legend, tooltip | Option | Default | What it does | | --- | --- | --- | | `xAxis / yAxis` | - | `{ title, min, max, format, ticks, step, integer, reverse, grid, type, beginAtZero, labels }`; see below | | `y2Axis` | - | The right y axis, only when a series has `axis: 'right'`; the same options plus `unit` and `prefix` | | `legend` | top for 2+ series | `boolean \| 'top' \| 'bottom' \| 'right'` | | `tooltip` | true | `boolean \| { format(value, ctx) }` | ## Numbers and dates | Option | Default | What it does | | --- | --- | --- | | `numberFormat` | 'korean' in Korean, else 'compact' | `'korean' \| 'compact' \| 'comma' \| 'percent' \| (v) => string` | | `scale` | - | Divide axis ticks by this (1000000 for millions) | | `unit` | - | Unit after values | | `prefix` | - | Symbol before values | | `decimals` | up to 2 | Decimals in tooltips and labels | | `dateFormat` | 'auto' | Date pattern, e.g. `'MMM D'`, `'YYYY-MM-DD'` | | `timeZone` | runtime zone | IANA zone for reading and writing dates, e.g. `'Asia/Seoul'` (needed for SSR on UTC servers) | ## Colors and language | Option | Default | What it does | | --- | --- | --- | | `colors` | - | Colors in series order | | `palette` | 'default' | `'default' \| 'korea' \| 'mono'` | | `negativeColor` | - | Color of bars below zero | | `theme` | 'auto' | `'light' \| 'dark' \| 'auto'` | | `language` | document lang, else ko | `'ko' \| 'en' \| 'ja' \| 'zh'` (add more with `registerLocale`) | | `locale` | - | Override some interface strings | ## Features | Option | Default | What it does | | --- | --- | --- | | `animation` | true | First-draw animation (off with reduced motion) | | `zoom` | false | Zoom and pan | | `downsample` | auto above 2,000 | Thin line/area points with LTTB to this many; `false` draws all. CSV and table keep every value | | `annotations` | - | `{ x, y, bands }` | | `exporting` | true | Download button and ⋯ menu | | `table` | false | Show the data table | | `noData` | per language | Message when there is nothing to draw | | `group` | - | Charts with the same name share the crosshair | | `loading` | false | Loading placeholder | ## Callbacks and the rest | Option | Default | What it does | | --- | --- | --- | | `onClick` | - | `(point: ChartPoint) => void` | | `onReady` | - | `(chart) => void`, once after creation | | `telemetry` | true | Once a day, report the domain and version to 82chart.com (never data); `false` turns it off | | `framework` | - | Set by the framework wrappers | ## xAxis · yAxis · y2Axis | Option | Default | What it does | | --- | --- | --- | | `title` | - | Axis title | | `min / max` | auto | Range | | `format` | numberFormat | Tick text (same values, or a function; a category axis receives the label) | | `ticks` | auto | Rough tick count (round numbers near it) | | `grid` | true | Grid lines | | `type` | auto | `'category' \| 'linear' \| 'time'` | | `beginAtZero` | true for bars and areas | Start at zero | | `labels` | true | Draw tick labels | | `integer` | false | Whole-number ticks only (counts, people) | | `step` | auto | Fixed tick step, e.g. 10 | | `reverse` | false | y axis: small values on top (ranks); horizontal bars: reverse the category order | | `unit / prefix` | - | `y2Axis` only: unit of the right axis and its series | > The right y axis (`y2Axis`) appears only when a series has `axis: 'right'`. The caveats are on the [combo chart](https://82chart.com/en/docs/combo/) page. --- # Moving from To●●t UI Chart > Replace To●●t UI Chart, archived (no longer maintained) in September 2026. Source: https://82chart.com/en/docs/migrate-toast-ui-chart/ NHN archived the To●●t UI Chart repository in September 2026; it no longer receives fixes. 82chart aims at the same place (Korean screens, free, no dependencies) and the data looks alike, so most of the work is renaming. ## Before ```html <link rel="stylesheet" href="https://uicdn.toast.com/chart/latest/to●●tui-chart.min.css"> <script src="https://uicdn.toast.com/chart/latest/to●●tui-chart.min.js"></script> <div id="chart"></div> <script> // columnChart = vertical bars (barChart is horizontal there) const chart = to●●tui.Chart.columnChart({ el: document.getElementById('chart'), data: { categories: ['1월', '2월', '3월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000] }], }, options: { chart: { title: '월별 매출', width: 600, height: 400 }, yAxis: { title: '원' } }, }); </script> ``` ## After ```html <link rel="stylesheet" href="https://82chart.com/cdn/v0.2.0/82chart.css"> <script src="https://82chart.com/cdn/v0.2.0/82chart.min.js"></script> <div id="chart"></div> <script> const chart = Chart82.createChart('#chart', { type: 'bar', title: '월별 매출', unit: '원', height: 400, data: { labels: ['1월', '2월', '3월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000] }], }, }); </script> ``` ## Name map | To●●t UI Chart | 82chart | | --- | --- | | `data.categories` | `data.labels` | | `data.series[].name / data` | Unchanged: `data.series[].name / data` | | `columnChart` | `type: 'bar'` | | `barChart` (horizontal) | `type: 'bar', horizontal: true` | | `lineChart` / `areaChart` | `type: 'line'` / `'area'` | | `pieChart` (+ `series.radiusRange` for donuts) | `type: 'pie'` / `'donut'` | | `scatterChart` / `bubbleChart` | `type: 'scatter'` (+ `r` on points) | | `radarChart`, `gaugeChart` | `type: 'radar'`, `'gauge'` | | `columnLineChart` | `type: 'bar'` with `type: 'line'` on the line series ([combo](https://82chart.com/en/docs/combo/)) | | `options.series.stack` | `stacked: true` or `'percent'` | | `options.series.zoomable` | `zoom: true` | | `options.exportMenu` | `exporting` (on by default) | | `options.chart.title` / `width` / `height` | `title` / parent width / `height` | | `options.theme` | [CSS variables `--c82-*`](https://82chart.com/en/docs/theme-colors/) | | `chart.setData(data)` | `chart.update(data)` | | `chart.on('clickSeries', fn)` | `chart.on('click', fn)` | - Heatmaps, treemaps, box plots, bullet charts, radial bars and nested pies are not there yet; heatmaps are planned for the next version. - 82chart follows its parent's width, so no `width` is needed. - On Korean pages the axis uses 만·억 units; for report-style commas use `numberFormat: 'comma'`. --- # Moving from Apex●●●rts or Chart.●s > When Apex●●●rts changed its license or Chart.●s needs one plugin too many. Source: https://82chart.com/en/docs/migrate-apexcharts-chartjs/ Apex●●●rts is no longer MIT from 5.0 (paid above $2M annual revenue) and adds a watermark without a key from 6.5. Chart.●s is free, but zoom, annotations and time axes need separate plugins and adapters. 82chart has all of them built in, under MIT. ## Apex●●●rts ```js const chart = new Apex●●●rts(document.querySelector('#chart'), { chart: { type: 'line', height: 350, zoom: { enabled: true } }, series: [{ name: 'Visitors', data: [120, 180, 150, 220] }], xaxis: { categories: ['Mon', 'Tue', 'Wed', 'Thu'] }, yaxis: { labels: { formatter: (v) => v.toLocaleString() } }, }); chart.render(); ``` ```js const chart = createChart('#chart', { type: 'line', height: 350, zoom: true, numberFormat: 'comma', data: { labels: ['Mon', 'Tue', 'Wed', 'Thu'], series: [{ name: 'Visitors', data: [120, 180, 150, 220] }], }, }); ``` | Apex●●●rts | 82chart | | --- | --- | | `chart.type` | `type` | | `xaxis.categories` | `data.labels` | | `series: [{ name, data }]` | `data.series: [{ name, data }]` | | `chart.stacked`, `stackType: '100%'` | `stacked: true`, `'percent'` | | `plotOptions.bar.horizontal` | `horizontal: true` | | `chart.zoom.enabled` | `zoom: true` | | `annotations.yaxis / xaxis` | `annotations.y / x / bands` | | `yaxis.labels.formatter` | `yAxis.format` or `numberFormat` | | `chart.toolbar.export` | `exporting` | | `chart.updateSeries()` | `chart.update(data)` | ## Chart.●s ```js // before new Chart(document.getElementById('c'), { type: 'bar', data: { labels: ['Jan', 'Feb', 'Mar'], datasets: [{ label: 'Sales', data: [12, 19, 14] }] }, }); // after (a div instead of a canvas) createChart('#c', { type: 'bar', data: { labels: ['Jan', 'Feb', 'Mar'], series: [{ name: 'Sales', data: [12, 19, 14] }] }, }); ``` | Chart.●s | 82chart | | --- | --- | | `<canvas>` | Any `<div>` (drawn as SVG) | | `datasets[].label` | `series[].name` | | `type: 'doughnut'` | `type: 'donut'` | | `type: 'bubble'` | `type: 'scatter'` with `r` on points | | `options.indexAxis: 'y'` | `horizontal: true` | | `scales.x.stacked` / `scales.y.stacked` | `stacked: true` | | Zoom plugin | `zoom: true` | | Annotation plugin | `annotations` | | Date adapter (date-fns, moment …) | Not needed; see [time axes](https://82chart.com/en/docs/data-format/) | | `chart.data = …; chart.update()` | `chart.update(data)` | > Chart.●s draws on a canvas, so tens of thousands of points stay fast. 82chart draws up to a few thousand as SVG; a canvas renderer for more is planned. --- # FAQ > License, browsers, size, Korean units, dual axes and large data. Source: https://82chart.com/en/docs/faq/ ## Can I use it for free in a commercial product? Yes. It is MIT licensed: free for commercial sites, internal tools and client projects. No license key, no watermark, no revenue threshold, no paid tier. Keep the license text, that is all. ## Does it work in Internet Explorer? No. It supports current Chrome, Edge, Safari, Firefox and Samsung Internet, desktop and mobile. IE11 is out of support from Microsoft and lacks the SVG and ResizeObserver features 82chart uses. ## How big is it? `82chart.min.js` is about 30KB gzipped and the CSS about 3KB. There are no dependencies: no date library, no jQuery. ## How do I get Korean units (만·억) on the axis? Nothing to do: on Korean pages (`<html lang="ko">`) or with `language: 'ko'` the axis reads 1.2만 and 3.4억 while tooltips show the full number with commas. For report-style "in millions of won", use `scale: 1000000, unit: '백만원'`. ## Is there a dual y axis? Overlaying two values with different units (revenue and order count) lets the choice of ranges make the same data look correlated or opposed. It is the most misread kind of chart, so since 0.2.0 it exists only as an **opt-in**: give a series `axis: 'right'` and set the unit and title with `y2Axis` (see [Combo](https://82chart.com/en/docs/combo/)). When the point is comparison, two stacked charts sharing the x axis with crosshairs linked by `group` are more honest. ## How many points can it handle? A few thousand draw smoothly as SVG. Lines and areas above 2,000 points are thinned with LTTB to about the plot width, so tens of thousands stay light ([Large data](https://82chart.com/en/docs/large-data/); CSV and the table keep every value). Aggregate thousands of bars, or hundreds of thousands of points, first. ## Are there candlestick charts? Not in the core. Candles, volume and indicators such as moving averages are coming as a separate package on top of it, `@82chart/stock`. ## Does 82chart send my data anywhere? No. Chart data and visitor data are never sent. Once a day it tells 82chart.com the domain and version it runs on; `telemetry: false` turns that off, and localhost, intranet hosts and IP addresses never report. ## Can it replace To●●t UI Chart? Yes. The To●●t UI Chart repository was archived in September 2026 and no longer gets fixes. 82chart aims at the same Korean use cases (만·억 units, Hangul labels, admin dashboards); the option-by-option map is in [Moving from To●●t UI Chart](https://82chart.com/en/docs/migrate-toast-ui-chart/). ## How is it different from Apex●●●rts or Chart.●s? Apex●●●rts moved off the MIT license from 5.0, and Chart.●s needs separate plugins for zoom and annotations. 82chart ships zoom, crosshair, annotations, PNG/SVG/CSV export and Korean units in its MIT core. See the [comparison](https://82chart.com/en/compare/). --- # Changelog --- ## 0.2.0 (2026-10-21): Excel CSV, an opt-in right y axis, time zones, axis options and downsampling for large data - Excel CSV: a header row (first column is xAxis.title, or 'Label'), dates as local 'YYYY-MM-DD' ('YYYY-MM-DD HH:mm' when there are times of day), raw numbers; fixed a doubled BOM on download - Right y axis: only when a series has axis: 'right' (default behaviour unchanged). y2Axis sets its format, unit (unit, prefix) and title; its ticks line up with the left grid; tooltips and the table use each series' own axis format; the legend marks it (R) - timeZone option (an IANA name such as 'Asia/Seoul') for reading and writing dates on axes, tooltips, tables and CSV, so renderToSVG on a UTC server no longer shifts days - Axis options: integer (whole-number ticks), step (tick step), reverse (rank axes; the category order for horizontal bars), and '\n' in a category label makes two lines - downsample: line and area charts with more than 2,000 points are thinned with LTTB to about the plot width (a number sets the count, false turns it off); tooltips work on the kept points, CSV and the table keep all data - New locale strings csvLabel and rightAxis; formatDate takes a time zone argument --- ## 0.1.0 (2026-10-21): First release: eight chart types, Korean units, card skin, zoom, export, accessibility, server rendering - Charts: line, area, bar (grouped, stacked, 100% stacked, horizontal, negative), pie, donut, scatter (bubble), radar and gauge, plus combos by giving series their own type - Card skin: eyebrow, title, subtitle, headline value, change badge (semantic or Korean colors) and footnote; sparkline and plain skins - Korean numbers and dates: 1.2만 / 3.4억 on axes, full numbers with commas in tooltips, scale + unit ('백만원'), prefix, decimals, date patterns (dateFormat) with weekdays - Data: number arrays, [x, y] pairs and { x, y, r } points, automatic time axes, null handling (gap, connect, zero) - Interaction: shared crosshair tooltips, legend toggles, zoom and pan (drag, Ctrl+wheel, pinch, keyboard), annotations (lines and bands), linked charts (group) - Export: PNG, SVG and CSV downloads and a table view in the menu; toPNG(), toSVG(), toCSV(), download() - Accessibility: an automatic summary (aria-label), keyboard reading, a hidden data table, reduced motion and high-contrast support - Themes: auto, light and dark, --c82-* CSS variables, default, korea and mono palettes - renderToSVG: the same SVG on a server (Node, Cloudflare Workers) without a DOM - The <chart-82> web component, React, Vue, Svelte and Angular wrappers, a CDN build (global Chart82), Korean, English, Japanese and Chinese strings, zero dependencies --- # 82chart 문서 (한국어) --- # 시작하기 > 5분 안에 내 화면에 첫 차트를 그리는 방법. Source: https://82chart.com/docs/getting-started/ 82chart는 의존성이 없는 SVG 차트 부품입니다. 빌드 도구가 없는 PHP·JSP 화면이라면 CDN 두 줄, npm을 쓰는 프로젝트라면 패키지 하나면 됩니다. 라이선스 키, 워터마크, 가입, 결제가 없습니다. ```js createChart('#chart', { type: 'bar', title: '월별 매출', unit: '원', data: { labels: ['1월', '2월', '3월', '4월', '5월', '6월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }, }); ``` 축에는 **3,200만·5,200만**처럼 만·억 단위가, 툴팁에는 **32,000,000원**처럼 정확한 숫자가 나옵니다. 문서 언어(`<html lang>`)가 한국어면 기본이 만·억입니다. ## CDN으로 붙이기 가장 빠른 방법입니다. 스크립트가 전역 `Chart82` 를 만들고 `<chart-82>` 태그도 등록합니다. ```html <link rel="stylesheet" href="https://82chart.com/cdn/v0.2.0/82chart.css"> <script src="https://82chart.com/cdn/v0.2.0/82chart.min.js"></script> <div id="chart"></div> <script> Chart82.createChart('#chart', { type: 'bar', title: '월별 매출', unit: '원', data: { labels: ['1월', '2월', '3월', '4월', '5월', '6월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }, }); </script> ``` ## npm으로 설치하기 ```sh npm i @82chart/core ``` ```js import { createChart } from '@82chart/core'; import '@82chart/core/styles.css'; const chart = createChart('#chart', { type: 'bar', title: '월별 매출', unit: '원', data: { labels: ['1월', '2월', '3월', '4월', '5월', '6월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }, }); chart.on('click', (point) => console.log(point.x, point.y)); ``` `createChart` 의 첫 인자는 요소나 선택자입니다. 그 요소 안의 내용은 차트로 바뀝니다. 너비는 부모 폭을 따라가고, 창 크기가 바뀌면 알아서 다시 그립니다. ## 데이터 바꾸기 ```js chart.update({ labels: ['7월', '8월'], series: [{ name: '매출', data: [64000000, 70500000] }], }); chart.setOptions({ type: 'line', smooth: true }); // 옵션 일부만 바꾸기 chart.destroy(); // 화면에서 없앨 때 ``` ## 다음 단계 - [데이터 형식](https://82chart.com/docs/data-format/): 날짜 축, 빈 값, 점 목록 - [숫자와 날짜](https://82chart.com/docs/numbers-dates/): 만·억, "단위: 백만원", 날짜 모양 - [막대](https://82chart.com/docs/bar/) · [선](https://82chart.com/docs/line/) · [파이·도넛](https://82chart.com/docs/pie-donut/) · [KPI 카드](https://82chart.com/docs/sparkline-kpi/) - [React](https://82chart.com/docs/react/) · [Vue](https://82chart.com/docs/vue/) · [Svelte](https://82chart.com/docs/svelte/) · [Angular](https://82chart.com/docs/angular/) --- # CDN 파일 > 버전 고정 주소와 latest 주소, 파일 종류. Source: https://82chart.com/docs/cdn/ 82chart.com이 직접 파일을 내려줍니다. 버전이 들어간 주소는 내용이 절대 바뀌지 않으므로 브라우저가 1년간 캐시합니다. 운영 사이트에는 버전 주소를 쓰세요. | 파일 | 설명 | | --- | --- | | `/cdn/v0.2.0/82chart.min.js` | 스크립트 태그용. 전역 `Chart82` 를 만들고 `<chart-82>` 를 등록합니다. | | `/cdn/v0.2.0/82chart.esm.js` | `<script type="module">` 에서 import 하는 단일 ES 모듈. | | `/cdn/v0.2.0/82chart.css` | 카드·툴팁·범례 스타일 (밝은·어두운 테마 포함). | | `/cdn/latest/…` | 항상 최신 버전. 1시간 캐시. 시험용으로만 쓰세요. | ## ES 모듈로 불러오기 ```html <link rel="stylesheet" href="https://82chart.com/cdn/v0.2.0/82chart.css"> <div id="chart"></div> <script type="module"> import { createChart } from 'https://82chart.com/cdn/v0.2.0/82chart.esm.js'; createChart('#chart', { type: 'line', data: { labels: ['월', '화', '수'], series: [{ data: [3, 5, 4] }] } }); </script> ``` ## 전역 Chart82 에 들어 있는 것 `createChart`, `renderToSVG`, `defineChartElement`, 숫자 도우미 `korean` · `compact` · `comma`, `registerLocale`, 팔레트 `PALETTE_LIGHT` · `PALETTE_DARK`, `VERSION`. npm 패키지에서 import 하는 것과 같습니다. > 파일을 내려받아 내 서버에 두어도 됩니다. MIT 라이선스라 고쳐 쓰고 다시 배포해도 됩니다. --- # 데이터 형식 > labels 와 series, 짝 배열, 점 객체, 빈 값, 날짜 축. Source: https://82chart.com/docs/data-format/ 모든 차트는 `data: { labels?, series }` 하나를 받습니다. 시리즈의 `data` 는 세 가지 모양 중 하나입니다. | 모양 | 예 | 쓰는 곳 | | --- | --- | --- | | 숫자 배열 | `[120, 98, null, 143]` | `labels[i]` 가 x. 대부분의 선·막대·영역 | | [x, y] 짝 | `[['2026-10-01', 120], ['2026-10-02', 98]]` | 날짜 축, 시리즈마다 x가 다를 때 | | 점 객체 | `[{ x: 3, y: 12, r: 8 }]` | 산점도, 거품(`r`) | ## 시리즈 하나의 옵션 | 속성 | 설명 | | --- | --- | | `name` | 범례·툴팁·표에 쓰는 이름 | | `data` | 위 세 가지 중 하나 | | `type` | `'line' \| 'area' \| 'bar' \| 'scatter'` — 시리즈마다 다르게 주면 [콤보](https://82chart.com/docs/combo/) | | `color` | 이 시리즈 색 (팔레트 대신) | | `stack` | 같은 이름끼리 쌓기 (`stacked: true` 면 막대·영역이 모두 한 묶음) | | `smooth` / `dashed` | 이 시리즈만 곡선 / 점선 | | `hidden` | 처음에 숨김 (범례를 누르면 보임) | | `axis` | `'right'` 면 오른쪽 y축에 ([콤보](https://82chart.com/docs/combo/), 직접 켜는 옵션) | ## 빈 값 `null` 은 "값 없음"입니다. 0으로 그리지 않고 선을 끊습니다. `nulls` 옵션으로 바꿀 수 있습니다: `'gap'`(기본, 끊김) · `'connect'`(점선으로 잇기) · `'zero'`(0으로). ```js createChart('#chart', { type: 'line', title: '일별 방문자', unit: '명', nulls: 'connect', data: { labels: ['10.14', '10.15', '10.16', '10.17', '10.18', '10.19', '10.20'], series: [{ name: '방문자', data: [1820, 2140, null, 2410, 2290, 1650, 1980] }], }, }); ``` ## 날짜 축 `labels` 나 x 값이 날짜(`Date`, `'2026-10'`, `'2026-10-21'`, `'2026-10-21T09:00'`)면 82chart가 알아보고 `10월` · `10.21` · `09:00` 처럼 간격에 맞게 적습니다. [x, y] 짝으로 주면 진짜 시간 축이 되어 빠진 날은 그만큼 띄워 그립니다. 날짜만 있는 문자열은 UTC가 아니라 그 지역의 자정으로 읽습니다. ```js createChart('#chart', { type: 'area', title: '주문 수', unit: '건', data: { series: [{ name: '주문', data: [['2026-10-01', 412], ['2026-10-02', 455], ['2026-10-05', 520], ['2026-10-06', 498], ['2026-10-07', 610], ['2026-10-08', 588]] }], }, }); ``` 축의 종류를 직접 정하려면 `xAxis: { type: 'category' | 'linear' | 'time' }` 를 씁니다. 날짜 라벨을 칸마다 똑같이 놓고 싶으면 `'category'` 입니다. ## 웹 컴포넌트·서버에서 받을 때 데이터는 JSON 으로 그대로 옮길 수 있는 모양입니다. 서버가 `{"labels": [...], "series": [...]}` 를 내려주면 `chart.update(json)` 한 줄, `<chart-82 data='...'>` 에는 속성 문자열로 넣으면 됩니다. --- # 선 차트 > 시간에 따른 변화. 곡선, 계단, 점, 마지막 값 강조. Source: https://82chart.com/docs/line/ `type: 'line'` 은 기본 차트입니다. 마우스를 올리면 세로 크로스헤어가 생기고 그 x의 모든 시리즈 값이 툴팁 하나에 나옵니다. 시리즈가 하나면 마지막 점에 값이 붙습니다(`lastPoint`). ```js createChart('#chart', { type: 'line', title: '월별 방문자', unit: '명', smooth: true, data: { labels: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월'], series: [ { name: '올해', data: [41200, 43800, 52100, 49800, 61200, 66400, 71800, 69500, 74300, 80100] }, { name: '작년', data: [36500, 35200, 40100, 42800, 47900, 51300, 55100, 53800, 58200, 60400], dashed: true }, ], }, }); ``` ## 옵션 | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `curve` | 'straight' | `'straight' \| 'smooth' \| 'step'`. smooth 는 값보다 튀어 오르지 않는 곡선(monotone) | | `smooth` | false | `curve: 'smooth'` 와 같음. 시리즈에 주면 그 시리즈만 | | `points` | 'auto' | 점 표시. auto 는 점이 30개 이하일 때만 | | `pointShape` | 'circle' | `'circle' \| 'square' \| 'diamond'` | | `lastPoint` | 선 하나면 true | 마지막 점을 강조 | | `nulls` | 'gap' | 빈 값: `'gap' \| 'connect' \| 'zero'` | | `dataLabels` | false | `true` 또는 `'end'`: 선 끝에만 값 (모든 점에 숫자를 붙이지 않음) | ## 계단 선 요금제·재고처럼 값이 바뀌기 전까지 그대로인 데이터는 `curve: 'step'` 이 정확합니다. ```js createChart('#chart', { type: 'line', title: '보유 재고', unit: '개', curve: 'step', points: true, data: { labels: ['월', '화', '수', '목', '금', '토', '일'], series: [{ name: '재고', data: [120, 120, 85, 85, 140, 132, 132] }], }, }); ``` --- # 영역 차트 > 양과 합계를 함께 보는 영역, 누적, 100% 누적. Source: https://82chart.com/docs/area/ `type: 'area'` 는 선 아래를 옅게 칠합니다(약한 그라디언트, `gradient: false` 로 끄기). y축은 항상 0에서 시작합니다. 여러 시리즈의 합이 중요하면 `stacked: true`, 비율이 중요하면 `stacked: 'percent'` 입니다. ```js createChart('#chart', { type: 'area', title: '유입 경로별 방문', stacked: true, unit: '명', data: { labels: ['월', '화', '수', '목', '금', '토', '일'], series: [ { name: '검색', data: [5200, 5600, 5900, 6100, 5800, 4200, 3900] }, { name: 'SNS', data: [2100, 2300, 2600, 2400, 2900, 3800, 4100] }, { name: '직접', data: [1400, 1500, 1450, 1600, 1550, 1100, 980] }, ], }, }); ``` ## 100% 누적 각 x에서 합을 100으로 맞춥니다. 툴팁에는 비율과 원래 값이 함께 나옵니다. ```js createChart('#chart', { type: 'area', title: '기기 비율', stacked: 'percent', data: { labels: ['2026-05', '2026-06', '2026-07', '2026-08', '2026-09', '2026-10'], series: [ { name: '휴대폰', data: [61, 63, 64, 66, 67, 69] }, { name: 'PC', data: [33, 31, 31, 29, 28, 26] }, { name: '태블릿', data: [6, 6, 5, 5, 5, 5] }, ], }, }); ``` > 영역 차트에서 겹치는 시리즈를 3개 넘게 쌓지 않는 것이 좋습니다. 많아지면 위쪽 영역의 모양이 아래 영역에 끌려가 읽기 어렵습니다. --- # 막대 차트 > 묶음, 누적, 100% 누적, 가로, 음수 막대. Source: https://82chart.com/docs/bar/ `type: 'bar'` 는 시리즈가 여러 개면 나란히 묶어 그립니다. 막대 끝은 둥글고(`barRadius`, 기본 6) 기준선 쪽은 각져 있습니다. 막대 위에 마우스를 올리면 그 칸 전체가 옅게 칠해지고 그 칸의 모든 값이 툴팁에 나옵니다. ```js createChart('#chart', { type: 'bar', title: '분기별 매출', unit: '원', data: { labels: ['1분기', '2분기', '3분기', '4분기'], series: [ { name: '2025', data: [182000000, 205000000, 198000000, 241000000] }, { name: '2026', data: [214000000, 236000000, 251000000, null] }, ], }, }); ``` ## 누적과 합계 `stacked: true` 면 모든 막대 시리즈가 한 묶음으로 쌓이고, `stackTotals: true` 는 막대 위에 합계를 적습니다. 시리즈마다 `stack: '이름'` 을 주면 묶음을 여러 개로 나눌 수 있습니다. ```js createChart('#chart', { type: 'bar', title: '연령·성별 가입자', stacked: true, stackTotals: true, unit: '명', data: { labels: ['10대', '20대', '30대', '40대', '50대 이상'], series: [ { name: '여성', data: [820, 4210, 3880, 2140, 960] }, { name: '남성', data: [640, 3650, 4120, 2530, 1180] }, ], }, }); ``` ## 가로 막대 (순위) 이름이 긴 항목, 순위표(상품 TOP 10)는 `horizontal: true` 가 읽기 쉽습니다. 큰 값부터 정렬해서 넘기세요. `dataLabels: true` 는 막대 끝에 값을 붙입니다. ```js createChart('#chart', { type: 'bar', horizontal: true, title: '많이 팔린 상품', unit: '개', dataLabels: true, data: { labels: ['무선 이어폰', '보조배터리 10000mAh', 'C타입 충전 케이블', '휴대폰 거치대', '노트북 파우치'], series: [{ name: '판매량', data: [1840, 1520, 1310, 980, 760] }], }, }); ``` ## 100% 누적 `stacked: 'percent'` 는 칸마다 합을 100%로 맞춥니다. 설문 응답처럼 비율이 중요할 때 씁니다. ## 음수 막대 증감처럼 음수가 섞이면 0 기준선 아래로 그립니다. `negativeColor` 로 음수 막대 색을 따로 주거나, `palette: 'korea'` 를 쓰면 증권 앱처럼 오르면 빨강, 내리면 파랑입니다. ```js createChart('#chart', { type: 'bar', title: '전월 대비 증감', unit: '%', palette: 'korea', data: { labels: ['5월', '6월', '7월', '8월', '9월', '10월'], series: [{ name: '증감률', data: [4.2, -1.8, 6.5, -3.1, 2.4, 8.9] }], }, }); ``` | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `horizontal` | false | 가로 막대 | | `stacked` | false | `true` 또는 `'percent'` | | `stackTotals` | false | 쌓은 막대 위에 합계 | | `dataLabels` | false | 막대 끝에 값 (`true` 또는 `'end'`) | | `barRadius` | 6 | 값 쪽 끝 둥글기(px) | | `barWidth` | 0.82 (묶음 0.78) | 칸 너비 중 막대 묶음이 차지하는 비율(0–1) | | `negativeColor` | - | 0보다 작은 막대 색 | | `gradient` | true | 막대에 옅은 세로 그라디언트 | --- # 파이·도넛 > 전체 중 비율. 가운데 합계, 작은 조각은 기타로. Source: https://82chart.com/docs/pie-donut/ `type: 'pie'` 와 `'donut'` 은 `labels` 와 첫 시리즈의 값을 씁니다. 범례는 오른쪽에 붙고, 누르면 그 조각을 빼고 다시 계산합니다. 도넛 가운데에는 합계가 들어갑니다(`donutLabel` 로 바꾸거나 `false` 로 숨김). ```js createChart('#chart', { type: 'donut', title: '결제 수단', unit: '원', data: { labels: ['카드', '간편결제', '계좌이체', '휴대폰', '기타'], series: [{ name: '결제액', data: [48200000, 31500000, 9800000, 4100000, 600000] }], }, }); ``` | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `donutHole` | 0.62 | 구멍 크기 (반지름 대비 0–1) | | `donutLabel` | 합계 | 가운데 글자. `false` 면 없음 | | `otherThreshold` | 0.02 | 이 비율(2%)보다 작은 조각은 "기타"로 묶음. 0 이면 모두 그림 | | `startAngle` | 0 | 시작 각도 (0 = 12시 방향) | | `legend` | 'right' | `'top' \| 'bottom' \| 'right' \| false` | > 조각이 6개를 넘으면 막대 차트가 더 정확하게 읽힙니다. 사람 눈은 각도보다 길이를 잘 비교합니다. --- # 산점도·거품 > 두 값의 관계. 점 크기로 세 번째 값. Source: https://82chart.com/docs/scatter/ `type: 'scatter'` 는 `[x, y]` 짝이나 `{ x, y, r }` 점을 받습니다. x가 숫자면 숫자 축이 됩니다. `r` 을 주면 거품 차트가 됩니다(가장 큰 r 이 지름 약 48px, 넓이가 값에 비례). ```js createChart('#chart', { type: 'scatter', title: '광고비와 매출', xAxis: { title: '광고비(만원)' }, yAxis: { title: '매출(만원)' }, numberFormat: 'comma', data: { series: [ { name: '검색 광고', data: [{ x: 120, y: 980, r: 14 }, { x: 180, y: 1320, r: 22 }, { x: 260, y: 1610, r: 30 }, { x: 340, y: 1790, r: 26 }] }, { name: 'SNS 광고', data: [{ x: 90, y: 520, r: 10 }, { x: 150, y: 760, r: 16 }, { x: 230, y: 1010, r: 18 }, { x: 310, y: 1150, r: 20 }] }, ], }, }); ``` - 시리즈는 3개까지가 색으로 구분하기 좋습니다. 더 많으면 차트를 나누세요. - 점 모양은 `pointShape`: `'circle' | 'square' | 'diamond'`. - x가 날짜면 시간 축이 됩니다. --- # 방사형(레이더) > 여러 항목의 점수를 한눈에 비교. Source: https://82chart.com/docs/radar/ `type: 'radar'` 는 `labels` 가 축이 되고 시리즈마다 다각형 하나를 그립니다. 축은 0에서 시작하고 모든 축이 같은 눈금을 씁니다. 그래서 단위가 같은 값(점수, 만족도)에만 쓰세요. ```js createChart('#chart', { type: 'radar', title: '서비스 만족도', data: { labels: ['속도', '디자인', '가격', '고객 응대', '배송'], series: [ { name: '우리', data: [4.6, 4.1, 3.8, 4.4, 4.0] }, { name: '업계 평균', data: [3.9, 3.7, 3.6, 3.5, 3.8] }, ], }, }); ``` --- # 게이지 > 목표 대비 달성률 같은 값 하나. Source: https://82chart.com/docs/gauge/ `type: 'gauge'` 는 `data.series[0].data[0]` 값 하나를 그립니다. 범위는 `gauge: { min, max }`(기본 0–100), `bands` 로 구간 색을 정하면 값이 들어간 구간의 색으로 칠합니다. ```js const chart = createChart('#chart', { type: 'gauge', title: '이번 달 목표 달성률', unit: '%', gauge: { min: 0, max: 100, label: '목표 5억 원', bands: [{ to: 50, color: '#e34948' }, { to: 80, color: '#eda100' }, { to: 100, color: '#1baf7a' }], }, data: { series: [{ name: '달성률', data: [72.4] }] }, }); setTimeout(() => chart.update({ series: [{ name: '달성률', data: [86.1] }] }), 2500); ``` | gauge 속성 | 설명 | | --- | --- | | `min` / `max` | 범위 (기본 0 / 100) | | `bands` | `[{ to, color }]` — 값이 `to` 이하인 첫 구간의 색 | | `label` | 숫자 아래 작은 글 | --- # 콤보 (막대+선) > 시리즈마다 type 을 정해 한 차트에 섞기. 단위가 다르면 오른쪽 y축(직접 켜기). Source: https://82chart.com/docs/combo/ 시리즈마다 `type` 을 주면 막대 위에 선을 올리는 식으로 섞을 수 있습니다. 관리자 통계에서 흔한 "일별 매출 막대 + 7일 평균 선"이 대표적입니다. 두 시리즈의 단위가 같으면 y축 하나로 충분합니다. ```js createChart('#chart', { type: 'bar', title: '일별 매출과 7일 평균', unit: '원', data: { labels: ['10.08', '10.09', '10.10', '10.11', '10.12', '10.13', '10.14', '10.15', '10.16', '10.17'], series: [ { name: '매출', data: [3120000, 2870000, 3540000, 4210000, 3980000, 2650000, 2410000, 3390000, 3710000, 3060000] }, { name: '7일 평균', type: 'line', smooth: true, data: [3010000, 3050000, 3110000, 3240000, 3330000, 3310000, 3250000, 3290000, 3310000, 3260000] }, ], }, }); ``` ## 이중 y축 (직접 켜기) 단위가 다른 두 값, 예를 들어 "월 매출(원) 막대 + 전년 대비 증감률(%) 선"은 시리즈에 `axis: 'right'` 를 주면 오른쪽에 두 번째 축이 생깁니다. 오른쪽 축의 눈금 모양·단위·제목은 `y2Axis` 로 정합니다. 어떤 시리즈도 `axis: 'right'` 를 쓰지 않으면 지금까지와 똑같이 축이 하나입니다. ```js createChart('#chart', { type: 'bar', title: '월 매출과 전년 대비 증감률', unit: '원', data: { labels: ['1월', '2월', '3월', '4월', '5월', '6월'], series: [ { name: '매출', data: [182000000, 164000000, 201000000, 223000000, 248000000, 236000000] }, { name: '증감률', type: 'line', axis: 'right', data: [4.2, -2.1, 6.8, 9.5, 12.3, 7.4] }, ], }, y2Axis: { unit: '%', title: '증감률' }, }); ``` - 오른쪽 축은 왼쪽 축과 눈금 개수를 맞춰서 두 축의 숫자가 같은 격자선 위에 놓입니다. `y2Axis` 에 `min` · `max` · `step` · `ticks` 를 주면 그 값을 따르고, 그때는 격자선이 왼쪽 축 기준입니다. - 오른쪽 축은 차트의 `unit` · `prefix` · `scale` 을 물려받지 않습니다(단위가 다르기 때문). 숫자 모양(만·억)과 `decimals` 만 물려받고, 단위는 `y2Axis.unit` · `y2Axis.prefix`, 모양은 `y2Axis.format`(`'percent'`, `'comma'` 또는 함수)으로 정합니다. - 툴팁과 표는 시리즈마다 자기 축의 형식으로 적고, 범례에는 오른쪽 축 시리즈 뒤에 **(우)** 가 붙습니다(영어 (R), `locale.rightAxis` 로 변경). - 오른쪽 시리즈는 왼쪽 시리즈와 쌓이지 않습니다. 가로 막대(`horizontal`)에서는 쓸 수 없습니다. > **이중 y축은 조심해서 쓰세요.** 두 축의 범위는 각자 정해지므로, 범위를 어떻게 잡느냐에 따라 같은 데이터가 "같이 오른다"로도 "엇갈린다"로도 보입니다. 두 선이 만나는 점이나 기울기의 차이에는 뜻이 없습니다. 그래서 기본은 꺼져 있고 시리즈마다 직접 켜야 합니다. 비교가 목적이라면 [차트 두 개를 위아래로 놓고 크로스헤어를 묶는 방법](https://82chart.com/docs/group-sync/)이 더 정직합니다. --- # 스파크라인·KPI 카드 > 큰 숫자, 증감 배지, 작은 추세선을 한 카드에. Source: https://82chart.com/docs/sparkline-kpi/ 기본 모양(`skin: 'card'`)은 카드입니다. 위에서부터 작은 머리글(`eyebrow`), 제목, 부제, 큰 숫자(`value`), 오른쪽 위 증감 배지(`delta`), 차트, 아래 주석(`footnote`) 순서입니다. 한국 대시보드의 KPI 카드 순서 그대로입니다. ```js createChart('#chart', { type: 'area', eyebrow: 'REVENUE', title: '이번 달 매출', subtitle: '10월 1일~21일', value: 48250000, unit: '원', delta: 18.2, deltaLabel: '전월 대비', footnote: '기준: 2026.10.21 09:00', height: 140, data: { labels: ['10.15', '10.16', '10.17', '10.18', '10.19', '10.20', '10.21'], series: [{ name: '매출', data: [2010000, 2240000, 2180000, 2650000, 2420000, 2890000, 3120000] }], }, }); ``` ## 증감 배지 | 옵션 | 설명 | | --- | --- | | `delta` | 숫자면 퍼센트(`18.2` → +18.2%), 문자열이면 그대로 | | `deltaUpIsGood` | 기본 true(오르면 초록). 비용·오류처럼 줄어야 좋은 지표는 `false` | | `deltaColors` | `'semantic'`(좋음 초록·나쁨 빨강, 기본) 또는 `'korea'`(오르면 빨강 ▲·내리면 파랑 ▼, 증권 앱 방식) | | `deltaLabel` | 배지 옆 작은 글, 예: `'전월 대비'` | ## 스파크라인 `skin: 'sparkline'` 은 축·격자·범례 없이 선만 그립니다. 표 안이나 카드 구석에 넣는 작은 추세선입니다. 높이는 폭에 맞춰 32–80px 사이로 정해집니다. ```js createChart('#chart', { type: 'line', skin: 'sparkline', data: { series: [{ name: '방문자', data: [12, 15, 14, 18, 17, 21, 19, 24, 26, 25, 29] }] }, }); ``` 카드 테두리 없이 차트만 필요하면 `skin: 'plain'` 입니다. --- # 숫자와 날짜 > 만·억 단위, "단위: 백만원", 통화 기호, 소수점, 날짜 모양, 시간대. Source: https://82chart.com/docs/numbers-dates/ 축 눈금은 짧게, 툴팁·표는 정확하게 적는 것이 기본입니다. 한국어 페이지에서 축은 **1.2만 · 3.4억 · 6,000만**, 툴팁은 **123,456,789원**처럼 쉼표가 들어간 전체 숫자입니다. ## numberFormat | 값 | 12,345 → | 340,000,000 → | 쓰는 곳 | | --- | --- | --- | --- | | `'korean'` | 1.2만 | 3.4억 | 한국어 페이지 기본 | | `'compact'` | 12.3K | 340M | 그 외 언어 기본 | | `'comma'` | 12,345 | 340,000,000 | 정확한 숫자가 필요한 표·보고서 | | `'percent'` | 12,345% | - | 값이 이미 퍼센트일 때 (0.5 가 아니라 50 으로 넘김) | | 함수 | `(v) => string` | | 축과 툴팁에 모두 씀 | 축 눈금만 따로 바꾸려면 `yAxis: { format }` (또는 `xAxis`) 에 같은 값을 줍니다. 숫자 도우미는 따로 꺼내 쓸 수도 있습니다. ```js import { korean, compact, comma } from '@82chart/core'; korean(123456789); // '1.2억' compact(123456789); // '123.5M' comma(1234.5); // '1,234.5' ``` ## "단위: 백만원" 보고서 방식 공공 자료·보고서처럼 축을 백만원 단위 쉼표 숫자로 쓰고 싶다면 `scale` 로 나누고 `unit` 을 붙입니다. `scale` 이 있으면 축·툴팁·표가 모두 나눈 숫자에 단위를 붙여 적고, 머리 숫자(`value`)는 원래 금액 그대로입니다. `footnote` 로 단위를 밝혀 두세요. ```js createChart('#chart', { type: 'bar', title: '사업부별 매출', scale: 1000000, unit: '백만원', footnote: '단위: 백만원', data: { labels: ['국내', '해외', '온라인', '도매'], series: [{ name: '매출', data: [1820000000, 940000000, 2310000000, 610000000] }], }, }); ``` | 옵션 | 설명 | | --- | --- | | `unit` | 값 뒤에 붙는 단위: `'원'`, `'명'`, `'%'`, `'백만원'` | | `prefix` | 값 앞에: `'₩ '`, `'$'`. `scale` 과 함께 쓰면 툴팁은 원래 금액 전체(₩ 1,820,000,000) | | `scale` | 축 눈금을 이 수로 나눔 (1000000 → 백만 단위) | | `decimals` | 툴팁·라벨의 소수 자리 (기본: 최대 2자리, 끝의 0은 지움) | ## 날짜 모양 날짜 축의 라벨은 간격에 맞게 정해집니다. 월 단위면 `1월 · 2월` (해가 바뀌면 `2027년 1월`), 일 단위면 `10.21`, 시간 단위면 `09:00`. 툴팁은 `2026.10.21(화)` 처럼 요일까지 씁니다. 직접 정하려면 `dateFormat` 에 패턴을 넘깁니다. | 토큰 | 뜻 | 예 | | --- | --- | --- | | `YYYY` / `YY` | 연도 | 2026 / 26 | | `M` / `MM` | 월 | 3 / 03 | | `MMM` | 월 이름(영어 등 이름이 있는 언어) | Mar | | `D` / `DD` | 일 | 7 / 07 | | `ddd` | 요일 | 화 | | `H` / `HH` | 시 | 9 / 09 | | `mm` / `ss` | 분 / 초 | 05 / 30 | ```js createChart('#chart', { type: 'line', dateFormat: 'MM/DD(ddd)', // 10/21(화) data: { series: [{ name: '방문자', data: [['2026-10-20', 1820], ['2026-10-21', 2140]] }] }, }); ``` `dateFormat` 은 축과 툴팁에 함께 쓰입니다. `'auto'` 는 기본 동작입니다. ## 시간대 (timeZone) 날짜는 기본으로 실행 환경의 시간대로 읽고 적습니다. 브라우저에서는 보통 문제가 없지만, 서버(Cloudflare Workers, 대부분의 Node 서버)는 UTC 라서 `renderToSVG` 로 그리면 한국 자정 데이터가 전날로 찍힙니다. `timeZone` 에 IANA 이름을 주면 축·툴팁·표·CSV 가 모두 그 시간대로 적힙니다. `'2026-10-21'` 처럼 날짜만 있는 문자열은 그 시간대의 그날, 시차 표시가 없는 `'2026-10-21T09:00'` 은 그 시간대의 9시로 읽습니다. ```js createChart('#chart', { type: 'line', timeZone: 'Asia/Seoul', // 어디서 실행해도 한국 시간 data: { series: [{ name: '주문', data: [['2026-10-21T00:00:00Z', 120], ['2026-10-21T06:00:00Z', 180]] }] }, }); ``` 시간대 이름이 틀리면 경고를 한 번 남기고 실행 환경의 시간대로 그립니다. 내보낸 숫자 도우미 `formatDate(date, pattern, locale, timeZone)` 도 네 번째 인자로 시간대를 받습니다. --- # 축 다루기 > 정수 눈금, 눈금 간격, 축 뒤집기(순위), 두 줄 라벨, 오른쪽 y축. Source: https://82chart.com/docs/axes/ 축 옵션은 `xAxis` · `yAxis` (오른쪽 축은 `y2Axis`) 에 넣습니다. 가로 막대에서도 값 축의 옵션은 `yAxis` 입니다. ## 정수 눈금과 눈금 간격 건수·인원처럼 소수가 없는 값은 `integer: true` 로 0.5 같은 눈금을 막습니다. `step` 은 눈금 사이를 고정합니다(예: 10 단위). 눈금이 200개를 넘을 만큼 작은 `step` 은 무시하고 자동으로 정합니다. ```js createChart('#chart', { type: 'bar', title: '요일별 문의', unit: '건', yAxis: { integer: true }, data: { labels: ['월', '화', '수', '목', '금'], series: [{ name: '문의', data: [2, 3, 1, 3, 2] }], }, }); ``` ## 축 뒤집기 (순위) `yAxis: { reverse: true }` 는 값 축을 뒤집어 작은 값이 위로 갑니다. 1위가 맨 위에 오는 순위 차트에 씁니다. 가로 막대에서는 카테고리 순서가 뒤집혀 첫 항목이 맨 아래로 갑니다. ```js createChart('#chart', { type: 'line', title: '검색 순위', yAxis: { reverse: true, integer: true, min: 1, title: '순위' }, data: { labels: ['1주', '2주', '3주', '4주', '5주'], series: [ { name: '우리 상품', data: [8, 5, 3, 2, 1] }, { name: '경쟁 상품', data: [1, 2, 2, 4, 6] }, ], }, }); ``` ## 두 줄 라벨 카테고리 라벨에 `'\n'` 을 넣으면 두 줄로 적습니다. "서울특별시 강남구" 같은 긴 이름에 좋습니다. 툴팁과 표에서는 한 줄로 이어 적습니다. ```js createChart('#chart', { type: 'bar', title: '구별 매장 수', unit: '곳', data: { labels: ['서울특별시\n강남구', '서울특별시\n송파구', '부산광역시\n해운대구', '대구광역시\n수성구'], series: [{ name: '매장', data: [42, 35, 28, 19] }], }, }); ``` ## 오른쪽 y축 단위가 다른 시리즈는 `axis: 'right'` 로 오른쪽 축에 둘 수 있습니다. 주의할 점과 예는 [콤보 차트](https://82chart.com/docs/combo/)에 있습니다. | 옵션 | 설명 | | --- | --- | | `integer` | 정수 눈금만 | | `step` | 눈금 간격 고정 | | `reverse` | 값 축 뒤집기 (가로 막대: 카테고리 순서) | | `unit` / `prefix` | `y2Axis` 전용: 오른쪽 축과 그 시리즈의 단위 | --- # 테마와 색 > 밝은·어두운 테마, CSS 변수, 팔레트, 시리즈 색. Source: https://82chart.com/docs/theme-colors/ `theme` 의 기본값 `'auto'` 는 페이지를 따라갑니다. 조상 요소의 `data-theme="dark|light"`, `<html class="dark">`, 그다음 운영체제 설정 순서로 보고, 바뀌면 다시 그립니다. 이 사이트의 오른쪽 위 테마 단추를 눌러 보세요. ```js chart.setTheme('dark'); // 'light' | 'dark' | 'auto' ``` ## 팔레트 기본 팔레트는 색약 사용자도 인접한 색을 구분할 수 있게 검증한 8색입니다(파랑·주황·청록·노랑·분홍·초록·보라·빨강). 9번째 시리즈부터는 같은 색을 반복하지 않고 회색 계열을 씁니다. 같은 색이 다른 뜻으로 두 번 나오지 않게 하기 위해서입니다. | palette | 설명 | | --- | --- | | `'default'` | 파랑부터 시작하는 8색 | | `'korea'` | 빨강(오름)·파랑(내림)부터. 음수 막대는 파랑 | | `'mono'` | 첫 색(`colors[0]` 또는 파랑)의 밝기만 바꾼 단색 | ```js createChart('#chart', { type: 'bar', title: '상품군별 매출', palette: 'mono', colors: ['#6250d6'], stacked: true, data: { labels: ['1분기', '2분기', '3분기', '4분기'], series: [ { name: '의류', data: [42, 48, 51, 63] }, { name: '잡화', data: [28, 31, 30, 38] }, { name: '식품', data: [18, 22, 25, 24] }, ], }, }); ``` ## 시리즈 색 정하기 `colors: ['#3182f6', '#eb6834']` 는 시리즈 순서대로 색을 고정합니다. 시리즈 하나만 바꾸려면 그 시리즈에 `color` 를 줍니다. 색은 순위가 아니라 시리즈에 붙으므로, 데이터가 바뀌어도 "매출"은 늘 같은 색입니다. ## CSS 변수로 덮어쓰기 차트나 그 조상 요소에 `--c82-*` 변수를 주면 사이트 색에 맞출 수 있습니다. 색은 SVG 속성에 직접 풀어 쓰므로 PNG·SVG로 내보내도 화면과 같습니다. ```css /* 밝은 테마 */ .my-dashboard { --c82-bg: #fffdf8; --c82-ink: #1c1917; --c82-grid: #ece7df; --c82-s1: #e8590c; /* 첫 번째 시리즈 */ --c82-font: 'Pretendard', sans-serif; } /* 어두운 테마일 때만 */ .my-dashboard .c82[data-theme='dark'] { --c82-bg: #1c1917; --c82-ink: #f5f5f4; --c82-grid: #2e2a27; } ``` | 변수 | 쓰이는 곳 | | --- | --- | | `--c82-bg` | 배경 (카드, 점 테두리) | | `--c82-ink` / `--c82-ink-2` / `--c82-muted` | 글자 (제목 / 본문 / 보조) | | `--c82-grid` / `--c82-axis` | 격자선 / 축선 | | `--c82-hover` | 마우스가 올라간 칸 바탕 | | `--c82-good` / `--c82-bad` | 증감 배지 좋음 / 나쁨 | | `--c82-up` / `--c82-down` | `deltaColors: 'korea'`, `palette: 'korea'` 의 오름 / 내림 | | `--c82-s1` … `--c82-s8` | 시리즈 색 | | `--c82-font` | 글꼴 | | `--c82-border`, `--c82-radius`, `--c82-shadow`, `--c82-tip-bg`, `--c82-tip-border` | 카드 테두리·둥글기·그림자, 툴팁 (CSS 전용) | --- # 툴팁과 범례 > 크로스헤어 툴팁, 값 모양 바꾸기, 범례로 시리즈 켜고 끄기. Source: https://82chart.com/docs/tooltip-legend/ 선·영역은 세로 크로스헤어와 그 x의 모든 시리즈를 담은 툴팁 하나, 막대는 칸 강조와 칸의 모든 값, 파이·산점·게이지는 가리킨 조각 하나를 보여 줍니다. 쌓은 막대는 마지막 줄에 합계가 붙습니다. 툴팁은 오른쪽 끝에서 왼쪽으로 뒤집혀 잘리지 않습니다. ```js const chart = createChart('#chart', { type: 'line', title: '방문과 가입', unit: '명', tooltip: { format: (v, ctx) => ctx.seriesName === '가입' ? v + '명 (' + (v / 20).toFixed(1) + '%)' : v.toLocaleString('ko-KR') + '명' }, legend: 'bottom', data: { labels: ['1일', '2일', '3일', '4일', '5일', '6일', '7일', '8일', '9일', '10일', '11일', '12일', '13일', '14일', '15일', '16일', '17일', '18일', '19일', '20일', '21일'], series: [ { name: '방문자', data: [1820, 2140, 1990, 2410, 2290, 1650, 1580, 2010, 2380, 2550, 2470, 2620, 1910, 1840, 2230, 2680, 2810, 2740, 2950, 2100, 2060] }, { name: '가입', data: [120, 156, 131, 188, 171, 98, 92, 140, 176, 205, 190, 214, 121, 117, 163, 221, 240, 228, 262, 141, 136] }, ], }, }); chart.on('legendclick', (e) => log(e)); ``` ## tooltip | 값 | 설명 | | --- | --- | | `true` | 기본 | | `false` | 툴팁 없음 (`group` 이 있으면 크로스헤어는 남음) | | `{ format(value, ctx) }` | 값 칸의 글자를 직접 만듦. `ctx` 는 `{ seriesName, seriesIndex, index, x }` | 툴팁 글자는 `textContent` 로 넣으므로 시리즈 이름에 HTML이 섞여 있어도 실행되지 않습니다. 코드로 툴팁을 띄우려면 `chart.showTooltip(index, seriesIndex?)`, 숨기려면 `chart.showTooltip(null)`. ## legend | 값 | 설명 | | --- | --- | | 기본 | 시리즈가 2개 이상이면 위, 1개면 없음(제목이 이름 역할). 파이·도넛은 오른쪽 | | `'top' \| 'bottom' \| 'right'` | 위치 | | `true` / `false` | 항상 보이기 / 숨기기 | 범례 항목은 버튼입니다. 누르면 그 시리즈를 숨기고 축을 다시 맞춥니다(마지막 하나는 숨겨지지 않음). 마우스를 올리면 나머지가 흐려집니다. 코드로는 `chart.toggleSeries(index, visible?)` 입니다. --- # 확대와 이동 > 드래그·휠·핀치로 구간 확대, 더블클릭으로 원래대로. 무료. Source: https://82chart.com/docs/zoom/ `zoom: true` 를 주면 선·영역·세로 막대·산점도의 x 범위를 확대할 수 있습니다. 다른 라이브러리에서 유료 등급에 있는 기능입니다. 아래 차트에서 마우스로 구간을 끌어 보세요. ```js const chart = createChart('#chart', { type: 'area', title: '10월 방문', zoom: true, data: { labels: ['1일', '2일', '3일', '4일', '5일', '6일', '7일', '8일', '9일', '10일', '11일', '12일', '13일', '14일', '15일', '16일', '17일', '18일', '19일', '20일', '21일'], series: [ { name: '방문자', data: [1820, 2140, 1990, 2410, 2290, 1650, 1580, 2010, 2380, 2550, 2470, 2620, 1910, 1840, 2230, 2680, 2810, 2740, 2950, 2100, 2060] }, { name: '가입', data: [120, 156, 131, 188, 171, 98, 92, 140, 176, 205, 190, 214, 121, 117, 163, 221, 240, 228, 262, 141, 136] }, ], }, }); chart.on('zoom', (e) => log(e ?? '원래 크기')); ``` | 동작 | 마우스 | 터치 | 키보드 | | --- | --- | --- | --- | | 구간 확대 | 끌어서 범위 고르기 | 두 손가락 벌리기 | `+` | | 축소 | Ctrl(⌘)+휠 | 두 손가락 오므리기 | `-` | | 옆으로 이동 | Shift+끌기, 가로 휠, Shift+휠 | 확대한 뒤 한 손가락으로 끌기 | 끝에서 `←`/`→` | | 원래대로 | 더블클릭, "원래 크기로" 단추, ⋯ 메뉴 | "원래 크기로" 단추 | `0` | - 그냥 휠은 페이지 스크롤을 막지 않습니다. 확대는 Ctrl(⌘)과 함께일 때만입니다. - 확대 범위는 `zoom` 이벤트로 `{ start, end }`(x 인덱스), 원래대로 돌아오면 `null` 이 옵니다. - 코드로 되돌리기: `chart.resetZoom()`. 데이터나 종류를 바꾸면 확대는 풀립니다. - x 값이 3개 이상일 때만 동작하고, 가로 막대에서는 꺼집니다. --- # 주석: 기준선과 구간 > 목표선, 평균선, 행사 기간 음영. Source: https://82chart.com/docs/annotations/ `annotations` 로 목표선(`y`), 특정 날짜 선(`x`), 기간이나 값 범위 음영(`bands`)을 그립니다. 선은 데이터 위에, 음영은 데이터 아래에 그려집니다. y 기준선은 축 범위에 포함되어 항상 보입니다. ```js createChart('#chart', { type: 'line', title: '일별 매출', unit: '원', annotations: { y: [{ value: 3000000, label: '목표 300만', color: '#1baf7a' }], x: [{ value: '10.16', label: '앱 업데이트' }], bands: [{ from: '10.18', to: '10.20', label: '주말 할인' }], }, data: { labels: ['10.14', '10.15', '10.16', '10.17', '10.18', '10.19', '10.20', '10.21'], series: [{ name: '매출', data: [2410000, 2650000, 2580000, 2990000, 3420000, 3610000, 3380000, 2870000] }], }, }); ``` | 속성 | 설명 | | --- | --- | | `y: [{ value, label?, color?, dashed? }]` | 가로 기준선. 점선이 기본, `dashed: false` 면 실선 | | `x: [{ value, label?, color?, dashed? }]` | 세로 선. `value` 는 라벨 문자열, 날짜, 숫자 중 x 축과 같은 것 | | `bands: [{ from, to, label?, color?, axis? }]` | 음영. `axis` 기본은 `'x'`(기간), `'y'` 면 값 범위(정상 범위 등) | --- # 내보내기 > PNG·SVG·CSV 내려받기, 표로 보기. 키·워터마크 없이. Source: https://82chart.com/docs/export/ 카드 오른쪽 위의 내려받기 단추는 PNG를 바로 저장하고, ⋯ 메뉴에는 PNG · SVG · CSV · 표로 보기가 있습니다(`exporting: false` 로 숨김). 같은 일을 코드로도 할 수 있습니다. ```js const chart = createChart('#chart', { type: 'bar', title: '지역별 주문', unit: '건', data: { labels: ['서울', '경기', '부산', '대구', '인천', '광주'], series: [{ name: '주문', data: [4820, 3910, 1640, 1120, 1080, 760] }], }, }); log(chart.toCSV()); ``` | 메서드 | 돌려주는 것 | | --- | --- | | `chart.toPNG(scale = 2)` | `Promise<Blob>` — 레티나용 2배 PNG | | `chart.toSVG()` | 색이 모두 풀린 독립 SVG 문자열 (제목·범례·주석 포함) | | `chart.toCSV()` | CSV 문자열. 단위 없는 원래 숫자라 엑셀에서 바로 더할 수 있음 | | `chart.download('png' \| 'svg' \| 'csv', filename?)` | 파일로 저장. 이름을 안 주면 제목 | - CSV는 엑셀에서 바로 열리게 만듭니다. 한글이 깨지지 않게 BOM을 붙이고, 첫 줄은 머리글(첫 열은 `xAxis.title`, 없으면 "항목"), 날짜는 `2026-10-21` (시각이 있으면 `2026-10-21 09:30`) 처럼 `timeZone` 의 현지 날짜로 적고, 숫자는 만·억·단위 없이 원래 값 그대로라 바로 더할 수 있습니다. - `=` · `+` · `@` 로 시작하는 글자는 수식으로 실행되지 않게 막습니다. - `table: true` 면 차트 아래에 데이터 표가 처음부터 보입니다. 표는 꺼져 있어도 스크린리더용으로 늘 들어 있습니다. - 인쇄할 때는 단추·툴팁이 숨겨집니다. ```js // 보고서 메일에 붙일 PNG const blob = await chart.toPNG(2); const form = new FormData(); form.append('file', blob, 'sales.png'); await fetch('/api/report', { method: 'POST', body: form }); ``` --- # 많은 점 (다운샘플링) > 수만 개 점의 선·영역을 LTTB 로 줄여 그리기. CSV·표는 원본 그대로. Source: https://82chart.com/docs/large-data/ 선·영역 차트에 점이 2,000개를 넘으면 82chart는 그림 폭(px)쯤의 점만 골라 그립니다. 고르는 방법은 LTTB(Largest-Triangle-Three-Buckets)로, 구간마다 모양을 가장 많이 바꾸는 점을 남기기 때문에 봉우리와 골짜기가 사라지지 않습니다. SVG 하나에 수만 개의 점을 넣지 않으니 화면·서버 렌더링·내보내기가 모두 가벼워집니다. ```js createChart('#chart', { type: 'line', title: '센서 값 (2만 개)', data: { series: [{ name: '온도', data: Array.from({ length: 20000 }, (_, i) => [Date.UTC(2026, 9, 1) + i * 60000, 20 + Math.sin(i / 300) * 6 + (i % 1733 === 0 ? 9 : 0)]).map(([t, v]) => [new Date(t), +v.toFixed(2)]), }], }, }); ``` | downsample | 동작 | | --- | --- | | (기본) | 선·영역만, 보이는 점이 2,000개를 넘으면 그림 폭쯤으로 줄임 | | 숫자, 예: `500` | 보이는 점이 이보다 많으면 이만큼으로 줄임 | | `false` | 줄이지 않고 모두 그림 | - 툴팁·크로스헤어·키보드는 남은 점 위에서 움직입니다. 확대(`zoom`)하면 보이는 구간만 다시 골라서 원래 점이 더 많이 보입니다. - `toCSV()`, 내려받은 CSV, 데이터 표에는 줄이기 전의 모든 값이 들어갑니다. - 빈 값(`null`) 앞뒤는 남겨서 선이 끊기는 곳은 그대로입니다. 시리즈가 여럿이면 각 시리즈에서 고른 점을 합쳐 모두 같은 x 에 놓입니다. - 막대·산점도는 줄이지 않습니다. 막대 수천 개는 미리 묶어서(일·주 합계) 넘기세요. --- # 불러오는 중·오류·빈 데이터 > setLoading, setError(다시 시도), noData 문구. Source: https://82chart.com/docs/loading-errors/ 데이터를 서버에서 받는 동안 막대 모양의 자리 표시(`loading: true` 또는 `chart.setLoading(true)`)를 보여 주고, 실패하면 `setError(문구, 다시시도함수)` 로 알립니다. ```js const chart = createChart('#chart', { type: 'bar', title: '주간 가입자', loading: true, data: { labels: [], series: [] }, }); let tries = 0; const load = () => { chart.setError(null); chart.setLoading(true); setTimeout(() => { chart.setLoading(false); if (++tries === 1) return chart.setError('통계를 불러오지 못했습니다.', load); chart.update({ labels: ['월', '화', '수', '목', '금'], series: [{ name: '가입', data: [42, 51, 38, 64, 57] }] }); }, 1200); }; load(); ``` | API | 설명 | | --- | --- | | `loading: true` / `chart.setLoading(bool)` | 자리 표시를 보이고 `aria-busy` 를 켬 | | `chart.setError(message, retry?)` | 오류 문구와 "다시 시도" 단추. `setError(null)` 로 지움 | | `noData` | 그릴 값이 없을 때 문구 (기본 '표시할 데이터가 없습니다') | --- # 차트 묶기 (크로스헤어 공유) > 단위가 다른 값은 이중 축 대신 위아래 차트로. Source: https://82chart.com/docs/group-sync/ `group` 이름이 같은 차트끼리는 한 차트에 마우스를 올리면 나머지도 같은 x를 가리킵니다. x 는 라벨 값으로 맞추므로 두 차트의 `labels` 가 같아야 합니다. 매출(원)과 주문 수(건)처럼 단위가 다른 값을 이중 축 없이 함께 읽는 방법입니다. ```js const labels = ['10.15', '10.16', '10.17', '10.18', '10.19', '10.20', '10.21']; createChart('#sales', { type: 'bar', title: '매출', unit: '원', group: 'daily', height: 180, data: { labels, series: [{ name: '매출', data: [2410000, 2650000, 2580000, 2990000, 3420000, 3610000, 3380000] }] }, }); createChart('#orders', { type: 'line', title: '주문 수', unit: '건', group: 'daily', height: 180, data: { labels, series: [{ name: '주문', data: [61, 70, 66, 74, 92, 98, 85] }] }, }); ``` `setOptions({ group: '다른이름' })` 으로 묶음을 바꿀 수 있고, `destroy()` 하면 묶음에서 빠집니다. --- # 접근성 > 키보드로 값 읽기, 자동 요약, 숨긴 표, 움직임 줄이기. Source: https://82chart.com/docs/accessibility/ 82chart는 따로 설정하지 않아도 스크린리더와 키보드로 읽을 수 있습니다. 무료 라이브러리 중에서는 드문 부분입니다. ## 자동 요약 바깥 요소는 `role="figure"` 이고 `aria-label` 에 요약이 들어갑니다. 예: "선 차트, 월별 방문자. 시리즈 2개. 1월~10월. 올해: 최고 80,100명(10월), 최저 41,200명(1월) …". `description` 옵션을 주면 그 문장이 앞에 붙습니다. ## 키보드 | 키 | 동작 | | --- | --- | | `Tab` | 차트로 들어가면 첫 값에 툴팁 | | `←` / `→` | 앞뒤 x로 (가로 막대는 `↑`/`↓`) | | `↑` / `↓` | 다른 시리즈로 | | `Home` / `End` | 처음 / 마지막 값 | | `Enter` / `Space` | 그 점을 누른 것과 같음 (`click` 이벤트) | | `Esc` | 툴팁 닫기 | | `+` / `-` / `0` | 확대 / 축소 / 원래대로 (`zoom: true`) | 툴팁 내용은 `aria-live` 영역으로도 읽어 줍니다. 범례 항목은 `aria-pressed` 가 있는 버튼이고, ⋯ 메뉴는 화살표 키와 Esc로 움직입니다. ## 표와 움직임 - 모든 값이 든 `<table>` 이 화면에 보이지 않게 늘 들어 있습니다. `table: true` 면 보이게 놓습니다. - 운영체제의 "동작 줄이기"(`prefers-reduced-motion`)가 켜져 있으면 처음 그릴 때의 애니메이션을 하지 않습니다. `animation: false` 로 직접 끌 수도 있습니다. - 고대비 모드(`forced-colors`)에서는 선을 시스템 글자색으로 그립니다. - 색만으로 구분하지 않도록, 점선 시리즈(`dashed`)와 점 모양(`pointShape`)을 함께 쓰세요. --- # 웹 컴포넌트 <chart-82> > 프레임워크 없이 HTML 태그 하나로. Source: https://82chart.com/docs/web-component/ CDN 스크립트(`82chart.min.js`, `82chart.esm.js`)는 `<chart-82>` 를 자동으로 등록합니다. npm 패키지에서는 `defineChartElement()` 를 한 번 부르세요. 서버 템플릿(PHP, JSP, Django)에서 JSON 만 찍어 주면 됩니다. ```html <chart-82 type="bar" title="월별 매출" unit="원" height="280" data='{"labels":["1월","2월","3월"],"series":[{"name":"매출","data":[32000000,41000000,38500000]}]}' ></chart-82> ``` ```js import { defineChartElement } from '@82chart/core'; import '@82chart/core/styles.css'; defineChartElement(); // <chart-82> // defineChartElement('my-chart'); // 다른 태그 이름으로 const el = document.querySelector('chart-82'); el.options = { zoom: true, annotations: { y: [{ value: 40000000, label: '목표' }] } }; // 객체로 넘기는 옵션 el.chart.download('png'); // 안쪽 차트 인스턴스 ``` | 속성 | 설명 | | --- | --- | | `type`, `theme`, `skin` | 문자열 그대로 | | `data` | JSON 문자열 (`{"labels": …, "series": …}`) | | `options` | 나머지 옵션의 JSON 문자열. 함수가 필요한 옵션은 `el.options` 속성으로 | | `height` | 숫자 또는 `auto` | | `title`, `subtitle`, `eyebrow`, `footnote`, `unit` | 카드 머리글과 단위 | | `value`, `delta` | 숫자로 읽을 수 있으면 숫자로 | 속성을 바꾸면 차트가 바로 다시 그려지고, 요소를 문서에서 빼면 차트도 정리됩니다. --- # React > React 컴포넌트로 82chart 쓰기. Source: https://82chart.com/docs/react/ ```sh npm i @82chart/core @82chart/react ``` ```js import { Chart82 } from '@82chart/react'; import '@82chart/core/styles.css'; const data = { labels: ['1월', '2월', '3월', '4월', '5월', '6월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }; export function SalesChart() { return ( <Chart82 type="bar" data={data} options={{ title: '월별 매출', unit: '원' }} onClick={(point) => console.log(point)} /> ); } ``` - `<Chart82 type data options theme onClick onReady />`. `options` 는 `type`·`data` 를 뺀 나머지 [옵션](https://82chart.com/docs/options/)입니다. - `data` 는 객체가 바뀌었을 때(새 객체) 다시 그립니다. 같은 배열을 고쳐 쓰지 말고 새로 만들어 넘기세요. - `options` 는 키 하나하나를 비교해서 바뀐 것만 `setOptions` 로 반영합니다. - `ref` 로 차트 인스턴스를 받습니다. 마운트된 뒤에 만들어지므로 Next.js 같은 SSR에서도 안전합니다. ## ref 로 내보내기 ```js import { useRef } from 'react'; import { Chart82, type Chart } from '@82chart/react'; function Report({ data }) { const ref = useRef<Chart>(null); return ( <> <Chart82 ref={ref} type="line" data={data} /> <button onClick={() => ref.current?.download('png', 'report')}>PNG</button> </> ); } ``` --- # Vue > Vue 컴포넌트로 82chart 쓰기. Source: https://82chart.com/docs/vue/ ```sh npm i @82chart/core @82chart/vue ``` ```html <script setup> import { Chart82 } from '@82chart/vue'; import '@82chart/core/styles.css'; const data = { labels: ['1월', '2월', '3월', '4월', '5월', '6월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }; const options = { title: '월별 매출', unit: '원' }; </script> <template> <Chart82 type="bar" :data="data" :options="options" @click="(p) => console.log(p)" /> </template> ``` - `<Chart82 :type :data :options :theme @click @ready />` (Vue 3). - `data` 는 깊이 지켜보므로 배열 안 값을 바꿔도 다시 그립니다. - `@ready` 로 차트 인스턴스를 받아 `toPNG()` 같은 메서드를 부릅니다. - Nuxt 에서는 `<ClientOnly>` 로 감싸세요. --- # Svelte > Svelte 컴포넌트로 82chart 쓰기. Source: https://82chart.com/docs/svelte/ ```sh npm i @82chart/core @82chart/svelte ``` ```html <script> import { Chart82 } from '@82chart/svelte'; import '@82chart/core/styles.css'; const data = { labels: ['1월', '2월', '3월', '4월', '5월', '6월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }; </script> <Chart82 type="bar" {data} options={{ title: '월별 매출', unit: '원' }} /> ``` - Svelte 5 컴포넌트: `<Chart82 {type} {data} {options} {theme} onclick onready bind:chart />`. - 컴포넌트 대신 아무 요소에 붙이는 `use:chart82` 액션도 있습니다. 옵션 객체 하나를 받습니다. - `data` 는 새 객체를 넘길 때 다시 그립니다. ## 액션으로 쓰기 ```html <script> import { chart82 } from '@82chart/svelte'; let data = $state({ labels: ['A', 'B', 'C'], series: [{ data: [3, 5, 4] }] }); </script> <div use:chart82={{ type: 'bar', data, onClick: (p) => console.log(p) }}></div> ``` --- # Angular > Angular 컴포넌트로 82chart 쓰기. Source: https://82chart.com/docs/angular/ ```sh npm i @82chart/core @82chart/angular ``` ```js import { Component } from '@angular/core'; import { Chart82Component } from '@82chart/angular'; import type { ChartData, ChartOptions, ChartPoint } from '@82chart/core'; @Component({ selector: 'app-sales', standalone: true, imports: [Chart82Component], template: `<chart-82 type="bar" [data]="data" [options]="options" (pointClick)="onPoint($event)"></chart-82>`, }) export class SalesComponent { data: ChartData = { labels: ['1월', '2월', '3월', '4월', '5월', '6월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000, 52000000, 61000000, 58000000] }], }; options: Partial<ChartOptions> = { title: '월별 매출', unit: '원' }; onPoint(p: ChartPoint) { console.log(p); } } // angular.json → "styles": ["node_modules/@82chart/core/dist/styles.css"] ``` - standalone 컴포넌트 `<chart-82 [type] [data] [options] [theme] (ready) (pointClick)>`. - `(pointClick)` 은 점·막대·조각을 눌렀을 때, `(ready)` 는 차트 인스턴스를 줍니다. - `data` 는 새 객체를 넘길 때 다시 그립니다. - Angular 17 이상. --- # 서버에서 그리기 (SSR) > renderToSVG 로 DOM 없이 SVG 문자열. Node, Workers, Astro, 메일, OG 이미지. Source: https://82chart.com/docs/ssr/ 화면에 그리는 함수와 같은 함수가 SVG 문자열을 만듭니다. 그래서 `renderToSVG` 로 서버에서 만든 그림, 브라우저에서 그린 차트, `toSVG()` 로 내보낸 파일이 똑같습니다. DOM 이 필요 없어 Node, Cloudflare Workers, Deno, Bun 어디서나 됩니다. ```js import { renderToSVG } from '@82chart/core'; const svg = renderToSVG( { type: 'line', language: 'ko', // 서버에는 문서 언어가 없으니 직접 timeZone: 'Asia/Seoul', // 서버는 대부분 UTC: 날짜가 하루 밀리지 않게 title: '주간 방문자', unit: '명', data: { labels: ['월', '화', '수', '목', '금'], series: [{ name: '방문자', data: [1820, 2140, 1990, 2410, 2290] }] }, }, { width: 640, height: 360, theme: 'light' }, ); // '<svg xmlns="http://www.w3.org/2000/svg" width="640" height="360" viewBox="0 0 640 360" role="img" aria-label="선 차트, 주간 방문자. …">…</svg>' ``` | 두 번째 인자 | 기본값 | 설명 | | --- | --- | --- | | `width` | 640 | 폭(px) | | `height` | 자동 | 전체 높이(px). 주지 않으면 그림 부분이 폭 × 0.5625 (`aspectRatio`) | | `theme` | 'light' | `'light' \| 'dark'` | | `header` | true | 카드 머리글·범례·주석까지 그리기 | | `idPrefix` | 자동 | 한 페이지에 여러 SVG를 넣을 때 id 충돌 방지 | ## Cloudflare Workers ```js import { renderToSVG } from '@82chart/core'; export default { async fetch(req, env) { const rows = await env.DB.prepare('SELECT day, visits FROM stats ORDER BY day DESC LIMIT 14').all(); const list = rows.results.reverse(); const svg = renderToSVG({ type: 'area', language: 'ko', title: '최근 14일 방문', data: { series: [{ name: '방문', data: list.map((r) => [r.day, r.visits]) }] }, }, { width: 800, height: 400 }); return new Response(svg, { headers: { 'content-type': 'image/svg+xml; charset=utf-8', 'cache-control': 'max-age=300' } }); }, }; ``` ## Astro 같은 정적 사이트 ```html --- import { renderToSVG } from '@82chart/core'; const svg = renderToSVG({ type: 'bar', language: 'ko', data: { labels: ['A', 'B'], series: [{ data: [3, 5] }] } }, { width: 560 }); --- <figure set:html={svg} /> ``` ## 아이디어: 공유 썸네일(OG 이미지) 블로그 글이나 보고서 페이지의 `og:image` 로 실제 숫자가 든 차트를 쓸 수 있습니다. Worker 에서 `renderToSVG` 로 1200×630 SVG를 만들고, SVG를 PNG로 바꾸는 라이브러리(예: resvg-wasm)로 변환해 캐시해 두면 됩니다. 메일에 붙일 때도 PNG로 바꿔 쓰세요(메일 프로그램 대부분이 SVG를 보여 주지 않음). > 서버는 대부분 UTC 시간대입니다. 날짜가 있는 차트는 `timeZone: 'Asia/Seoul'` 처럼 시간대를 정해 두면 브라우저에서 그린 것과 같은 날짜가 찍힙니다. [숫자와 날짜](https://82chart.com/docs/numbers-dates/) 참고. > 서버에서 만든 SVG는 그림입니다. 툴팁·확대 같은 상호작용이 필요하면 브라우저에서 `createChart` 로 다시 그리세요. 스크린리더용 요약은 `aria-label` 과 `<title>` 에 들어 있습니다. --- # API > createChart 와 차트 인스턴스의 메서드, 내보내는 함수. Source: https://82chart.com/docs/api/ ```js import { createChart } from '@82chart/core'; const chart = createChart(target, options); // target: 요소 또는 선택자 ``` ## 인스턴스 메서드 | 메서드 | 설명 | | --- | --- | | `update(data)` | 데이터만 바꿔 다시 그림 | | `setOptions(partial)` | 옵션 일부를 바꿈 (`type`·`data` 가 바뀌면 확대가 풀림) | | `getOptions()` | 지금 옵션의 사본 | | `setTheme(theme)` | `'light' \| 'dark' \| 'auto'` | | `resize()` | 크기를 다시 재서 그림 (보통은 자동) | | `toggleSeries(index, visible?)` | 시리즈(파이는 조각) 켜고 끄기 | | `resetZoom()` | 확대 풀기 | | `setLoading(bool)` | 불러오는 중 표시 | | `setError(message \| null, retry?)` | 오류 표시와 다시 시도 단추 | | `showTooltip(index \| null, seriesIndex?)` | 코드로 툴팁 띄우기·숨기기 | | `toSVG()` | SVG 문자열 | | `toPNG(scale = 2)` | `Promise<Blob>` | | `toCSV()` | CSV 문자열 | | `download('png' \| 'svg' \| 'csv', filename?)` | 파일로 저장 | | `on(event, fn)` / `off(event, fn)` | [이벤트](https://82chart.com/docs/events/) | | `destroy()` | 차트를 없애고 리스너 정리 | | `root` / `svg` | 바깥 요소 / 지금 SVG 요소 | ## 그 밖에 내보내는 것 | 이름 | 설명 | | --- | --- | | `renderToSVG(options, { width, height, theme, header, idPrefix })` | DOM 없이 SVG 문자열 ([SSR](https://82chart.com/docs/ssr/)) | | `defineChartElement(tag = 'chart-82')` | [웹 컴포넌트](https://82chart.com/docs/web-component/) 등록 | | `korean(v)` / `koreanLong(v)` | '1.2억' / '1억 2,345만 6,789' | | `compact(v)` / `comma(v)` | '123.5M' / '123,456,789' | | `formatTick(v, style)` / `formatValue(v, style)` / `formatDate(d, pattern, locale)` | 축·툴팁과 같은 규칙으로 글자 만들기 | | `niceStep` / `linearTicks` | 깔끔한 눈금 계산 | | `PALETTE_LIGHT` / `PALETTE_DARK` | 기본 8색 | | `LIGHT_THEME` / `DARK_THEME` | 기본 테마 색 값 | | `LOCALES` / `registerLocale(lang, strings)` | 언어 문구 / 언어 추가 | | `VERSION` | '0.1.0' 같은 버전 문자열 | 타입(`ChartOptions`, `ChartData`, `Series`, `ChartPoint`, `Chart` …)도 모두 함께 내보냅니다. --- # 이벤트 > click, hover, legendclick, zoom, ready, render, destroy. Source: https://82chart.com/docs/events/ `chart.on(이름, 함수)` 로 받고 `chart.off(이름, 함수)` 로 뗍니다. 아래 차트에 마우스를 올리거나 눌러 보세요. ```js const chart = createChart('#chart', { type: 'bar', title: '요일별 주문', unit: '건', data: { labels: ['월', '화', '수', '목', '금'], series: [{ name: '주문', data: [61, 70, 66, 74, 92] }], }, }); chart.on('click', (p) => log({ click: p.x, y: p.y, series: p.seriesName })); chart.on('hover', (p) => p && log('hover ' + p.x)); ``` | 이벤트 | 받는 값 | 언제 | | --- | --- | --- | | `click` | `ChartPoint` | 점·막대·조각을 누르거나 Enter (옵션 `onClick` 과 같음) | | `hover` | `ChartPoint \| null` | 마우스가 옮겨 갈 때, 벗어나면 null | | `legendclick` | `{ seriesIndex, visible }` | 범례로 켜고 끌 때 | | `zoom` | `{ start, end } \| null` | 확대 범위가 바뀔 때 (x 인덱스) | | `ready` | `Chart` | 만든 직후 한 번 (옵션 `onReady` 와 같음) | | `render` | `Chart` | 다시 그릴 때마다 | | `destroy` | - | `destroy()` 할 때 | ## ChartPoint ```js { seriesIndex: 0, seriesName: '주문', index: 4, // x 순서 x: '금', // 라벨, 숫자 또는 날짜 y: 92, percent?: 31.5, // 파이·도넛·100% 누적일 때 비율 color: '#3182f6', } ``` --- # 옵션 > createChart 에 넘기는 모든 옵션. Source: https://82chart.com/docs/options/ `data` 말고는 모두 선택입니다. 아무것도 주지 않으면 카드 모양의 선 차트, 문서 언어(없으면 한국어), 페이지를 따라가는 테마로 그립니다. ## 종류와 데이터 | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `type` | 'line' | `'line' \| 'area' \| 'bar' \| 'pie' \| 'donut' \| 'scatter' \| 'radar' \| 'gauge'` | | `data` | (필수) | `{ labels?, series }` — [데이터 형식](https://82chart.com/docs/data-format/) | ## 카드 머리글 | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `eyebrow` | - | 제목 위 작은 글 (대문자로 표시) | | `title` | - | 제목. 요약·표 제목·파일 이름에도 쓰임 | | `subtitle` | - | 제목 아래 회색 글 | | `value` | - | 큰 대표 숫자. 숫자면 쉼표·`prefix`·`unit` 을 붙임 | | `delta` | - | 증감 배지. 숫자면 퍼센트, 문자열이면 그대로 | | `deltaUpIsGood` | true | 오르면 좋은 지표인지 | | `deltaColors` | 'semantic' | `'semantic'`(좋음 초록·나쁨 빨강) 또는 `'korea'`(오름 빨강 ▲·내림 파랑 ▼) | | `deltaLabel` | - | 배지 옆 글, 예: '전월 대비' | | `footnote` | - | 차트 아래 작은 글, 예: '단위: 백만원' | | `description` | - | 스크린리더가 자동 요약 앞에 읽는 설명 | | `skin` | 'card' | `'card' \| 'plain' \| 'sparkline'` | ## 크기 | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `width` | 부모 폭 | 부모 폭을 알 수 없을 때 쓰는 폭(px) | | `height` | 종류별 자동 | 그림 높이(px) 또는 `'auto'`(폭 × `aspectRatio`) | | `aspectRatio` | 0.5625 | `height: 'auto'` 일 때 높이 ÷ 폭 | ## 모양 | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `horizontal` | false | 가로 막대 | | `stacked` | false | `true` 또는 `'percent'`(100% 누적) | | `smooth` | false | `curve: 'smooth'` 와 같음 | | `curve` | 'straight' | `'straight' \| 'smooth' \| 'step'` | | `nulls` | 'gap' | `'gap' \| 'connect' \| 'zero'` | | `points` | 'auto' | 점 표시 (auto: 30개 이하) | | `pointShape` | 'circle' | `'circle' \| 'square' \| 'diamond'` | | `lastPoint` | 선 하나면 true | 마지막 점 강조 | | `barRadius` | 6 | 막대 값 쪽 끝 둥글기(px) | | `barWidth` | 0.82 / 0.78 | 칸 중 막대 묶음이 차지하는 비율 | | `gradient` | true | 막대·영역의 옅은 그라디언트 | | `stackTotals` | false | 쌓은 막대 위 합계 | | `dataLabels` | false | `true \| 'end'`: 막대 끝·선 끝에만 값 | | `otherThreshold` | 0.02 | 파이·도넛에서 이보다 작은 조각은 "기타" | | `startAngle` | 0 | 파이·도넛 시작 각도(도, 0 = 12시) | | `donutHole` | 0.62 | 도넛 구멍 크기(0–1) | | `donutLabel` | 합계 | 도넛 가운데 글, `false` 면 없음 | | `gauge` | - | `{ min, max, bands: [{ to, color }], label }` | ## 축, 범례, 툴팁 | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `xAxis / yAxis` | - | `{ title, min, max, format, ticks, step, integer, reverse, grid, type, beginAtZero, labels }` — 아래 표 | | `y2Axis` | - | 오른쪽 y축. 시리즈에 `axis: 'right'` 가 있을 때만 생김. 같은 옵션 + `unit`, `prefix` | | `legend` | 시리즈 2개 이상이면 위 | `boolean \| 'top' \| 'bottom' \| 'right'` | | `tooltip` | true | `boolean \| { format(value, ctx) }` | ## 숫자와 날짜 | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `numberFormat` | ko: 'korean', 그 외 'compact' | `'korean' \| 'compact' \| 'comma' \| 'percent' \| (v) => string` | | `scale` | - | 축 눈금을 이 수로 나눔 (1000000 + 백만원) | | `unit` | - | 값 뒤 단위 | | `prefix` | - | 값 앞 기호 | | `decimals` | 최대 2 | 툴팁·라벨 소수 자리 | | `dateFormat` | 'auto' | 날짜 패턴, 예: `'M월'`, `'YYYY.MM.DD'` | | `timeZone` | 실행 환경 | 날짜를 읽고 적는 IANA 시간대, 예: `'Asia/Seoul'` (UTC 서버의 SSR 에 필요) | ## 색과 언어 | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `colors` | - | 시리즈 순서대로 색 | | `palette` | 'default' | `'default' \| 'korea' \| 'mono'` | | `negativeColor` | - | 0보다 작은 막대 색 | | `theme` | 'auto' | `'light' \| 'dark' \| 'auto'` | | `language` | 문서 lang, 없으면 ko | `'ko' \| 'en' \| 'ja' \| 'zh'` (`registerLocale` 로 추가) | | `locale` | - | 화면 문구 일부만 바꾸기 | ## 기능 | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `animation` | true | 처음 그릴 때 애니메이션 (동작 줄이기 설정이면 꺼짐) | | `zoom` | false | 확대·이동 | | `downsample` | 2,000개 넘으면 자동 | 선·영역의 점을 LTTB 로 이만큼 줄임. `false` 면 모두 그림. CSV·표는 원본 | | `annotations` | - | `{ x, y, bands }` | | `exporting` | true | 내려받기 단추와 ⋯ 메뉴 | | `table` | false | 데이터 표를 보이게 | | `noData` | 언어별 문구 | 그릴 값이 없을 때 문구 | | `group` | - | 같은 이름끼리 크로스헤어 공유 | | `loading` | false | 불러오는 중 자리 표시 | ## 콜백과 기타 | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `onClick` | - | `(point: ChartPoint) => void` | | `onReady` | - | `(chart) => void` — 만든 직후 한 번 | | `telemetry` | true | 하루 한 번 도메인과 버전만 82chart.com 에 알림 (데이터는 보내지 않음). `false` 로 끔 | | `framework` | - | 래퍼가 채우는 값 | ## xAxis · yAxis · y2Axis | 옵션 | 기본값 | 설명 | | --- | --- | --- | | `title` | - | 축 제목 | | `min / max` | 자동 | 범위 | | `format` | numberFormat | 눈금 글자 (같은 값 또는 함수; 카테고리 축은 라벨을 받음) | | `ticks` | 자동 | 눈금 개수 대략 (근처의 깔끔한 수를 고름) | | `grid` | true | 격자선 | | `type` | 자동 | `'category' \| 'linear' \| 'time'` | | `beginAtZero` | 막대·영역 true | 0에서 시작 | | `labels` | true | 눈금 글자 표시 | | `integer` | false | 정수 눈금만 (건수·인원) | | `step` | 자동 | 눈금 간격 고정, 예: 10 | | `reverse` | false | y축: 작은 값이 위로(순위). 가로 막대: 카테고리 순서 뒤집기 | | `unit / prefix` | - | `y2Axis` 전용: 오른쪽 축과 그 시리즈의 단위 | > 오른쪽 y축(`y2Axis`)은 시리즈에 `axis: 'right'` 를 줄 때만 생깁니다. 주의할 점은 [콤보 차트](https://82chart.com/docs/combo/)에 있습니다. --- # To●●t UI Chart에서 옮기기 > 2026년 9월 보관(유지보수 종료)된 To●●t UI Chart를 82chart로 바꾸는 순서. Source: https://82chart.com/docs/migrate-toast-ui-chart/ NHN의 To●●t UI Chart 저장소는 2026년 9월 보관 처리되어 더 이상 고쳐지지 않습니다. 82chart는 같은 자리(한국어 화면, 무료, 의존성 없음)를 목표로 하고, 데이터 모양도 비슷해서 대부분 이름만 바꾸면 됩니다. ## 바꾸기 전 ```html <link rel="stylesheet" href="https://uicdn.toast.com/chart/latest/to●●tui-chart.min.css"> <script src="https://uicdn.toast.com/chart/latest/to●●tui-chart.min.js"></script> <div id="chart"></div> <script> // columnChart = vertical bars (barChart is horizontal there) const chart = to●●tui.Chart.columnChart({ el: document.getElementById('chart'), data: { categories: ['1월', '2월', '3월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000] }], }, options: { chart: { title: '월별 매출', width: 600, height: 400 }, yAxis: { title: '원' } }, }); </script> ``` ## 바꾼 뒤 ```html <link rel="stylesheet" href="https://82chart.com/cdn/v0.2.0/82chart.css"> <script src="https://82chart.com/cdn/v0.2.0/82chart.min.js"></script> <div id="chart"></div> <script> const chart = Chart82.createChart('#chart', { type: 'bar', title: '월별 매출', unit: '원', height: 400, data: { labels: ['1월', '2월', '3월'], series: [{ name: '매출', data: [32000000, 41000000, 38500000] }], }, }); </script> ``` ## 이름 대응표 | To●●t UI Chart | 82chart | | --- | --- | | `data.categories` | `data.labels` | | `data.series[].name / data` | 그대로 `data.series[].name / data` | | `columnChart` | `type: 'bar'` | | `barChart` (가로) | `type: 'bar', horizontal: true` | | `lineChart` / `areaChart` | `type: 'line'` / `'area'` | | `pieChart` (+ `series.radiusRange` 로 도넛) | `type: 'pie'` / `'donut'` | | `scatterChart` / `bubbleChart` | `type: 'scatter'` (+ 점의 `r`) | | `radarChart`, `gaugeChart` | `type: 'radar'`, `'gauge'` | | `columnLineChart` | `type: 'bar'` + 선 시리즈에 `type: 'line'` ([콤보](https://82chart.com/docs/combo/)) | | `options.series.stack` | `stacked: true` 또는 `'percent'` | | `options.series.zoomable` | `zoom: true` | | `options.exportMenu` | `exporting` (기본 켜짐) | | `options.chart.title` / `width` / `height` | `title` / 부모 폭 / `height` | | `options.theme` | [CSS 변수 `--c82-*`](https://82chart.com/docs/theme-colors/) | | `chart.setData(data)` | `chart.update(data)` | | `chart.on('clickSeries', fn)` | `chart.on('click', fn)` | - 히트맵, 트리맵, 박스플롯, 불릿, 방사형 막대, 겹친 파이는 아직 없습니다. 히트맵은 다음 버전에 넣을 예정입니다. - 82chart는 폭을 부모에 맞춰 자동으로 바꾸므로 `width` 를 주지 않아도 됩니다. - 한국어 페이지에서 축은 기본이 만·억 단위입니다. 보고서처럼 쉼표 숫자를 원하면 `numberFormat: 'comma'`. --- # Apex●●●rts·Chart.●s에서 옮기기 > 라이선스가 바뀐 Apex●●●rts, 플러그인이 필요한 Chart.●s 를 바꿀 때. Source: https://82chart.com/docs/migrate-apexcharts-chartjs/ Apex●●●rts는 5.0부터 MIT가 아니며(연 매출 200만 달러 이상은 유료), 6.5부터는 키가 없으면 워터마크가 붙습니다. Chart.●s는 무료지만 확대·주석·날짜 축에 별도 플러그인과 어댑터가 필요합니다. 82chart는 이것들이 모두 기본으로 들어 있고 MIT입니다. ## Apex●●●rts ```js const chart = new Apex●●●rts(document.querySelector('#chart'), { chart: { type: 'line', height: 350, zoom: { enabled: true } }, series: [{ name: 'Visitors', data: [120, 180, 150, 220] }], xaxis: { categories: ['Mon', 'Tue', 'Wed', 'Thu'] }, yaxis: { labels: { formatter: (v) => v.toLocaleString() } }, }); chart.render(); ``` ```js const chart = createChart('#chart', { type: 'line', height: 350, zoom: true, numberFormat: 'comma', data: { labels: ['Mon', 'Tue', 'Wed', 'Thu'], series: [{ name: 'Visitors', data: [120, 180, 150, 220] }], }, }); ``` | Apex●●●rts | 82chart | | --- | --- | | `chart.type` | `type` | | `xaxis.categories` | `data.labels` | | `series: [{ name, data }]` | `data.series: [{ name, data }]` | | `chart.stacked`, `stackType: '100%'` | `stacked: true`, `'percent'` | | `plotOptions.bar.horizontal` | `horizontal: true` | | `chart.zoom.enabled` | `zoom: true` | | `annotations.yaxis / xaxis` | `annotations.y / x / bands` | | `yaxis.labels.formatter` | `yAxis.format` 또는 `numberFormat` | | `chart.toolbar.export` | `exporting` | | `chart.updateSeries()` | `chart.update(data)` | ## Chart.●s ```js // 전 new Chart(document.getElementById('c'), { type: 'bar', data: { labels: ['1월', '2월', '3월'], datasets: [{ label: '매출', data: [12, 19, 14] }] }, }); // 후 (canvas 대신 div) createChart('#c', { type: 'bar', data: { labels: ['1월', '2월', '3월'], series: [{ name: '매출', data: [12, 19, 14] }] }, }); ``` | Chart.●s | 82chart | | --- | --- | | `<canvas>` | 아무 `<div>` (SVG로 그림) | | `datasets[].label` | `series[].name` | | `type: 'doughnut'` | `type: 'donut'` | | `type: 'bubble'` | `type: 'scatter'` + 점의 `r` | | `options.indexAxis: 'y'` | `horizontal: true` | | `scales.x.stacked` / `scales.y.stacked` | `stacked: true` | | 줌 플러그인 | `zoom: true` | | 주석 플러그인 | `annotations` | | 날짜 어댑터(date-fns·moment 등) | 필요 없음 — [날짜 축](https://82chart.com/docs/data-format/) | | `chart.data = …; chart.update()` | `chart.update(data)` | > Chart.●s는 canvas 라서 수만 개의 점도 빠릅니다. 82chart는 수천 개까지 SVG로 그리며, 그보다 많은 점을 위한 Canvas 렌더러는 이후 버전에 넣을 예정입니다. --- # 자주 묻는 질문 > 라이선스, 브라우저, 크기, 만·억 단위, 이중 축, 많은 점. Source: https://82chart.com/docs/faq/ ## 회사 서비스에 무료로 써도 되나요? 네. MIT 라이선스라 상업용 사이트, 사내 시스템, 납품하는 프로젝트 어디에나 무료입니다. 라이선스 키, 워터마크, 매출 기준, 유료 등급이 없습니다. 라이선스 문구만 함께 두면 됩니다. ## 인터넷 익스플로러(IE)에서도 되나요? 아니요. 최신 Chrome, Edge, Safari, Firefox, 삼성 인터넷과 그 모바일 브라우저를 지원합니다. IE11은 마이크로소프트도 지원을 끝냈고, 82chart가 쓰는 SVG·ResizeObserver 기능이 없습니다. ## 파일 크기는 얼마나 되나요? `82chart.min.js` 가 gzip 으로 약 30KB, CSS가 약 3KB입니다. 의존성이 없어서 날짜 라이브러리나 jQuery를 따로 받지 않습니다. ## 축에 만·억 단위를 쓰려면 무엇을 해야 하나요? 아무것도 하지 않아도 됩니다. 페이지가 한국어(`<html lang="ko">`)이거나 `language: 'ko'` 면 축은 1.2만·3.4억, 툴팁은 쉼표가 든 전체 숫자입니다. 보고서처럼 "단위: 백만원"을 쓰려면 `scale: 1000000, unit: '백만원'` 입니다. ## 이중 y축이 있나요? 단위가 다른 두 값(매출과 주문 수)을 한 그림에 겹치면, 두 축의 범위를 어떻게 잡느냐에 따라 같은 데이터가 "함께 오른다"로도 "엇갈린다"로도 보입니다. 차트가 가장 자주 잘못 읽히는 경우라서 0.2.0 부터 **직접 켜는 옵션**으로만 넣었습니다. 시리즈에 `axis: 'right'` 를 주면 오른쪽 축이 생기고, `y2Axis` 로 단위와 제목을 정합니다([콤보 차트](https://82chart.com/docs/combo/)). 비교가 목적이면 같은 x를 공유하는 차트 두 개를 위아래로 놓고 `group` 으로 크로스헤어를 묶는 방법이 더 정직합니다. ## 점이 몇 개까지 괜찮나요? 수천 개까지는 SVG로 부드럽게 그립니다. 선·영역은 2,000개가 넘으면 LTTB 로 그림 폭만큼 줄여 그리므로 수만 개도 가볍습니다([많은 점](https://82chart.com/docs/large-data/), CSV·표는 원본 그대로). 막대 수천 개나 수십만 개 이상은 미리 묶어서 넘기세요. ## 주식 캔들 차트도 있나요? 코어에는 없습니다. 캔들과 거래량, 이동평균 같은 지표는 코어 위에 얹는 별도 패키지(`@82chart/stock`)로 준비하고 있습니다. ## 82chart가 내 데이터를 어딘가로 보내나요? 아니요. 차트 데이터와 방문자 정보는 보내지 않습니다. 하루 한 번 차트가 실행된 도메인과 버전만 82chart.com에 알리며, `telemetry: false` 로 끌 수 있습니다. localhost, 사내 주소, IP 주소에서는 보내지 않습니다. ## To●●t UI Chart 대신 쓸 수 있나요? 네. To●●t UI Chart 저장소는 2026년 9월에 보관(archived) 처리되어 더 이상 고쳐지지 않습니다. 82chart는 같은 한국형 쓰임새(만·억 단위, 한글 라벨, 관리자 대시보드)를 목표로 하고, 옵션 대응표는 [To●●t UI Chart에서 옮기기](https://82chart.com/docs/migrate-toast-ui-chart/)에 있습니다. ## Apex●●●rts·Chart.●s와 무엇이 다른가요? Apex●●●rts는 5.0부터 MIT가 아닌 라이선스로 바뀌었고, Chart.●s는 확대나 주석을 별도 플러그인으로 붙여야 합니다. 82chart는 확대, 크로스헤어, 주석, PNG·SVG·CSV 내보내기, 만·억 단위가 MIT 코어에 모두 들어 있습니다. 자세한 비교는 [비교 페이지](https://82chart.com/compare/)에 있습니다.