This documentation has been deprecated

You are viewing the documention for v3.x, which is no longer supported.
Check out the documentation for the latest version.

What is Cal-heatmap ?

Cal-Heatmap is a javascript module to create a calendar heatmap.

Getting started


The classic way
  1. Download the latest version of Cal-Heatmap in your application
  2. Include the d3.js library (refer to d3.js website for the latest version number)
    <script src="" charset="utf-8"></script>
    Or you can download it and use it locally
  3. Install Cal-HeatMap in your application by including the javascript and the css file
    <link rel="stylesheet" href="path/to/css/cal-heatmap.css" />
    <script type="text/javascript" src="path/to/cal-heatmap.min.js"></script>
With Bower
bower install cal-heatmap

Then continue with step 3 of the classic way to include it in your application

With Jam
jam install cal-heatmap

Then continue with step 3 of the classic way to include it in your application

Via a CDN (jsDelivr)

Just include the following lines in your page head section:

<script type="text/javascript" src="//"></script>
<script type="text/javascript" src="//"></script>
<link rel="stylesheet" href="//" />

Cal-Heatmap is compatible with AMD module.

Basic usage

Creating your first calendar is easy

<div id="cal-heatmap"></div>
<script type="text/javascript">
  var cal = new CalHeatMap();

By default, calling init() with an empty object will initialize a blank calendar of 12 hours, with 60 minutes each, starting from the current hour.

Read the data section for how to fill the calendar with your own data, and the presentation section for the calendar layout.

Browsers support

Cal-Heatmap works on most modern browsers supporting SVG, and was tested on


Calendar can be customized by setting various options in the object passed to init().




Selector string or Element

DOM node to insert the calendar in.

With a Selector String

All CSS3 selectors can be used as a Selector String, so you can, for example, select by:

tag cal.init({ itemSelector: "div"})
ID cal.init({ itemSelector: "#id"})
class cal.init({ itemSelector: ".class"})
attribute cal.init({ itemSelector: "[title=hi]"})
containement cal.init({ itemSelector: "div > span + b"})

If the Selector String is returning multiple node, the first one will be used.

With a DOM Element

If you already have a reference to a DOM Element, you can directly use it.

getElementByID() cal.init({ itemSelector: document.getElementByID("myId") });
getElementsByClassName() cal.init({ itemSelector: document.getElementsByClassName(".class")[0] });
querySelector() cal.init({ itemSelector: document.querySelector(".class") });
jQuery Selector cal.init({ itemSelector: $(".class")[0] });
d3.js selector cal.init({ itemSelector:".class")[0][0] });



Type of domain

Valid domains:



Type of subDomain

Valid subDomains:

By default, all subdomain cells are read from top to bottom, and from left to right. The x_ variants are used to rotate the reading order to left to right, then top to bottom.

day subDomain

x_day subDomain

When not set, Cal-heatmap will decide the most appropriate subDomain for the current type of domain.

Not all subDomains can be used with a domain.
Only "responsible" domain/subDomain couple are available,
e.g: {domain: "year", subDomain: "min"} will not work.
(Because painting one half million svg nodes will probably crash your browser ...)

subDomain must always be smaller than domain.



Number of domain to display



Size of each subDomain cell, in pixel.

Default cellSize of 10px

cellSize of 15px



Space between each subDomain cell, in pixel.

Default cellPadding of 2px

cellPadding of 5px



subDomain cell's border radius, for rounder corner, in pixel.

Default cellRadius of 0px

cellRadius of 5px

Set shape-rendering value to other than crispedges for better rendering.



Space between each domain, in pixel.

Default domainGutter of 2px

domainGutter of 10px


integer or Array
[0, 0, 0, 0]

Margin around each domain, in pixel.

Ordered like in CSS (top, right, bottom, left), it also accepts CSS like values:

No margin

10px margin

domainMargin is specially useful when coupled with the domain highlighting function.



Whether to enable domain dynamic width and height.

Some domain>subdomain couple, like month>days, doesn't always have the same number of subDomain cells. Some months have 6 weeks, some only 4.

With dynamic dimension enabled, the domain width and height will be adjusted to fit the domain content, whereas when it's disabled, all domains will have the same dimension : the biggest.

