Heatmap

Values represented by visual intensity.

html

<div class="heatmap heatmap--medium" style="--accent:#BFD9F2;" role="img"
  aria-label="Heatmap of Sessions by day and time. Mon: 6-9 is 82, 9-12 is 55, 12-3 is 30, 3-6 is 10. Tue: 6-9 is 65, 9-12 is 90, 12-3 is 45, 3-6 is 20. Wed: 6-9 is 15, 9-12 is 40, 12-3 is 70, 3-6 is 95.">
  <p class="heatmap__caption">Sessions by day and time of day</p>
  <div class="heatmap__colheads" aria-hidden="true">
    <span class="heatmap__corner"></span>
    <span class="heatmap__colhead">6-9</span>
    <span class="heatmap__colhead">9-12</span>
    <span class="heatmap__colhead">12-3</span>
    <span class="heatmap__colhead">3-6</span>
  </div>
  <div class="heatmap__grid" aria-hidden="true">
    <span class="heatmap__rowhead">Mon</span>
    <div class="heatmap__cell" style="--v:0.82;"><span class="heatmap__cellval">82</span></div>
    <div class="heatmap__cell" style="--v:0.55;"><span class="heatmap__cellval">55</span></div>
    <div class="heatmap__cell" style="--v:0.30;"><span class="heatmap__cellval">30</span></div>
    <div class="heatmap__cell" style="--v:0.10;"><span class="heatmap__cellval">10</span></div>
    <span class="heatmap__rowhead">Tue</span>
    <div class="heatmap__cell" style="--v:0.65;"><span class="heatmap__cellval">65</span></div>
    <div class="heatmap__cell" style="--v:0.90;"><span class="heatmap__cellval">90</span></div>
    <div class="heatmap__cell" style="--v:0.45;"><span class="heatmap__cellval">45</span></div>
    <div class="heatmap__cell" style="--v:0.20;"><span class="heatmap__cellval">20</span></div>
    <span class="heatmap__rowhead">Wed</span>
    <div class="heatmap__cell" style="--v:0.15;"><span class="heatmap__cellval">15</span></div>
    <div class="heatmap__cell" style="--v:0.40;"><span class="heatmap__cellval">40</span></div>
    <div class="heatmap__cell" style="--v:0.70;"><span class="heatmap__cellval">70</span></div>
    <div class="heatmap__cell" style="--v:0.95;"><span class="heatmap__cellval">95</span></div>
  </div>
