npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@licharts/core

v1.0.2

Published

LiCharts core — essential chart types, zero dependencies

Downloads

38

Readme

@licharts/core

Free, MIT-licensed SVG charting library. Zero dependencies. Works in any framework or plain HTML.


Table of Contents


Installation

npm install @licharts/core
// Import styles once — usually in your app's entry file
import '@licharts/core/dist/licharts.css';

Angular: add the CSS in angular.json instead of importing it in JS:

"styles": ["node_modules/@licharts/core/dist/licharts.css"]
import { XYChart } from '@licharts/core';

Quick Start

<!-- index.html -->
<div id="chart" style="width: 600px; height: 400px;"></div>
import { XYChart, LineSeries, CategoryAxis, ValueAxis } from '@licharts/core';

// 1. Create the chart
const chart = new XYChart('#chart', { title: 'Monthly Sales' });

// 2. Add axes
chart.xAxes.push(new CategoryAxis({ position: 'bottom' }));
chart.yAxes.push(new ValueAxis({ position: 'left' }));

// 3. Create a series and set its data
const sales = new LineSeries({ name: 'Sales', color: '#4285f4' });
sales.data = [
  { category: 'Jan', y: 120 },
  { category: 'Feb', y: 180 },
  { category: 'Mar', y: 150 },
  { category: 'Apr', y: 210 },
  { category: 'May', y: 190 },
];

// 4. Add the series to the chart
chart.series.push(sales);

// 5. Render
chart.render();

That's it — a fully animated, responsive line chart with tooltip and legend.


Chart Types

XYChart

The main chart for all X/Y based series: Line, Area, Column, Bar, Scatter, StepLine.

import { XYChart } from '@licharts/core';

const chart = new XYChart('#container', {
  title:    'Revenue',           // Chart title
  subtitle: 'Q1–Q4 2024',       // Smaller text below title
  width:    800,                 // px (default: container width)
  height:   450,                 // px (default: container height)
  padding:  { top: 20, right: 20, bottom: 50, left: 60 },
  background: '#ffffff',
  responsive: true,              // auto-resize with container (default: true)
  animationsEnabled: true,       // default: true
  defaultAnimationDuration: 600, // ms
  contextMenu: true,             // shows ≡ button for CSV/XLS export
});

Key properties you can set after construction:

chart.showLegend  = true;   // show/hide the legend
chart.showTooltip = true;   // show/hide the hover tooltip
chart.title       = 'New Title';

Add/remove series dynamically:

chart.series.push(mySeries);
chart.series.remove(mySeries);
chart.render(); // always call render() after changes

PieChart

import { PieChart, PieSeries } from '@licharts/core';

const chart = new PieChart('#container', {
  title: 'Market Share',
  width: 500,
  height: 400,
});

const series = new PieSeries({
  innerRadius: 0,      // 0 = full pie; '40%' or 80 = donut hole
  outerRadius: '85%',  // size of the pie (default: fills chart)
  labels: true,        // show % labels on slices (default: true)
  cornerRadius: 4,     // rounded slice corners
  padAngle: 0.01,      // gap between slices (radians)
  explodeDistance: 8,  // how far exploded slices pop out (px)
});

series.data = [
  { category: 'Chrome',  value: 65, color: '#4285f4' },
  { category: 'Safari',  value: 19 },
  { category: 'Firefox', value: 9 },
  { category: 'Other',   value: 7 },
];

chart.series.push(series);
chart.render();

Make a donut chart — just set innerRadius:

const donut = new PieSeries({ innerRadius: '50%' });

Explode a specific slice (pop it out):

series.data = [
  { category: 'Featured', value: 40, explode: true, explodeOffset: 15 },
  { category: 'Other',    value: 60 },
];

RadarChart

Useful for showing multi-dimensional comparisons (skills, attributes, stats).

import { RadarChart, RadarSeries } from '@licharts/core';

const chart = new RadarChart('#container', {
  title:  'Developer Skills',
  width:  500,
  height: 450,
});