Dynamic dimensions enabled.
Browse with the next/previous buttons, and note that the whole calendar is also resized to fit the domains width

Dynamic dimensions disabled.
Notice that, sometimes, there's a gap between domains, when the month has less than 6 weeks



To display the calendar vertically, with each domain one under the other

Default horizontal orientation

Vertical orientation

Use the x_ variant subDomain for a more natural reading order.

For a smoother reading, see the label option to move the label position on the side.



Position and alignment of the domain label.

It takes an object with up to 5 properties:

name default values description
position bottom top, right, bottom, left Position of the label, relative to the domain
align center left, center, right Horizontal align of the domain
rotate null null, left, right Rotation for a vertical label
width 100 any integer Only used when label is rotated, defines the width of the label
offset {x:0, y:0} An object with x and y More control about label positioning, if the default value does not fit your need, especially when label is rotated, or when using a big font-size.
height null any integer 3.1.1+

Height of the domain label in pixels.
By leaving it to null, the label will be set to 2 times the height of the subDomain cell.

If you want to remove the label, set domainLabelFormat to "" (empty string), instead of setting the label height to 0.

Default values are only for when label is positioned on bottom, without rotation.

Label on bottom, centered

Label on top, centered

Label on left

Label on right, with offset

Label on left, rotate left

Label on right, rotated left, with a bigger width

Label on right, rotate left

Label on right. rotate left, aligned right

Styling tips
Use .graph-label to style the label.



Control the number of columns to split the domain dates into.

Each domain is split into an arbitrary number of columns (or rows depending on the reading direction). You can overwrite that number with colLimit, and force all dates on the same line, or split them into more columns.

That setting limit the maximum number of columns, and doesn't necessary means that each rows will contains that number of columns.

Default splitting, 4 columns with 6 hours each

All hours on the same line



Control the number of rows to split the domain dates into.

If rowLimit and colLimit are both used, rowLimit will be ignored.

7 days per column (default)

10 days per column



Whether to display a tooltip when hovering over a date.

Default title on hover

Tooltip on hover



new Date()

Starting date of the calendar

It doesn't have to be precise, the calendar will not start at that date, but at the first domain containing that date.


For the date: January 15, 2000 @15:36 :

Starting today

Starting January 15th, 2000


String or Json object

Data used to fill the calendar.

It can accepts either a string, or a json object


The string is interpreted as an URL to an API, which should be returning the data used to fill the calendar.

var cal = new CalHeatMap();
  data: "http://localhost/datas.json"

The data must be formatted according to the expected data format. In case it's not possible, you should first set the correct dataType if it's not returning a json response, then use the afterLoadData() to transform the data into the expected format.

4 template strings are available to limit the scope of data returned by the API

Value Description
{{t:start}} Timestamp of the first subDomain cell
{{t:end}} Timestamp of the last subDomain cell
{{d:start}} ISO-8601 formatted date of the first subDomain cell
{{d:end}} ISO-8601 formatted date of the last subDomain cell

The first time, the first subDomain cell will be the one in the first domain, and the last subDomain cell, the one in the last domain, so that, if your calendar have 5 domains, it'll get the data for the 5 domains.

For each subsequent call, like when loading new domains dynamically with next() and previous(), the first and last subDomain cell will only be the ones inside the newly loaded domain.

var cal = new CalHeatMap();
  start: new Date(2000, 0), // January, 1st 2000
  range: 12,
  domain: "year",
  subDomain: "month",
  data: "http://localhost/api?start={{d:start}}&stop={{d:end}}"

  {{d:start}} === 2000-01-01T00:00:00Z
  {{d:end}}   === 2000-12-31T23:59:59Z
  => Fetch 12 months
 */; // Load January 2001

  {{d:start}} === 2001-01-01T00:00:00Z
  {{d:end}}   === 2001-01-31T23:59:59Z
  => Fetch 1 month
Json Object

The object will be used to fill the calendar.

Again, if the object is not formatted according to the expected format, use afterLoadData() callback to transform it.



Engine used to parse the data.

Value Description
json Interpret the data as json
csv Interpret the data as csv
tsv Interpret the data exactly like csv, but are delimited with a tab character, instead of a comma.
txt Just return the data as a string
var cal = new CalHeatMap();
  data: "http://localhost/datas.csv",
  dataType: "csv"

