apexcharts-skill 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SKILL.md CHANGED
@@ -5,7 +5,7 @@ description: >
5
5
  Use when the user asks to create, configure, or troubleshoot any chart using ApexCharts
6
6
  (line, area, bar, pie, donut, radialBar, scatter, bubble, heatmap, candlestick, boxPlot,
7
7
  violin, histogram, radar, polarArea, rangeBar, rangeArea, treemap, funnel, pyramid, gauge,
8
- sunburst, unit, waffle, waterfall, dumbbell, streamgraph, raincloud). Covers
8
+ sunburst, icicle, unit, waffle, waterfall, dumbbell, streamgraph, raincloud). Covers
9
9
  correct data formats, lifecycle, formatters, tree-shaking, SSR, and the feature
10
10
  platform (plugins, canvas renderer, custom series, small multiples, undo/redo,
11
11
  shareable views, themes, crossfilter, annotation authoring, storyboard, streaming,
@@ -15,8 +15,8 @@ description: >
15
15
  (`react-apexcharts`, `vue3-apexcharts`, `ng-apexcharts`) over the core API.
16
16
  metadata:
17
17
  author: ApexCharts
18
- version: "3.0.0"
19
- library_version: "7.1.0"
18
+ version: "3.1.0"
19
+ library_version: "7.6.1"
20
20
  category: data-visualization
21
21
  tags: [charts, visualization, javascript, typescript, svg, apexcharts]
22
22
  docs: https://apexcharts.com/docs/
@@ -58,13 +58,13 @@ metadata:
58
58
  > | Rewind (undo / redo) | `apexcharts/features/history` | 3.2 KB |
59
59
  > | Context menu | `apexcharts/features/context-menu` | 2.3 KB |
60
60
  >
61
- > The `raincloud` chart type (7.1) is Tier 2 on the same terms: `import 'apexcharts/raincloud'`. Storyboard registers Perspectives, so importing both costs no more than Storyboard alone. **Nothing fails silently**: each warns in the console when its config is present but the module is not, and degrades where it can (a trellis renders as one chart; `renderer: 'canvas'` falls back to SVG). Default bundle: 291,654 B gzipped in 6.10, 252,005 B in 7.0, 264,326 B in 7.1.
61
+ > The `raincloud` chart type (7.1) is Tier 2 on the same terms: `import 'apexcharts/raincloud'`. Storyboard registers Perspectives, so importing both costs no more than Storyboard alone. **Nothing fails silently**: each warns in the console when its config is present but the module is not, and degrades where it can (a trellis renders as one chart; `renderer: 'canvas'` falls back to SVG). Default bundle: 291,654 B gzipped in 6.10, 252,005 B in 7.0, 264,326 B in 7.1, about 270 KB in 7.6.
62
62
  >
63
- > Everything else stays in the default bundle: **all chart types except raincloud**, axes, tooltips, legend, toolbar, exports, annotations, keyboard navigation, stats, morph, drilldown, themes (Facet), plugins (Weave) and custom series (Marks). See `references/tree-shaking.md` for the full tier table and `references/feature-platform.md` for each feature's API.
63
+ > Everything else stays in the default bundle: **all chart types except `raincloud` and `icicle`**, axes, tooltips, legend, toolbar, exports, annotations, keyboard navigation, stats, morph, drilldown, themes (Facet), plugins (Weave) and custom series (Marks). See `references/tree-shaking.md` for the full tier table and `references/feature-platform.md` for each feature's API.
64
64
  >
65
- > **Other v7.0 breaking changes.** (1) `plotOptions.bar.borderRadiusWhenStacked` is **removed**; corner ownership now follows the stack's outer edge automatically. An unknown option is ignored, so leaving it in a config is harmless but does nothing. (2) `dataLabels.animate.enabled` now defaults to `true` (bar/column only): labels ride to their new position on a data update instead of snapping. Set `dataLabels: { animate: { enabled: false } }` for the old behavior. Everything else from v5 and v6 keeps working unchanged.
65
+ > **Other v7.0 breaking changes.** (1) `plotOptions.bar.borderRadiusWhenStacked` was dropped as dead config in v7.0 and is a **working option again in v7.4**: `'all'` (the default) rounds both stack ends, `'last'` rounds only the far end and leaves the baseline square. Do not delete it from a config. (2) `dataLabels.animate.enabled` now defaults to `true` (bar/column only): labels ride to their new position on a data update instead of snapping. Set `dataLabels: { animate: { enabled: false } }` for the old behavior. Everything else from v5 and v6 keeps working unchanged.
66
66
  >
67
- > **Behaviors that are on by default** (both respect `prefers-reduced-motion`): data updates that add/remove points animate coherently, and mobile pinch-zoom / two-finger pan gestures are enabled.
67
+ > **Behaviors that are on by default** (both respect `prefers-reduced-motion`): data updates that add/remove points animate coherently, and one-finger kinetic pan is enabled (`chart.pan.inertia`). The two-finger gestures, pinch-zoom and two-finger pan, sit behind `chart.zoom.pinch`, which defaults to `'auto'`: **on only where the toolbar reset button is present**, so an unintended zoom is always undoable. `chart.zoom.allowMouseWheelZoom` is gated the same way. Set `pinch: true` to force the gestures on a toolbar-less chart, or see `chart.zoom.resetControl` (v7.4) for the other way to satisfy the gate.
68
68
  >
69
69
  > **Premium features and licensing.** Eight features are premium: `trellis` (small multiples, 7.0), `storyboard`, `link` (crossfilter / linked views), `ink` (annotation authoring), `measure`, `context-menu`, `perspectives` (shareable views), and `history` (undo/redo). Two chart types are premium: `unit` / `waffle`, and `raincloud` (7.1). These still function fully, but render an `APEXCHARTS` trial watermark until an entitled license is set via `ApexCharts.setLicense(key)` (static) or per-chart `chart.license`. They require a `premium` or `enterprise` plan; a `pro` key or the free tier keeps the watermark. Every other chart type and free module is never gated. Note that premium and Tier 2 are independent axes: `waterfall`, `dumbbell` and `streamgraph` are free *and* in the default bundle, while `trellis` is both premium *and* an explicit import. See the Licensing section in `references/feature-platform.md`.
70
70
 
@@ -81,7 +81,7 @@ metadata:
81
81
  9. **For mixed/combo charts**, set `type` on each individual series object, not just on `chart.type`.
82
82
  10. **RadialBar values must be 0–100** (they represent percentages).
83
83
  11. **Color hex values must include the `#` prefix** (e.g., `'#FF5733'`, not `'FF5733'`).
84
- 12. **Nine features are NOT in the v7 default bundle.** Before writing config for `trellis`, `storyboard`, `perspectives`, `ink`, `renderer: 'canvas'`, `link`, `measure`, `history` or the context menu, add its `apexcharts/features/*` import. Same for the `raincloud` chart type. Full-bundle users need this too, not just tree-shakers. See the box above.
84
+ 12. **Nine features are NOT in the v7 default bundle.** Before writing config for `trellis`, `storyboard`, `perspectives`, `ink`, `renderer: 'canvas'`, `link`, `measure`, `history` or the context menu, add its `apexcharts/features/*` import. Same for the `raincloud` and `icicle` chart types, which are not in the default bundle either. Full-bundle users need this too, not just tree-shakers. See the box above.
85
85
  13. **Tree-shaking**: importing `apexcharts/core` gives you a bare class, and you must also import chart-type entries and feature entries separately.
86
86
  14. **First-class aliases**: `funnel` and `pyramid` render through the bar engine; `gauge` renders through radialBar. Use them as `chart.type` directly (no `plotOptions.bar.isFunnel` needed). They are covered by the `apexcharts/bar` and `apexcharts/radialBar` tree-shaking entries respectively.
87
87
  15. **`violin` (statistical)** uses a per-point density profile: `data: [{ x, y: { density: [[value, weight], ...], points?: [number] } }]`, not a plain number. Since v6.9 a datum may instead supply only raw observations (`points: [number]`, no `y`) and the library derives the density via KDE (needs `apexcharts/features/stats` when tree-shaking).
@@ -139,7 +139,9 @@ These use a **flat number array** for `series`, NOT the object format:
139
139
 
140
140
  **Unit / Waffle *(v6.6, premium)*** render one mark per unit of value (dot clusters, pictograms, waffles, beeswarms). Same flat-array + `labels` shape as pie; layout is chosen through `plotOptions.unit.layout`. A per-mark object form (`series: [{ name, data: [{ value, x, z, name, fillColor, id }] }]`) gives each mark its own color, position, size, and tooltip. `waffle` is a preset alias of `unit` (grid layout). Both render an `APEXCHARTS` watermark until an entitled license is set. See `references/circular-charts.md`.
141
141
 
142
- ### Hierarchical Charts (sunburst)
142
+ ### Hierarchical Charts (sunburst, icicle)
143
+
144
+ Both take the same tree, and the choice between them is a layout one: a sunburst spends the plot radially, an icicle spends it along a straight axis.
143
145
 
144
146
  **Sunburst *(v6.7, free)*** draws tree-structured data as concentric rings (a nested pie / donut). It uses the axis-style `[{ data: [...] }]` wrapper, but each datum is an `{ x, y, children }` node nested to any depth:
145
147
 
@@ -160,12 +162,26 @@ These use a **flat number array** for `series`, NOT the object format:
160
162
 
161
163
  Configure the centre hole, corner rounding, and inter-arc gap through `plotOptions.sunburst` (see `references/circular-charts.md`).
162
164
 
165
+ **Icicle *(v7.6, free, opt-in import)*** is the same partition in cartesian coordinates: one band per depth level, each child sized inside its parent's extent. Same data shape as the sunburst, and it also reads an existing `drilldown` config as plain data (no drilldown runtime needed). Labels stay horizontal at every depth and same-depth siblings line up across branches, which is what makes it the better pick for deep trees, call stacks and path funnels.
166
+
167
+ ```js
168
+ import ApexCharts from 'apexcharts/icicle' // NOT in the default bundle
169
+
170
+ {
171
+ chart: { type: 'icicle' },
172
+ series: [{ data: [ /* the same { x, y, children } tree as above */ ] }],
173
+ plotOptions: { icicle: { direction: 'up' } } // flame-graph orientation
174
+ }
175
+ ```
176
+
177
+ A branch may omit its own `y` and be the sum of its children *(fixed in v7.6 for both partition types)*. Configure depth direction, band thickness, leaf behaviour and zoom through `plotOptions.icicle` (see `references/grid-charts.md`).
178
+
163
179
  ---
164
180
 
165
181
  ## 3. Package / Module Map
166
182
 
167
183
  ```
168
- # Default bundle (every chart type except raincloud, plus the Tier 1 features)
184
+ # Default bundle (every chart type except raincloud and icicle, plus the Tier 1 features)
169
185
  import ApexCharts from 'apexcharts'
170
186
 
171
187
  # Bare core (no chart types, no optional features — must register manually)
@@ -196,6 +212,7 @@ import ApexCharts from 'apexcharts/radar' # radar only
196
212
  import ApexCharts from 'apexcharts/heatmap' # heatmap only
197
213
  import ApexCharts from 'apexcharts/treemap' # treemap only
198
214
  import ApexCharts from 'apexcharts/sunburst' # sunburst (v6.7, hierarchical)
215
+ import ApexCharts from 'apexcharts/icicle' # icicle (v7.6, hierarchical): opt-in, NOT in the default bundle
199
216
  import ApexCharts from 'apexcharts/unit' # unit + waffle (v6.6, premium)
200
217
  # funnel + pyramid render through the bar engine; use apexcharts/bar
201
218
 
@@ -241,7 +258,7 @@ import ApexCharts from 'apexcharts/ssr' # Node.js: renderToString, renderToHT
241
258
  import ApexCharts from 'apexcharts/client' # Browser: explicit client import for hydration
242
259
  ```
243
260
 
244
- **Note (v7):** `apexcharts` gives you every chart type except `raincloud`, plus the Tier 1 features. The nine Tier 2 features and `raincloud` need their own import **whatever entry point you started from**. `apexcharts/features/all` is the Tier 1 set, so it is not a shortcut past this. Tree-shaking entries (`/core`, `/line`, etc.) are for reducing bundle size below the default.
261
+ **Note (v7):** `apexcharts` gives you every chart type except `raincloud` and `icicle`, plus the Tier 1 features. The nine Tier 2 features, `raincloud` and `icicle` need their own import **whatever entry point you started from**. `apexcharts/features/all` is the Tier 1 set, so it is not a shortcut past this. Tree-shaking entries (`/core`, `/line`, etc.) are for reducing bundle size below the default.
245
262
 
246
263
  **Note (v6.9):** ApexCharts is no longer dependency-free: it depends on `apex-commons` at runtime. npm resolves it automatically and the browser (script-tag) bundles inline it, so no action is needed; it only matters for tooling that assumed zero dependencies.
247
264
 
@@ -416,6 +433,12 @@ Also: do NOT use `xaxis.categories` when your data already has `{ x, y }` format
416
433
  { tooltip: { shared: false, intersect: true } }
417
434
  ```
418
435
 
436
+ **Reaching content inside a `custom` tooltip *(v7.6)***: a tooltip closes as the pointer leaves the data point, so a link or button returned by `custom` can never be clicked. `tooltip.interactive: true` holds it open while the pointer is over it. It is off by default, and while it is on `followCursor` is **ignored** (a tooltip that trails the cursor retreats from every approach); the configured `followCursor` value is left alone and applies again the moment `interactive` goes back off.
437
+
438
+ ```js
439
+ { tooltip: { interactive: true, custom: ({ seriesIndex }) => `<a href="/detail/${seriesIndex}">Open</a>` } }
440
+ ```
441
+
419
442
  ### Pitfall 7: Wrong formatter signatures
420
443
 
421
444
  ❌ **WRONG** — using y-axis formatter signature for tooltip:
@@ -551,26 +574,20 @@ import ApexCharts from 'apexcharts'
551
574
  import 'apexcharts/features/trellis'
552
575
  ```
553
576
 
554
- Same for `storyboard`, `perspectives`, `ink`, `renderer: 'canvas'`, `link`, `measure`, `history`, `contextMenu`, and the `raincloud` chart type. Check the console: each one names both import routes when its config is present without it.
577
+ Same for `storyboard`, `perspectives`, `ink`, `renderer: 'canvas'`, `link`, `measure`, `history`, `contextMenu`, and the `raincloud` chart type. Check the console: each one names both import routes when its config is present without it. `icicle` is the exception to the "warns and degrades" rule: an unregistered chart type **throws**, and the thrown message names both routes.
555
578
 
556
579
  ### Pitfall 15: SSR — wrong import path
557
580
 
558
- ❌ **WRONG** — using browser bundle in Node.js:
559
- ```js
560
- // On server:
561
- import ApexCharts from 'apexcharts'
562
- await ApexCharts.renderToString(options) // renderToString doesn't exist!
563
- ```
564
-
565
- ✅ **CORRECT** — use SSR entry point:
581
+ `apexcharts` resolves by environment: the `"."` export's `node` condition points at the SSR bundle, so `import ApexCharts from 'apexcharts'` on a server **does** carry `renderToString` / `renderToHTML`. Import `apexcharts/ssr` explicitly anyway, so the intent survives a bundler that resolves the `browser` condition while targeting Node:
566
582
  ```js
567
583
  // On server:
568
584
  import ApexCharts from 'apexcharts/ssr'
569
585
  const svg = await ApexCharts.renderToString(options)
570
586
 
571
- // On client (for hydration):
587
+ // On client (for hydration): target the wrapper renderToHTML emitted,
588
+ // which carries data-apexcharts-hydrate and NO id.
572
589
  import ApexCharts from 'apexcharts' // or 'apexcharts/client'
573
- ApexCharts.hydrate(document.querySelector('#chart'))
590
+ ApexCharts.hydrateAll() // or hydrate(document.querySelector('[data-apexcharts-hydrate]'))
574
591
  ```
575
592
 
576
593
  ### Pitfall 16: updateOptions vs updateSeries vs appendData
@@ -729,7 +746,7 @@ Every feature below is off by default in config terms. The **Tier** column is ab
729
746
 
730
747
  | Feature | Tier | Import | One-liner |
731
748
  |---|---|---|---|
732
- | **Weave** (plugins) | 1 | `apexcharts/features/weave` | Publish reusable chart plugins against a stable `ApexCharts.registerPlugin({ name, setup })` API; activate per chart with `plugins: [{ name }]`. |
749
+ | **Weave** (plugins) | 1 | `apexcharts/features/weave` | Publish reusable chart plugins against a stable `ApexCharts.registerPlugin({ name, setup })` API; activate per chart with `plugins: [{ name }]`. Contract is at **v6** (v7.5): `info`/`categories`/`markDerived` (v2), `reserve` (v3), `pointer` (v4), `claim`/`drawn`/`declare`/`can` (v6). Declare the **lowest** `apiVersion` you can run on and feature-detect the rest: a host older than the version you declare skips your plugin outright. |
733
750
  | **Marks** (custom series) | 1 | `apexcharts/features/marks` | `ApexCharts.registerSeriesType(name, { renderItem })` for first-class custom marks (lollipop, bullet). Baseline is `scales.y(0)`, **not** `api.zeroY`. |
734
751
  | **Facet** (themes/tokens) | 1 | `apexcharts/features/facet` | `theme: { follow: 'os', name }`, `--apx-*` CSS custom props, `ApexCharts.registerTheme`. |
735
752
  | **Drilldown** | 1 | `apexcharts/features/drilldown` | `drilldown: { enabled, series }` + a `drilldown` id on data points; `chart.drillDown()/drillUp()`. v6.9 adds line/area support, async levels (`onDrillDown` may return a Promise), a loading overlay, and the `drillDownError` event. |
@@ -759,7 +776,7 @@ For detailed chart-family-specific options, data format variants, and full worki
759
776
  | Bar, Column, Range Bar, Timeline/Gantt, Funnel, Pyramid, Waterfall, Dumbbell | `references/bar-charts.md` |
760
777
  | Candlestick, Box Plot, Violin, Histogram, Raincloud | `references/financial-charts.md` |
761
778
  | Pie, Donut, Polar Area, Radial Bar, Gauge, Sunburst, Unit, Waffle, Pictograms | `references/circular-charts.md` |
762
- | Heatmap, Treemap | `references/grid-charts.md` |
779
+ | Heatmap (incl. cell shapes), Treemap, Icicle | `references/grid-charts.md` |
763
780
  | Radar | `references/radar-charts.md` |
764
781
  | Feature platform (small multiples, plugins, canvas, undo/redo, themes, crossfilter, storyboard, ...) | `references/feature-platform.md` |
765
782
  | Bundle tiers, Tree-shaking, Bundle optimization | `references/tree-shaking.md` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apexcharts-skill",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "AI coding skill for ApexCharts.js — structured reference docs that help AI assistants (Claude, Cursor, Copilot, etc.) generate correct ApexCharts code.",
5
5
  "keywords": [
6
6
  "apexcharts",
@@ -304,7 +304,7 @@ plotOptions: {
304
304
  distributed: false, // true = different color per data point
305
305
  borderRadius: 4, // rounded corners (number or object)
306
306
  borderRadiusApplication: 'end', // 'end' | 'around'
307
- // borderRadiusWhenStacked was REMOVED in v7.0, see the note below
307
+ borderRadiusWhenStacked: 'all', // 'all' (default) | 'last'; stacked bars only, see the note below
308
308
 
309
309
  dataLabels: {
310
310
  position: 'top', // 'top' | 'center' | 'bottom'
@@ -322,7 +322,12 @@ plotOptions: {
322
322
  }
323
323
  ```
324
324
 
325
- **⚠️ `borderRadiusWhenStacked` was removed in v7.0.** Rounded corners on a stacked bar are no longer a setting: corner ownership follows the **outer edge** of the stack, which is what `'last'` approximated and what `'all'` got wrong on any stack whose last series was empty. An unknown option is ignored, so leaving it in a config is harmless but does nothing. Delete it. Related v7.0 fixes: a stacked bar's bottom cap is now the top-rounded path mirrored (caps no longer invert or pop when a series collapses and rises again), and grouped stacked charts resolve caps per group rather than chart-wide, so every group gets its own bottom and top radius.
325
+ **⚠️ `borderRadiusWhenStacked`: dropped in v7.0, restored working in v7.4.** v7.0 removed it as dead config (it had not been read for some time) and let corner ownership follow the stack's **outer edge** automatically. v7.4 made it a real option again, so at v7.4+ it is live and must not be deleted from a config:
326
+
327
+ - `'all'` (the default) keeps the v7.0 look, both ends of the stack rounded. Existing charts are unchanged.
328
+ - `'last'` rounds only the far end and leaves the baseline square.
329
+
330
+ A stack of a single data point also stopped rounding its baseline in v7.4, which on a horizontal 100% stack with one category used to round the first segment on its inner edge. Related v7.0 fixes: a stacked bar's bottom cap is now the top-rounded path mirrored (caps no longer invert or pop when a series collapses and rises again), and grouped stacked charts resolve caps per group rather than chart-wide, so every group gets its own bottom and top radius.
326
331
 
327
332
  **⚠️ `dataLabels.animate.enabled` now defaults to `true` (v7.0), bar/column only.** Labels ride to their new position on a data-change update instead of snapping there, so they reflow on the same clock as the bars, markers and axis ticks. A label that has not moved is a per-label no-op; speed and easing follow `chart.animations.dynamicAnimation`. For the pre-v7 behavior:
328
333
 
@@ -332,7 +337,7 @@ dataLabels: { animate: { enabled: false } }
332
337
 
333
338
  ### Bar Chart Race (v6.4)
334
339
 
335
- A reorder update animates into a bar chart race: when you re-sort the data and update the chart, the bars slide to their new ranks and their category labels ride along automatically (whenever `dynamicAnimation` is on). Two opt-in `dataLabels` flags complete the effect. Both are off by default and apply to **bar / column only**; their speed and easing follow `chart.animations.dynamicAnimation`.
340
+ A reorder update animates into a bar chart race: when you re-sort the data and update the chart, the bars slide to their new ranks and their category labels ride along automatically (whenever `dynamicAnimation` is on). Two `dataLabels` flags complete the effect, both **bar / column only**: `animate.enabled` is **on by default** since v7.0 (it was opt-in before), and `countUp.enabled` is off. their speed and easing follow `chart.animations.dynamicAnimation`.
336
341
 
337
342
  ```js
338
343
  const options = {
@@ -428,5 +433,5 @@ series: [{
428
433
  8. **Pre-computing the running total for a waterfall** *(v7.1)*: the series carries the **deltas**; the chart accumulates. Feeding it cumulative values draws each bar from zero to the total, which is a column chart with extra steps.
429
434
  9. **Giving a waterfall subtotal row a `y`** *(v7.1)*: `isSubtotal` / `isTotal` rows are measured for you. Supply `x` and the flag, nothing else.
430
435
  10. **Zipping dumbbell values into `y: [lo, hi]`** *(v7.1)*: that is the older `plotOptions.bar.isDumbbell` range-bar form. `chart.type: 'dumbbell'` wants one series per measure, each with plain `{ x, y }` points.
431
- 11. **Setting `borderRadiusWhenStacked`**: removed in v7.0. The option is ignored; stacked corner ownership follows the stack's outer edge automatically.
436
+ 11. **Assuming `borderRadiusWhenStacked` is still removed**: it was dead config in v7.0 through v7.3 and is a working option again from v7.4 (`'all'` default, `'last'` to leave the baseline square). Guidance written for v7.0 says to delete it; on v7.4+ that silently reverts a deliberate `'last'` to `'all'`.
432
437
  12. **Expecting data labels to snap on update** *(v7.0)*: `dataLabels.animate.enabled` now defaults to `true` on bar/column. Set it to `false` for the old behavior.
@@ -254,6 +254,25 @@ xaxis: {
254
254
  }
255
255
  ```
256
256
 
257
+ ### A way out of a zoom when the toolbar is hidden (v7.4)
258
+
259
+ Hiding the toolbar does not disable drag-to-zoom (a drag is a deliberate gesture, unlike the wheel and the pinch), so a viewer can zoom a bare chart and then have nothing on screen that puts the range back.
260
+
261
+ ```js
262
+ chart: {
263
+ toolbar: { show: false },
264
+ zoom: { enabled: true, resetControl: 'auto' } // 'auto' is the default
265
+ }
266
+ ```
267
+
268
+ While the chart **is** zoomed and nothing else can reset it, one reset control is drawn where the toolbar would have been, and it goes when the range does. `Escape` resets under the same gate, and the event is left alone so a page listening for it still hears it.
269
+
270
+ - `'auto'` (default): supply both only where there is no reset button already. A chart nobody zooms is untouched, so `toolbar: { show: false }` still means an empty chart for every viewer who does not zoom.
271
+ - `true`: also force the control on where `toolbar.tools.reset` is off, which is the same dead end as a hidden toolbar.
272
+ - `false`: turn off both the control and the key, for a page that supplies its own reset.
273
+
274
+ Requires `zoom.enabled: true`. A completed drag-zoom now also focuses the chart, so the `+`, `-` and `0` keys are reachable straight after a mouse zoom.
275
+
257
276
  ---
258
277
 
259
278
  ## Complete Working Example — Line Chart with Datetime
@@ -155,6 +155,10 @@ A sunburst draws a hierarchy as concentric rings: the first level fills a donut
155
155
 
156
156
  Colours, `stroke`, `legend`, and `title` behave as they do on pie and donut. A sunburst can also adapt an existing `drilldown` config instead of a native `children` hierarchy.
157
157
 
158
+ A branch may **omit its own `y`** and be the sum of its children. That is the documented shape, but it only began working in v7.6: before that the non-axis parser required every datum to carry `x` and `y`, so a branch with only `children` was dropped, the series came back empty, and the chart drew nothing while warning about pie data. The samples all gave their top-level nodes a `y`, which is what hid it from the moment sunburst shipped.
159
+
160
+ **The same tree in cartesian coordinates** is the `icicle` type *(v7.6)*: one band per depth level instead of concentric rings, labels horizontal at every depth, and `direction: 'up'` for flame graphs. It is an opt-in import. See `references/grid-charts.md`.
161
+
158
162
  ### Unit / Waffle (v6.6, premium)
159
163
 
160
164
  A `unit` chart renders one discrete mark for every unit of value instead of a single bar or slice, so "37 of 200" reads as a countable quantity. It is a **non-axis** chart (dispatched like pie or treemap) and is **premium**: it renders fully in trial mode with an `APEXCHARTS` watermark until an entitled license is set (see `references/feature-platform.md`). On every update each mark tweens from its old position to its new one, so re-grouping, filtering, or a changing count re-forms the marks.
@@ -7,7 +7,7 @@ ApexCharts ships an opt-in feature platform on top of the chart core. **Every v5
7
7
  Whether you need the `features/*` import depends on the feature's **tier**, and this changed in v7.0.
8
8
 
9
9
  - **Tier 1** is in the default `import ApexCharts from 'apexcharts'` bundle: `exports`, `legend`, `toolbar`, `annotations`, `keyboard`, `morph`, `drilldown`, `weave`, `marks`, `facet`, `stats`, and the v7.1 `waterfall` / `dumbbell` / `streamgraph` chart types. Import these only if you started from `apexcharts/core` or a per-type entry.
10
- - **Tier 2 is not in any bundle.** Nine features (`trellis`, `storyboard`, `perspectives`, `ink`, `renderer-canvas`, `link`, `measure`, `history`, `context-menu`) plus the `raincloud` chart type require an explicit import **whatever entry point you used**, the full bundle included.
10
+ - **Tier 2 is not in any bundle.** Nine features (`trellis`, `storyboard`, `perspectives`, `ink`, `renderer-canvas`, `link`, `measure`, `history`, `context-menu`) plus the `raincloud` (premium) and `icicle` (free) chart types require an explicit import **whatever entry point you used**, the full bundle included.
11
11
 
12
12
  ```js
13
13
  import ApexCharts from 'apexcharts'
@@ -20,11 +20,11 @@ import 'apexcharts/features/trellis' // Tier 2: required even here
20
20
 
21
21
  Nothing fails silently: each Tier 2 module warns in the console when its configuration is present but the module is not, and names both import routes. Where the chart can still draw something sensible it does and says what it did: a trellis renders as a single chart, `renderer: 'canvas'` falls back to SVG. `apexcharts/features/all` is the **Tier 1** set and is not a shortcut past this.
22
22
 
23
- Why: 24% of the 6.10.0 default bundle was licence-gated premium code that an unlicensed user could only run watermarked, and everybody downloaded it. The default bundle went 291,654 B gzipped (6.10.0) to 252,005 B (7.0.0), and 264,326 B in 7.1.0 with the three new free chart types. See `references/tree-shaking.md` for the full tier table and per-module sizes.
23
+ Why: 24% of the 6.10.0 default bundle was licence-gated premium code that an unlicensed user could only run watermarked, and everybody downloaded it. The default bundle went 291,654 B gzipped (6.10.0) to 252,005 B (7.0.0), and 264,326 B in 7.1.0 with the three new free chart types. It is about 270 KB at 7.6.1 (measured on the shipped dist with gzip -9; the upstream build report gzips at a different level and prints a few hundred bytes more). See `references/tree-shaking.md` for the full tier table and per-module sizes.
24
24
 
25
25
  ## Two v7.0 breaking changes
26
26
 
27
- 1. **`plotOptions.bar.borderRadiusWhenStacked` is removed.** Rounded corners on a stacked bar are no longer a setting: corner ownership follows the **outer edge** of the stack, which is what `'last'` approximated and what `'all'` got wrong on any stack whose last series was empty. An unknown option is ignored, so leaving it in place is harmless but does nothing.
27
+ 1. **`plotOptions.bar.borderRadiusWhenStacked` was dropped in v7.0, and came back working in v7.4.** In v7.0 it had not been read for some time and was removed as dead config, with corner ownership following the stack's **outer edge** automatically. v7.4 made it a real option again: `'all'` (the default) keeps that look with both stack ends rounded, and `'last'` rounds only the far end and leaves the baseline square. A one-point stack no longer rounds its baseline either. Treat it as live, not as legacy.
28
28
  2. **`dataLabels.animate.enabled` now defaults to `true`** (bar and column only). Labels ride to their new position on a data-change update instead of snapping there, so they reflow on the same clock as the bars, markers and axis ticks. Speed and easing follow `chart.animations.dynamicAnimation`. A label that has not moved is a per-label no-op. Set `dataLabels: { animate: { enabled: false } }` for the old behavior.
29
29
 
30
30
  ## Behaviors that are on by default
@@ -32,7 +32,7 @@ Why: 24% of the 6.10.0 default bundle was licence-gated premium code that an unl
32
32
  Carried over from v6, both respecting `prefers-reduced-motion`:
33
33
 
34
34
  1. **Coherent variable-length data transitions**: updates that add/remove data points animate as one coordinated motion instead of popping. Appended bars grow from the baseline, removed bars shrink and fade, line/area fills reshape over the union of old and new points, and markers/bubbles/axis labels ride along. Scatter and bubble now animate on dynamic updates for the first time. Disable per chart with `chart.animations.dynamicAnimation.enabled: false`; auto-skipped above `chart.animations.largeDatasetThreshold` (default 1000) and under `prefers-reduced-motion`.
35
- 2. **Native-feeling mobile gestures**: two-finger pinch-zoom, two-finger pan, and kinetic inertia after a flick, with axis rails so a vertical swipe still scrolls the page. Configure via `chart.zoom.pinch` (default `true`) and `chart.pan.inertia` (default `true`, `friction` default `0.92`).
35
+ 2. **Native-feeling mobile gestures**: two-finger pinch-zoom, two-finger pan, and kinetic inertia after a flick, with axis rails so a vertical swipe still scrolls the page. Configure via `chart.zoom.pinch` (default `'auto'`) and `chart.pan.inertia` (default `true`, `friction` default `0.92`). **`'auto'` is not the same as `true`**: it enables pinch only where the toolbar reset button is present, so a zoom is always undoable. A chart with `toolbar: { show: false }` gets no pinch zoom until `pinch: true` forces it. `chart.zoom.allowMouseWheelZoom` is gated the same way, and `chart.zoom.resetControl` (v7.4) is the other half of that bargain.
36
36
 
37
37
  Also new by default: **`render()` is idempotent**: a repeated `render()` (e.g. a framework double-invoking an effect) returns the same promise instead of building a duplicate chart. `destroy()` clears it so an instance can render fresh.
38
38
 
@@ -60,7 +60,7 @@ Introduced in **6.5.0**, tightened in **6.7.0**. Not every feature is free. Eigh
60
60
  - the `unit` / `waffle` chart type (premium since **6.6.0**, the first premium chart type)
61
61
  - the `raincloud` chart type (premium since **7.1.0**)
62
62
 
63
- **Everything else is free and never gated**: every other chart type (`sunburst`, and the v7.1 `waterfall` / `dumbbell` / `streamgraph`), and every other module (Weave, Strata / canvas, Marks, Facet / themes, Cadence, drilldown, streaming, exports, legend, toolbar, annotations, keyboard, stats).
63
+ **Everything else is free and never gated**: every other chart type (`sunburst`, the v7.1 `waterfall` / `dumbbell` / `streamgraph`, and the v7.6 `icicle`, which is an opt-in import but free), and every other module (Weave, Strata / canvas, Marks, Facet / themes, Cadence, drilldown, streaming, exports, legend, toolbar, annotations, keyboard, stats).
64
64
 
65
65
  **Premium and Tier 2 are independent axes.** Premium is about the watermark; Tier 2 is about the bundle. `renderer-canvas` is Tier 2 and free. `waterfall`, `dumbbell` and `streamgraph` are Tier 1 and free. `unit` / `waffle` is premium but Tier 1 (it is a chart type in the default bundle). `trellis` and `raincloud` are both.
66
66
 
@@ -181,13 +181,117 @@ The `setup(api)` facade:
181
181
 
182
182
  - `api.on(hook, fn)` / `api.off(hook, fn)`: hooks: `'afterParse' | 'afterScales' | 'draw' | 'afterUpdate' | 'destroy'`.
183
183
  - `api.layer(opts?)`: a plugin-owned drawing surface (`path`, `line`, `rect`, `circle`, `text`, `clear`); call it inside draw handlers only, layers are wiped each pass.
184
- - `api.scales` (readonly): `x(v)`, `y(v, axis?)`, `domainX`, `domainY(axis?)`, `gridWidth`, `gridHeight`, `ratios`.
185
- - `api.data` (readonly): `[{ name?, hidden, color?, points: [{ x, y }] }]`.
184
+ - `api.scales` (readonly): `x(v)`, `y(v, axis?)`, `domainX`, `domainY(axis?)`, `gridWidth`, `gridHeight`, `ratios`. **`null` on a non-axis chart**, so it is a fact about the chart rather than about the host and is deliberately not a capability.
185
+ - `api.data` (readonly): `[{ name?, hidden, color?, points: [{ x, y }], raw }]`.
186
186
  - `api.theme`: `{ mode, foreColor, seriesColor(i), token(name) }`.
187
187
  - `api.store`: per-instance mutable state bag.
188
188
  - `api.emit(name, detail?)`: dispatches `plugin:<name>:<event>` on the chart; `api.chart`, `api.el`, `api.name`, `api.version`, `api.options` round out the facade.
189
189
 
190
- `apiVersion` gates the contract so raw internals can keep changing safely.
190
+ ### Declare the lowest version you can run on, then feature-detect (v2+)
191
+
192
+ `apiVersion` gates the contract so raw internals can keep changing safely, but it is **not** how you ask for a newer member. A plugin declaring a version **newer than the host is skipped outright**, so declaring the latest is how one build stops working on older hosts. Declare the lowest version your plugin can actually run on, and discover anything above that:
193
+
194
+ ```js
195
+ // v6 hosts: ask by name.
196
+ if (api.can('claim')) { /* ... */ }
197
+ api.capabilities // the whole frozen list, for logging and support
198
+
199
+ // Older hosts, or a plugin that must run on both: probe the member.
200
+ if (typeof api.pointer === 'function') { /* ... */ }
201
+ ```
202
+
203
+ Each capability name is probed off the facade that was actually built, so a member that is ever made conditional drops out of the list instead of being advertised and then missing. Names are permanent once published.
204
+
205
+ The version gate was an exact-match check before v2, which would have disabled every v1 plugin on the first additive change. It is forward-compatible now: an older plugin is served, and only one needing a newer host is skipped.
206
+
207
+ ### v2: know what you are attached to
208
+
209
+ ```js
210
+ api.info // { type, axisChart, datetimeX, horizontalBars,
211
+ // dataLabels: { enabled, enabledOnSeries },
212
+ // stroke: { dashArray }, // v5
213
+ // title } // v6, '' when untitled
214
+ api.categories // display labels per x position, resolved config-first
215
+ api.data[0].raw // the caller's own data array for that series, unparsed
216
+ api.markDerived(['Revenue (forecast)']) // these series are mine, not the caller's
217
+ ```
218
+
219
+ - **`info` is how a plugin decides whether it applies at all**, instead of adding a series and letting the core warn at the user.
220
+ - **Read `categories`, never `globals.categoryLabels`.** The globals are populated after a mount and empty after an `updateSeries()`, so reading them directly gives real labels on first paint and ordinals after any update.
221
+ - **`raw` is fine to read and wrong to copy**: the three accepted data shapes are not interchangeable, so the normalised `points` are what you compute from.
222
+ - **`markDerived` is not optional if the plugin adds series.** The core cannot tell a plugin's series from the caller's, and the host uses this to keep them out of the initial-series snapshot. Without it, `resetSeries()` hands the user the plugin's computed output as if it were their own data. It is idempotent; pass `[]` when the plugin's series are gone. It is also what puts the plugin's name on those series in `drawn()`.
223
+
224
+ Also fixed in v2: every Weave plugin silently did nothing on a pie, donut or radialBar, because those hold one number per entry and the snapshot called `.map` on it. Each slice is presented as a one-point series now.
225
+
226
+ ### v3: reserve room for the plugin's own UI
227
+
228
+ ```js
229
+ api.reserve({ right: 120 }) // px; pass null (or all zeros) to give it back
230
+ ```
231
+
232
+ A plugin rendering beside the chart cannot make room for itself: the chart sizes from the element the caller handed it, so a sibling inserted there does not narrow it. Reservations are per plugin and summed, so two plugins each asking for a right-hand gutter get one each, and the total is clamped so the chart keeps at least half the container on each axis. An unchanged box does nothing, so calling it on every render is free.
233
+
234
+ ### v4: what the viewer is pointing at
235
+
236
+ ```js
237
+ const off = api.pointer((e) => {
238
+ // e.type 'enter' | 'leave' | 'select'
239
+ // e.seriesIndex / e.dataPointIndex
240
+ // e.category resolved display label ('Mar'), not an index
241
+ // e.seriesName undefined on a pie
242
+ // e.selected on 'select' only: is the point now in or out
243
+ // e.modifiers { shift, ctrl, alt, meta } (v6)
244
+ })
245
+ off() // pointer() returns its unsubscribe
246
+ ```
247
+
248
+ Forwards the chart's own `dataPointMouseEnter`, `dataPointMouseLeave` and `dataPointSelection` as one payload, so a plugin does not hit-test the SVG and then disagree with the tooltip on the same pixel. **Nothing here can intercept or cancel**: the tooltip, the selection state and the caller's own `dataPoint*` events are unaffected, and a handler that throws is contained.
249
+
250
+ `category` is the resolved label because a plugin coordinating two charts keys on the label; an index means something different on every chart. `modifiers` is always all four booleans, never partial and never undefined (all false where there was no DOM event), because a plugin reads `e.modifiers.shift` inside a viewer's click and a sometimes-missing key is how that becomes a crash.
251
+
252
+ ### v6: set a positional option for your own series (`claim`)
253
+
254
+ `stroke.dashArray` and `dataLabels.enabledOnSeries` are indexed by series position with no per-series form, so a plugin wanting one value for its own series used to write the array covering every series and put the caller's back afterwards.
255
+
256
+ ```js
257
+ const claim = api.claim('stroke.dashArray', [
258
+ { series: 'Revenue (forecast)', value: 6 },
259
+ ])
260
+ claim.update([{ series: 'Revenue (forecast)', value: 4 }])
261
+ claim.release()
262
+ ```
263
+
264
+ The host resolves a claim **where the option is read**, so nothing is written: there is nothing to restore, releasing is a deletion, and the caller's own `updateOptions` composes with the claim instead of reverting it. Claims resolve in order, so two plugins on one series produce a defined winner rather than whichever ran last.
265
+
266
+ - **Name the series rather than its position where you can.** A name is resolved each time the option is read, so the claim follows that series when the caller adds, removes or reorders others.
267
+ - **Claimable options are an allowlist** (`stroke.dashArray`, `dataLabels.enabledOnSeries` to begin with), each declaring its value type so a wrong one is dropped rather than drawn. An option that is not on the list returns **`null` rather than throwing**, so a plugin built against a newer host degrades instead of breaking. Check the return.
268
+ - Every claim is released on teardown, on destroy, and if the host disables the plugin after repeated errors, so none can outlive the plugin holding it.
269
+
270
+ `api.info.stroke` (v5) exists for the older read-and-restore pattern and reports what is configured, a scalar or one entry per series, copied on the way out. Prefer `claim()` where the host has it: the restore is stale the moment the caller calls `updateOptions`, it leaks if the plugin throws in between, and two plugins doing it at once fight.
271
+
272
+ ### v6: an inventory of what is on the chart (`drawn` / `declare`)
273
+
274
+ ```js
275
+ api.drawn() // [{ id, kind: 'series' | 'annotation' | 'overlay', label, owner, visible }, ...]
276
+
277
+ api.on('draw', () => {
278
+ api.declare({ id: 'trend-1', label: 'Trend' }) // declare from the draw handler
279
+ })
280
+ ```
281
+
282
+ `drawn()` reports the series, the caller's annotations and whatever plugins have declared. An ink stroke a viewer drew is an annotation too, but reports `owner: 'ink'` rather than `'core'` (fixed in v7.6.1): crediting a viewer's note to the caller is the one case the `owner` field exists to get right. **Every entry names its `owner`**, and that is the point rather than decoration: the list is only ever as complete as the features that opted in, so a layers panel or an export summary can say what its inventory covers instead of presenting a partial list as everything.
283
+
284
+ - **Read only, deliberately.** Removing or hiding another feature's output would mean one plugin reaching into another's state with no way for the owner to refuse.
285
+ - **Declare from the draw handler.** Declarations are cleared with the layers at the start of every draw, so an inventory cannot outlive the drawing it describes. Declaring the same id twice replaces it rather than growing a duplicate row.
286
+ - **Annotations carry no caller id.** The `id` on a point annotation is the key of a deferred-execution entry rather than a handle on the drawing, so entries fall back to `annotation:<type>:<index>` and take their label from `label.text` where the caller wrote one.
287
+
288
+ ### The types a plugin author works with
289
+
290
+ Declared in the shipped `apexcharts.d.ts`: `ApexPluginAPI` (the `setup(api)` facade), `ApexPluginSeries`, `ApexPluginScales`, `ApexPluginLayer`, `ApexPluginHook`, `ApexPluginPayload`, and for the v6 additions `ApexPluginChartInfo` (`api.info`), `ApexPluginPointerEvent` (the `pointer` payload), `ApexPluginClaim` (`{ release(), update(entries) }`) and `ApexDrawnItem` (a `drawn()` row).
291
+
292
+ ### Typing gap at 7.6.1
293
+
294
+ `api.categories` and `data[].raw` are real at runtime (both shipped in v2) but are **not declared** in the shipped `apexcharts.d.ts`: `ApexPluginAPI` has no `categories` member and `ApexPluginSeries` is typed as `{ name?, hidden, color?, points }`. TypeScript rejects both. Cast the facade where you need them, and do not read the absence as "removed".
191
295
 
192
296
  ---
193
297
 
@@ -204,7 +308,7 @@ import 'apexcharts/features/renderer-canvas'
204
308
  const options = {
205
309
  chart: {
206
310
  renderer: 'auto', // 'svg' | 'canvas' | 'auto'
207
- rendererThreshold: 5000, // point count above which 'auto' picks canvas
311
+ rendererThreshold: 8000, // point count above which 'auto' picks canvas (default 8000)
208
312
  },
209
313
  }
210
314
  // chart.getActiveRenderer() // => 'svg' | 'canvas'
@@ -261,7 +365,7 @@ Generic Ctrl-Z over a command journal. Zooms, series toggles, option changes, an
261
365
 
262
366
  ```js
263
367
  const options = {
264
- chart: { history: { enabled: true, maxDepth: 50, coalesceMs: 300, keyboard: true } },
368
+ chart: { history: { enabled: true, maxDepth: 100, coalesceMs: 250, keyboard: true } }, // those are the defaults
265
369
  }
266
370
 
267
371
  chart.history.undo(animate?) // and .redo(animate?)
@@ -563,7 +667,8 @@ const options = {
563
667
  // false disables it. role="status", aria-live="polite",
564
668
  // spinner flattens to a pulse under prefers-reduced-motion
565
669
  cache: true, // (v6.9) cache onDrillDown results by id (default true);
566
- // clear with the drilldown module's clearCache()
670
+ // clear with chart.clearDrilldownCache(id?), the public
671
+ // typed method (it delegates to the module's clearCache())
567
672
  // (v6.9) line/area only: the dot marking a drillable point when the chart
568
673
  // draws no markers. Only drillable points get one, so it reads as "these open".
569
674
  marker: { show: true, size: 6, shape: 'circle', fillColor: undefined, strokeColor: '#fff' },
@@ -631,7 +736,7 @@ const options = {
631
736
 
632
737
  ## Common Pitfalls
633
738
 
634
- 1. **Assuming the full bundle still carries everything *(the #1 v6 → v7 mistake)*.** In v6, `import ApexCharts from 'apexcharts'` was enough for every feature. In v7 the nine Tier 2 features and the `raincloud` type need their own import on top of it. Read the console: each one names both routes when its config is present without it.
739
+ 1. **Assuming the full bundle still carries everything *(the #1 v6 → v7 mistake)*.** In v6, `import ApexCharts from 'apexcharts'` was enough for every feature. In v7 the nine Tier 2 features and the `raincloud` and `icicle` types need their own import on top of it. Read the console: each one names both routes when its config is present without it.
635
740
  2. **Forgetting the feature import on a tree-shaken build**: a Tier 1 feature is in the default bundle but not in `apexcharts/core` or a per-type entry, so those builds still need `import 'apexcharts/features/<name>'`.
636
741
  3. **Reaching for `apexcharts/features/all` to fix either of the above**: it is the Tier 1 set only, by design (`tests/unit/feature-tier-budget.spec.js` upstream fails the build if a Tier 2 feature reappears in it).
637
742
  4. **Using `api.zeroY` in a custom mark**: it does not exist. Use `ctx.scales.y(0)` for the baseline.
@@ -30,7 +30,7 @@ import ApexCharts from 'apexcharts/raincloud'
30
30
  import 'apexcharts/features/stats'
31
31
  ```
32
32
 
33
- **⚠️ Raincloud is Tier 2 (v7.1).** Unlike every other chart type, it is **not in the default `apexcharts` bundle**. On the full bundle add `import 'apexcharts/raincloud'` (or `import 'apexcharts/features/raincloud'`); from a script tag add `dist/features/raincloud.js` after `apexcharts.js`. It is also premium, so it renders an `APEXCHARTS` watermark until an entitled license is set.
33
+ **⚠️ Raincloud is Tier 2 (v7.1).** It is **not in the default `apexcharts` bundle**, one of only two chart types that are not (the other is `icicle`, v7.6, which is free). On the full bundle add `import 'apexcharts/raincloud'` (or `import 'apexcharts/features/raincloud'`); from a script tag add `dist/features/raincloud.js` after `apexcharts.js`. It is also premium, so it renders an `APEXCHARTS` watermark until an entitled license is set.
34
34
 
35
35
  **Raw samples (v6.9):** boxPlot, violin, and histogram can all take the raw observations and compute the statistics themselves (quartiles for boxPlot, KDE density for violin, bin counts for histogram). Precomputed summaries keep working exactly as before. With the full `apexcharts` bundle the stats feature is always present; with tree-shaking entries it must be imported (without it, a raw-sample boxPlot/violin datum has nothing to summarize and a histogram warns and draws nothing).
36
36
 
@@ -383,6 +383,6 @@ plotOptions: {
383
383
  8. **Feeding a histogram pre-binned counts**: the series must carry raw observations. If you already have counts per bucket, use a `bar` chart; a histogram would bin your counts as if they were measurements.
384
384
  9. **`whiskers: 'tukey'` on a precomputed boxPlot summary**: `plotOptions.boxPlot.whiskers` only applies when the summary is derived from `points`. A five-number `y` is drawn exactly as given.
385
385
  10. **Raw samples with tree-shaking but no stats feature**: `apexcharts/candlestick` and `apexcharts/violin` alone cannot summarize `points`. Add `import 'apexcharts/features/stats'` (the `apexcharts/histogram` entry and the full bundle already include it).
386
- 11. **Expecting `raincloud` to work on the full bundle** *(v7.1)*: it is the one chart type that is Tier 2. `import ApexCharts from 'apexcharts'` alone gives you a console warning and no raincloud. Add `import 'apexcharts/raincloud'`.
386
+ 11. **Expecting `raincloud` to work on the full bundle** *(v7.1)*: it is one of two chart types outside the default bundle (the other is `icicle`, v7.6). `import ApexCharts from 'apexcharts'` alone gives you a console warning and no raincloud, because `raincloud` aliases the violin engine and degrades; an unregistered `icicle` throws instead. Add `import 'apexcharts/raincloud'`.
387
387
  12. **Looking for `plotOptions.raincloud`**: there is none. Raincloud is a preset over the violin engine, so configure it through `plotOptions.violin` (`side`, `box`, `points`, `kde`, `normalize`).
388
388
  13. **Giving a raincloud a `y`**: it takes the raw sample per category: `data: [{ x, points: [number] }]`. A precomputed `y.summary` is honored for the box, but the density and the rain need the observations.
@@ -236,7 +236,7 @@ updateChart() {
236
236
 
237
237
  ```
238
238
  https://cdn.jsdelivr.net/npm/apexcharts
239
- https://cdn.jsdelivr.net/npm/apexcharts@5/dist/apexcharts.min.js
239
+ https://cdn.jsdelivr.net/npm/apexcharts@7/dist/apexcharts.min.js
240
240
  https://unpkg.com/apexcharts
241
241
  ```
242
242
 
@@ -4,14 +4,18 @@
4
4
 
5
5
  - **Heatmap** (`'heatmap'`) — Color-coded grid cells representing data intensity
6
6
  - **Treemap** (`'treemap'`) — Nested rectangles with area proportional to value
7
+ - **Icicle** (`'icicle'`) *(v7.6)* — A hierarchy as stacked bands, one per depth level
7
8
 
8
9
  ## Tree-Shakeable Import
9
10
 
10
11
  ```js
11
12
  import ApexCharts from 'apexcharts/heatmap' # heatmap only
12
13
  import ApexCharts from 'apexcharts/treemap' # treemap only
14
+ import ApexCharts from 'apexcharts/icicle' # icicle (v7.6): opt-in, see below
13
15
  ```
14
16
 
17
+ `icicle` is **not in the default bundle**. Unlike every other type here, `import ApexCharts from 'apexcharts'` does not carry its class, so `chart.type: 'icicle'` throws until the sub-entry (or `dist/icicle.js` after the ApexCharts script) has registered it. Loading the full bundle is not a workaround, which is what the v7.6.1 error message was corrected to stop suggesting.
18
+
15
19
  ---
16
20
 
17
21
  ## Data Formats
@@ -182,6 +186,33 @@ plotOptions: {
182
186
 
183
187
  The hierarchy resolver is shared with sunburst, including the `drilldown: '<id>'` adapter (opted into via `nested.drilldownAsLevels`).
184
188
 
189
+ ### Icicle (v7.6)
190
+
191
+ Same hierarchy as the sunburst, drawn in cartesian coordinates: one band per depth level, each child sized inside its parent's extent along the value axis. Axis-style `[{ data: [...] }]` wrapper, `{ x, y, children }` nodes to any depth.
192
+
193
+ ```js
194
+ import ApexCharts from 'apexcharts/icicle' // required: not in the default bundle
195
+
196
+ const options = {
197
+ chart: { type: 'icicle' },
198
+ series: [{
199
+ data: [
200
+ { x: 'Mobile', y: 55, children: [
201
+ { x: 'iOS', y: 30, children: [{ x: 'iOS 17', y: 18 }, { x: 'iOS 16', y: 9 }] },
202
+ { x: 'Android', y: 23 }
203
+ ] },
204
+ { x: 'Desktop', y: 33, children: [{ x: 'Windows', y: 20 }, { x: 'macOS', y: 10 }] }
205
+ ]
206
+ }]
207
+ }
208
+ ```
209
+
210
+ - A branch may **omit its own `y`** and be the sum of its children. This is the documented shape for both partition types, and it only started working in v7.6: before that the non-axis parser dropped any node carrying `children` without `x` and `y`, and the chart drew nothing at all while warning about pie data.
211
+ - A node may carry `drilldown: '<id>'` instead of `children`, and the matching `drilldown.series` entry is read as the next level. It is read as plain data, so no drilldown feature import is needed.
212
+ - Sibling order follows the array by default (`sort: 'none'`). Set `plotOptions.icicle.sort` to `'value'` or `'name'` to override it.
213
+
214
+ **Choosing between icicle and sunburst.** Both resolve the same tree. An icicle keeps labels horizontal at every depth and puts depth on a straight axis, so same-depth siblings line up across branches and deep trees stay readable; a sunburst spends its area radially and reads better for shallow, wide trees. `direction: 'up'` is the flame-graph orientation, for call stacks and path funnels.
215
+
185
216
  ---
186
217
 
187
218
  ## Key plotOptions
@@ -191,7 +222,8 @@ The hierarchy resolver is shared with sunburst, including the `drilldown: '<id>'
191
222
  ```js
192
223
  plotOptions: {
193
224
  heatmap: {
194
- radius: 0, // border radius of cells
225
+ shape: 'rect', // 'rect' (default) | 'hexagon' | 'circle' | 'diamond' (v7.2)
226
+ radius: 0, // border radius of cells ('rect' only)
195
227
  enableShades: true, // shade intensity based on value
196
228
  shadeIntensity: 0.5, // shade range (0-1)
197
229
  distributed: false, // different color per series
@@ -212,6 +244,21 @@ plotOptions: {
212
244
  }
213
245
  ```
214
246
 
247
+ #### Cell shapes (v7.2)
248
+
249
+ `shape` changes what a cell is drawn as, without changing the data format or anything else about the chart:
250
+
251
+ - `'circle'` and `'diamond'` are inscribed in the cell box the heatmap already lays out.
252
+ - `'hexagon'` is a real honeycomb tilemap: pointy-top hexagons, alternate rows offset half a cell so neighbours share a full edge, with the lattice's overhang reserved as extra grid padding so offset rows never cover the axis labels.
253
+
254
+ Three constraints worth knowing before choosing one:
255
+
256
+ - **`radius` applies to `'rect'` only.** Corner rounding is ignored by the other three.
257
+ - **`'hexagon'` is categorical-layout only.** On a numeric or datetime x axis it falls back to `'rect'` silently.
258
+ - **Non-rect shapes always render as SVG.** The canvas renderer declines them the same way it declines image fills, so a large heatmap that needs `renderer: 'canvas'` for its cell count has to stay on `'rect'`.
259
+
260
+ Shaped cells keep the `apexcharts-heatmap-rect` class and the same attribute contract, so tooltips, keyboard navigation, legend range highlighting and the color tween on a data update all behave unchanged.
261
+
215
262
  ### Treemap
216
263
 
217
264
  ```js
@@ -233,6 +280,46 @@ plotOptions: {
233
280
  }
234
281
  ```
235
282
 
283
+ ### Icicle (v7.6)
284
+
285
+ ```js
286
+ plotOptions: {
287
+ icicle: {
288
+ direction: 'down', // 'down' (root on top, default) | 'up' (flame graph) | 'right' | 'left'
289
+ levelSize: 'equal', // 'equal' | px number | '%' string: thickness of one depth band
290
+ maxDepth: 'auto', // 'auto' (whole tree) | number of levels from the focused one
291
+ spacing: 1, // gap between adjacent cells (px)
292
+ borderRadius: 0, // corner rounding (px)
293
+ leaf: 'stop', // 'stop' (default): a shallow branch ends at its own band
294
+ // 'extend': it stretches to the far edge
295
+ partition: 'normalize', // 'normalize' | 'strict'
296
+ sort: 'none', // 'none' | 'value' | 'name'
297
+ tint: 0, // per-depth lightening of the parent colour (0 = same, 1 = white)
298
+ zoomOnClick: true, // click a cell to zoom into its branch (breadcrumb to go back)
299
+ zoomType: 'value', // 'value' (default) | 'both'
300
+ dataLabels: {
301
+ show: true,
302
+ minSizeToShow: 30, // hide the label on cells shorter than this (px)
303
+ align: 'center', // 'left' | 'center' | 'right'; a flame graph wants 'left'
304
+ rotate: 'auto', // 'auto' | 'always' | 'never'
305
+ showValue: false,
306
+ style: { fontSize: '12px', fontFamily: undefined, fontWeight: 400, colors: undefined }
307
+ }
308
+ }
309
+ }
310
+ ```
311
+
312
+ Four defaults differ from the sunburst's, each for a reason worth knowing before overriding it:
313
+
314
+ - **`leaf: 'stop'`** leaves the white space that shows how deep each branch goes. `'extend'` fills the plot, which is what a treemap does and makes an icicle stop reading as a hierarchy.
315
+ - **`tint: 0`** keeps one hue per branch, so the eye can follow a branch down the levels.
316
+ - **The palette lands on the shallowest level that branches.** A single-root tree is the normal shape here, and colouring by root would paint everything one hue.
317
+ - **The legend is off**, since every cell carries its own label.
318
+
319
+ **`zoomType` is the one to understand.** `'value'` (the default) rescales the value axis only: the clicked branch stretches to fill the width, its subtree stretches with it, and no level moves. The ancestors stay above it, clamped to the plot, which is what makes them read as context. `'both'` also re-bands the depth axis, promoting the branch to the top level, which spends the whole plot on it at the cost of the reader's place in the tree. The values are not called `'x'` and `'y'` because `direction` decides which screen axis the value axis is.
320
+
321
+ A click that would not change the view is refused rather than performed: clicking a lone root, or a leaf inside the focused branch, leaves the chart alone, and the cursor reads `pointer` only on cells where a zoom would actually move something.
322
+
236
323
  ---
237
324
 
238
325
  ## Complete Working Example — Heatmap
@@ -289,3 +376,6 @@ plotOptions: {
289
376
  3. **Treemap with negative values** — treemap `y` values must be positive (they represent area). Negative values cause rendering issues.
290
377
  4. **Confusing heatmap `y` with position** — in heatmap data `{ x, y }`, the `y` is the VALUE (color intensity), NOT the y-axis position. The series `name` determines the row.
291
378
  5. **Nested treemap branch carrying its own `y` (v6.9)**: a branch node normally omits `y`; its area is the sum of its children. Only leaves supply values. Flattening a hierarchy by hand is no longer necessary; pass `children` instead.
379
+ 6. **`chart.type: 'icicle'` on the default bundle (v7.6)** — icicle is opt-in and the default build carries no class for it, so this throws. Add `import ApexCharts from 'apexcharts/icicle'` (or load `dist/icicle.js` after the ApexCharts script). Loading the full `apexcharts.js` does **not** help: it does not carry the class either.
380
+ 7. **Reaching for `zoomType: 'both'` to make a branch fill the plot** — `'value'` already stretches the branch across the full width; what `'both'` additionally does is move every level, which costs the reader their place in the tree. Change it only when the ancestors genuinely do not matter.
381
+ 8. **Expecting `sort: 'value'` on a flame graph** — `'name'` is the flame-graph convention, because it holds a frame in the same place across profiles so two runs of one program can be compared by eye.
package/references/ssr.md CHANGED
@@ -52,7 +52,7 @@ const htmlString = await ApexCharts.renderToHTML(
52
52
  {
53
53
  chart: {
54
54
  type: 'bar',
55
- id: 'my-chart' // id is important for hydration targeting
55
+ id: 'my-chart' // for chart.exec() targeting; NOT what hydration looks for
56
56
  },
57
57
  series: [{ data: [44, 55, 41, 67] }],
58
58
  xaxis: { categories: ['A', 'B', 'C', 'D'] }
@@ -60,7 +60,12 @@ const htmlString = await ApexCharts.renderToHTML(
60
60
  { width: 600, height: 350 }
61
61
  )
62
62
 
63
- // htmlString is <div id="my-chart" data-apexcharts>...<svg>...</svg></div>
63
+ // htmlString is:
64
+ // <div class="apexcharts-ssr-wrapper" data-apexcharts-hydrate data-apexcharts-config="...">
65
+ // <svg>...</svg>
66
+ // </div>
67
+ // The wrapper carries NO id. `chart.id` is not copied onto it, and the config
68
+ // travels in data-apexcharts-config, which is what hydration reads.
64
69
  ```
65
70
 
66
71
  ---
@@ -74,14 +79,17 @@ After the server-rendered HTML is in the DOM, hydrate it to make it interactive:
74
79
  ```js
75
80
  import ApexCharts from 'apexcharts'
76
81
 
77
- // Hydrate a specific chart element (optionally merge client-only options)
78
- const chart = ApexCharts.hydrate(document.querySelector('#my-chart'))
82
+ // Hydrate a specific chart element (optionally merge client-only options).
83
+ // Select the emitted wrapper, not a chart id: `#my-chart` matches nothing.
84
+ const chart = ApexCharts.hydrate(document.querySelector('[data-apexcharts-hydrate]'))
79
85
  ```
80
86
 
81
87
  ### hydrateAll(selector?, clientOptions?)
82
88
 
83
89
  ```js
84
- // Hydrate all server-rendered charts on the page (optionally scope by selector)
90
+ // Hydrate all server-rendered charts on the page (optionally scope by selector).
91
+ // The selector defaults to '[data-apexcharts-hydrate]', which is why this works
92
+ // with no argument and a #id selector does not.
85
93
  ApexCharts.hydrateAll()
86
94
  ```
87
95
 
@@ -210,7 +218,8 @@ onMounted(async () => {
210
218
 
211
219
  ## Common Pitfalls
212
220
 
213
- 1. **Using `apexcharts` in Node.js without SSR methods** — the default export auto-resolves based on environment, but `renderToString` is only available via `apexcharts/ssr`.
214
- 2. **Forgetting to hydrate on the client** — server-rendered charts are static SVGs. Without `hydrate()`, they have no interactivity (no tooltips, zoom, click events).
215
- 3. **Missing width/height**: SSR has no DOM to measure. Pass explicit dimensions in the **second** argument: `renderToString(options, { width, height })` / `renderToHTML(options, { width, height })`.
216
- 4. **Hydrating before DOM is ready** — call `hydrate()` after the server-rendered HTML is in the DOM (use `onMounted`, `useEffect`, or `DOMContentLoaded`).
221
+ 1. **Assuming `apexcharts` has no SSR methods in Node** — it does: the `"."` export's `node` condition resolves to the SSR bundle, so `renderToString` and `renderToHTML` are there. Import `apexcharts/ssr` explicitly anyway, because a bundler told to target Node can still resolve the `browser` condition, and then they really are missing.
222
+ 2. **Hydrating by chart id** — `renderToHTML` emits `<div class="apexcharts-ssr-wrapper" data-apexcharts-hydrate data-apexcharts-config="...">` with no `id`, so `querySelector('#my-chart')` returns null. Use `hydrateAll()`, or select `[data-apexcharts-hydrate]`.
223
+ 3. **Forgetting to hydrate on the client** — server-rendered charts are static SVGs. Without `hydrate()`, they have no interactivity (no tooltips, zoom, click events).
224
+ 4. **Missing width/height**: SSR has no DOM to measure. Pass explicit dimensions in the **second** argument: `renderToString(options, { width, height })` / `renderToHTML(options, { width, height })`.
225
+ 5. **Hydrating before DOM is ready** — call `hydrate()` after the server-rendered HTML is in the DOM (use `onMounted`, `useEffect`, or `DOMContentLoaded`).
@@ -23,7 +23,7 @@ ApexCharts supports four import strategies with different bundle sizes:
23
23
 
24
24
  | Strategy | Import | Includes |
25
25
  |---|---|---|
26
- | Default bundle | `import ApexCharts from 'apexcharts'` | Every chart type except `raincloud`, plus the **Tier 1** features. **Not** the nine Tier 2 features. |
26
+ | Default bundle | `import ApexCharts from 'apexcharts'` | Every chart type except `raincloud` and `icicle`, plus the **Tier 1** features. **Not** the nine Tier 2 features. |
27
27
  | Per-type entry | `import ApexCharts from 'apexcharts/line'` | Specific chart family + core (no optional features at all) |
28
28
  | Bare core | `import ApexCharts from 'apexcharts/core'` | Core class only — must manually register everything |
29
29
  | Lean core, no bundler *(v7.0)* | `<script src=".../dist/apexcharts.core.js">` | Script-tag equivalent of bare core, assembled from separate tags. See below. |
@@ -50,7 +50,7 @@ Each entry point registers a family of related chart types:
50
50
  | `apexcharts/candlestick` | candlestick, boxPlot |
51
51
  | `apexcharts/boxPlot` | same as /candlestick |
52
52
  | `apexcharts/violin` | violin *(new in v6)* |
53
- | `apexcharts/raincloud` | raincloud *(new in v7.1, premium)*: the violin engine plus the raincloud feature. **Tier 2**: the only chart type absent from the default bundle. |
53
+ | `apexcharts/raincloud` | raincloud *(new in v7.1, premium)*: the violin engine plus the raincloud feature. **Tier 2**: absent from the default bundle (with `icicle`, the two that are). |
54
54
  | `apexcharts/histogram` | histogram *(new in v6.9)*: the bar engine plus the stats feature |
55
55
  | `apexcharts/pie` | pie, donut, polarArea |
56
56
  | `apexcharts/donut` | same as /pie |
@@ -60,6 +60,7 @@ Each entry point registers a family of related chart types:
60
60
  | `apexcharts/heatmap` | heatmap |
61
61
  | `apexcharts/treemap` | treemap |
62
62
  | `apexcharts/sunburst` | sunburst *(new in v6.7, free)* |
63
+ | `apexcharts/icicle` | icicle *(new in v7.6, free)*: the sunburst's partition in cartesian coordinates. **Opt-in**: the default bundle carries no icicle class, only the settings literals and a few string branches, so `chart.type: 'icicle'` throws until this entry, or `dist/icicle.js` after the ApexCharts script, has registered it. Loading the full `apexcharts.js` is **not** a way around that. The entry contains no other chart class. |
63
64
  | `apexcharts/unit` | unit, **waffle** *(new in v6.6, premium: watermarked until licensed)* |
64
65
  | `apexcharts/unit-shapes` | *(new in v6.10)* not a chart entry: named shape exports for the unit chart (`heart`, `house`, ... plus `outlined`, `glyphs`, `preview`), tree-shaken per shape (~4 KB gzipped each). See `references/circular-charts.md`. |
65
66
  | `apexcharts/pictograms` | *(new in v7.0)* not a chart entry: a drawn glyph per unit for the unit chart (`person`, `house`, `heart`, `tree`, `droplet`, `star`, `car`, `bag`, `book`, `cup`, `bulb`, `plane`), plus `definePictogram` / `registerMarks` / `catalog`. Import the glyph you use, not `catalog`, which ships all of them. |
@@ -152,7 +153,9 @@ Tree-shaking only ever helped people with a build step. A page using a `<script>
152
153
  <script src=".../dist/features/legend.js"></script>
153
154
  ```
154
155
 
155
- **136,921 B gzipped**, against 252,005 for the full bundle. Purely additive: `apexcharts.js` is unchanged, and a page that wants everything should keep loading it rather than assembling it from parts.
156
+ **About 144 KB gzipped**, against about 270 KB for the full bundle (`dist/apexcharts.core.min.js` and `dist/apexcharts.min.js` at 7.6.1, gzip -9). Treat both as approximate: the upstream build report gzips at a different level and prints figures a few hundred bytes apart from these, and the numbers move every release. Purely additive: `apexcharts.js` is unchanged, and a page that wants everything should keep loading it rather than assembling it from parts.
157
+
158
+ The two opt-in chart types are not in that table, because neither is a `features/*` module: `raincloud` is reached through `apexcharts/raincloud` and `icicle` through `apexcharts/icicle` (see the entry table above).
156
159
 
157
160
  Every Tier 2 feature has a `dist/features/<name>.js` artifact for this channel too, and a full-bundle page still needs it:
158
161
 
@@ -256,7 +259,7 @@ export default {
256
259
 
257
260
  ## Common Pitfalls
258
261
 
259
- 1. **Assuming the default bundle still has everything *(v7)***: the nine Tier 2 features and the `raincloud` type need their own import on top of `import ApexCharts from 'apexcharts'`. This is the most common v6 → v7 upgrade failure.
262
+ 1. **Assuming the default bundle still has everything *(v7)***: the nine Tier 2 features and the `raincloud` and `icicle` types need their own import on top of `import ApexCharts from 'apexcharts'`. This is the most common v6 → v7 upgrade failure.
260
263
  2. **Reaching for `apexcharts/features/all` to fix it**: that is the Tier 1 set by design and pulls in no Tier 2 module.
261
264
  3. **Missing feature imports on a tree-shaken build**: Tier 1 features fail silently. If legend/toolbar/annotations don't appear, check imports.
262
265
  4. **Importing `apexcharts` AND `apexcharts/line`**: creates duplicate bundles. Use one strategy. *(v7.0 fixed the related internal bug where the default bundle inlined its own core copy while add-ons resolved `apexcharts/core`, so an app importing both got two classes, ~130 KB gzipped of duplicate core, and a feature that never registered. They now share one core.)*