// Each series is one "player". Data points map to the radar axes.
const dev1 = new RadarSeries({
  name:        'Alice',
  color:       '#4285f4',
  fillOpacity: 0.25,     // fill transparency (default: 0.25)
  strokeWidth: 2,
  closed:      true,     // connect last point back to first (default: true)
});

dev1.data = [
  { category: 'TypeScript', y: 90 },
  { category: 'React',      y: 85 },
  { category: 'Node.js',    y: 70 },
  { category: 'CSS',        y: 60 },
  { category: 'Testing',    y: 75 },
];

const dev2 = new RadarSeries({ name: 'Bob', color: '#ea4335' });
dev2.data = [
  { category: 'TypeScript', y: 70 },
  { category: 'React',      y: 95 },
  { category: 'Node.js',    y: 50 },
  { category: 'CSS',        y: 90 },
  { category: 'Testing',    y: 65 },
];

chart.series.push(dev1, dev2);
chart.render();

Rule: Every series in a RadarChart must have the same category values in the same order.


Axes

CategoryAxis

Use when X values are strings (months, names, labels).

import { CategoryAxis } from '@licharts/core';

const xAxis = new CategoryAxis({
  position:      'bottom',        // 'top' | 'bottom' | 'left' | 'right'
  title:         'Month',
  gridLines:     true,            // show vertical grid lines
  gridColor:     'rgba(0,0,0,0.07)',
  labelRotation: -45,             // rotate labels (degrees)
  labelColor:    '#666',
  labelFontSize: 12,
  visible:       true,
});

chart.xAxes.push(xAxis);

ValueAxis

Use for numeric axes (usually Y).

import { ValueAxis } from '@licharts/core';

const yAxis = new ValueAxis({
  position:    'left',
  title:       'Revenue ($)',
  min:         0,              // force minimum value
  max:         1000,           // force maximum value
  tickCount:   6,              // approximate number of ticks
  tickFormat:  (v) => `$${v}`, // custom label formatter
  gridLines:   true,
  logarithmic: false,          // set true for log scale
  opposite:    false,          // set true to put on the right side
});

chart.yAxes.push(yAxis);

Second Y-axis on the right (e.g., for a different unit):

const rightAxis = new ValueAxis({ position: 'right', title: 'Volume' });
chart.yAxes.push(rightAxis);

// Tell a series to use the second Y-axis (index 1)
series2.options.yAxisIndex = 1;

DateAxis

Use when X values are dates or timestamps.

import { DateAxis } from '@licharts/core';

const xAxis = new DateAxis({
  position:   'bottom',
  tickFormat: (d) => new Date(d).toLocaleDateString('en', { month: 'short' }),
});

chart.xAxes.push(xAxis);

Data points for a DateAxis use date or x as a Date/string/timestamp:

series.data = [
  { date: '2024-01-01', y: 120 },
  { date: '2024-02-01', y: 180 },
  { date: new Date('2024-03-01'), y: 150 },
];

Series

LineSeries

import { LineSeries } from '@licharts/core';

const line = new LineSeries({
  name:              'Revenue',
  color:             '#4285f4',
  strokeWidth:       2,
  strokeDash:        '',         // e.g. '4,2' for dashed line
  smooth:            false,      // curved line (default: false)
  fill:              false,      // area fill color, or false to disable
  fillOpacity:       0.15,       // area fill opacity
  showLabels:        false,      // show value label on each point
  negativeColor:     '#ea4335',  // color for negative-value segments
  animate:           true,
  animationDuration: 600,
  // Bullet (dot) on each data point:
  bullet: {
    type:        'circle',  // 'circle' | 'square' | 'diamond' | 'triangle' | 'star'
    radius:      4,
    fill:        '#4285f4',
    stroke:      '#fff',
    strokeWidth: 2,
  },
  // Animated pulsing dot at the last point:
  endBullet: { radius: 6, fill: '#4285f4' },
});