When using the txt dataType, the data is returned as a plain text. You have to use the afterLoadData() callback to transform it to the expected data format.


String or Array

Highlight selected subDomain cells.

Takes an array of Date object. Can also accepts the now string, equivalent to


Highlight current hour, and tomorrow at the same hour

Styling tips
The .highlight class is used to style highlighted cells.
Additionally, a .now is also applied when the highlighted cells correspond to the current time.

Use rect.highlight/ and text.highlight/ to style the cell and the text inside respectively.

See the domain highlighting section if you want to highlight the entire domain.



Whether to start the week on Monday, instead of Sunday.

First column is Monday

First column is Sunday



Lower limit of the domain navigation, preventing navigating beyond a certain date.

When set, calling previous() will only work only until the leftmost domain containing minDate. Like with start, minDate does not have to be precise, and just have to be a date inside the domain.

previous() will always return true, unless the domain containing minDate is reached, in which case, it'll return false.


Considering a calendar with month domain, and day subDomain, let's set minDate to January 25th, 2000. The last previous() call will load the January domain, as January 25th is a subDomain inside the January domain.

Calling previous() again will do nothing and just return false, since the next incoming domain is December, and January 25th is not a subDomain of the December domain.

Bound the calendar from February to September

The onMinDomainReached() event is triggered when you've hit the leftmost domain allowed by minDate.



Upper limit of the domain navigation, preventing navigating beyond a certain date.

Same as minDate, but for the upper limit. Refer to minDate documentation.



Whether to consider missing date:value couple in the data source as equal to 0.

By default, when the a date is not associated to a value, it's considered as null, and rendered as a no value cell.

You should ask yourself, if the API is not returning result for a date, is it because there is really no value associated to this date, or because it's supposed to be equal to 0, and it's skipped in order to save bandwidth ?



[10, 20, 30, 40]

Assign each range of values to a color.

legend accepts an array of any number of arguments, as long as they are number (even negative numbers), and in ascendant order.
An array of N values (thresholds) will generate a legend of N + 1 colors. Each colors correspond to a CSS class, formatted like .q{n}.


With [10, 20, 30, 40], you end up with 5 colors:

CSS Class Range
.q1 Less than 10
.q2 Between 10 and 20
.q3 Between 20 and 30
.q4 Between 30 and 40
.q5 More than 40

By default, the CSS only come with 5 .q{n} class. You're free to add your own if needed, and modify the colors of the existing one.

Hover on a legend cell to see the associated range

Default legend

Float and negative legend values

Styling tips
.graph-rect class is applied to all subDomain cells, and used when the cell is not associated to a value.

Styling tips
.q0 will be applied to all dates with values == 0.

Styling tips
.qi will be applied to all dates with "overflowing" values, like negative values, when the legend is all positive.

Styling tips
Use the .q{n} + title + .subdomain-text CSS selector to change cell text style.

You can also generate the colors automatically without using CSS, refer to legendColors.



Whether to display the legend.


No legend

Legend can be added or removed after initialisation, by calling showLegend() and removeLegend().



Size of the legend cells, in pixel.

Default legendCellSize of 10px

legendCellSize of 5px



Padding between each legend cell, in pixel.

Default legendCellPadding

legendCellPadding of 5px


integer or Array
[10, 0, 0, 0]

Margin around the legend, in pixel

Default legendMargin

Margin of 50px on top and left



Vertical position of the legend.

Value Description
top Place the legend above the calendar

Place the legend on the calendar's side

Use with legendHorizontalPosition, to position the legend on the left (default) or on the right.

bottom Place the legend below the calendar

Default legendVerticalPosition

legend on top, with a legend margin-bottom of 10px

Legend on the left side

Legend on the right side



Horizontal position of the legend.

Value Description
left Align the legend to the left
center Center the legend
right Align the legend to the right

Default legendHorizontalPosition

legend align on the right



Orientation of the legend.

Value Description
horizontal Legend is displayed horizontally, from left to right
vertical Legend is displayed vertically, from top to bottom

legendOrientation is best used together with legendHorizontalPosition when the legend is positioned on the side.

Vertical legend on the left side

