Charts

Calendar Heatmap

A contribution calendar, one square per day

A calendar heatmap shows one value per day. Columns are weeks, rows are weekdays, and the color gets stronger with the value, like the contribution graph on a GitHub profile.

Contributions

Monday first, own colors, other locale

week-start moves the first row to Monday. colors sets every level from empty to full; max sets the value that reaches the top color. locale changes month, weekday and tooltip text.

Days and time zones

A day is a calendar date, not a moment in time. date-key values are either YYYY-MM-DD strings or Date objects; a Date counts by its local calendar date, so a local midnight never moves to the day before. Rows with the same day are added up. Invalid dates such as 2026-02-30 are skipped.

Without end, the calendar ends on the latest day in data, not on today, so the server and the browser render the same days. Without start, it shows the 52 weeks before end.

Props

PropTypeDefaultDescription
dataRow[]requiredRows with a day and a value
date-keyrow path or accessor'date'Field with the day
data-keyrow path or accessor'value'Field with the number to color by
start / endstring | Date52 weeks up to the latest dayRange shown
week-start0–60 (Sunday)First weekday row
colorstringvar(--v-charts-series-1, var(--v-charts-series, #2563eb))Full-intensity color
empty-colorstringvar(--v-charts-muted, #ebedf0)Color for no value and zero
levelsnumber4Steps above empty; each mixes color into empty-color
colorsstring[]—Explicit fill per level, from empty to full
maxnumberlargest value in rangeValue that reaches the top level
gapnumber3Space between squares in px
radiusnumber2Corner radius
localestring'en-US'Locale for month, weekday and date text, and for numbers
value-formatter(value, day) => stringlocale number format (1,234)Value text in the tooltip and accessible names. String keeps raw numbers
month-labels / weekday-labelsbooleantrueShow the labels
width / height / aspectnumberfull width, square cellsSize. See chart size
titlestring'Activity calendar'Accessible name
active-indexnumber | null—Rendered cell position, for v-model:active-index
is-animation-active / transitiontrue / motion tokensAnimate data changes

Events and slots

PayloadDescription
@cell-click, @cell-mouseenter, @cell-mouseleave(day: CalendarDay<Row>, index, event)The day behind the square. cell-click gets a MouseEvent, or a KeyboardEvent for Enter
#cell{ cell, index, active, x, y, width, height, fill, radius }Replace the square's <rect>
default slot—Place <Tooltip> here

Keyboard

Arrow keys move by day (up and down) and by week (left and right), skipping days outside the range. Home and End jump to the first and last day, Enter emits cell-click for the active day (with a KeyboardEvent), Escape clears the selection.

Bind v-model:active-index to control selection; null clears it. Pointer and keyboard changes request an update. See Events and v-model for control precedence.

The row type Row is inferred from data. Key props accept typed row paths or accessors.

CalendarDay<Row>.rows contains the source rows for that day. Missing days have an empty rows array. Events, the cell slot and tooltip payloads use the same day object.

Copyright © 2026