line.data = [
  { category: 'Jan', y: 100 },
  { category: 'Feb', y: -20 }, // negative values go below zero line
  { category: 'Mar', y: 150 },
];

Disable bullets entirely:

const line = new LineSeries({ bullet: false });

AreaSeries

AreaSeries is a LineSeries with a fill. Use it exactly like LineSeries but set fill:

import { AreaSeries } from '@licharts/core';

const area = new AreaSeries({
  name:        'Visitors',
  color:       '#34a853',
  fill:        'rgba(52,168,83,0.2)', // or just set to true to auto-color
  smooth:      true,
  bullet:      false,
});

ColumnSeries

Vertical bars. Categories on X, values on Y.

import { ColumnSeries } from '@licharts/core';

const cols = new ColumnSeries({
  name:         'Sales',
  cornerRadius: 4,      // rounded top corners
  maxWidth:     60,     // max column width in px
  // Per-column colors (overrides single color):
  palette:      ['#4285f4', '#ea4335', '#fbbc04', '#34a853'],
  grainy:       false,  // texture effect on bars
  stacked:      false,
  stacked100:   false,
});

cols.data = [
  { category: 'Jan', y: 120 },
  { category: 'Feb', y: 180 },
  { category: 'Mar', y: 150, color: '#ff6d00' }, // override one bar's color
];

Column with image above bar (avatar / player card style):

cols.data = [
  { category: 'Alice', y: 95, imageUrl: 'https://example.com/alice.jpg' },
  { category: 'Bob',   y: 78, imageUrl: 'https://example.com/bob.jpg' },
];

Moving bullet (image follows hover):

const cols = new ColumnSeries({ movingBullet: true });
cols.data = [
  { category: 'Alice', y: 95, imageUrl: '/alice.jpg', bulletColor: '#4285f4' },
];

BarSeries

Horizontal bars. Categories on Y, values on X. Same options as ColumnSeries.

import { BarSeries } from '@licharts/core';

const bars = new BarSeries({ name: 'Population', cornerRadius: 3 });
bars.data = [
  { category: 'USA',   y: 331 },
  { category: 'India', y: 1380 },
  { category: 'China', y: 1441 },
];

// For BarSeries, use ValueAxis on X and CategoryAxis on Y:
chart.xAxes.push(new ValueAxis({ position: 'bottom' }));
chart.yAxes.push(new CategoryAxis({ position: 'left' }));

ScatterSeries

import { ScatterSeries } from '@licharts/core';

const scatter = new ScatterSeries({
  name:  'Readings',
  color: '#7b1fa2',
});

scatter.data = [
  { x: 10, y: 55 },
  { x: 25, y: 72 },
  { x: 40, y: 38 },
  { x: 60, y: 91 },
];

// Both axes are ValueAxis for scatter:
chart.xAxes.push(new ValueAxis({ position: 'bottom', title: 'Temperature' }));
chart.yAxes.push(new ValueAxis({ position: 'left',   title: 'Pressure' }));

StepLineSeries

Like LineSeries but draws horizontal-then-vertical steps instead of a diagonal line.

import { StepLineSeries } from '@licharts/core';

const steps = new StepLineSeries({
  name:  'Stock Price',
  color: '#00bcd4',
});

steps.data = [
  { date: '2024-01-01', y: 150 },
  { date: '2024-01-08', y: 162 },
  { date: '2024-01-15', y: 158 },
];

PieSeries

See PieChart above. PieSeries options:

| Option | Type | Default | Description | |---|---|---|---| | innerRadius | number \| string | 0 | 0 = pie, '40%' = donut hole | | outerRadius | number \| string | '85%' | Outer size | | startAngle | number | -Math.PI/2 | Start angle in radians | | endAngle | number | startAngle + 2π | End angle in radians | | padAngle | number | 0.01 | Gap between slices | | cornerRadius | number | 4 | Rounded corners on slices | | explodeDistance | number | 8 | Distance exploded slices pop out | | labels | boolean | true | Show % labels on slices | | labelRadius | number \| string | '70%' | Where labels are placed |

