layerchart@2.0.0
LayerChart 2.0 rebuilds the library on Svelte 5 (runes & snippets) and no longer requires Tailwind CSS — components ship with their own default styles, with Tailwind 4 now optional. It also streamlines several component and context APIs for consistency. Upgrading from v1? Start with the v1 → v2 migration guide.
Highlights
- Svelte 5 — migrated to runes & snippets (#458)
- No CSS framework required — components ship default styles (easy to override via
@layer,:where(.lc-*), and CSS variables); Tailwind 4 optional (#449) - New documentation site
Key breaking changes (full details below)
- Unified context access — standalone
get*Context()getters removed in favor ofgetChartContext() BrushContext/TransformContextnow usebind:state(chartContext.brushState/transformState)- Render context → layer context (
getRenderContext→getLayerContext,supportedContexts→layers) GeoContext→GeoProjectionConnectormerged intoLink(pixel + data modes)ChartState.isVertical→valueAxis('x'|'y')tooltipContext→tooltip(boolean) onArc/Pie/Calendar/GeoPathdomainExtent: 'original'→'data'
Major Changes
-
breaking(BrushContext|TransformContext): Rename
bind:brushContext/bind:transformContexttobind:state(#663)Both
BrushContextandTransformContextnow usebind:stateinstead of their previous named bindings. Additionally, properties onChartStatehave been renamed:chartContext.brushContext→chartContext.brushStatechartContext.transformContext→chartContext.transformState
- <BrushContext bind:brushContext> + <BrushContext bind:state> - <TransformContext bind:transformContext> + <TransformContext bind:state>
-
breaking(TransformContext): Rename
domainExtent: 'original'todomainExtent: 'data'(#663)The
'original'value fordomainExtenthas been renamed to'data'to better describe that it constrains pan/zoom to the data's domain bounds:- <Chart transform={{ domainExtent: 'original' }}> + <Chart transform={{ domainExtent: 'data' }}>
-
breaking(GeoContext): Rename
GeoContextcomponent toGeoProjection(#663)The
GeoContextcomponent has been renamed toGeoProjectionto better describe its purpose. Update your imports and template usage:- import { GeoContext } from 'layerchart' + import { GeoProjection } from 'layerchart'
- <GeoContext projection={geoAlbersUsa}> + <GeoProjection projection={geoAlbersUsa}>
-
feat: New docs site (#449)
-
breaking: Rename render context APIs to layer context (#663)
getRenderContext()→getLayerContext()setRenderContext()→setLayerContext()supportedContextsprop →layersprop on components- Internal
layout/directory moved tolayers/(affects deep imports)
- import { getRenderContext } from 'layerchart' + import { getLayerContext } from 'layerchart'
-
breaking: Merge
ConnectorintoLink, removeConnectorcomponent (#449)Linknow supports both pixel mode (x1/y1/x2/y2props) and data mode (data+source/target/x/yaccessors), mirroring the pattern used by primitives likeCircle,Text, andRect.Migration:
<Connector source={{...}} target={{...}} ... />→<Link x1={...} y1={...} x2={...} y2={...} ... /><Link explicitCoords={{ x1, y1, x2, y2 }} />→<Link {x1} {y1} {x2} {y2} />(or<Link {...linkPositions[i]} />)
All Connector props (
type,curve,sweep,radius,bend,orientation,radial, markers, motion) are available directly onLink. TheexplicitCoordsprop andConnectorexport are removed. -
breaking(Chart): Remove
isVerticalfrom ChartState, addvalueAxisprop toChart(#663)ChartState.isVerticalhas been removed in favor ofChartState.valueAxis('x'|'y'), which explicitly defines which axis represents the value (dependent variable).Simplified charts (
BarChart,LineChart,AreaChart,ScatterChart) still accept theorientationprop as before — each chart maps it to the correctvalueAxisinternally. The<Chart>component itself now usesvalueAxisdirectly, sinceorientationis ambiguous at that level (a "vertical" BarChart hasvalueAxis="y"while a "vertical" LineChart hasvalueAxis="x").When accessing chart state:
- if (chartContext.isVertical) { ... } + if (chartContext.valueAxis === 'y') { ... }
When using
<Chart>directly (not simplified charts):- <Chart ...> + <Chart valueAxis="x" ...>
-
Tailwind 4 support (#449)
-
breaking: Remove standalone context getter/setter functions (#663)
The following standalone context functions have been removed in favor of the unified
getChartContext()API:getTooltipContext()/setTooltipContext()→ usegetChartContext().tooltipgetBrushContext()/setBrushContext()→ usegetChartContext().brushStategetTransformContext()/setTransformContext()→ usegetChartContext().transformState
- import { getTooltipContext } from 'layerchart' - const tooltip = getTooltipContext() + import { getChartContext } from 'layerchart' + const chart = getChartContext() + // access via chart.tooltip
-
breaking(Arc|Pie|Calendar|GeoPath): Rename
tooltipContextto simpletooltip(boolean), simplifying use case (#663) -
feat: Migrate to Svelte 5 runes/snippets (issue #159) (#458)
Minor Changes
-
feat: Add BoxPlot component for box-and-whisker plots (#663)
New composite mark that renders whiskers, caps, IQR box, median line, and outlier dots. Supports both pre-computed statistics (
min,q1,median,q3,max,outliersaccessors) and automatic computation from raw values via thevaluesprop. Orientation-aware viavalueAxiscontext. -
feat(Dodge): Add Dodge component for deterministic non-overlapping layout (#862)
A new composition component (similar to
ForceSimulation) that packs items along one axis to avoid overlaps. Modeled after Observable Plot'sdodgetransform:axis:'x'or'y'— which axis to dodge along (default'y')anchor:'top'/'middle'/'bottom'(foraxis='y') or'left'/'middle'/'right'(foraxis='x') — controls which edge items grow away frompadding: minimum px gap between itemsr: collision radius per item (constant or accessor). When omitted, falls back to the chart'sraccessor /rScale(matchingPoints), then to a default of5.position: override the anchor-axis pixel accessor (defaults to chart'sxGet/yGet)
Yields each item's computed pixel
x/y(and originalindex) via the children snippet, so you can render with any primitive (Circle,Text, etc.).Also includes a
rowHeightmode that switches from circular to row-based rectangular packing — useful for text labels where circular collision would produce unnecessarily large vertical gaps. The puredodge()algorithm is exported fromDodge.shared.svelte.tsfor direct use.Algorithm modeled after Observable Plot / SveltePlot: maintains an interval tree of placed items keyed by anchor-axis extent, queries it for items in the new item's collision zone, and builds candidate dodge-axis positions from circle-tangency math. Currently implemented as a linear-scan tracker with the same API; can be swapped for a real interval tree without API changes if profiling demands it.
-
feat: New
GeoClipPathcomponent for clipping content to GeoJSON boundaries in both SVG and Canvas modes (#449) -
feat: New
GeoRastercomponent for reprojecting raster imagery (e.g. NASA Blue Marble) onto any d3-geo projection via per-pixel inverse sampling on Canvas (#815) -
feat(Pattern): Add
rectsshape definition for tile patterns for rendering one or more rectangles per pattern tile (#864) -
feat(Labels): Add
smartplacement option (#799)New
placement="smart"mode that dynamically positions labels based on neighboring point values (peak, trough, rising, falling) to reduce overlapping. -
feat: Add statistical utility functions
computeBoxStats()andkde()(#663)computeBoxStats(values, k?)computes the five-number summary and outliers using the Tukey IQR methodkde(values, options?)computes kernel density estimation using the Epanechnikov kernel with Silverman's rule-of-thumb bandwidth
-
feat(Text): Add
segmentsprop for inline mixed-style text (#449)New
segmentsprop accepts an array of{ value, class }objects to render text with different styles (font size, weight, color) inline. Works across SVG (via tspans), Canvas (via sequential measureText/fillText), and HTML layers. Useful for labels that combine a bold name with a lighter value, such as treemap headers. -
feat: New Trail component for variable-width lines (#449)
-
feat: Add Violin component for violin plots (#663)
New composite mark that renders a symmetric density curve (mirrored area) from raw data using kernel density estimation (Epanechnikov kernel). Supports pre-computed density data via
densityprop or automatic KDE from raw values viavaluesprop. Optionalboxandmedianoverlays. Configurablebandwidth,thresholds, andcurve. -
feat(Waffle): Add Waffle component for countable-cell visualizations (#864)
-
feat(Spline): Support geo projection (#663)
-
feat(AnnotationLine): Add
x1/y1/x2/y2props for sloped lines (#449)- Pass any combination of
x1,y1,x2,y2to draw a line between arbitrary points. Missing coordinates fall back to the corresponding axis range (sox1/x2alone still span the y range, etc.). The existingx/yshorthand for full-span vertical/horizontal lines is unchanged. - Labels on sloped lines automatically rotate to follow the line angle (normalized to stay upright), with
labelPlacement,labelXOffset, andlabelYOffsetapplied along and perpendicular to the line.
- Pass any combination of
-
feat(AnnotationPoint): Add
linkprop for ring-note style callouts, plus geo projection support (#449)- Pass
link={true}orlink={{ type: 'beveled', radius: 20, ... }}etc. to draw a<Link>from the ring edge to the label. AnyLinkprop (type,curve,sweep,radius,bend,class, ...) can be passed through. - Inside a geo
<Chart>,x/yare now interpreted as[lon, lat]and projected directly, soAnnotationPointcan be used on maps.
- Pass
-
feat(ArcLabel): New component for positioning text labels on arc segments (#817)
ArcLabelis a new marking component for placing text (and optional leader lines) relative to an arc. It's used internally byPieChartandArcChartwhen thelabelsprop is set, but can also be rendered directly inside anArcchildren snippet.Supported placements:
centroid— at the arc centroid (horizontal text, default)centroid-rotated— at the centroid, rotated to follow the arc tangent, flipped where needed so text stays uprightcentroid-radial— at the centroid, rotated to read along the radial direction (center → outer edge)inner/middle/outer— along the inner, medial, or outer arc path (centered viastartOffset: '50%'by default)callout— outside the arc with a leader line that bends horizontally to the label
ArcLabelaccepts a singleoffsetprop that is routed to the placement-appropriate radial padding (centroid offset,innerPadding/outerPadding, orcalloutLineLength), pluscalloutLineLength/calloutLabelOffset/calloutPaddingfor fine-grained control of callout leader lines. The leader line renders via thePathprimitive, so it works in both SVG and Canvas chart layers. -
breaking(Arc): Center arc text along path by default for
inner/middle/outerpositions (#817)getArcTextProps('inner' | 'middle' | 'outer')now defaults tostartOffset: '50%'withtextAnchor: 'middle', centering the text along the arc path rather than anchoring it at the arc start. When an explicitstartOffsetis provided, the anchor falls back to'start'so the text begins at that position (matching prior behavior for callers that set a start offset). -
feat(Arc): Add
innerPaddingoption togetArcTextProps/getTrackTextProps(#817)ArcTextOptionsnow supports aninnerPaddingoption, symmetric to the existingouterPadding. Positive values shrink the inner radius used to build theinner/middlearc text paths, moving text inward (toward the chart center). Previously, offsetting aninner-placed arc label away from the arc edge required overriding the path manually; now it works the same asouterPaddingdoes foroutertext. -
feat(Chart, BrushState): Add band scale (categorical) support for transform pan/zoom and brush selection. Uses range-rescaling pattern to smoothly zoom and pan categorical bar charts. Automatically constrains panning to data boundaries and prevents zooming out past initial view. (#449)
-
fix(Axis): Default
tickSpacingtonullfor categorical band scales, showing all ticks by default instead of reducing them. UsetickSpacing={80}to opt-in to tick reducing on categorical band scale axes. (#449) -
feat(Bar, Bars): Support
<Html>layer (#449)Bar/Bars now render in
<Html>layers in addition to<Svg>and<Canvas>, including per-cornerroundedvariants (top,bottom,left,right,edge, and individual corners). Previously, any non-uniformroundedvalue fell through to a<Path>and was SVG-only. -
feat: Add geo projection support for primitives (Circle, Rect, etc) (#663)
-
feat(Highlight): Add
rprop to scale highlight points using the chart's rScale. Supportsr={true}to use the chart's r config or a custom accessor. (#663) -
breaking(Brush): Redesign brush API (#663)
Breaking changes:
- Remove
modeprop ('integrated' | 'separated') — sync behavior is now driven by presence ofx/yprops - Remove
resetOnEnd— calle.brush.reset()in youronBrushEndhandler instead - Remove
ignoreResetClick— replaced byclickToReset(defaulttrue) - Remove
onResetevent — checkbrush.active === falseinonBrushEnd/onChangeinstead
New features:
- Add
BrushState.move({ x?, y? })for programmatic selection control (like d3'sbrush.move()) - Add
BrushState.selectAll()to select the full domain extent - Add
BrushState.reset()to clear the selection - Add
clickToResetprop (defaulttrue) - Add
zoomOnBrushprop on Chart for simplified charts to opt into brush-to-zoom - Move domain clamping, edge adjustment, and range computation logic into
BrushStateclass - Add
BrushChartContextinterface for easier testing
- Remove
-
feat(Brush): Add constraints to
BrushContext/BrushState, similar toTransformContext'sscaleExtent/translateExtent/constrain. (#879) -
feat: Integrate
annotationsinto simplified charts (#458) -
feat(CircleLegend): New component for visualizing radius (
rScale) values as nested circles (#818)CircleLegenddisplays a set of bottom-aligned nested circles representing values from a radius scale, useful alongside bubble maps and scatter charts that encode magnitude via circle area. By default it readsrScalefrom the chart context, but ascaleprop can also be passed to render standalone.Supports
tickValues/ticks/tickFormatfor value selection and formatting, atitlerendered centered above the circles, andlabelPlacement="right" | "left" | "inline"to render tick labels with a leader line on either side of the circles or centered inside each circle near the top. -
feat(ClipPath, RectClipPath, CircleClipPath, GeoClipPath): HTML layer support + unified
pathAPI (#449)ClipPathnow accepts a singlepath: string(SVG pathdsyntax) that drives all three layers:- SVG: rendered as
<path d={path}>inside the<clipPath>element. - Canvas: wrapped in
Path2Dand applied viactx.clip(...). - HTML: emitted as
clip-path: path("${path}")on a wrapper<div>covering the chart container.
This replaces the previous
canvasClip/canvasClipDepscallbacks (and skipped HTML entirely) with a single declarative value. Theclipsnippet is still accepted for advanced/custom SVG content.RectClipPath,CircleClipPath, andGeoClipPathare rewritten on top of this — they each compute a path string (d3-geo-path already emits one natively) and pass it through. All three now support<Html>layers in addition to<Svg>and<Canvas>.Note:
clip-path: path()requires Chrome 88+, Safari 13.1+, Firefox 118+. - SVG: rendered as
-
feat(ClipPath, RectClipPath, CircleClipPath, GeoClipPath): Add
invertprop to render content outside the clip shape (cutouts/masks) across SVG, Canvas, and HTML layers (#449) -
feat: Unified component tree for Canvas rendering with proper Group transform scoping. Fixes #662 (#663)
- New
registerComponentNode({ name, kind, canvasRender })API replaces bothregisterCanvasComponentand theInsideCompositeMarkboolean context with a single unified component tree. - Canvas rendering now walks the tree recursively with proper
save()/restore()scoping, fixing Group transforms (translate, opacity) leaking to sibling components instead of only affecting children. - Composite marks (Area, Threshold, Hull, Labels, Grid) register as
'composite-mark'nodes, automatically preventing child marks from registering with the chart without manual_skipRegistrationprops. - Removed
retainStateandnamefromComponentRendertype — Group's transform scoping is handled by tree position, and component names live on the tree node.
- New
-
feat(Connector): Add
'swoop'connector type (#449)New
'swoop'connector type draws a circular arc between source and target, equivalent to ObservablePlot's Arrowbendoption. Configured via a newbendprop (degrees, default22.5) — positive bends right (clockwise from source to target), negative bends left,0draws a straight line. Works in both cartesian and radial modes;Linkforwards it automatically. -
- Made
ForceSimulationgeneric over its nodes and links, i.e.ForceSimulation<Node, Link>.(#527) - Exposed
linksviachildrensnippet ofForceSimulation.
- Made
-
Decoupled
ForceSimulationfromChartContext, by taking nodes and links viadataprop. (#526) -
feat(Line, Rect, Circle, Text): Multi-layer compatible
dashArrayand inline color props (#449)- Added a typed
dashArrayprop toLine,Rect, andCircle. Accepts a number, array, or SVG-style string and maps tostroke-dasharray(SVG),setLineDash(Canvas), and eitherrepeating-linear-gradient(HTML lines) orborder-style: dashed(HTML borders). Previously dashed styling was SVG-only when applied via CSS class or attribute. TextandLineHTML branches now honor thefill/strokeprops as inlinecolor/background, so prop-based colors work across all three layers (not just SVG/Canvas).Grid.x/Grid.yandAxis.gridnow acceptstroke,strokeWidth,opacity, anddashArrayin their object form, matching the props forwarded to the underlying line.Rulealready forwarded arbitrary Line props via spread;dashArraynow works there unchanged.- Exports
parseDashArrayanddashArrayToGradienthelpers frompathutils.
- Added a typed
-
feat: add
downloadImage,downloadSvg,getChartImageBlob, andgetChartSvgStringutilities to export charts as PNG/JPEG/WebP images or SVG files (#663) -
feat(Pattern): Canvas support (#458)
-
feat(SeriesState): Support passing
selectedas part of series declaration (Ex.<Chart series={...}>) (#663) -
feat(GeoLegend): New scale-bar legend showing real-world distance for the current
Chartprojection (#818)GeoLegendreads the active geo projection from the chart context and renders a labeled scale bar with tick subdivisions. By default it picks a "nice" round distance that covers ~25% of the chart width, butdistancecan be passed for an explicit value. Supportsunits="km" | "mi", configurableticks,tickFormat,title, and the standardplacementprops. Inspired by Harry Stevens' d3-geo-scale-bar. -
feat(Chart): In projection mode,
scaleExtentandtranslateExtentare now interpreted as relative values (like d3-zoom).scaleExtent: [0.5, 8]means 0.5x to 8x of the fitted projection scale.translateExtentis offset from the initial fitted position in pixels. (#449) -
feat: Support pre-projected topologies in
GeoLegendviareferenceScale(#449)Add a
referenceScaleprop toGeoLegendfor charts that render pre-projected data withgeoIdentity(e.g.us-atlas'scounties-albers-10m/states-albers-10m, pre-projected withgeoAlbersUsa().scale(1300)). When provided, pixels-per-distance is derived from the chart's fit scale and the supplied base scale, bypassing theprojection.invert+geoDistancepath which only works for real lon/lat projections. TheGeoPathbubble-map example now renders a correct scale bar. -
feat(BarChart): Radial support (vertical and horizontal) (issue #469) (#458)
-
feat(Arc/Text): Arc path labels with inner/outer/middle placement and smart flipping (issue #7) (#458)
-
feat(Circle, Ellipse): Support pattern/gradient
fillvalues on the<Html>layer by switching frombackground-colorto thebackgroundshorthand (withbackground-origin: border-boxto keep patterns aligned under the border). Accepts values produced by<Pattern>/<LinearGradient>in HTML mode. (#449) -
feat(Hull): Add CommonStyleProps (fill, fillOpacity, stroke, strokeOpacity, strokeWidth, opacity) for Canvas layer compatibility (#449)
-
feat(Labels): Add
middleplacement and changecenterto center within the bar body (#449)placement="center"now positions the label at the center of the bar body (between the value edge and the baseline). The previouscenterbehavior (label aligned to the value edge with a middle anchor) is now available as the newplacement="middle". -
feat(Rule): Support using as data-driven mark (ex. candlestick, lollipop) by default (
<Rule>using Chart accessors) or passing explicitx/yaccessors (ex.<Rule y={["high", "low"]} />). Resolves #64 (#622) -
feat(Legend, CircleLegend): Show an indicator of the current tooltip value on the legend (#818)
Legend(ramp variant) now draws a small upward-pointing arrow below the color ramp at the position of the currently hovered value, andCircleLegenddraws a 50%-opacity filled circle at the corresponding radius. Both auto-read the hovered data fromctx.tooltip.dataand pipe it through the chart's color (ctx.c) / radius (ctx.r) accessors, so wiring is automatic for charts that configurec/r/cScale/rScaleviaChartprops.A new
valueprop on both components allows explicitly setting the indicator value (overriding the auto-detection), useful when the tooltip data shape doesn't match the chart's accessor.For
scaleThreshold/scaleQuantize/scaleQuantilescales, theLegendindicator centers on the matching bucket swatch. -
feat(ForceSimulation): Added
onNodesChangecallback toForceSimulation(#607) -
feat(Blur): Add Canvas support (#449)
-
feat: Add data mode to primitive components (Circle, Ellipse, Group, Line, Polygon, Rect, Text) (#663)
Primitives now accept string or function accessors for positional props (e.g.
x="date",y={d => d.value}) to automatically resolve values through chart scales and iterate over data. Components also accept an optionaldataprop to override chart context data.Color properties (
fill,stroke) can also be data-driven, resolving per-item through the chart's color scale (cScale). String values are disambiguated: data property names resolve throughcScale, while literal CSS colors pass through unchanged. -
feat: Mark registration for automatic domain calculation, accessor aggregation, and implicit series (#663)
- Marks (Spline, Area, Points, Bars) now register their data, accessors, and colors with the Chart via
registerMark(). - Chart automatically aggregates y/x accessors from marks, removing the need to pass
y={['apples', 'oranges']}when each mark specifies its owny. Works for both horizontal (valueAxis='y') and vertical (valueAxis='x') charts. - Per-mark
dataprops are included in the chart's domain calculation automatically. - Implicit series are generated from mark registrations when no explicit
seriesprop is provided, enabling tooltip and legend support without requiring series definitions.
- Marks (Spline, Area, Points, Bars) now register their data, accessors, and colors with the Chart via
-
feat: Add inertia (momentum) support for transform drag gestures (#663)
-
feat: New ArcChart component (#458)
-
feat(Pattern): Support
<Html>layer by producing CSSrepeating-linear-gradient(lines) andradial-gradient(circles) values usable as abackground/fill. Gradient-valuedbackground(e.g.<Pattern background={gradient}>) is also supported. (#449) -
feat: Per-layer variants for primitives, compound marks, and high-level charts (
layerchart/svg,layerchart/canvas,layerchart/html) (#848)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.// 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,RadialGradientCompound 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,MonthGeo components (
layerchart/geo)
GeoPath,GeoSpline,GeoPoint,GeoCircle,GeoTile,TileImage,Graticule,GeoClipPath,GeoEdgeFadeGraph components (
layerchart/graph)
RibbonHigh-level chart wrappers — pre-composed charts with built-in tooltips, highlights, and series handling
LineChart,AreaChart,BarChart,ScatterChart,PieChart,ArcChartThe 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:
Patternhtml: 14.81 → 0.92 KB (-94%) — HTML implementation is just CSS-string generationLinearGradienthtml: 14.38 → 0.53 KB (-96%)Imagecanvas: 14.95 → 3.73 KB (-75%)Textsvg/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) andCell(-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/svgorlayerchart/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>pathIn 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 theLayer/Axis/Grid/Rule/Highlight/ChartClipPathimport chain it pulls in.Use it for geo maps, custom layouts, or any chart that renders its own primitives directly via the
childrensnippet:<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
corefor users on the agnostic API (transitive cost fromHighlight/Axis/Chart) — a worthwhile tradeoff for the opt-in per-layer savings.See the updated "Bundle Size" guide for the full table, tradeoffs, and when to opt into per-layer imports.
-
feat(PieChart/ArcChart): Add top-level
labelsprop (#817)PieChartandArcChartnow accept alabelsprop that renders text labels on each arc without requiring a customarcsnippet. Passtrueto enable defaults (centroid placement, default value accessor), or an object to configure anyArcLabelprops — placement, offset, value accessor, callout line lengths, leader line style, text class, etc.<PieChart {data} labels={{ placement: 'callout', value: 'fruit' }} />
-
breaking(Chart): Rename
tooltipprop totooltipContextto better describe purpose and fix conflict with newtooltipsnippet (#663) -
feat(Circle, Text): Inherit chart accessors by default in data mode (#862)
<Circle>and<Text>now fall back to the chart'sx/y/raccessors (viaxGet/yGet/rGet) when the corresponding position prop is omitted — matching how<Points>and the new<Dodge>work. This lets the chart be the single source of truth forx/y/rand removes the boilerplate of repeating those props on every primitive:<!-- Before --> <Chart {data} x="date" y="value" r="size" rRange={[2, 10]}> <Circle {data} cx="date" cy="value" r="size" /> </Chart> <!-- After --> <Chart {data} x="date" y="value" r="size" rRange={[2, 10]}> <Circle {data} /> </Chart>
CircleandTextalso now enter data mode whendatais explicitly passed (in addition to the existing trigger whencx/cy/x/yare data-driven), so the implicit-accessor pattern works without needing to pass redundant string accessors just to trigger iteration.Behavior is unchanged whenever any position prop is set explicitly — the hardcoded defaults (0/0/1) only apply when neither prop nor chart-level config is present. All existing usages in the docs pass explicit position props, so this is purely additive.
-
breaking(Points): Remove
<Points links>prop. Use<Rule>with x/y accessor instead (#622) -
feat: Add Chord layout and Ribbon primitive (#663)
-
feat(Pattern): Simplified definitions via
lines/circlesprops (issue #472) (#458) -
feat(Tree, Link, Connector): Add radial support (#831)
Treenow detects<Chart radial>and lays out withd3.tree().size([2π, min(width, height)/2])plus radial separation. Nodes emit polar coords (x= angle,y= radius).Connectorgains aradialprop (defaults toctx.radial) that interpretssource/targetas polar and dispatches to newgetConnectorRadialPresetPath/getConnectorRadialD3Pathhelpers. Radial behavior per connectortype:straight— straight cartesian linesquare— radial → arc at midR → radialbeveled— chord at source radius with chamfered corner (controlled byradius)rounded— visx LinkRadialCurve Bezierd3—d3.linkRadialby default; with acurveprop,curveStep/curveStepBefore/curveStepAftermap to polar arcs/radials, other curves go throughd3.lineRadial
LinkforwardsradialtoConnectorautomatically when inside a radial<Chart>. -
feat: Add
Layercomponent to easily switch between Svg, Canavs, and Html layers (#458) -
feat(Rect): Add
cornersprop for per-corner rounding (#449)New
cornersprop accepts either a number (equivalent torx), a[topLeft, topRight, bottomRight, bottomLeft]tuple, or{ topLeft, topRight, bottomRight, bottomLeft }. Works across<Svg>,<Canvas>, and<Html>layers — Svg renders a<rect>when corners are uniform and a<path>when they differ, Canvas usesroundRect's per-corner radii, and Html uses the 4-valueborder-radiusshorthand.Also exports a shared
roundedRectPath(x, y, width, height, [tl, tr, br, bl])helper frompathutils for building per-corner rounded-rect path data. -
breaking(TransformContext): Rename
initialScrollModetoscrollModeand make it reactive (#663) -
feat(TransformContext): Add
scrollActivationKeyoption to require a modifier key (meta, alt, control, shift) for scroll/wheel zoom/pan, preventing accidental page scroll from triggering transforms (#663) -
feat: Add
renderChart()tolayerchart/serverfor server-side chart-to-image rendering (PNG/JPEG) (#813) -
feat(Transform): Add zoom/pan constraints (
scaleExtent,translateExtent,constrain,domainExtent), replacegeo.applyTransformwithtransform.mode: 'projection' | 'rotate', and reactively sync initial transform values on projection changes (#663) -
feat(Bar): Support fixed
widthandheightprops to override scale-derived dimensions, centering the bar within its band. Resolves #360 (#663) -
feat: Auto-compute Bar/Bars mount animation initial values from chart scales (#663)
Bar now automatically derives
initialY/initialHeight(vertical) orinitialX/initialWidth(horizontal) from the chart's scale range whenmotionis configured, removing the need to hardcode pixel values.Also improves
valueAxisinference onChartState— when not explicitly set, it is now derived from scale types (band scale on y →valueAxis: 'x', band scale on x →valueAxis: 'y'). -
feat(Chart): Add cartesian pan/zoom via
transform={{ mode: 'domain' }}with single or both axis support. Resolves #366 (#663) -
feat(Spline): Add motion support for mount animation from baseline (#663)
-
feat(Spline): Support function-valued
stroke,fill, andopacityfor per-segment styling (#449) -
breaking: Move heavy-dep components into sub-path exports (#845)
The following components are no longer re-exported from
'layerchart'and must be imported from new sub-paths:'layerchart/geo'—GeoCircle,GeoClipPath,GeoEdgeFade,GeoLegend,GeoPath,GeoPoint,GeoProjection,GeoRaster,GeoSpline,GeoTile,GeoVisible,Graticule,TileImage'layerchart/hierarchy'—Tree,Treemap,Pack,Partition'layerchart/force'—ForceSimulation'layerchart/graph'—Dagre,Sankey,Chord,Ribbon
This isolates each group's external d3 dependency (
@dagrejs/dagre~22 KB,d3-geo~15 KB,d3-force~7 KB,d3-hierarchy~6 KB,d3-sankey~6 KB,d3-chord~2 KB) behind an opt-in import — defending against bundlers that don't tree-shake the root barrel cleanly.Voronoi/Hullstay at root (already lazy-loaded viaTooltipContext).Contour/Density/Raster/BoxPlot/Violin/Thresholdand high-level charts (LineChart,BarChart, etc.) remain at root.Migration: update affected imports, e.g.
-import { Tree, GeoPath, ForceSimulation } from 'layerchart'; +import { Tree } from 'layerchart/hierarchy'; +import { GeoPath } from 'layerchart/geo'; +import { ForceSimulation } from 'layerchart/force';
-
feat(Text): Add
fontSizeprop with auto-derivedcapHeight(#862)Adds a typed
fontSize?: number | stringprop on<Text>(number = pixels, string passes through). When set,capHeightdefaults tofontSize * 0.71instead of the legacy'0.71em'— so per-item scaled labels withverticalAnchor="middle"align to a common visual baseline without an explicitcapHeightoverride.Previously,
getPixelValueresolved'0.71em'against a hard-coded 16px, so vertical centering was only correct for ~16px text. Larger labels sat too low, smaller ones too high — visible on text-driven beeswarms or any caller scaling labels per-element.<!-- Before: needed both font-size and capHeight to center correctly --> <Text font-size={r * 1.4} capHeight="{r * 1.4 * 0.71}px" verticalAnchor="middle" ... /> <!-- After: one prop, centering handled automatically --> <Text fontSize={r * 1.4} verticalAnchor="middle" ... />
-
feat(Text): Add
formatprop and tween numericvaluewhenmotionis configured (#449) -
breaking: Extract
Pathprimitive component fromSplinefor better separation of concerns (#659) -
feat(Tooltip): Add
fadeDurationprop to control fade in/out transition (#828)The fade transition on
Tooltip.Rootis now configurable via thefadeDurationprop (default:100ms). Set to0to disable the fade transition entirely. -
feat(Tooltip): Portal tooltip to body by default to fix overflow clipping. Resolves #446 (#828)
Tooltip.Root now portals to
document.body(or.PortalTarget) by default using theportalaction from@layerstack/svelte-actions. This prevents tooltips from being clipped by ancestor elements withoverflow: hidden. The tooltip usesposition: fixedwith viewport-relative coordinates when portaled. Setportal={false}to restore the previous inline behavior. Bothcontained="container"andcontained="window"modes continue to work correctly with portaling. -
feat(tooltipContext, Voronoi): Add
x/yaccessor overrides and default array endpoint to max (#449)- New
x/yprops ontooltipContextandVoronoiaccept anAccessor(property name string or function). When set, hit-detection points use these accessors instead of the Chart'sx/y. Useful when the Chart's accessor returns an array (e.g.x={['POP_1980', 'POP_2015']}) and you want detection at a specific endpoint:<Chart {data} x={['POP_1980', 'POP_2015']} tooltipContext={{ mode: 'voronoi', x: 'POP_2015', y: 'R90_10_2015' }}>
- Breaking (minor): when the chart's x/y accessor returns an array (duration bars, candlesticks, stacked areas, etc.), quadtree and voronoi hit-detection now default to the max value of the array instead of the min. For most use cases (target endpoint, stack top) this is the more natural hover position. If you need the old behavior, pass an explicit
x/yaccessor ontooltipContext/Voronoi.
- New
-
feat: Add Annotation components (AnnotationPoint, AnnotationLine, AnnotationRange) (#458)
-
feat: Support continuous color scales via
cScaleprop without requiringcRange(#663)- Allow
cScale(e.g.scaleSequential(interpolateTurbo)) to activate withoutcRange, enabling pre-configured sequential/diverging color scales - Guard
createScaleagainst undefinedrangeto avoid breaking interpolator-based scales - Auto-detect numeric
cDomainvalues and useextentinstead ofunique, producing correct[min, max]domains for continuous scales - Prefer
cScalecolor over default series color in tooltip/highlight when a color scale is configured
- Allow
-
feat(Labels): Support
seriesKeyinlabelsprop to filter which series renders labels. Resolves #633 (#663) -
feat: Support css-only usage (no Tailwind required) while retaining first-class Tailwind support (#557)
-
feat(Rect): New edge-based props (
x0/x1/y0/y1) along with existing (x/y/width/height) andinsetssupport (#663) -
feat: Add Vector component (#663)
Patch Changes
-
fix: Prevent submitting forms when clicking legend buttons (#841)
-
fix(Area): Default y0 baseline to chart's yBaseline when set (#663)
Area's y0 fallback now respects the chart's
yBaselineprop (e.g.yBaseline={0}set by AreaChart) instead of always usingmin(yScale.domain()). This fixes areas filling to the bottom of the chart instead of to the baseline when data goes negative. -
feat: Add
strokeandfillprops toAxisandGridfor explicit color control (useful for SSR where CSS variables are unavailable) (#813) -
Support
tickSpacingfor band scales on Axis, thinning tick labels when the domain is larger than the available space. Automatically shows more tick labels when zoomed in on band scale transforms. (#449) -
fix(ScatterChart): Change default tooltip mode from
voronoitoquadtree(#578) -
fix(Threshold): Properly clip
abovesnippet (resolving 1/2 width clipping issues when using Spline) (#659) -
fix(Bars): Fix inverted rect when rendered top-to-bottom or right-to-left. Fixes #540 (#613)
-
fix(AreaChart|BarChar|LineChart): Use value axis (typically y) property name/accessor for tooltip label if defined as string (ex.
<AreaChart x="date" y="visitors">will usevisitorsinstead ofvalue) (#523) -
fix(TooltipContext): Handle chart padding when using
quadtreemode (#449) -
breaking(AnnotationLine|AnnotationPoint): Change
labelOffsetinto explicitlabelXOffsetandlabelYOffsetfor greater control (aligns with AnnotationRange) (#492) -
fix(Points|Labels): Correctly position when using x1 / y1 scales (issue #773) (#663)
-
fix(GeoPath): Fix reactivity with
curvewhen using Canvas context (#561) -
fix(Canvas): Fix pointer events (hit canvas) when using Brave browser with fingerprinting protection enabled (#449)
-
feat(TooltipContext): Support
quadtree-xandquadtree-ymodes. Resolves #525 (#578) -
fix(ArcChart): (Re-)disable grid by default (regression from recent refactor) (#449)
-
fix(Tooltip): Correctly set tooltip position on chart enter and exit (#655)
-
fix(GeoPath): Do not register with hit canavs unless pointer events (onclick, onpointermove, etc) or tooltipContext are defined (#549)
-
fix(canvas): Compose globalAlpha multiplicatively so Group opacity propagates to children (#831)
Canvas
renderPathDatawas overwritingctx.globalAlphawith absolute values for element opacity, fill opacity, and stroke opacity. This meant a parent<Group opacity={0.2}>had no effect on child marks rendered on canvas — the child's own opacity (defaulting to 1) would replace the inherited value.Now all three sites multiply against the current
globalAlpha, which correctly composes with ancestor Group opacity set viasave()/restore()scoping. Also removes double-application of elementopacityinside the fill/stroke blocks (it's already applied at the element level). -
fix(Chart): Explicit
<Chart data>now takes precedence over marks' implicit-series data (#449)When a mark registered its own filtered dataset via
markInfo(e.g. a decorative<Text data={highlighted}>showing labels for a subset), two things went wrong:ctx.datawould silently switch to the filtered subset viaseriesState.visibleSeriesData, causing sibling array-driven marks (like<Link>) to iterate only the subset.- An implicit series would be created from the decorative mark, narrowing the domain calculation to only the subset's values.
Now when
<Chart data>is explicit (non-empty):ctx.dataalways returns the chart's data.- Marks whose axis accessor matches the chart's axis accessor (including any element of an array accessor like
y={['v1', 'v2']}) are treated as decorative and don't create implicit series — even if they have their owndataarray.
Marks with their own data still contribute to
flatDatafor domain calculation when their accessor differs from the chart's (the multi-dataset / multi-series scenario). -
fix(Chart): Disable text selection to prevent selection while dragging (#879)
-
perf(Chart): Eliminate per-instance props spread in
ChartState(#857) -
fix(Chart): Don't compute
[undefined, undefined]domain whenseriesis metadata-only (#449)When
seriesis configured for legend/color metadata only (no per-seriesdata, and items lack the series-key properties on the value axis), the value-axis domain calculation collected all-undefined values and returned[undefined, undefined]. Combined withmotion, this threwCannot spring undefined valueswhenever the series visibility changed (e.g. toggling a legend swatch).The series-aware branch now filters out null/undefined values and falls through to the other domain-resolution paths when nothing remains, instead of returning a poisoned extent.
-
fix(HighlightKey): Define
set()with arrow function to solvecurrentaccess when passed directly (#449) -
feat(Canvas): Support disabling the hit canavs (useful when animations are playing) (#449)
-
fix(TooltipContext): Revert back to pointer events (instead of mouse/touch) but with
touch-action: pan-y. Provides simplified events while allowing horizontal scrubbing with vertical scrolling. (#557) -
fix(PieChart): Do not pass
yaccessor to use linear scale fallback (#631) -
fix(AnnotationPoint): Do not propagate mouse/touch move/leave events to TooltipContext after switching from pointer events. Fixes #598 (#602)
-
fix(LineChart): Restore passing xScale / yScale overrides (#449)
-
refactor(Chart): Add
debugprop and updatesettingscontext (#663) -
breaking: Change
defaultChartPadding(axis, legend)todefaultChartPadding({ axis, legend })and support overrides (ex.defaultChartPadding({ left: 50 })) (#659) -
fix(TooltipContext): Fix touch scrolling on mobile. Fixes #255 (#566)
-
fix(Connector, Link): Orient d3 step curves by
orientation(#449)- Added
orientation?: 'horizontal' | 'vertical'prop toConnector(defaults to'horizontal').Linkforwards its own orientation so step curves step along the natural flow direction. curveStep,curveStepBefore, andcurveStepAfternow step alongyin vertical orientation instead of always stepping alongx.
- Added
-
fix(Highlight|TooltipContext): Support xInterval / yInterval (#449)
-
fix(TooltipContext): Fix
bandmode regression when both x/y are scaleBand (ex. punchcard chart) (#557) -
breaking(Treemap): Remove
selectedprop (#516) -
feat(Spline): Add
valuetostartContentandendContentsnippets to easily access thexandydata values (#537) -
fix(Canvas): Only apply text/font properties to canvas to improve performance (#561)
-
fix(AnnotationRange|TooltipContext|Highlight): Fix using interval scales with reversed data (ex. xReverse) (#659)
-
fix(SimplifiedCharts): Properly handle
legendprop as object when determining bottom padding (#557) -
fix(Axis): Fix display of axis labels (#591)
-
fix(Text): Apply
fill: currentColorto support more straightforward way of changing color (ex.class="text-red-500"orstyle="color:red") (#557) -
fix: Default chart padding now applied when using ChartChildren layout (marks snippet) (#663)
When
<Chart>is used withmarks/grid/axissnippets (no explicitchildrensnippet), default padding is now applied to match the axes that ChartChildren renders by default. Whenchildrenis provided directly (e.g. Treemap, Pack), no default padding is applied since the user controls layout. This can still be overridden with explicitaxisorpaddingprops. -
feat: Support passing
FormatConfig(ex.{ type: '...', options: { ... } }) anywhereFormatTypeis supported to simplify custom formatting (ex.variant) (#521) -
fix(Tooltip): Apply inverse transform for quadtree lookup when zoomed/panned (#663)
-
fix(Treemap): Add
maintainAspectRatio(default: false) to opt into tiling function adjustment (primarily for zoom) (#516) -
fix(ForceSimulation): Restore performance to at/near Svelte 4 performance (issue #451) (#458)
-
breaking(Legend): Rename
classes.swatchestoclasses.item(#571) -
fix: Improve memory leak caused by detached DOM increase when using Canvas rendering due to sometimes still rendering Svg components (ex.
<g>vs<Group>) (#490) (#490) -
fix(Highlight): Properly handle area highlights with y-axis time scales (#562)
-
fix(Group): Default
opacitytoundefinedinstead of1to allow overriding via class (ex.opacity-20) (#520) -
feat: Support passing
PeriodTypeCodestrings for simplified date formatting and reduce imports. Example:format={PeriodType.Day}is nowformat="day". Also supported with config object for passing additional options (ex.format={{ type: 'day', options: { variant: 'long' } }}). Supported for allformatprops include Axis, Labels, Legend and Tooltip. (#521) -
fix(Legend): Improve / simplify responsive use cases with additional default classes (center, shrink, truncate) (#571)
-
docs: Add examples for standalone, daisyUI v5, shadcn-svelte v1, Skeleton v3, and Svelte UX v2 (next) (including light/dark theming) (#557)
-
Update dependencies (#629)
-
fix(autoScale): Ignore
nulldomain values, fixing some brush examples (#449) -
breaking(Axis): Rename
x="left|right"andy="top|bottom"props with$prefix (ex.<Axis x="$left">) (#622) -
refactor: Rename simplified charts
renderContextprop tolayer(#659) -
feat(LineChart): Support
orientation="vertical". Resolves #640 (#557) -
perf: Optimize primitive component instantiation (~3-5x faster for Rect, Circle, Ellipse, Line, Text, Path, Group) (#449)
createMotion: Fast-path passthrough when nomotionprop is provided, avoiding$state/$effectoverhead per axiscreateDataMotionMap: Short-circuit whenmotionisundefined, skippingparseMotionPropoverheadcreateKey: Only create fill/stroke key trackers in canvas layer (skipped for SVG/HTML)registerComponent: SkipregisterMarkfor emptyMarkInfo(pixel-mode marks)- All primitives: Skip
$effectfor data motion tracking when no motion is configured - Rect/Image: Avoid per-axis
parseMotionPropcalls whenmotionisundefined
-
feat(Marker): Add
squareandsquare-stroketypes (#805) -
fix(AnnotationRange): Don't extend past chart bounds when
xis omitted on band scales, and treatnullon either side ofx/yas "extend to chart edge". (#449) -
fix(ArcLabel): Support rotation in Canvas mode (#449)
Changed
centroid-rotatedandcentroid-radialplacements to passrotateprop instead of SVGtransformstring toText, enabling rotation in Canvas rendering. -
fix(Area): Handle degenerate domains (e.g. all-zero data) and unify y0/y1 baseline logic (#663)
- Guard against degenerate scale domains where min === max (e.g.
[0, 0]), which causedyScale()to returnNaNand break area path rendering - Unify Area y0/y1 path computation to consistently use accessors through the scale, removing duplicate fallback logic
- Guard against degenerate scale domains where min === max (e.g.
-
fix(BarChart): automatically round the outer edge of each direction in
stackDiverginglayout (#663) -
fix(canvas): Resolve
currentColorforfill/stroke(and other style props) (#449) -
fix(Pattern): fix alignment and sharply render on high-DPI displays when using Canvas layers (#864)
-
fix(downloadImage / getChartImageBlob): Fix image download (container sizing and text clipping) (#449)
-
fix: Remove TypeScript
ascasts fromclass={...}attributes inPathandGeoPointto fix unocss compat. (#449) -
fix: guard against undefined accessor in printScale (#875)
When activeGetters includes z or r scales that are not configured, the accessor is undefined, causing acc.toString() to throw. Added null check.
-
fix: Skip mark x/y/data from domain/series calculation when geo projection is active (#449)
-
fix(GeoPath): Fix canvas tooltip by conditionally passing onclick to Path, preventing non-interactive overlays from capturing hit canvas events (#449)
-
fix(GeoPath): Avoid passing
undefinedevent handlers to underlyingPath, preventing a Svelte error while preserving canvas hit-testing for non-interactive paths (#449) -
fix(ChartState): Don't create spurious implicit series when mark accessor matches chart's own axis accessor, fixing domain corruption for heatmap/Cell charts (#449)
-
fix(Pattern): Restore canvas layer support by registering as a
groupnode so snippet children (e.g.<Rect fill={pattern}>) render correctly (#449) -
fix: Pie and Arc components now correctly use Chart's
xRangeprop for angle degrees instead of the computed viewport pixel range, and compute radius from chart dimensions instead of scale range (#449) -
fix(scaleBandInvert): Account for range offset in band scale inversion. Previously assumed range started at 0, causing incorrect pixel-to-category mapping when the scale range was transformed. (#449)
-
fix(SeriesState): Avoid
derived_inertcrash when chart unmounts under a<svelte:boundary>(#855)The
selectedKeyssync effect was wrapped in$effect.root, creating an isolated scope that survived chart unmount. When the parent chart was destroyed (e.g. an example reloading inside the docs<svelte:boundary>after an async$derivedre-evaluated), the#seriesderived became inert while the orphaned effect kept reading it — producingReading a derived belonging to a now-destroyed effect may result in stale valueswarnings followed byTypeError: e.some is not a function. The effect now lives in the constructor, scoped to the component that instantiatedSeriesState, so it is torn down with the chart. -
fix(Spline): Allow CSS class
opacityto fade lines on the Canvas layer.Splinewas always passingopacity={1}to the underlyingPathwhen no series fade was active, which becameconstantStyles.opacity = 1in the canvas renderer and shadowed the value resolved from a user'sclass(e.g.class="opacity-20"). Now skip passingopacitywhen the computed series fade is the no-fade default, so the class can take effect — matching SVG behavior where CSS class rules override the presentation attribute. (#449) -
fix(Spline): Restore
series.props.opacity(and other style props) precedence over the computed series fade opacity. Regression introduced by per-segment styling refactor where the explicitopacitywas spread afterseries.props, clobbering per-series opacity values (e.g.series={[{ props: { opacity: 0.1 } }, ...]}). (#449) -
fix: Allow negative string values (e.g.
y="-6") inTextposition props to be treated as pixel values instead of data property names (#449) -
fix(TransformContext): Reactively sync
processTranslateanddisablePointerto TransformState when props change. Fixes inverted globe dragging when dynamically switching between flat and globe projections. (#449) -
fix(Arc, RectClipPath, ChartClipPath): Restore on-mount tween animations (#855)
Two related regressions introduced in the layer-component split (#848) prevented
motion+initial*props from animating on mount.Arc—motion,value,initialValueand the rest of Arc's geometry props (domain,range,startAngle,endAngle,innerRadius,outerRadius,cornerRadius,padAngle,track*,offset) were not destructured inArc.base.svelte, so they leaked through{...restProps}onto the inner<Path>. The forwardedmotionmadePathalso tween the path-string on top of the end-angle tween thatArcStatealready drives, producing visibly wrong arcs (NaN coordinates, runaway radii). They are now extracted and passed explicitly toArcState.RectClipPath/ChartClipPath—motion,initialX,initialY,initialWidth,initialHeightwere declared on the type but never consumed: the path was a plain$derivedof the staticx/y/width/heightprops, so passing<ChartClipPath initialWidth={0} motion={{ width: { type: 'tween', … } }}>rendered the final width on mount with no animation. Each dimension now flows through its owncreateMotion(using the correspondinginitial*value as the animation start), and the path is built from the animated values. -
fix(ChartState): Don't filter explicit
x1Domain/y1Domainby visible series when no series are configured. Restores grouped layout for composable<Chart>usage (e.g.<Bars>withx1/x1Domain/x1Range) where the visible-series filter previously emptied the secondary band scale domain, collapsing all bars to a single category position. (#449) -
fix:
flattenPathDatanow handles relative arc commands, fixing rounded bars starting below the baseline during mount animation (#663) -
refactor: Move contexts to separate
$lib/contextsmodule (#659) -
feat(Layer): Allow
typeto be optional, fallbacking back tosettings.layertype (#659) -
fix(Calendar): Pass
cellSizeto children snippet (useful when responsive) (#558) -
fix(Axis): Correctly place multiline parts based on placement (#574)
-
fix(Axis): Key using tick value instead string representation to support millisecond precision (#508)
-
fix(Rect): Support rounded (rx/ry) in Canvas layers (fixes #481) (#659)
-
fix(Axis): Filter distinct tick values (useful when manually injecting extra values) (#615)
-
feat: Support global settings (layer type, debug, etc) (#659)
-
feat(TooltipContext): Support
bandmode with time scale (similar to band scale) (#562) -
fix(Axis): Add time duration aware tick value/format support (#508)
-
fix: Default geo projection
translateto container center whentranslateandfitGeojsonare not specified, instead of using d3-geo's fixed default ([480, 250]) (#815) -
fix(Chart): Enable scroll zoom for globe projections by including
scale: truein defaulttransformApplyfor globes. (#449) -
Removed lodash-es dependency (#659)
-
fix(Area|Highlight): Properly handling diveraging stack layouts for negative values (line, highlight point) (#663)
-
fix(Bar): Fix browser lockup when switching between group and stack layouts (#659)
-
fix(AreaChart|LineChart|DefaultTooltip): Handle per-series data with different length (#557)
-
fix(Calendar): Respect
startinstead of always start of year (#657) -
feat(Highlight): Support passing
opacity(#557) -
fix(LineChart): Change default tooltip mode from
bisect-xtoquadtree-x(works with catagorical data and does not require data to be sorted) (#578) -
feat(Circle|Rect): Support passing children snippet for Html layers (#659)
-
fix(SimplifiedChart): Still add selected legend item opacity when item classes are also applied (#557)
-
fix(Rect): On the
<Html>layer, setbackground-origin: border-boxso fills/patterns start at the outer edge — previously the CSSbackgroundshorthand reset origin topadding-box, shifting patterns inward byborder-widthwhen a stroke was applied. (#449) -
fix(Rect, Circle, Ellipse): Apply
box-sizing: border-boxon the<Html>layer so the visual extent equalswidth×height(orr * 2,rx * 2×ry * 2) — the border is drawn within that extent instead of added to it, matching SVG bounds. (#449) -
fix(Rect, Circle, Ellipse): On the
<Html>layer, defaultborder-widthto1pxwhenstrokeis set without an explicitstrokeWidth, matching SVG's implicitstroke-width: 1. Also ensures Circle/Ellipseborder-widthgets the requiredpxunit. (#449) -
docs: Document each component's context support (svg, canvas, html) with interactive toggle (#549)
-
feat(Legend): Add
selectedprop to fade out unselected items (if passed and non-empty) (#557) -
docs: Add non-radial BarChart duration example and improve radial example (#496)
-
feat(Chart): Support passing explicit
widthandheightinstead of requiring parent dimensions (#659) -
fix(Image): Stop disabling pointer events by default (#862)
-
fix(force-simulation): Fixed a bug that would sometimes keep a simulation running, when its inputs change, even if
alpha < alphaMin(#546) -
fix(Bar): Fix bar rounding direction when using xReverse/yReverse with interval scales (#659)
-
feat: Add
applyLanes()array util to support densely packing timelines (#449) -
feat(TooltipContext): Add
touchEventsto control touch event behavior. Defaults topan-yto allow vertical scrolling but horizontal scrubbing. (#557) -
perf: Lazy-load opt-in features in
corepath (#845)5 components/dependencies that previously sat in every
<Chart>user's sync graph are now dynamically imported only when the corresponding feature is used:BrushContextinChart— only loads when<Chart brush={...}>is set (defaultundefined)DefaultTooltipinChartChildren— only loads whentooltipContextis set and no customtooltipsnippet is providedd3-quadtreeinTooltipContext— only loads whenmodeis'quadtree','quadtree-x', or'quadtree-y'SplineinGrid— only loads when rendering radial linear grid lines (<Chart radial>withradialY="linear")BarinHighlight— only loads when<Chart highlight={{ bar: ... }}>is set (defaultfalse)
Result: ~10 KB gz off
core(115.6 → 105.25 KB) and comparable savings on every cartesian/geo/graph/hierarchy scenario, with no impact on rendered output for users who already opt into these features.Also switches internal
@layerstack/svelte-actionsimports from the barrel (@layerstack/svelte-actions) to sub-paths (@layerstack/svelte-actions/styles,@layerstack/svelte-actions/portal). No production bundle effect — bundlers already tree-shake the unusedpopover.js— but it stops the Svelte REPL/CDN from eagerly fetching@floating-ui/dom(popover's transitive dep) when consumers loadlayerchartfrom a CDN. -
feat(Voronoi): Support passing
rto define a max radius (clip path) (#449) -
feat(Axis): Use
formatto filter ticks (integer and date/time). Helpful to keep ticks above a threshold for wide charts or short durations. (#615) -
fix(GeoPath): Improve performance by only using custom geoCurvePath when
curveoverridden (#561) -
fix(Highlight): Fix display of lines for first values (
0coord). Fixes #568 (#569) -
fix(Tooltip): Use standard CSS classes (non-tailwind) for root element to simplify some usage (including shadcn-svelte) (#511)
-
fix(Points|Labels): Correctly position when using array accessors (duration charts, etc). Fixes #633 (#663)
-
fix: Workaround Svelte 5.52+ regression (Parse failure: Expected '{', got '(') (#449)
-
fix(Points): Update
point.x/point.ybased onctx.radialto simplify children snippet usage (#611) -
feat(SeriesState): Add
isHighlighted(seriesKey)to easy check if series is hightlight (or should be faded) (#557) -
fix(TooltipContext): correct
bisect-bandhit detection by accounting for chart padding (#806) -
feat(Axis): Support multiline ticks for time intervals (#508)
-
fix(Treemap): Fix reactivity of props (tile, padding, etc) (#516)
-
feat(Raster, Contour): support bounded geo raster overlays with projected interpolation (#449)
-
feat(BarChart): Support time scale with
xInterval/yIntervalprops (#562) -
fix(TooltipList): Align label to top (start) instead of center by default (#449)
-
fix(Bar): Clamp radius to width/height to not cause artifacts with small values (including
0) when rounding a single edge. Fixes #383 (#610) -
feat: Add Polygon primitive (#533)
-
breaking(Blur): Remove children snippet props (not needed and easier to support canvas in the future) (#549)
-
fix(Axis): Fix memory leak and improve performance when tick values are
Dateinstances (#586) -
fix(BarChart): Improve handling time scale for value axis (ex. xScale for horizontal orientation) (#496)
-
fix(Calendar): Support showing month labels without path via
monthLabelprop (true by default) (#449) -
perf: Reduce per-tick reactive overhead in
Path/Link(force-simulation graphs) (#855)In mark-heavy scenes (force simulations with hundreds of links flowing through
Link → Path) several reactive structures unconditionally subscribed every<path>template updater to props that don't change on a tick, causing per-frame work to scale with the number of props × the number of marks. Each fix below is independent; together they take the lattice (n=20, 760 links) example from ~5–6 fps to ~9 fps during simulation.PathState.tweenedPathDatanow reads onlypathData, not all Path props.
Pre-fix, the getter resolvedpathDataviagetProps(), a function that constructs an object literal of every reactive Path prop. Each read oftweenedPathData(i.e. each per-tick<path d=...>update) therefore subscribed the updater to every Path prop and re-read all of them.PathStatenow takes a dedicatedgetPathDatagetter alongsidegetProps, and the hot-path tween / DOM read only touchespathData.Path.svg.svelteandPath.canvas.sveltepass them as separate getters.Link.base.sveltepasses a stablegetPathDatafunction rather thanmotionPath.currentdirectly.
ReadingmotionPath.currentfromLink.base.svelte's template subscribed the entire<Path>block to every tick, forcing the parent's prop spread ({...restProps}) andcls(...)evaluation to re-run on every change. Passing a stable function reference moves the per-tick read inside<Path>'s own template, keepingLink.base.sveltestable. Requires the newpathData?: string | (() => string)form onPath.Path.svg.svelteallocatesdraw-related state lazily.endPoint = createControlledMotion(..., { type: 'none' })was created for everyPath, even when nodrawtransition was configured. Now only created whendrawis set.- The
$effectthat trackedtweenedPathDataforstartContent/endContentpositioning ran on everyPath, even when neither prop was provided. Now only registered when at least one is set. drawKeyis only ever set whendrawis configured, so the{#key c.drawKey}block is a no-op for paths without a draw transition. The block stays unconditional — splitting it behind{#if draw}showed no measurable benefit over leaving the inert subscription in place.
Path.svg.svelteextracts styling props out of...rest.
pathData,class,fill/fillOpacity/stroke/strokeOpacity/strokeWidth/opacityandmotionare now destructured out of$props()rather than left in...rest, so the<path>element's{...rest}spread doesn't re-evaluate every frame when those props change (pathDatachanges on every force-sim tick;classis typically a freshcls(...)string per parent render).Link.base.sveltedrops a redundant prop spread.
Removed{...extractLayerProps(restProps, 'lc-link')}before{...restProps}— the call's only contribution (class) was being immediately overridden by the explicitclass={cls('lc-link', …)}that follows, making the spread pure overhead. -
fix(Calendar|MonthPath): Support canvas by using
Splineinstead ofpath(#549) -
refactor: Replace
date-fnsusage with existingd3-timeto reduce bundle size (#551) -
feat: Add Month component (#671)
-
fix: Support
opacityprop/style when Canvas rendered for all primatives (#449) -
feat(LinearGradient): Support Html context (#557)
-
fix(ArcChart): Do not pass y accessor to use linear scale fallback (#449)
-
feat(ForceSimulation): Refined
onstart/ontick/onendevents ofForceSimulation(#547) -
refactor: Update
@layerstack/svelte-stateand replace remaining@layerstack/svelte-storesusage (media query) (mostly docs related except Canvas) (#449) -
feat(Axis): Support responsive tick counts via
tickSpacingprop (#508) -
fix(Primatives): Apply default classes when using Canvas context (like Svg). Resolves #544 (#557)
-
chore: Update deps (including dagre from 1.x to 2.x) (#449)
-
breaking(Bar): Rename
barprop todatato better represent usage (#449) -
fix: improve compatibility with UnoCSS Svelte scoped preprocessing (#813)
- Remove TypeScript-only
asassertions from exported Svelte markup in core mark components so preprocessors that parse markup expressions as plain JavaScript can consume packaged components without failing
- Remove TypeScript-only
-
fix(Axis): Additional multiline month fix for day ticks (#449)
-
fix: Reduce bundle size by removing culori as transitive dependency (#563)
-
fix(ScatterChart): Support color scales based on value (such as threshold) (#458)
-
fix(Rect): Support non-uniform
cornersin data/edge mode (#449) -
feat(TooltipContext): Support
quadtreemode for geo visualizations (#449) -
feat(Chart): Support
motionprop to transition x/y scales using tween or spring (#663) -
feat(Chart): Add
xInterval/yIntervalfor time scales usage with bar charts (#562) -
fix: Update dependencies, notable @layerstack/utils with improved metric number formatting (#449)
-
feat(Chart): Automatically determine scale based on data and domain values (instead of defaulting to scaleLinear) (#624)
-
feat(Chart|Svg|Html): Support passing
clipprop to hide overflown content (#659) -
fix(AreaChart): Change default tooltip mode from
bisect-xtoquadtree-x(works with catagorical data and does not require data to be sorted) (#578) -
fix(Text): Support explicit "\n" and set line-height (to match svg/canvas) for html layers (#659)
-
perf: Skip mark-info
$effectfor pixel-mode primitives (#855)registerComponentnow probesmarkInfo()once at construction; if the result is initially empty (pixel-mode primitives wherecx/cy/r/etc. are numbers rather than string/function accessors), it skips creating the tracking$effectentirely. Saves one effect frame per primitive — adds up in mark-heavy scenes (force simulations, scatter plots with hundreds of nodes).Trade-off: a primitive that starts in pixel mode and later flips to data mode at runtime (e.g.
cxmutates from a number to a string) will not register a mark. Mark mode is typically static; if a chart needs runtime data-mode marks, define an explicitserieson the chart instead. -
fix(ForceSimulation): Expose default values by exporting them as constants (#530)
-
feat: Update
applyLanes()util to support nested string key and function accessors for start/end properties (#449) -
fix(Spline): Only re-draw on data/path changes and not other context (such as width/height). Fixes #504 (#505)
-
fix(Axis): Fix multiline month when day tick does not align on first of month (#517)
-
fix: Update
dagreAncestors()anddagreDescendants()util types (#629) -
fix: Add
applyLanes()as top-level export (#449) -
feat: Add classes for underlying element styling (#458)
-
fix: Resolves "Target div has zero or negative height" console warning (issue #291) (#458)
-
fix(Canavs): Support
styleattribute (#659) -
fix(Canavs): support
strokeOpacityfor Path component (#663) -
feat(Chart): Support
classprop (#659) -
fix(Spline): Make motion prop reactive so toggling between tween/none updates without remount (#449)
-
refactor: Remove use of
layerClassand applylc-{name}class directly to allow easy component <style> targetting within LayerChart (#557) -
fix(Treemap): Fix
padding*prop types to support function or number constant (#516) -
feat(TooltipContext): Support
radiusfor voronoi mode (#449) -
fix(Text): Apply
dominant-baselineto<textPath>instead of<text>to fix Safari. Fixes #507 (#508) -
Switch from @tailwindcss/postcss to @tailwindcss/vite. Update all non-major packages (#449)
-
fix: Support passing
<*Chart tooltip={...}>to underlying TooltipContext (as types already indicate) (#576) -
breaking(Spline): Rename
splineReftopathRef(#549) -
feat(Text): Support explicit word wrapping with
\ncharacter (#508) -
fix(Text): handle inline styles and CSS class-based text-anchor (#663)
-
fix(AnnotationRange): Fix passing non-class/fill props (ex. pointer events) via
props.rect(#449) -
fix(Tooltip): Fade non-highlighted series items on hover to match chart highlight state (#663)
-
fix(Canvas): Improve performace by reducing computed style lookups and memoizing responses (#561)
-
fix(ForceSimulation): Export Forces<NodeDatum, LinkDatum> type. (#531)
-
fix(Text): Respect font weight when canvas rendered (#458)
-
fix(Axis|Text): Improve Text y placement using lineHeight/capHeight and remove explicit adjustments, aligning with Html rendering (#449)
-
feat(Layer): Support showing chart and full frame boundaries with
settings.debug(#659) -
fix(TooltipContext): Support band mode with array-based range accessors (e.g. histograms using
x={['x0', 'x1']}) (#663) -
fix(Canvas): Improve performance by skipping unnecessary work when hit canvas is unneeded (#561)
-
docs: Rename "Dot Plot" to "Duration" and add bars example along with points (#496)
-
fix(Text): Performance improvement by only determining word width if
widthprop defined (for word wrapping) (#554) -
feat: Simplify daisyUI, shadcn-svelte, and Skeleton integrations with single line
@import 'layerchart/{library}.css'added toapp.css(#557) -
fix(LinearGradient, RadialGradient): Register as
groupinstead ofmarkin canvas component tree so wrapped children (e.g. Arc, Path) are rendered (#449) -
feat: Add Ellipse primitive (#533)
-
fix(Spline): Pass
fillOpacityprop (instead offill-opacity) to support Canvas (#449)