heatmap
Dashy vault/Reference/heatmap
Reference/heatmap.md

heatmap: the year

Properties
code block
heatmap
keys
13 on the block, 3 on each layer
checked on
Dashy 1.5.2

A year by days: one cell per day, coloured by a number from frontmatter.

From the demo vault

heatmap
```heatmap
source: Diary
field: sleep_score
color: purple
bands: [90, 80, 70]
title: Sleep, last twelve months
```
renders as
A year of days coloured by sleep score, with a legend

A minimal block

The shortest config worth pasting. Change the folder and the property names to yours.

heatmap
```heatmap
source: 01-Areas/Personal/Diary
field: sleep_score
color: purple
bands: [90, 80, 60]
```

Keys on the block

KeyWhat it doesTypeDefault
sourcealso folder, from folder; includes nested ones text none
tag tag, with or without the hash text none
where a condition like year = 2026, rating >= 4, tags contains books, or several that must all hold: joined with and (year = 2026 and rating >= 4) or written as a list ([year = 2026, "rating >= 4"]). The field name may be a dotted path into a nested property, like health.sleep > 70. or is not supported; quote a value holding the word and or or. One unreadable condition drops the whole filter with a warning, and the grid is drawn unfiltered text or list none
fieldalso property, prop required unless layers is set, and not allowed together with it: exactly one of the two. A numeric frontmatter property, or a checkbox: ticked days are painted, unticked stay empty; dotted for a nested one like health.sleep. Also takes a list, like [mood_am, mood_pm], to collapse several properties from the same note into one day with per_day. A field missing from the selection, or holding text rather than a number, errors and says which; count text elsewhere with a stats card's where: "field contains ..." and agg: count. A duration string like 5h 58min or 7:30 counts as minutes, and tooltips, the caption's average and the legend then read 5h 58m; H:MM is always a duration, never a time of day. A field mixing durations and plain numbers counts both as minutes, shows plain numbers and warns text or list none
per_day how several values landing on one day combine: sum, avg or max. Several values happen either from two or more notes on the same day, or from a field list on one note, or both at once. An unrecognised value warns and falls back to sum. With layers, it applies to each layer's own field(s) separately text sum
date_field a date frontmatter property to read instead of the note name: 2026-03-02 or 2026-03-02T10:30, or a dotted path into a nested property like meta.date text none
skip_field a property marking a day special, like vacation: true or sick: flu; a day is special once any note landing on it sets the property to anything other than false, a blank string, 0 or absent. Its cell gets a hatched overlay, on top of its painted colour when it has one. Works with layers too text none
coloralso colour blue green cyan purple pink orange red gray, or #rrggbb. Ignored, with a warning, when layers is set: each layer carries its own colour instead text blue
layers several activities on one grid, each in its own colour, instead of one field: [{field, color, label}]. Not used together with the block's own field. The first layer painted on a day colours that cell and supplies its value and link; the tooltip lists every layer with a value that day, in list order. The caption then counts days where any layer painted and drops the average, since averaging different fields together says nothing useful list none
bands thresholds from the top down: [90, 80, 60] or [{min, alpha, label}]; anything below the lowest falls into the bottom band. Without bands, each grid (a calendar year, or the one range window) shades itself by its own minimum and maximum: a checkbox field or a grid where every painted value is the same still paints one flat colour instead. A threshold may be a duration, [8h, 7h] or {min: 7h}, read as minutes; a plain number against a field of durations means minutes, and the legend reads as durations list none
range one grid over a window ending today, instead of a grid per calendar year: week, month, year (1 January of the current year to today) or a rolling count of days like 365d. Columns still align to the week the same way; a day outside the window is neither drawn nor counted. An unrecognised value warns and falls back to a grid per year text or number none
title a custom heading instead of the automatic one text none

Keys on each layer

Each entry under items (here layers) is one layer.

KeyWhat it doesTypeDefault
field requiredalso property, prop this layer's own numeric frontmatter property, or a checkbox; dotted for a nested one, or a list to collapse several properties into this layer's day. Same shapes as the block's own field, durations included text or list none
coloralso colour blue green cyan purple pink orange red gray, or #rrggbb. Without one, the next free colour from the palette, skipping colours other layers already claimed text none
labelalso title, name name shown in the legend and the cell tooltip. Defaults to the field name(s) text none

How it behaves

  • A note's date is its name, as long as it starts with YYYY-MM-DD (2026-01-05 Monday counts, 2026-01-051 does not), unless date_field names a property instead. That is how the block knows which cell it belongs to.
  • Two or more notes landing on the same day paint one cell, and so does a field list on one note: per_day (default sum) says how the day's values combine, and a ticked or numeric contributor always outweighs a false one on the same day.
  • Years are taken from the data, newest first, but never one later than today: a note dated in the future draws nothing extra and counts nowhere in the grid. If every dated note turns out to be in the future, the current year is still drawn, empty.
  • layers replaces field for tracking several activities on one grid, each its own colour: layers: [{field: gym, color: blue}, {field: run, color: green, label: Running}]. When two layers land on the same day, the first one in the list colours the cell; the tooltip still lists every layer that has a value that day.
  • skip_field marks special days, vacation or sick for example: they still show a hatched cell, keeping any painted colour underneath, and their count in the caption is unchanged (a special day with no value is still not present).
  • range draws one grid over a window ending today instead of a grid per year: range: 365d is a rolling year that crosses 1 January in a single grid rather than splitting into two. Everything else works the same over that one grid: bands, layers, skip_field and its legend row, the caption's count and average, tooltips and links.