PieDataPoint fields:

series.data = [
  {
    category:      'Chrome',   // required — used as legend label
    value:         65,         // required — determines slice size
    color:         '#4285f4',  // optional — override auto color
    label:         'Chrome',   // optional — custom label text (default: percentage)
    explode:       false,      // optional — pop this slice outward
    explodeOffset: 15,         // optional — custom explode distance
    outerRadius:   '100%',     // optional — this slice's outer radius
    tooltipLabel:  'Chrome Browser', // optional — tooltip text
  },
];

RadarSeries

See RadarChart above.

| Option | Type | Default | |---|---|---| | closed | boolean | true | | fillOpacity | number | 0.25 | | strokeWidth | number | 2 |

Data points use category (the radar axis name) and y (the value):

series.data = [
  { category: 'Speed',    y: 80 },
  { category: 'Power',    y: 65 },
  { category: 'Stamina',  y: 90 },
];

Themes

LiCharts ships three built-in themes. Apply a theme in ChartOptions:

import { DefaultTheme, DarkTheme, MaterialTheme } from '@licharts/core';

// Light theme (default)
const chart = new XYChart('#chart', { theme: DefaultTheme });

// Dark theme
const chart = new XYChart('#chart', { theme: DarkTheme });

// Material design
const chart = new XYChart('#chart', { theme: MaterialTheme });

Custom theme — override any property:

import type { Theme } from '@licharts/core';

const MyTheme: Theme = {
  name: 'my-brand',
  colors: {
    palette:        ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4'],
    background:     '#1a1a2e',
    plotBackground: 'transparent',
    text:           '#eee',
    axis:           '#aaa',
    grid:           'rgba(255,255,255,0.05)',
    tooltip:        { background: '#16213e', border: '#0f3460', text: '#e0e0e0' },
    legend:         { background: 'transparent', text: '#ccc' },
    scrollbar:      { background: '#16213e', thumb: '#0f3460' },
  },
  fonts: {
    family:    'Roboto, sans-serif',
    size:      12,
    titleSize: 16,
  },
};

const chart = new XYChart('#chart', { theme: MyTheme });

Chart Options Reference

Every chart accepts these options in its constructor:

{
  // Content
  title?:    string;   // large title at top
  subtitle?: string;   // smaller text below title

  // Size
  width?:  number | string;  // px
  height?: number | string;  // px
  padding?: {
    top?: number; right?: number; bottom?: number; left?: number;
  };

  // Appearance
  theme?:      Theme;   // DefaultTheme | DarkTheme | MaterialTheme | custom
  background?: string;  // CSS color string

  // Behavior
  responsive?:               boolean; // auto-resize (default: true)
  animationsEnabled?:        boolean; // default: true
  defaultAnimationDuration?: number;  // ms, default: 600
  contextMenu?:              boolean; // ≡ export button (default: false)
}

Data Point Shapes

XYDataPoint (for line, column, bar, scatter, etc.)

{
  // XY coordinates (pick what matches your axis type)
  x?:        number | string | Date;  // for value/date X axes
  y?:        number;                  // Y value
  category?: string;                  // for CategoryAxis X
  date?:     Date | string;           // for DateAxis X
  value?:    number;                  // alias for y

  // Candlestick / OHLC
  open?:  number;
  high?:  number;
  low?:   number;
  close?: number;

  // Bubble chart
  radius?: number;

  // Per-point overrides
  color?:        string;
  label?:        string;
  tooltipLabel?: string;

  // Column with image
  imageUrl?:    string;
  bulletColor?: string;

  // Any extra fields you need
  [key: string]: unknown;
}

Stacking

Stacked Columns

import { ColumnSeries, XYChart, CategoryAxis, ValueAxis } from '@licharts/core';