</div>
<style>
.heatmap { font: 13px/1.4 system-ui, sans-serif; color: #241430; max-width: 340px; }
.heatmap__caption { margin: 0 0 10px; font-size: 13px; font-weight: 600; }
.heatmap__colheads { display: grid; grid-template-columns: 40px repeat(4, 1fr); gap: 4px; margin-bottom: 4px; }
.heatmap__colhead { text-align: center; font-size: 11px; color: #241430aa; }
.heatmap__grid { display: grid; grid-template-columns: 40px repeat(4, 1fr); gap: 4px; }
.heatmap__rowhead { display: flex; align-items: center; font-size: 11px; color: #241430aa; }
.heatmap__cell { aspect-ratio: 1.4; border-radius: 4px; display: flex; align-items: center; justify-content: center;
  --eff: var(--v); background: color-mix(in srgb, var(--accent) calc(var(--eff) * 100%), #FAF7F2); }
.heatmap--low .heatmap__cell { --eff: calc(0.4 + var(--v) * 0.3); }
.heatmap--medium .heatmap__cell { --eff: calc(0.15 + var(--v) * 0.75); }
.heatmap--high .heatmap__cell { --eff: var(--v); }
.heatmap__cellval { font-size: 11px; font-weight: 700; }
</style>

tailwind

<div class="heatmap-tw heatmap-tw--medium max-w-[340px] font-sans text-[13px]" style="--accent:#BFD9F2;" role="img"
  aria-label="Heatmap of Sessions by day and time. Mon: 6-9 is 82, 9-12 is 55, 12-3 is 30, 3-6 is 10. Tue: 6-9 is 65, 9-12 is 90, 12-3 is 45, 3-6 is 20. Wed: 6-9 is 15, 9-12 is 40, 12-3 is 70, 3-6 is 95.">
  <p class="mb-2.5 text-[13px] font-semibold">Sessions by day and time of day</p>
  <div class="grid gap-1 mb-1" style="grid-template-columns:40px repeat(4,1fr)" aria-hidden="true">
    <span></span>
    <span class="text-center text-[11px] text-[#241430aa]">6-9</span>
    <span class="text-center text-[11px] text-[#241430aa]">9-12</span>
    <span class="text-center text-[11px] text-[#241430aa]">12-3</span>
    <span class="text-center text-[11px] text-[#241430aa]">3-6</span>
  </div>
  <div class="grid gap-1" style="grid-template-columns:40px repeat(4,1fr)" aria-hidden="true">
    <span class="flex items-center text-[11px] text-[#241430aa]">Mon</span>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.82;"><span class="text-[11px] font-bold">82</span></div>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.55;"><span class="text-[11px] font-bold">55</span></div>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.30;"><span class="text-[11px] font-bold">30</span></div>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.10;"><span class="text-[11px] font-bold">10</span></div>
    <span class="flex items-center text-[11px] text-[#241430aa]">Tue</span>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.65;"><span class="text-[11px] font-bold">65</span></div>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.90;"><span class="text-[11px] font-bold">90</span></div>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.45;"><span class="text-[11px] font-bold">45</span></div>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.20;"><span class="text-[11px] font-bold">20</span></div>
    <span class="flex items-center text-[11px] text-[#241430aa]">Wed</span>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.15;"><span class="text-[11px] font-bold">15</span></div>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.40;"><span class="text-[11px] font-bold">40</span></div>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.70;"><span class="text-[11px] font-bold">70</span></div>
    <div class="heatmap-tw__cell aspect-[1.4] rounded flex items-center justify-center" style="--v:0.95;"><span class="text-[11px] font-bold">95</span></div>
  </div>
</div>
<style>
.heatmap-tw__cell { --eff: var(--v); background: color-mix(in srgb, var(--accent) calc(var(--eff) * 100%), #FAF7F2); }
.heatmap-tw--low .heatmap-tw__cell { --eff: calc(0.4 + var(--v) * 0.3); }
.heatmap-tw--medium .heatmap-tw__cell { --eff: calc(0.15 + var(--v) * 0.75); }
.heatmap-tw--high .heatmap-tw__cell { --eff: var(--v); }
</style>

react

export function Heatmap({
  metric = "Sessions",
  intensityScale = "medium",
  accent = "#BFD9F2",
  showValues = true,
}) {
  const rows = ["Mon","Tue","Wed"];
  const cols = ["6-9","9-12","12-3","3-6"];
  const vals = [[82,55,30,10],[65,90,45,20],[15,40,70,95]];
  const curve = {
    low: (v) => 0.4 + v * 0.3,
    medium: (v) => 0.15 + v * 0.75,
    high: (v) => v,
  }[intensityScale] ?? ((v) => v);
  const label = `Heatmap of ${metric} by day and time. ` + rows.map((r, ri) =>
    `${r}: ` + cols.map((c, ci) => `${c} is ${vals[ri][ci]}`).join(", ") + "."
  ).join(" ");
  return (
    <div role="img" aria-label={label} style={{ fontFamily: "system-ui, sans-serif", color: "#241430", maxWidth: 340, fontSize: 13 }}>
      <p style={{ margin: "0 0 10px", fontSize: 13, fontWeight: 600 }}>{metric} by day and time of day</p>
      <div style={{ display: "grid", gridTemplateColumns: "40px repeat(4, 1fr)", gap: 4, marginBottom: 4 }}>
        <span />
        {cols.map((c) => <span key={c} style={{ textAlign: "center", fontSize: 11, color: "#241430aa" }}>{c}</span>)}
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "40px repeat(4, 1fr)", gap: 4 }}>
        {rows.map((row, ri) => (
          <React.Fragment key={row}>
            <span style={{ display: "flex", alignItems: "center", fontSize: 11, color: "#241430aa" }}>{row}</span>
            {cols.map((col, ci) => {
              const v = vals[ri][ci] / 100;
              const eff = curve(v);
              return (
                <div key={row + col} style={{
                  aspectRatio: "1.4", borderRadius: 4, display: "flex", alignItems: "center", justifyContent: "center",
                  background: `color-mix(in srgb, ${accent} ${eff * 100}%, #FAF7F2)`,
                }}>
                  {showValues && <span style={{ fontSize: 11, fontWeight: 700 }}>{vals[ri][ci]}</span>}
                </div>
              );
            })}
          </React.Fragment>
        ))}
      </div>
    </div>
  );
}