feat(card): stack heatmap into two halves; unify font-size vocabulary (#23)

## Heatmap — two halves, 8x8 cells

The single-row 53-week layout could never hold square cells larger than
4x4 inside a 340 px card — cramped. Split the year in half (ceil(weeks/2)
on top, floor on bottom) and each half is ~27 weeks wide, freeing the
cells to be 8x8 (4x the area) while keeping comfortable left (30 px)
and right (~67 px) gutters. Grid:

  topPadA = 45, half = 7*9 - 1 = 62 tall
  halfGap = 13
  topPadB = 120, same 62 tall
  grid bottom at y = 182, 18 px frame margin

Year still reads top-to-bottom left-to-right, just with one extra line
break at the midpoint. Dropped the separate Less/More legend — at 8x8
the intensity gradient is self-explanatory, and the removal buys the
vertical space the new layout needs.

Refactored into a helper `renderHeatmapHalf` so the two halves share a
single code path (labels + month markers + cells).

## Font-size vocabulary

Four named constants in svg.go: fontBody=12, fontLabel=11, fontAxis=10,
fontBigNum=28. Every card's existing literals already sit on this
ladder except the heatmap, which was using 9 for weekday/month labels.
Heatmap now routes through fontAxis so the whole gallery shares one
size scale.

Other cards' literals weren't rewritten to reference the constants
(pure churn for no behavior change); the constants give future cards
the vocabulary and the design-guidelines the schema.
This commit is contained in:
2026-04-19 11:39:50 +07:00
committed by GitHub
parent 672bfe9d8d
commit 70bcbd43f2
3 changed files with 73 additions and 67 deletions
+3 -3
View File
@@ -77,9 +77,9 @@ When there's **exactly one slice** (one language at 100%), the renderer emits tw
| Metric | Value |
| --- | --- |
| Grid | 7 rows × 53 columns (Sunday → Saturday, oldest week → newest) |
| Cell size | 4 × 4 px square, 1 px gap. 4 is the largest square that fits with breathing room on both sides (`leftPad 30 + 53 × 5 = 295 px`, 45 px right gutter). 5 × 5 with a gap overflows; 5 × 5 touching cells loses visible separation; rectangular cells look stretched. Card has vertical headroom to spare — we accept that in exchange for a clean square grid |
| Grid y-range | `topPad(70) .. 70 + 7 × 5 = 105 px` |
| Layout | **Two stacked halves** of ~27 weeks each. One-row 53-week layout forces cells down to 4 × 4 to fit in 340 px; splitting in half lets each half be 27 weeks wide at **8 × 8 square cells** — 4× the cell area. Year still reads top-to-bottom, left-to-right. |
| Cell size | 8 × 8 px square, 1 px gap |
| Grid geometry | `leftPad 30`, `topPadA 45` (top half), `halfGap 13`, `topPadB 120`. Each half is 7 × 9 1 = 62 px tall. Grid bottom at y=182 leaves 18 px for the frame border. |
| Cell colour | 5-bucket ramp `mixHex(Background, Accent, k/4)` for `k ∈ 0..4` — no dedicated ramp field on the theme schema |
| Weekday labels | Mon / Wed / Fri only, right-anchored in the `leftPad` gutter |
| Month labels | Printed above the first week where a 1st-of-month day falls; skipped when `x > width 20` so `Dec` / `Apr` can't spill past the frame |