Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
423e1bd
split Circle.svelte into 3 layer-specific components (Circle.svg.svel…
techniq Apr 27, 2026
fb41f75
split Text.svelte into 3 layer-specific components
techniq Apr 27, 2026
70d58eb
Organize into component directories
techniq Apr 27, 2026
75d75a6
split Rect, Line, and Path into 3 layer-specific components
techniq Apr 27, 2026
2feb7cb
split Ellipse, Polygon, Group, Image, ClipPath, Pattern, LinearGradie…
techniq Apr 27, 2026
9611a20
Add changeset for per-layer primitive
techniq Apr 27, 2026
de81af1
split Axis into 3 layer-specific components
techniq Apr 28, 2026
acdc6f3
Merge branch 'next' into primitive-layer-split
techniq Apr 28, 2026
1797821
Add full-chart layer examples (to monitor progress)
techniq Apr 28, 2026
e50ae0f
split Grid and Rule into 3 layer-specific components
techniq Apr 28, 2026
938e2e6
split Chart and ChartChildren into 3 layer-specific components, and a…
techniq Apr 28, 2026
c9e8846
refine bundle scenarios
techniq Apr 28, 2026
53c2326
refine bundle scenarios
techniq Apr 28, 2026
b3cb770
Add more foundation examples
techniq Apr 28, 2026
bae43d3
improve bundle comment
techniq Apr 28, 2026
c2b7023
split Highlight, ChartClipPath, RectClipPath into 3 layer-specific co…
techniq Apr 28, 2026
1711c77
split Arc, Area, and Spline into 3 layer-specific components
techniq Apr 28, 2026
5730250
split ArcLabel, Bar, Bars, Labels, Pie, and Points into 3 layer-speci…
techniq Apr 28, 2026
a970273
split Frame, Cell, Threshold, AnnotationLine, AnnotationPoint, and Tr…
techniq Apr 28, 2026
00b234d
split AnnotationRange, CircleClipPath, Vector, Link, Hull, Density, a…
techniq Apr 28, 2026
5cfa978
split BoxPlot, Violin, Raster, Month, Contour, and Voronoi into 3 lay…
techniq Apr 28, 2026
6ffc9ac
split geo components (GeoPath, GeoSpline, etc) into 3 layer-specific …
techniq Apr 28, 2026
c9a43b2
split Ribbon into 3 layer-specific components. Re-export all layout/…
techniq Apr 28, 2026
2b8ab55
split high-level charts (BarChart, LineChart, etc) into 3 layer-speci…
techniq Apr 28, 2026
5f89670
update changeset and docs
techniq Apr 28, 2026
c22954a
Update bundle size PR comment with collapsible sections
techniq Apr 28, 2026
041459a
Remove `Svg` from core (just Chart)
techniq Apr 29, 2026
c680a6d
Split `Foundation` scenarios into `Core (agnostic)` and `Core (layer-…
techniq Apr 29, 2026
65740dc
update bundle report
techniq Apr 29, 2026
0247327
Lazy load transform context (and only when used) similar to brush con…
techniq Apr 29, 2026
1290fc1
Add ChartCore. Fix TransformContext test
techniq Apr 29, 2026
32296da
update bundle sizes in docs
techniq Apr 29, 2026
c8ba50a
move core section above base
techniq Apr 29, 2026
f8397b6
Fix primitive fill/stroke for canvas primitives
techniq Apr 29, 2026
98a40a8
fix(Text): Render on `<Svg>` layer when only one of `x`/`y` is set
techniq Apr 29, 2026
53e2898
fix components missing html impls (Calendar, Bars, Annotation*, etc)
techniq Apr 29, 2026
3a67a8a
Fix Text within group
techniq Apr 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
97 changes: 97 additions & 0 deletions .changeset/per-layer-primitive-variants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
'layerchart': minor
---

feat: Per-layer variants for primitives, compound marks, and high-level charts (`layerchart/svg`, `layerchart/canvas`, `layerchart/html`)

Layer-agnostic components auto-detect the surrounding `<Svg>`, `<Canvas>`, or `<Html>` layer and bundle every render path. The new sub-path exports expose layer-specific variants so consumers committed to a single rendering layer can opt into a smaller bundle.

```ts
// Default: agnostic, dispatches at runtime — works in any layer
import { Rect, Circle, Text, Path, LineChart } from 'layerchart';

// SVG-only — skips canvas + html branches
import { Rect, Circle, Text, Path, LineChart } from 'layerchart/svg';

// Canvas-only
import { Rect, Circle, Text, LineChart } from 'layerchart/canvas';

// HTML-only — drops canvas + svg overhead (some primitives are ~95% smaller)
import { Rect, Circle, Text, Pattern, LinearGradient } from 'layerchart/html';
```

Each agnostic component (e.g. `Rect.svelte`) now dispatches to the corresponding per-layer variant under the hood (`Rect.svg.svelte`, `Rect.canvas.svelte`, `Rect.html.svelte`) — no breaking change for existing consumers.

### What's split

**Primitives (13)** — the basic graphics building blocks
`Circle`, `Text`, `Rect`, `Line`, `Path`, `Ellipse`, `Polygon`, `Group`, `Image`, `ClipPath`, `Pattern`, `LinearGradient`, `RadialGradient`

**Compound marks (~30)** — chart axes, marks, annotations, and chart-relative shapes
`Axis`, `Grid`, `Rule`, `Highlight`, `Layer`, `ChartChildren`, `ChartClipPath`, `CircleClipPath`, `Bars`, `Bar`, `Spline`, `Area`, `Pie`, `Arc`, `ArcLabel`, `Points`, `Cell`, `Frame`, `Threshold`, `Trail`, `Vector`, `Link`, `Labels`, `AnnotationLine`, `AnnotationPoint`, `AnnotationRange`, `Hull`, `Density`, `Voronoi`, `Contour`, `Raster`, `Violin`, `BoxPlot`, `Calendar`, `Month`

**Geo components (`layerchart/geo`)**
`GeoPath`, `GeoSpline`, `GeoPoint`, `GeoCircle`, `GeoTile`, `TileImage`, `Graticule`, `GeoClipPath`, `GeoEdgeFade`

**Graph components (`layerchart/graph`)**
`Ribbon`

**High-level chart wrappers** — pre-composed charts with built-in tooltips, highlights, and series handling
`LineChart`, `AreaChart`, `BarChart`, `ScatterChart`, `PieChart`, `ArcChart`

The geo, graph, hierarchy, and force sub-paths also re-export every layer-agnostic helper they previously included, so a single `from 'layerchart/svg'` import covers a typical SVG chart end-to-end without falling back to `'layerchart'`.

### Standout per-layer wins (gz, vs agnostic baseline)

**Primitives where the per-layer rendering is dramatically simpler:**
- `Pattern` html: 14.81 → 0.92 KB (-94%) — HTML implementation is just CSS-string generation
- `LinearGradient` html: 14.38 → 0.53 KB (-96%)
- `Image` canvas: 14.95 → 3.73 KB (-75%)
- `Text` svg/html: 29.13 → ~16 KB (-45%)
- `Circle` / `Rect` / `Ellipse` / `Line` / `Path`: ~22–27% smaller per-layer

**Compound marks:** typically 8–15% gz savings per-layer; outliers like `Highlight` (-30% canvas) and `Cell` (-22% svg) are larger because their HTML/canvas vs. SVG paths diverge significantly.

**High-level charts:** ~5–12% gz savings (~5–11 KB) when imported from `layerchart/svg` or `layerchart/canvas`. A single-layer LineChart drops from 89.6 KB → 79.0 KB gz on the SVG path.

For a consumer who migrates all imports to a single layer, cumulative savings across primitives and compound marks are 60–80 KB gz.

### Bundle reductions on the default `<Chart>` path

In addition to opt-in per-layer variants, this release also makes a few previously-eager features lazy:

- **`<TransformContext>`** is now dynamically imported when `<Chart transform={...}>` is set — saves ~2.8 KB gz on every chart that doesn't pan/zoom.
- **`<BrushContext>`** was already lazy; nothing changes there.

### `<ChartCore>` for non-cartesian charts (new)

A new `<ChartCore>` component is exported alongside `<Chart>` from each layer sub-path (`layerchart`, `layerchart/svg`, `layerchart/canvas`, `layerchart/html`). It provides the chart context, sizing, brush, transform, and tooltip plumbing — but skips `<ChartChildren>` and the `Layer` / `Axis` / `Grid` / `Rule` / `Highlight` / `ChartClipPath` import chain it pulls in.

Use it for geo maps, custom layouts, or any chart that renders its own primitives directly via the `children` snippet:

```svelte
<script>
import { ChartCore, Svg, GeoProjection, GeoPath } from 'layerchart/svg';
</script>

<ChartCore data={countries}>
{#snippet children({ context })}
<Svg>
<GeoProjection projection={geoMercator} fitGeojson={countries}>
<GeoPath geojson={countries} fill="steelblue" />
</GeoProjection>
</Svg>
{/snippet}
</ChartCore>
```

Measured savings (bundle scenarios):
- `base` (`<Chart>`) → `core` (`<ChartCore>`): 83.42 → 50.93 KB gz (**−39%**)
- `geo` (`<Chart>` + `GeoPath`/`GeoPoint`) → `core-geo` (`<ChartCore>` + `GeoProjection` + `GeoPath`): 87.23 → 54.67 KB gz (**−37%**)
- `base-svg` (per-layer) → `core-svg` (per-layer): 77.37 → 50.88 KB gz (**−34%**)

### Behavior

Identical to the agnostic versions: visual output, props, types, and bindable refs all match. The dispatcher pattern adds ~0.2 KB per primitive to `core` for users on the agnostic API (transitive cost from `Highlight` / `Axis` / `Chart`) — a worthwhile tradeoff for the opt-in per-layer savings.

See the updated ["Bundle Size" guide](https://layerchart.com/docs/guides/bundle-size) for the full table, tradeoffs, and when to opt into per-layer imports.
13 changes: 8 additions & 5 deletions bundle-analyzer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,22 @@ Svelte runtime is excluded from measurements since it's shared across all compon

## Scenarios

Scenarios are defined in [`define-scenarios.ts`](./define-scenarios.ts) and represent real-world usage patterns:
Scenarios are defined in [`bundle-scenarios.ts`](./bundle-scenarios.ts) and represent real-world usage patterns:

| Scenario | Description |
|----------|-------------|
| `core` | Bare minimum: `Chart` + `Svg` |
| `base` | Full `Chart` (with the cartesian frame: Axis, Grid, Rule, Highlight) |
| `base-svg` / `base-canvas` / `base-html` | `Chart` from `layerchart/svg`, etc. |
| `core` | Bare-bones `ChartCore` (no Axis/Grid/Rule/Highlight) |
| `core-svg` / `core-canvas` / `core-html` | `ChartCore` from `layerchart/svg`, etc. |
| `core-geo` / `core-line` / `core-scatter` | `ChartCore` + manual primitives (geo / spline / points) |
| `line-chart` | Line chart with axes and grid |
| `line-chart-interactive` | Line chart with tooltip and highlight |
| `area-chart` | Area chart with axes |
| `bar-chart` | Bar chart with axes |
| `scatter-chart` | Scatter plot with points |
| `pie-chart` | Pie/donut chart with arcs |
| `high-level-charts` | All high-level chart components |
| `LineChart` / `AreaChart` / `BarChart` / etc. | High-level chart wrappers |
| `geo` | Geographic map with paths |
| `geo-tiles` | Geographic map with tile layer |
| `geo-full` | All geo components |
Expand All @@ -50,7 +54,6 @@ Scenarios are defined in [`define-scenarios.ts`](./define-scenarios.ts) and repr
| `dagre` | Dagre directed graph |
| `sankey` | Sankey flow diagram |
| `chord` | Chord diagram |
| `canvas` | Canvas-based rendering |
| `all` | Everything from layerchart |

## CLI options
Expand Down Expand Up @@ -83,7 +86,7 @@ Two GitHub Actions workflows automate bundle tracking:

## Adding scenarios

Edit [`define-scenarios.ts`](./define-scenarios.ts) to add new scenarios to the `scenarios` array:
Edit [`bundle-scenarios.ts`](./bundle-scenarios.ts) to add new scenarios to the `scenarios` array:

```ts
{
Expand Down
7 changes: 5 additions & 2 deletions bundle-analyzer/bundle-analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
getScenarios,
getComponentScenarios,
type Scenario,
} from "./define-scenarios.js";
} from "./bundle-scenarios.js";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Expand Down Expand Up @@ -190,9 +190,12 @@ import * as LayerChartGraph from "layerchart/graph";
`;
} else {
// Group imports by source module (root vs each sub-path).
// Per-scenario `layers` win over the default mapping —
// used to test layer-specific variants like `layerchart/svg`.
const groups = new Map<string, string[]>([["layerchart", []]]);
for (const name of scenario.imports) {
const sub = SUBPATH_FOR_COMPONENT[name];
const overrideSub = scenario.layers?.[name];
const sub = overrideSub ?? SUBPATH_FOR_COMPONENT[name];
const mod = sub ? `layerchart/${sub}` : "layerchart";
const list = groups.get(mod) ?? [];
list.push(name);
Expand Down
Loading
Loading