energy-visualization-stacked-area-chart
v2.0.0
Published
A modern TypeScript library for energy data stacked area charts with D3
Maintainers
Readme
Energy Stacked Area Chart Library
A modern TypeScript library for creating interactive energy data visualizations. Purpose-built for energy consumption analysis with built-in support for multiple energy sources, time series data, and various representation modes.
Why Energy Data Visualization Matters
Energy consumption patterns are complex, involving multiple sources (solar, nuclear, wind, coal, etc.) changing over decades of time. This library makes it simple to create beautiful, interactive charts that help researchers, policymakers, and educators understand energy trends and make data-driven decisions.
Key Features
Energy-Focused Design
- 9 Energy Sources: Solar, Nuclear, Hydro, Wind, Geothermal, Natural Gas, Coal, Biomass, Petroleum
- Smart Color Coding: Industry-standard colors for each energy source
- Multiple Views: Fuel-based vs Sector-based energy analysis
- Data Representations: Fractional, Per Capita, and Total consumption modes
Modern Developer Experience
- Simple Data API: Provide data directly, no HTTP dependencies
- TypeScript First: Complete type safety with strict mode
- Zero Dependencies: Embedded D3 v7 for standalone distribution
- Responsive Design: Automatic resizing and mobile-friendly
- Instant Switching: Change modes/representations without re-fetching data
Production Ready
- High Performance: Handles 200+ years × 9+ energy sources smoothly
- Comprehensive Testing: 90%+ code coverage with unit and integration tests
- Professional Styling: Matches industry visualization standards
- Robust Error Handling: Graceful fallbacks and clear error messages
Quick Start
Installation
npm install energy-visualization-stacked-area-chartBasic Usage
<!DOCTYPE html>
<html>
<head>
<title>Energy Visualization</title>
</head>
<body>
<div id="energy-chart"></div>
<!-- Include the library -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/stacked-area-chart.umd.js"></script>
<script>
// Fetch your energy data
fetch('./energy-data.json')
.then(response => response.json())
.then(energyData => {
// Create chart with data
new StackedAreaChart.default('energy-chart', {
mode: 'fuel',
representation: 'perCapita',
data: energyData
});
// Chart auto-renders when data is provided
});
</script>
</body>
</html>ES6 Module Usage
// Named import (recommended)
import {StackedAreaChart} from 'energy-visualization-stacked-area-chart';
import 'energy-visualization-stacked-area-chart/style.css';
// Fetch data (you control the data loading)
const energyData = await fetch('./energy-data.json').then(r => r.json());
// Option 1: Provide data in constructor
const chart = new StackedAreaChart('energy-chart', {
mode: 'fuel',
representation: 'perCapita',
data: energyData
});
// Auto-renders when data provided
// Option 2: Provide data via fluent API
const chart2 = new StackedAreaChart('energy-chart-2', {
mode: 'fuel',
representation: 'perCapita'
});
chart2.data(energyData).render();
// Instant switching between modes (no re-fetch needed)
chart.switchDataSource('sector', 'total');Modern Framework Integration
The library is designed to work seamlessly with modern JavaScript frameworks and build tools. All exports are tree-shakeable and fully typed.
Importing
// Named import (recommended for frameworks)
import {StackedAreaChart} from 'energy-visualization-stacked-area-chart';
// Default import
import StackedAreaChart from 'energy-visualization-stacked-area-chart';
// Type imports
import type {ChartConfig, MultiDatasetFormat} from 'energy-visualization-stacked-area-chart';
// CSS import
import 'energy-visualization-stacked-area-chart/style.css';Nuxt 3
Create a composable for easy chart management:
// composables/useEnergyChart.ts
import {ref, onMounted, onUnmounted, type Ref} from 'vue';
import {StackedAreaChart, type PartialChartConfig} from 'energy-visualization-stacked-area-chart';
import 'energy-visualization-stacked-area-chart/style.css';
export function useEnergyChart(
containerId: string,
config?: PartialChartConfig
): Ref<StackedAreaChart | null> {
const chart = ref<StackedAreaChart | null>(null);
onMounted(() => {
chart.value = new StackedAreaChart(containerId, config || {});
});
onUnmounted(() => {
chart.value?.destroy();
});
return chart;
}Use in a component:
<template>
<div>
<div id="energy-chart" style="width: 100%; height: 400px;"></div>
</div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue';
import { useEnergyChart } from '~/composables/useEnergyChart';
const chart = useEnergyChart('energy-chart', {
mode: 'fuel',
representation: 'perCapita'
});
onMounted(async () => {
const data = await $fetch('/api/energy-data');
chart.value?.data(data).render();
});
</script>Next.js (App Router)
'use client';
import { useEffect, useRef, useState } from 'react';
import { StackedAreaChart } from 'energy-visualization-stacked-area-chart';
import type { MultiDatasetFormat } from 'energy-visualization-stacked-area-chart';
import 'energy-visualization-stacked-area-chart/style.css';
export function EnergyChart({ data }: { data: MultiDatasetFormat }) {
const chartRef = useRef<StackedAreaChart | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
// Create chart instance
chartRef.current = new StackedAreaChart('energy-chart', {
mode: 'fuel',
representation: 'perCapita',
data: data
});
// Cleanup on unmount
return () => {
chartRef.current?.destroy();
};
}, [data]);
return (
<div
id="energy-chart"
ref={containerRef}
style={{ width: '100%', height: '400px' }}
/>
);
}Vue 3
<template>
<div id="energy-chart" style="width: 100%; height: 400px;"></div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { StackedAreaChart } from 'energy-visualization-stacked-area-chart';
import type { PartialChartConfig, MultiDatasetFormat } from 'energy-visualization-stacked-area-chart';
import 'energy-visualization-stacked-area-chart/style.css';
interface Props {
data: MultiDatasetFormat;
config?: PartialChartConfig;
}
const props = defineProps<Props>();
const chart = ref<StackedAreaChart | null>(null);
onMounted(() => {
chart.value = new StackedAreaChart('energy-chart', {
...props.config,
data: props.data
});
});
onUnmounted(() => {
chart.value?.destroy();
});
</script>React (Create React App / Vite)
import { useEffect, useRef } from 'react';
import { StackedAreaChart } from 'energy-visualization-stacked-area-chart';
import type { MultiDatasetFormat, PartialChartConfig } from 'energy-visualization-stacked-area-chart';
import 'energy-visualization-stacked-area-chart/style.css';
interface EnergyChartProps {
data: MultiDatasetFormat;
config?: PartialChartConfig;
}
export function EnergyChartComponent({ data, config }: EnergyChartProps) {
const chartRef = useRef<StackedAreaChart | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
chartRef.current = new StackedAreaChart('energy-chart-container', {
...config,
data: data
});
return () => {
chartRef.current?.destroy();
};
}, [data, config]);
return (
<div
id="energy-chart-container"
ref={containerRef}
style={{ width: '100%', height: '400px' }}
/>
);
}TypeScript Support
All types are fully exported and documented:
import type {
// Main class
StackedAreaChart,
// Configuration types
ChartConfig,
PartialChartConfig,
ChartDimensions,
TooltipConfig,
AnimationConfig,
// Data types
MultiDatasetFormat,
RawChartData,
EnergySource,
ProcessedDataPoint,
// Enums and constants
ChartMode, // 'fuel' | 'sector'
DataRepresentation, // 'fractional' | 'perCapita' | 'total'
EnergySourceName,
// Utility types
ChartBounds,
DatasetKey
} from 'energy-visualization-stacked-area-chart';
// Default configuration is also exported
import {DEFAULT_CONFIG} from 'energy-visualization-stacked-area-chart';Energy Data Features
Energy Sources & Colors
The library includes built-in support for standard energy sources with industry-recognized colors:
const energySources = {
'Solar': '#fed530', // Bright yellow
'Nuclear': '#ca0813', // Red
'Water': '#0b24fb', // Blue (Hydro)
'Wind': '#901d8f', // Purple
'Geothermal': '#905a1c', // Brown
'Natural Gas': '#4cabf2', // Light blue
'Coal': '#000000', // Black
'Biomass': '#46be48', // Green
'Petroleum': '#095f0b' // Dark green
};Data Representations
Fractional View: Shows energy sources as proportions (0-1) of total consumption
chart.representation('fractional'); // Perfect for seeing energy mix changesPer Capita View: Shows energy consumption per person
chart.representation('perCapita'); // Great for population-adjusted analysisTotal View: Shows absolute energy consumption values
chart.representation('total'); // Best for seeing overall consumption growthEnergy Unit Formatting
Automatic formatting with appropriate energy units:
formatEnergyValue(1500); // "1.5 kW"
formatEnergyValue(2500000); // "2.5 MW"
formatEnergyValue(1200000000); // "1.2 GW"Data Format
The library uses a Multi-Dataset Format that contains all 6 data variants in a single object:
{
"fuel-fractional": [...],
"fuel-perCapita": [...],
"fuel-total": [...],
"sectoral-fractional": [...],
"sectoral-perCapita": [...],
"sectoral-total": [...]
}Each array follows the energy source structure:
{
"fuel-fractional": [
{
"year": [1800, 1801, 1802, ..., 2021]
},
{
"name": "Solar",
"color": "#fed530",
"data": [0, 0, 0, ..., 131.23],
"opacity": 1
},
{
"name": "Nuclear",
"color": "#ca0813",
"data": [0, 0, 0, ..., 843.45],
"opacity": 1
}
// ... more energy sources
],
"fuel-perCapita": [
// Same structure with per-capita values
],
// ... other datasets
}Key Points:
- First object in each array contains the years
- Subsequent objects represent energy sources with their data
- All 6 datasets loaded once, enabling instant mode/representation switching
API Reference
Constructor
new StackedAreaChart(containerId: string, config ?: PartialChartConfig)Parameters:
containerId: CSS selector or element ID for the chart containerconfig: Optional configuration object
Configuration Options
interface ChartConfig {
// Chart dimensions
dimensions: {
width: number; // Chart width (auto-calculated if not set)
height: number; // Chart height (auto-calculated if not set)
margin: { // Chart margins
top: number; right: number; bottom: number; left: number;
};
};
// Energy data settings
mode: 'fuel' | 'sector'; // Analysis mode
representation: 'fractional' | 'perCapita' | 'total'; // Data representation
data?: MultiDatasetFormat; // Multi-dataset object (optional)
// Styling and interaction
interactive: boolean; // Enable hover and tooltip interactions
colors: Record<string, string>; // Custom energy source colors
// Tooltip configuration
tooltip: {
background: string; // Tooltip background color
color: string; // Tooltip text color
borderColor: string; // Tooltip border color
format: string; // Number format pattern
};
// Animation settings
animations: {
duration: number; // Animation duration in ms
easing: string; // CSS easing function
enabled: boolean; // Enable/disable animations
};
}Methods
Fluent API Methods (Chainable)
// Dimension control
chart.width(800) // Set chart width
chart.height(400) // Set chart height
chart.resize(1000, 500) // Resize chart
// Data control
chart.mode('fuel') // Set to fuel-based analysis
chart.mode('sector') // Set to sector-based analysis
chart.representation('perCapita') // Set data representation
chart.data(multiDatasetObject) // Provide multi-dataset
chart.updateData(multiDatasetObject) // Update with new multi-dataset
// Data source switching (instant - no HTTP)
chart.switchDataSource('fuel', 'perCapita') // Switch mode and representation
// Configuration
chart.configure({ // Update configuration
interactive: true,
animations: {duration: 500}
});Rendering Methods
chart.render() // Render the chart
chart.update() // Update with current data
chart.destroy() // Clean up and remove chartUtility Methods
chart.getConfig() // Get current configuration
chart.getData() // Get processed data points
chart.getEnergySources() // Get current energy sources
chart.getSVG() // Get SVG element
chart.hasData() // Check if chart has dataAdvanced Usage
Custom Styling
const chart = new StackedAreaChart('chart', {
dimensions: {
width: 1200,
height: 500,
margin: {top: 20, right: 30, bottom: 40, left: 50}
},
colors: {
'Solar': '#ffeb3b', // Custom solar color
'Wind': '#9c27b0' // Custom wind color
},
tooltip: {
background: '#ffffff',
borderColor: '#333333',
color: '#333333'
}
});Programmatic Control
// Fetch data once
const energyData = await fetch('./energy-data.json').then(r => r.json());
const chart = new StackedAreaChart('chart', {
data: energyData,
mode: 'fuel',
representation: 'perCapita'
});
// Instant switching (no re-fetch needed)
document.getElementById('fuel-btn').addEventListener('click', () => {
chart.switchDataSource('fuel', 'perCapita');
});
document.getElementById('sector-btn').addEventListener('click', () => {
chart.switchDataSource('sector', 'total');
});
// Responsive behavior
window.addEventListener('resize', () => {
const container = document.getElementById('chart-container');
chart.resize(container.clientWidth, 400);
});Custom Data Loading
// Load and provide your own energy data
async function loadCustomData() {
const response = await fetch('/api/energy-data');
const multiDataset = await response.json();
// Ensure data matches MultiDatasetFormat structure
const chart = new StackedAreaChart('chart', {
mode: 'fuel',
representation: 'perCapita'
});
chart.data(multiDataset).render();
}Performance
Optimized for Large Datasets
- Time Range: Efficiently handles 200+ years of historical data
- Energy Sources: Supports 9+ concurrent energy sources
- Data Points: Processes thousands of data points smoothly
- Rendering: <100ms render time for typical energy datasets
- Memory: Efficient D3 data joins minimize memory usage
Bundle Size
- UMD Bundle: 67KB (includes embedded D3)
- ESM Bundle: 67KB (tree-shakeable)
- Compressed: ~18KB gzipped
- Dependencies: Zero runtime dependencies
Development
Setup Development Environment
# Clone and install
git clone <repository-url>
cd stacked-area-1
npm install
# Development with hot reload
npm run dev
# Run tests
npm test
npm run test:watch
npm run test:coverage
# Build for production
npm run buildProject Structure
src/
├── core/ # Main chart engine
│ ├── StackedAreaChart.ts # Primary chart class
│ ├── DataProcessor.ts # Data transformation
│ └── Renderer.ts # D3 rendering engine
├── components/ # UI components
│ └── Tooltip.ts # Interactive tooltips
├── utils/ # Utility functions
│ ├── formatters.ts # Energy value formatting
│ ├── colorMaps.ts # Energy source colors
│ └── constants.ts # Configuration constants
├── types/ # TypeScript definitions
└── styles/ # CSS stylingTesting Strategy
# Unit tests - Core functionality
npm run test:unit
# Integration tests - Full workflows
npm run test:integration
# Performance tests - Large datasets
npm run test:performance
# All tests with coverage
npm run test:coverageBrowser Support
- Modern Browsers: Chrome 63+, Firefox 60+, Safari 12+, Edge 79+
- JavaScript: ES2018+ support required
- Features Used: ResizeObserver, Fetch API, ES6 Modules
- Fallbacks: Graceful degradation for older browsers
Build Tools & Bundlers
The library is fully compatible with modern build tools:
- Vite: Works out of the box with ESM imports
- Webpack 5: Full support with tree-shaking
- Rollup: Native compatibility
- esbuild: Full ESM support
- Parcel: Zero-config support
- Turbopack: Next.js 13+ compatible
Module Formats
- ESM (
dist/stacked-area-chart.esm.js): For modern bundlers and browsers - UMD (
dist/stacked-area-chart.umd.js): For CDN usage and legacy environments - TypeScript (
dist/index.d.ts): Full type definitions with source maps
Tree-Shaking
The library is marked with sideEffects: ["*.css"] in package.json, enabling optimal tree-shaking in modern bundlers.
Only import what you use:
// Only imports the chart class and types used
import {StackedAreaChart, type ChartConfig} from 'energy-visualization-stacked-area-chart';Contributing
We welcome contributions! Here's how to get started:
- Fork the repository
- Clone your fork
- Create a feature branch:
git checkout -b feature/energy-analysis - Add tests: Ensure new features have test coverage
- Run the test suite:
npm test - Submit a pull request: Include description of changes
Development Guidelines
- TypeScript: All code must be strictly typed
- Testing: Maintain 90%+ test coverage
- Energy Focus: Features should enhance energy data visualization
- Performance: Consider impact on large datasets
- Documentation: Update README for new features
License
MIT License. See LICENSE file for details.
Developed by the Research Computing Center (RCC), University of Chicago