const chart = new XYChart('#chart', { title: 'Stacked Sales' });
chart.xAxes.push(new CategoryAxis({ position: 'bottom' }));
chart.yAxes.push(new ValueAxis({ position: 'left' }));

const online = new ColumnSeries({ name: 'Online',  color: '#4285f4', stacked: true });
const inStore = new ColumnSeries({ name: 'In-Store', color: '#ea4335', stacked: true });

online.data   = [{ category: 'Q1', y: 40 }, { category: 'Q2', y: 55 }];
inStore.data  = [{ category: 'Q1', y: 60 }, { category: 'Q2', y: 45 }];

chart.series.push(online, inStore);
chart.render();

100% Stacked Columns

const s1 = new ColumnSeries({ stacked100: true });
const s2 = new ColumnSeries({ stacked100: true });
// Y axis automatically becomes 0–100%

Stacked Lines / Areas

const line1 = new LineSeries({ name: 'A', stacked: true, fill: 'rgba(66,133,244,0.3)' });
const line2 = new LineSeries({ name: 'B', stacked: true, fill: 'rgba(234,67,53,0.3)' });

Animations

Animations are on by default. Control them per-series:

const line = new LineSeries({
  animate:           true,   // default: true
  animationDuration: 1000,   // ms
});

Disable for all series in a chart:

const chart = new XYChart('#chart', { animationsEnabled: false });

Export Menu

Enable the button in the top-right corner that lets users export the chart as CSV, XLS, or view as table:

const chart = new XYChart('#chart', { contextMenu: true });

Responsive Charts

Charts resize automatically when their container changes size (responsive: true is the default). To opt out:

const chart = new XYChart('#chart', { responsive: false });

Destroying a Chart

Always call dispose() when removing a chart from the page. This cleans up event listeners and DOM elements.

// In React:
useEffect(() => {
  const chart = new XYChart('#chart', { ... });
  chart.render();
  return () => chart.dispose(); // cleanup on unmount
}, []);

// In Vue:
onUnmounted(() => chart.dispose());

// In Angular:
ngOnDestroy() { this.chart.dispose(); }

Complete Examples

Multi-Series Line Chart with Date Axis

import { XYChart, LineSeries, DateAxis, ValueAxis, DarkTheme } from '@licharts/core';

const chart = new XYChart('#chart', {
  title:    'Website Traffic',
  subtitle: 'Unique visitors per day',
  theme:    DarkTheme,
  height:   400,
});

chart.xAxes.push(new DateAxis({ position: 'bottom' }));
chart.yAxes.push(new ValueAxis({ position: 'left', title: 'Visitors', tickFormat: v => `${(v/1000).toFixed(0)}k` }));

const visits = new LineSeries({ name: 'Visits',      color: '#4285f4', smooth: true });
const unique  = new LineSeries({ name: 'Unique',     color: '#34a853', smooth: true });

visits.data = [
  { date: '2024-01-01', y: 12400 },
  { date: '2024-01-08', y: 18200 },
  { date: '2024-01-15', y: 15600 },
  { date: '2024-01-22', y: 22100 },
];

unique.data = [
  { date: '2024-01-01', y: 9400 },
  { date: '2024-01-08', y: 13200 },
  { date: '2024-01-15', y: 11600 },
  { date: '2024-01-22', y: 17100 },
];

chart.series.push(visits, unique);
chart.render();

Donut Chart

import { PieChart, PieSeries, DefaultTheme } from '@licharts/core';

const chart = new PieChart('#chart', { title: 'Budget Breakdown', theme: DefaultTheme });

const series = new PieSeries({
  innerRadius: '55%',  // donut hole
  labels: true,
  cornerRadius: 6,
});

series.data = [
  { category: 'Marketing', value: 35, color: '#4285f4' },
  { category: 'Engineering', value: 40, color: '#34a853' },
  { category: 'Operations', value: 15, color: '#fbbc04' },
  { category: 'HR', value: 10, color: '#ea4335' },
];

chart.series.push(series);
chart.render();