PhiaUi.Components.HeatmapCalendar (phia_ui v0.1.17)

Copy Markdown View Source

Heatmap calendar grid component for PhiaUI.

Renders a GitHub-style contribution heatmap — a 2-D grid where each cell's colour intensity represents the magnitude of a metric at that position.

CSS-only — no JS hook required. Fully server-rendered.

When to use

Common use cases for heatmap grids:

  • GitHub contribution graph — activity over 52 weeks × 7 days
  • Site traffic heatmap — hour-of-day vs. day-of-week (24 × 7)
  • Server load matrix — CPU/memory over time buckets
  • Sales frequency grid — products vs. months
  • Error rate map — services vs. deployment environments

Intensity levels

Values are bucketed into 5 levels (0–4) relative to max_value. Add the following CSS to your stylesheet or priv/static/theme.css to apply colours (these use the primary token so they respect the active theme):

.heatmap-0 { background-color: oklch(var(--muted)); }
.heatmap-1 { background-color: oklch(var(--primary) / 0.2); }
.heatmap-2 { background-color: oklch(var(--primary) / 0.4); }
.heatmap-3 { background-color: oklch(var(--primary) / 0.65); }
.heatmap-4 { background-color: oklch(var(--primary) / 0.9); }

Data format

Pass a flat list of %{col: integer, row: integer, value: integer} maps. Positions not present in data are treated as value 0 (level 0, empty).

Example — GitHub-style contribution heatmap

# Build 52 weeks × 7 days of contribution data from the database:
data =
  Contributions.last_year(user_id)
  |> Enum.map(fn c -> %{col: c.week, row: c.day_of_week, value: c.count} end)

<.heatmap_calendar
  data={data}
  rows={7}
  cols={52}
  row_labels={~w[Sun Mon Tue Wed Thu Fri Sat]}
  max_value={10}
  show_legend={true}
/>

Example — hourly traffic heatmap (24 hours × 7 days)

<.heatmap_calendar
  data={@traffic_data}
  rows={7}
  cols={24}
  row_labels={~w[Mon Tue Wed Thu Fri Sat Sun]}
  col_labels={~w[0h 3h 6h 9h 12h 15h 18h 21h]}
  max_value={@peak_traffic}
  show_legend={true}
/>

Accessibility

The grid uses role="grid" on each row container. Each cell uses role="gridcell" and an aria-label describing its position and raw value so keyboard users and screen readers can navigate the matrix.

Summary

Functions

Renders a heatmap calendar grid.

Functions

heatmap_calendar(assigns)

Renders a heatmap calendar grid.

Builds a {col, row} => value lookup map from the data list at render time, then iterates rows × cols to produce a WAI-ARIA grid. Each cell receives a heatmap-{0..4} CSS class derived from the bucketed intensity.

The outer overflow-x-auto wrapper allows wide grids (e.g. 52 columns) to scroll horizontally on narrow screens without breaking the page layout.

Attributes

  • data (:list) - List of %{col: integer, row: integer, value: integer} maps. col and row are 0-based indices. Positions absent from the list are treated as value: 0 (rendered at intensity level 0).

    Defaults to [].

  • rows (:integer) (required) - Number of rows in the grid (e.g. 7 for days of the week).

  • cols (:integer) (required) - Number of columns in the grid (e.g. 52 for weeks in a year, 24 for hours).

  • max_value (:integer) - Maximum expected value used to normalise raw values into intensity buckets 0–4. Values at or above max_value are clamped to level 4. Set this to your 95th-percentile value to avoid a single outlier washing out the rest.

    Defaults to 10.

  • col_labels (:list) - Optional list of column header strings. For readability, pass a subset — not every column needs a label. Length need not match cols. Example: ~w[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec] for monthly buckets.

    Defaults to nil.

  • row_labels (:list) - Optional list of row label strings. Length should match rows. Example: ~w[Sun Mon Tue Wed Thu Fri Sat] for day-of-week rows.

    Defaults to nil.

  • show_legend (:boolean) - When true, renders a small "Less → More" intensity legend below the grid. Recommended when the colour scale is not self-evident from context.

    Defaults to false.

  • class (:string) - Additional CSS classes for the root wrapper. Defaults to nil.

  • Global attributes are accepted. HTML attributes forwarded to the root div.