Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
07c59d6
feat(charts): add heatmapChart theme section
adamwoodnz Jun 30, 2026
e165025
feat(charts): add HTML HeatmapChart component
adamwoodnz Jun 30, 2026
a702ba9
test(charts): cover HeatmapChart behaviour and helpers
adamwoodnz Jun 30, 2026
f99dcf8
docs(charts): add HeatmapChart stories, docs and sample data
adamwoodnz Jun 30, 2026
61d674d
style(charts): trim redundant comments and doc cross-references
adamwoodnz Jun 30, 2026
7aff0f0
test(charts): drop vestigial SVG title assertion
adamwoodnz Jun 30, 2026
e4407f0
test(charts): query by role instead of suppressing no-node-access
adamwoodnz Jun 30, 2026
dc0f351
feat(charts): compact in-cell formatting for large heatmap values
adamwoodnz Jun 30, 2026
d5be04b
fix(charts): pick in-cell text color by fill luminance for WCAG contrast
adamwoodnz Jun 30, 2026
8dc8968
style(charts): use WPDS token for light in-cell text
adamwoodnz Jun 30, 2026
5862d7b
fix(charts): reflow heatmap fluidly via CSS sizing
adamwoodnz Jun 30, 2026
afe45b2
docs(charts): note heatmap in-cell text contrast handling
adamwoodnz Jun 30, 2026
407ce32
fix(charts): cap aspect-ratio wrapper at maxWidth
adamwoodnz Jul 1, 2026
8a58e69
fix(charts): honor fixed dimensions in unresponsive heatmap
adamwoodnz Jul 1, 2026
d898ef8
refactor(charts): stabilize heatmap handleCellMouseMove callback
adamwoodnz Jul 1, 2026
c4c3e43
test(charts): cover with-responsive aspect-ratio wrapper styles
adamwoodnz Jul 1, 2026
eeb8f35
docs(charts): fix code-block indentation in heatmap docs
adamwoodnz Jul 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Charts: add HeatmapChart for matrix and calendar/contribution-style data, with a compact mode and a composition color-scale legend.
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
.heatmap-chart {
width: 100%;
height: 100%;
min-height: 0;
font-size: var(--wpds-typography-font-size-sm, 12px);
}

.heatmap-chart__empty {
padding: var(--wpds-dimension-padding-lg, 16px);
color: var(--wpds-color-fg-content-neutral-weak, #707070);
}

// Track template is set inline because CSS `repeat()` won't take a `var()`
// count. ARIA rows are `display: contents` so their cells join this grid.
.heatmap-chart__grid {
display: grid;
gap: var(--heatmap-cell-gap, var(--wpds-dimension-gap-xs, 4px));
flex: 1 1 0;
width: 100%;
// Floor so the grid stays usable when the parent has no explicit height.
min-height: 200px;

&:focus-visible {
outline:
var(--wpds-border-width-focus, var(--wp-admin-border-width-focus, 2px)) solid
var(--wpds-color-stroke-focus-brand, var(--wp-admin-theme-color, #3858e9));
outline-offset: 2px;
border-radius: var(--wpds-border-radius-sm, 2px);
}
}

.heatmap-chart__grid--compact {
flex: 0 0 auto;
width: max-content;
height: max-content;
min-height: 0;
}

.heatmap-chart__row {
display: contents;
}

.heatmap-chart__col-label,
.heatmap-chart__row-label {
color: var(--wpds-color-fg-content-neutral-weak, #707070);
font-size: var(--wpds-typography-font-size-xs, 11px);
white-space: nowrap;
overflow: visible;
}

.heatmap-chart__col-label {
align-self: end;
text-align: left;
}

.heatmap-chart__row-label {
align-self: center;
justify-self: end;
text-align: right;
padding-inline-end: var(--wpds-dimension-padding-xs, 4px);
}

.heatmap-chart__cell {
display: flex;
align-items: center;
justify-content: center;
min-width: 0;
min-height: 0;
border-radius: var(--wpds-border-radius-sm, 2px);
// Empty (no-data) default; cells with a value override it below.
background: var(--wpds-color-bg-track-neutral-weak, #f0f0f0);
}

// Floor the mix at 15% so the lowest value stays visibly tinted, distinct from
// an empty cell.
.heatmap-chart__cell--filled {
background: color-mix(in sRGB, var(--heatmap-primary) calc((0.15 + 0.85 * var(--intensity)) * 100%), transparent);
}

.heatmap-chart__cell--selected {
outline:
var(--wpds-border-width-focus, var(--wp-admin-border-width-focus, 2px)) solid
var(--wpds-color-stroke-focus-brand, var(--wp-admin-theme-color, #3858e9));
outline-offset: calc(-1 * var(--wpds-border-width-focus, var(--wp-admin-border-width-focus, 2px)));
}

.heatmap-chart__cell-value {
color: var(--wpds-color-fg-content-neutral, #1e1e1e);
font-size: var(--wpds-typography-font-size-md, 13px);
}

// Light text on fills dark enough to out-contrast dark text (decided in JS from
// the blended fill luminance).
.heatmap-chart__cell--strong .heatmap-chart__cell-value {
color: var(--wpds-color-fg-interactive-neutral-strong, #f0f0f0);
}

.heatmap-chart__legend-swatch {
width: var(--wpds-dimension-size-3xs, 12px);
height: var(--wpds-dimension-size-3xs, 12px);
border-radius: var(--wpds-border-radius-sm, 2px);
background: color-mix(in sRGB, var(--heatmap-primary) calc((0.15 + 0.85 * var(--intensity)) * 100%), transparent);
}
Loading
Loading