Skip to content


Repository files navigation


hill-chart preview

Basecamp's Hill Chart implementation using D3.js, Try the demo

Build Status Scrutinizer Code Quality Code Coverage Codacy Badge Demo contributions welcome License: MIT


  • Implemented in pure ES6/JS using D3.js
  • Customizable
  • Tested (kinda 😅)
  • Easily wrapped and integrated into other UI frameworks
  • Dark mode by @jeffandersen
  • TypeScript support @natterstefan

Getting started

Using a package manager

Install using yarn

yarn add hill-chart

Install using npm

npm i hill-chart

Import styles

@import 'hill-chart/dist/cjs/styles.css';

Using a CDN

Chart styles ~ 1kb


D3.js + Hill Chart combined bundle ~ 48kb (16kb gzip)

<script src=""></script>

D3.js + Hill Chart separate bundles

Custom D3.js only minified ~ 41kb (14 gizp)

<script src=""></script>

Hill Chart only minified ~8kb (2kb gzip)

<script src=""></script>


  1. make sure to have empty with an id or class <svg class="hill-chart" /> on your dom
  2. define some points
// if dont want to use cdn
import 'hill-chart/dist/styles.css';

import HillChart from 'hill-chart';

const data = [
    id: '3' // (optional)
    color: 'red',
    description: 'Late af task',
    size: 10,
    x: 12.069770990416055,
    y: 12.069770990416057,
    link: '/fired.html',

    id: '1' // (optional)
    color: 'yellow',
    description: 'Gettin there',
    size: 10,
    x: 55.11627906976744,
    y: 44.88372093023257,
    id: '2' // (optional)
    color: 'green',
    description: 'Hell yeah!',
    x: 93.48837209302326,
    y: 6.511627906976724,
    size: 10,
  1. (optional) define options
const config = {
  target: '.hill-chart',
  width: 700,
  height: 270,
  preview: false,
const hill = new HillChart(data, config);

hill.on('move', (x, y) => {
  console.log(`x: ${x}`);
  console.log(`y: ${y}`);

Config (Object)

All default config can be override by passing config object to the HillChart instance.

const config = {
  target: 'svg', // element selector it could be class, id, or svg element on page but it has to be an <svg /> element
  width: 900, // chart width
  height: 300, // chart height
  preview: false, // if true points wont be draggable
  darkMode: false, // Whether the dark color scheme is to be used
  backgroundColor: 'transparent', // Color to be used as bg, Use true for default color
  footerText: {
    // control footer text
    show: true,
    fontSize: 0.75, // in rems
  margin: {
    // the margins of the inner chart from the svg frame
    top: 20,
    right: 20,
    bottom: 40,
    left: 20,

Take a look at the current defaults here.

Data (Array)

You can define as may points as you want as long as the it have these options

data: [
    id: 'd4f4sd', // (optional) any unique id, if not provided it'll be auto generated for you.
    color: 'red', // color of the point it can be named color 'blue', 'red' or hex #000fff, #cccccc
    description: 'Late af task', // text next to the point, it's recommended to keep it short
    size: 10, // (optional) default size of the point
    x: 12.069770990416055, // x coordinates, (optional, defaults to 0) avoid setting it manually
    y: 12.069770990416057, // y coordinates, (optional, defaults to 0) avoid setting it manually
    link: '/fired.html', // (optional, defaults to '#') if in preview mode point becomes clickable


Name: Move

Trigger: .on('move', (x, y)=>{})

triggered: while point being dragged

payload: the point's new coordinates

Pro tip: you may use this event to store points in the database

Name: Moved

Trigger: .on('moved', (data)=>{})

triggered: after point dragged

payload: the point's data object with new coordinates

Name: Home

Trigger: .on('home', (data)=>{})

triggered: if the point hit 0 on X axis

payload: the point's data object

Name: End

Trigger: .on('end', (data)=>{})

triggered: if the point hit last point on X axis

payload: the point's data object

Name: pointClick

Trigger: .on('pointClick', (data)=>{})

triggered: when clicked on the point

payload: the point's data object


To get started...

  • Take a quick look on Todo List
  • Open issue to share and discuses new ideas or features.

Step 2

  • Fork this repo!
  • Install dev dependencies
  • Create new branch describes the new feature/bug.
  • Start the watch mode (e.g. npm run start or yarn start)
  • Do your thing :)

Step 3

  • Be sure to include coverage tests for your awesome idea
  • Submit a new pull request

Todo List

  • Refactor this nasty drag() event handler
  • Add more test. (depends on drag() refactoring)
  • Add more events, or improve the existence ones
  • Extract to vue components
  • Feature add snow ball effect, add option to increase the point size gradually near the end.
  • Improve the demo
  • Add SSR demo
  • Fully extract d3 modules
  • Update rollup config bundles

D3 testing problems

I tried my best to unit test every use case. It seems that d3js have issues regards event handling in a test environment specially with jest, add on top of this babel which has trouble importing a mutable field from d3 using the import / export syntax.


If you are wondering, don't be afraid to look under the hood.

Every Step is well documented

Inspiration (credits)

🛑 Copyright notice

Hill chart is an original idea by Basecamp. While this project aims to replicate the behavior and/or the design of the mentioned idea, it isn't by any means an attempt to reclaim the credit of the idea nor the design.

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Ahmed Nagi
Ahmed Nagi

💻 🤔
Jeff Andersen
Jeff Andersen

Stefan Natter
Stefan Natter


The Cashew Trader
The Cashew Trader


This project follows the all-contributors specification. Contributions of any kind welcome!