Vertical legend on the right side, on a vertical oriented calendar


Array or Object

Set of colors to automagically compute the heatmap colors.

Instead of relying on the CSS for your heatmap's colors, you can also set the heatmap's colors directly with cal-heatmap on initilialisation, or even dynamically change them after.

The legendColors setting takes an object with the following keys:

Key Description CSS equivalent
min Color of the smallest value on the legend .q1
max Color of the highest value on the legend .qx, where x is the highest number
empty Optional Color for the dates with value == 0 .q0
base Optional Base color of the date cell's .graph-rect
overflow Optional Color for the special value .qi

Colors can be defined using the hexadecimal notation #FFFFFF or its english equivalent name white.
Other fancy function like hsl(350, 100%, 50%) are also accepted.

var cal = new CalHeatMap();
  legendColors: {
    min: "#efefef",
    max: "steelblue",
    empty: "white"
    // Will use the CSS for the missing keys

When using only the mandatory min and max keys, you can use an array [min, max], instead of the whole object.

var cal = new CalHeatMap();
  legendColors: ["#efefef", "steelblue"]

All the intermediate colors on the legend will be interpolated from the min and max colors, using the HCL interpolation.


All legend settings can be changed dynamically after calendar initialisation, with setLegend().

Legend Playground

(top, right, bottom, left)
[10, 20, 30, 40]


String or Array
["item", "items"]

Name of the entity you're representing on the calendar.

Takes an array of String, with the first index as singular form, and the second index, the plural form.

var cal = new CalHeatMap();
  itemName: ["cat", "cats"]

For the lazy, you can also pass a simple string, ar a single element array, and it'll automatically guess the plural form, as long as it's the singular form plus the "s" suffix.

The 2 following examples are the same as the one above

var cal = new CalHeatMap();
  itemName: "cat"
var cal = new CalHeatMap();
  itemName: ["cat"]



Format of the title displayed when hovering a subDomain.

Takes an object with 2 properties:

Property name Default value Description
empty {date} Format of the title when there is no value associated to the date.
filled {count} {name} {connector} {date} Format of the title when it's associated to a value

Some template string are available, and enclosed in braces.

Template Description
{name} Name of the entity represented in the calendar (see itemName)
{count} The value associated to the date
{date} The date of the cell. It's automatically formatted according to the type of subDomain.
See subDomainDateFormat to further customize that date formatting.
{connector} An English preposition placed before a datetime (on Monday, at 15:00, etc..). Each subDomain have their own default connector, corresponding to the default date format.


String or function

Format of the {date} template string inside subDomainTitleFormat.

{date} is by default formatted according to the subDomain type.


subDomainDateFormat can accepts any string with directive accepted by d3.time.format(), like "%Y-%m-%d".

As d3.time.format() will only output english dates, subDomainDateFormat can also accepts a function, with the subDomain date as argument.

var cal = new CalHeatMap();
  subDomainDateFormat: function(date) {
    return moment(date).format("LL"); // Use the moment library to format the Date


String or function

Format of the text inside a subDomain cell.

Disabled by default, you can display a text inside each subDomain cell. Works exactly like subDomainDateFormat, except that the function takes the cell value as second argument.

Display the date in cell

Display the value in cell

Styling tips
Use .subdomain-text in your CSS to style the text. Text is centered horizontally and vertically, and can not by changed.


String or function

Format of the domain label.

Works exactly like subDomainDateFormat, and will format the domain label with any string accepted by d3.time.format(), or a function.

Default label according to the domain type

Custom domain label

See subDomainDateFormat for more infos on accepted values.

To not display the domain label, set domainLabelFormat to "" (empty string). 3.1.1+



Formatting of the legend title, displayed when hovering a legend cell.

It takes an object with up to 3 properties:

Property Default value Description
lower less than {min} {name} Formatting of the smallest (leftmost) value of the legend
inner between {down} and {up} {name} Formatting of all the value but the first and the last
upper more than {max} {name} Formatting of the biggest (rightmost) value of the legend

Some template string are available, and enclosed in braces.

Template Description
{name} Name of the entity represented in the calendar (see itemName)
{min} The first value of the legend array.
{max} The last value of the legend array.
{down} The lower bound of a color
{up} The upper bound of a color
Hover over the legend cells to see how it's formatted



Animation duration, in milliseconds.


1.5 second


String selector or Element

Will attach the previous() event to the specified element, on a mouse click, shifting the calendar one domain back.

Targeting a simple #ID

Using a more complex Selector String

  • Next
  • Next (ignored)
  • Next (ignored)

Then, let's go back to the previous domain.

If you want to shift by more than one domain, see the previous() method

See itemSelector for details about String Selector and Element type.


String selector or Element

Will attach the next() event to the specified element, on a mouse click, shifting the calendar one domain forward.

If you want to shift by more than one domain, see the next() method

See previousSelector for an example.

See itemSelector for details about String Selector and Element type.



The calendar instance namespace.

If you have more than one instance of Cal-Heatmap, you should assign each instance its own namespace, in order to isolate each instance event handler.

This will not work

var cal = new CalHeatMap();
  nextSelector: "#next" // Attach #next onClick event to


var cal2 = new CalHeatMap();
  nextSelector: "#next" // Attach #next onClick event to, overiding


This will work

var cal = new CalHeatMap();
  nextSelector: "#next",
    // Attach onClick event to
  itemNamespace: "cal"


var cal2 = new CalHeatMap();
  nextSelector: "#next",
    // Attach #next.cal2 onClick event to
  itemNamespace: "cal2"



Beside init(), Cal-HeatMap also boast other methods to control the behavior of the calendar.


Shift the calendar n domains back

n is optional, and by default is equal to 1.

var cal = new CalHeatmap();

See previousSelector if you wish to directly attach previous() to an Element mouse click event.


Shift the calendar n domains forward

n is optional, and by default is equal to 1.

var cal = new CalHeatmap();
cal.init({});;; // Move back by 5 domains

See nextSelector if you wish to directly attach next() to an Element mouse click event.

jumpTo(date, reset)


Jump the calendar to the specified date

This method will shift the calendar backward or forward, until the domain containing the specified date is visible.

Argument Type Description
Date Date The date to jump to.
reset boolean Optional Default: false

Whether to set the domain with the specified as the calendar's first domain.

With reset == false, jumpTo will stop as soon as the wanted date is visible

With reset == true, jumpTo will move the wanted domain to the start of the calendar



Reset the calendar back to the start date

The following calendar will jump 5 domains forward immediatly on load. Use the Rewind button to reset to the initial start date.

update(data, afterLoad, updateMode)


Update the calendar with new data

Use update() when you want to refresh the calendar with a new set of data.
Particularly useful if you're filling the calendar in realtime, or if you want to display a subset of the current data.

Argument Type Description
data string|object Accept the same format as the data option.
afterLoad boolean|function Optional Default: true

Whether to execute the afterLoad() callback to convert your data into the json object, expected by cal-heatmap.

It can also directly takes a function, in case your data can not be converted with the afterLoad() function you defined.

updateMode constant

Optional Define how to insert the new data into the calendar.

Accepted values are:

Instance.RESET_ALL_ON_UPDATE (default)
Reset the whole calendar data before inserting the new data.
Update only the dates (subDomain) you pass in the data argument, replace their value by the new ones. All other dates are leaved untouched.
Instead of replacing a date's value by a new one, increment it by the new value. All other dates are leaved untouched. That's the one you want to use of you're populating the calendar in realtime!

The updated data are not persisted across domain navigation. If the domain was removed then reinserted after a navigation, all updates will be lost, since it'll fetch the datasource given in data.

If you're using the Instance.RESET_ALL_ON_UPDATE mode, you can change data to the new datasource, to persist the change.

var cal = new CalHeatmap();
  data: "user=Eric"

// Wrong way !
// Same as calling cal.update("user=Marc", true, cal.RESET_ALL_ON_UPDATE);;
// => The new loaded domain contains data from Eric !

// Right way
cal.update("user=Marc"); = "user=Marc";;

Toggling between 2 data sets

Increment a random date by 2

Set a random date to 1



Change the highlighed dates

Accepts the same arguments as the highlight option in init().

var cal = new CalHeatMap();
  highlight: new Date(2000, 0, 1) // Highlight January 1st

// I changed my mind, let's highlight January 2nd instead
cal.highlight(new Date(2000, 0, 2));

// Add January 5th to already highlighted dates
cal.highlight(cal.options.highlight.push(new Date(2000, 0, 5)));


Return the SVG source code with the appropriate CSS

The returned string code is valid and ready to be placed in a .svg file.

setLegend(legend, legendColors)


Change the legend settings and/or threshold

Can takes 2 optional arguments:

Value Description
legend Same as legend: an array of thresholds
legendColors Same as legendColors: an object with the heatmap's colors, or an array of 2 colors

When called without arguments, setLegend() will just redraw the legend.

var cal = new CalHeatMap();
  legendVerticalPosition: "top",
  legendHorizontalPosition: "left",
  legendOrientation: "horizontal"

// Lets' move the legend on the right side, and rotate it by 90deg
cal.legendVerticalPosition: "center";
cal.legendHorizontalPosition: "right";
cal.legendOrientation: "vertical";

// Let's redraw the calendar to apply the settings

Check the legend playground for example.



Remove the legend from the calendar

Settings are kept and you can re-add the legend with the same settings using showLegend().

Check the legend playground for example.



Display the legend, if not already shown

Check the legend playground for example.



Remove the calendar from the DOM

destroy() always return null.

It can take a function as argument, that will be executed when the calendar is removed from the DOM, at the end of the animation.

Remember to self-assign the result of destroy() to your calendar instance, or it'll lead to a memory leak.

var cal = new CalHeatMap();
cal.init({}); // Creating the calendar
cal = cal.destroy(); // Remove the calendar from the DOM, and assign the 'cal' variable to `null`


onClick(date, value)

Called after a mouse click event on a subDomain cell.

Click a date on the calendar

Styling tips
.hover_cursor style is applied to all click-able cells.


Called after drawing the empty calendar, and before filling it with data.

Calendar waiting to be drawn ...


Called after shifting the calendar one domain back.

The date argument is the start date of the domain that was added.

No previous domain loaded yet


Called after shifting the calendar one domain forward.

The date argument is the start date of the domain that was added.

No next domain loaded yet


Called after drawing and filling the calendar.

Useful in case you're loading data via ajax, as it's loading data asynchronously. This event will wait for the ajax request to complete before triggering.

This event will only trigger once, on the initial setup. See afterLoadPreviousDomain and afterLoadNextDomain for callback events after a domain navigation.

Calendar waiting to be drawn


Called after getting the data from source, but before filling the calendar.

This callback must return a json object formatted in the expected data format.

afterLoadData() is used to do some works on the data, especially when the data source is not returning data in the expected format.

// datas is an array of object
var datas = [
  {date: 946702811, value: 15},
  {date: 946702812, value: 25},
  {date: 946702813, value: 10}

var parser = function(data) {
  var stats = {};
  for (var d in data) {
    stats[data[d].date] = data[d].value;
  return stats;

/* Parser will output the object
  "946702811": 15,
  "946702812": 25,
  "946702813": 10

var cal = new CalHeatMap();
  data: datas,
  afterLoadData: parser



Triggered after previous(), when the incoming domain is containing minDate.

When the leftmost domain set by minDate is loaded into the calendar, onMinDomainReached() will be triggered with true as argument.

This event is useful if you want to disable your previous button, since there is no more previous domains to load.

In order to reverse the action, onMinDomainReached() will be called with false as argument after next(), only once, and only if the leftmost domain is not the lower limit domain anymore.



    Triggered after next(), when the incoming domain is containing maxDate.

    See onMinDomainReached().

    Data Format

    Cal-Heatmap expects data in a JSON object in the following format:

      "timestamp": value,
      "timestamp2": value2,

    All timestamp are in seconds.

    value can be any number (integer or float).

    If your API is not returning a JSON object, set the dataType option according to the returned result mimetype. In addition to JSON, Cal-Heatmap can also parse CSV, TSV and plain text file.

    Using the right dataType allows cal-heatmap to interpret and convert the response to a valid json object, but does not guarantee that it'll be formatted in the timestamp:value format.

    If the first and second column of your CSV or TSV files are respectively the timestamp and value, no additional action is required, cal-heatmap will try to extract the appropriate data, else, you have to use the afterLoadData() callback to convert the data yourself. See example with Google Analytics data.

    All timestamps belonging to the same subDomain will be stacked together, and all subDomains with no associated data will be considered as empty (no data), and does not fall back to value = 0.

    Domain Highlighting

    Opposed to subDomain Highlight, domain highlighting is "passive", you don't need to inject code into the calendar to highlight a domain, you have to edit the stylesheet.

    Each domain have a set of class according to a date.

    y_xxxx Year, xxxx is a four digit year
    m_1 to m_12 From January to December
    w_1 to w_54 Week 1 to Week 54
    d_1 to d_31 Day of the month
    dy_0 to dy_6 Day of the week, Sunday is 0 and Saturday is 6 3.0.7+
    h_0 to h_23 From 00h to 23h

    To highlight January 2000, you can use

    /* Apply a red background to january 2000 */
    .y_2000.m_1 .domain-background {
      fill: red;

    For better result, apply some margin with domainMargin.

    You can also apply some "recursive" style, to highlight sleep hours.

    .h_23 .domain-background, .h_0 .domain-background, .h_1 .domain-background, .h_2 .domain-background, .h_3 .domain-background, .h_4 .domain-background, .h_5 .domain-background, .h_6 .domain-background, .h_7 .domain-background {
      fill: steelblue;
    .h_23 .graph-label, .h_0 .graph-label, .h_1 .graph-label, .h_2 .graph-label, .h_3 .graph-label, .h_4 .graph-label, .h_5 .graph-label, .h_6 .graph-label, .h_7 .graph-label {
      fill: #fff;

    Migrating from 2.x

    v2 documentation is still available at /v2/

    There's a lot of changes coming with the 3.x branch. You should read the full documentation to use all the new features. Instruction below are only to migrate your v2 code to the v3 version.


    onClick event is now applied even to date with no data.
    The second argument will be equal to null when there is no data associated to this date.


    The duration option has been renamed to animationDuration


    The id option has been replaced by itemSelector, and now accepts a Selector String or an Element, instead of an ID name.


    cellsize has been renamed to cellSize (camel case)


    cellpadding has been renamed to cellPadding (camel case)


    cellradius has been renamed to cellRadius (camel case)


    cellLabel has been renamed to subDomainTitleFormat


    scaleLabel has been renamed to legendTitleFormat


    weekStartOnMonday now takes a boolean (true/false) instead of an integer (0/1)


    format has been spitted into multiple options.

    The domain label can now be customized, by passing a function to domainLabelFormat.


    browsing option has been removed, see the next(), previous() events, and the nextSelector and previousSelector options.


    highlightToday has been replaced by highlight, and accepts an array of dates, allowing highlighting more than one date.


    itemName now accepts a single value array, or just a string, and will auto guess the plural form as long as it's just the singular form plus "s".

    Calendar dynamic width

    Previously, calendar width was dynamic, when using the month domain, coupled with the x_week subDomain, as a month not always contains the same number of weeks. Domain width was adjusted to the number of weeks to avoid blank space.

    This function has been removed in v3, the domains will now always have the same width, so there'll be a blank space on some months with fewer weeks. This will avoid complex calculation, and allow smoother animation when navigating between domains.

    This does not affect other domain/subDomain couple.


    All examples are using the datas-hours and datas-years data source.

    Read the page source for the examples' complete code, as the stylesheet is not visible via the View code buttons.

    Localized in French, this graph is displaying the number of flies falling in Master Lao Zhi's living room because of his suffocating ki energy.

    Hover over the subDomain and legend cells to view the new customized title.

    Let's try other calendar view (month > day), and re-calibrate the legend colors for our data.

    Week > hour view.

    Year > day view.

    Year > Month view.

    Week > day view, vertically.
    Dynamic domain dimension was disabled to avoid page height "fluctuation".
    Pass domain label to an external function to uppercase it.

    Week > day view

    Hover on a subDomain cell to view the date

    Give another background color for all February.

    Display all the hours of the day on the same line.

    Use Google Analytics data

    You have to pre-process the csv files generated by Google Analytics to remove the header comments, as well as the last 2 lines.

    Same datasource as previous example, with one line per day


    Coming soon ...


    Cal-HeatMap is licensed under the MIT License

    Developed with love by Wan Qi Chen