// heatmap: sage redesign. See docs in the scratchpad PORT.md brief. // // The contribution card on the dashboard (views/heatmap.rs). Cells are squares with a 3px gap; // the grid lays out seven rows and fills columns, so a week is one column. Colours come from the // --h0..--h4 tokens, so both themes follow main.scss. // // The cell size follows the card: 53 columns fill the card's width, between 11px (then the grid // scrolls inside the card) and 16px. The card is a size container, so 100cqi is its width. $cell: var(--cell); $gap: 3px; .heat { container-type: inline-size; --cell: 11px; // the legend squares; the grid overrides it in .heat-scroll margin-top: 16px; padding: 24px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); min-width: 0; h2 { font-size: 18px; } } .heat-head { display: flex; flex-wrap: wrap; gap: 20px 40px; justify-content: space-between; align-items: flex-end; margin-bottom: 22px; min-width: 0; } .heat-stats { display: flex; flex-wrap: wrap; gap: 18px 36px; min-width: 0; } .heat-stat { b { display: block; font-size: 24px; font-weight: 600; line-height: 1.2; letter-spacing: -0.02em; color: var(--text); font-variant-numeric: tabular-nums; } b.accent { color: var(--accent-ink); } span { font-size: 13px; color: var(--muted); } } // Scrolls sideways inside the card when the 53 columns do not fit (narrow screens). // 200px is the label column, the gaps between columns and the padding: 53 cells fill the rest. .heat-scroll { --cell: clamp(11px, calc((100cqi - 200px) / 53), 16px); overflow-x: auto; padding: 4px 2px 12px; max-width: 100%; } .heat-body { display: grid; grid-template-columns: 32px max-content; column-gap: 8px; width: max-content; } .heat-days { display: grid; grid-template-rows: repeat(7, $cell); row-gap: $gap; margin-top: 18px; font-size: 11px; line-height: $cell; color: var(--muted); span { white-space: nowrap; } } // One slot per column, so each label sits over the column where its month starts. .heat-months { display: grid; grid-auto-flow: column; grid-auto-columns: $cell; column-gap: $gap; height: 18px; font-size: 12px; line-height: 18px; color: var(--muted); span { white-space: nowrap; } } .heat-grid { display: grid; grid-template-rows: repeat(7, $cell); grid-auto-flow: column; grid-auto-columns: $cell; gap: $gap; } .cell { display: block; width: $cell; height: $cell; border-radius: 3px; } .l0 { background: var(--h0); } .l1 { background: var(--h1); } .l2 { background: var(--h2); } .l3 { background: var(--h3); } .l4 { background: var(--h4); } .heat-grid .cell:hover { outline: 1px solid var(--muted); outline-offset: 0; } .cell.blank { visibility: hidden; } .heat-foot { display: flex; flex-wrap: wrap; gap: 12px 24px; justify-content: space-between; align-items: center; margin-top: 8px; min-width: 0; p { margin: 0; } } .legend { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); span:not(.cell) { padding: 0 4px; } } @media (max-width: 600px) { .heat { padding: 18px; } .heat-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 16px 24px; width: 100%; } .heat-stat b { font-size: 20px; } }