@composable-svelte/charts
v0.1.0
Published
Interactive data visualization components for Composable Svelte - Built with Observable Plot and D3
Maintainers
Readme
@composable-svelte/charts
Interactive data visualization components for Composable Svelte
Status: ✅ Feature Complete (Phase 11C)
Overview
@composable-svelte/charts provides state-driven, interactive data visualization components built on top of Observable Plot and D3 utilities. All chart state is managed using the Composable Architecture patterns from @composable-svelte/core.
Features
- 🎯 State-Driven: All chart state managed via reducers (data, selections, zoom, tooltips)
- 🎨 Observable Plot: Declarative chart specifications with concise, powerful API
- 🖱️ Interactive: Zoom, pan, brush selection, and smooth animations
- 🔄 Data Transforms: Composable transforms (filter, sort, group, bin, rollup, topN)
- ⚡ Performant: GPU-accelerated animations with
requestAnimationFrame - ♿ Accessible: ARIA labels, keyboard navigation, screen reader support
- 📱 Responsive: Automatic container-based sizing with ResizeObserver
- 🧪 Testable: Comprehensive integration and visual regression tests (34 tests)
Installation
pnpm add @composable-svelte/chartsPeer dependencies:
@composable-svelte/core^0.3.0svelte^5.0.0
Quick Start
import { Chart } from '@composable-svelte/charts';
import { createStore } from '@composable-svelte/core';
import { chartReducer, createInitialChartState } from '@composable-svelte/charts';
const data = [
{ x: 1, y: 10, category: 'A' },
{ x: 2, y: 20, category: 'B' },
{ x: 3, y: 30, category: 'A' }
];
const store = createStore({
initialState: createInitialChartState({
data,
spec: {
marks: [
{ type: 'dot', x: 'x', y: 'y', fill: 'category' }
]
}
}),
reducer: chartReducer,
dependencies: {}
});<Chart
{store}
width={600}
height={400}
enableZoom={true}
enableBrush={true}
enableTooltip={true}
/>Chart Types
Available Charts
- ✅ Scatter Plot - Points with interactive selection
- ✅ Line Chart - Time series with zoom/pan
- ✅ Bar Chart - Categorical data with tooltips
- ✅ Area Chart - Filled time series
- ✅ Histogram - Distribution visualization with brushing
Future Charts (Deferred)
- 📋 Heatmap - 2D density with color scales
- 📋 Network Graph - Force-directed layouts
- 📋 Tree/Hierarchy - Tree layouts, sunburst, treemap
Data Transforms
Transform your data declaratively before visualization:
import { DataTransforms } from '@composable-svelte/charts';
// Compose multiple transforms
const pipeline = DataTransforms.compose(
DataTransforms.filter(d => d.active),
DataTransforms.sortBy('value', 'desc'),
DataTransforms.topN(10, 'value')
);
const transformed = pipeline(data);Available Transforms
filter(predicate)- Filter data by predicate functionsortBy(field, order)- Sort by field ('asc' or 'desc')groupBy(key)- Group data into Record<string, T[]>aggregate(operation, field)- Aggregate (sum, mean, median, count, min, max)binData(field, thresholds)- Bin continuous data for histogramsrollup(window, field, operation)- Rolling window aggregationtopN(n, field)- Top N items by field valueunique(field)- Remove duplicates by fieldsample(n)- Random sample of n itemscompose(...transforms)- Compose multiple transforms into pipeline
API
Types
interface ChartState<T> {
data: T[];
filteredData: T[];
spec: PlotSpec;
dimensions: { width: number; height: number };
selection: SelectionState<T>;
transform: ZoomTransform;
tooltip: TooltipState<T>;
isAnimating: boolean;
transitionDuration: number;
}
type ChartAction<T> =
| { type: 'setData'; data: T[] }
| { type: 'filterData'; predicate: (d: T) => boolean }
| { type: 'selectPoint'; data: T; index: number }
| { type: 'zoom'; transform: ZoomTransform }
| { type: 'showTooltip'; data: T; position: { x: number; y: number } }
// ... and moreFunctions
// Create initial chart state
function createInitialChartState<T>(config: {
data?: T[];
spec?: Partial<PlotSpec>;
dimensions?: { width: number; height: number };
}): ChartState<T>
// Chart reducer
const chartReducer: Reducer<ChartState, ChartAction, {}>Architecture
State Management Pattern
All chart state lives in the reducer:
// User clicks a point
store.dispatch({
type: 'selectPoint',
data: point,
index: 5
});
// Reducer updates selection state
selection: {
type: 'point',
selectedData: [point],
selectedIndices: [5]
}
// UI reactively updates
$: isSelected = $store.selection.selectedIndices.includes(pointIndex);Observable Plot Integration
Charts use Observable Plot's declarative API:
const spec = {
marks: [
Plot.dot(data, {
x: 'sepalLength',
y: 'sepalWidth',
fill: 'species',
stroke: (d, i) => isSelected(i) ? 'black' : null
})
],
x: { domain: [4, 8] },
y: { domain: [2, 5] }
};Animation System
State-driven animations using Motion One:
// In component
$effect(() => {
if ($store.isAnimating) {
animate(chartElement,
{ opacity: [0, 1] },
{ duration: $store.transitionDuration }
);
}
});Examples
See examples/charts/ for full examples:
- 📊 Interactive Scatter Plot
- 📈 Zoomable Time Series
- 🔗 Linked Charts (coordinated views)
Testing
All state transitions are testable via TestStore:
import { createTestStore } from '@composable-svelte/core';
import { chartReducer, createInitialChartState } from '@composable-svelte/charts';
const store = createTestStore({
initialState: createInitialChartState({ data }),
reducer: chartReducer
});
await store.send(
{ type: 'selectPoint', data: data[0], index: 0 },
(state) => {
expect(state.selection.selectedIndices).toEqual([0]);
}
);Performance
- ✅ Handles 10,000+ data points smoothly
- ✅ GPU-accelerated animations (60fps)
- ✅ Bundle size < 100KB (gzipped)
- ✅ Interaction response < 16ms
Accessibility
- ♿ WCAG 2.1 AA compliant
- ⌨️ Full keyboard navigation
- 📢 Screen reader support with ARIA labels
- 📊 Data table fallback
Development Status
Phase 11C Complete! See the Phase 11 Plan for detailed roadmap.
Completed Features
- ✅ Core types and interfaces
- ✅ Chart reducer with full state management
- ✅ Chart and ChartPrimitive components
- ✅ Interactive behaviors (zoom, pan, brush selection)
- ✅ Smooth animation system with requestAnimationFrame
- ✅ Data transformation utilities (10 transforms)
- ✅ Responsive sizing with ResizeObserver
- ✅ Accessibility features (ARIA, keyboard, screen reader)
- ✅ Comprehensive integration tests (34 tests)
- ✅ Visual regression test framework
- ✅ Complete JSDoc documentation
Dependencies
@observablehq/plot^0.6.0 - Declarative visualizationd3-*- D3 utilities (zoom, brush, selection, scales)motion^12.0.0 - Animation engine
License
MIT © Jonathan Belolo
Related Packages
@composable-svelte/core- Core Composable Architecture@composable-svelte/code- Code editor components
