Skip to content

Commit 7ac1297

Browse files
committed
fix(site): redesign the History growth chart and snapshot panel
The growth timeline rendered 27 fixed-width bars in a half-width panel, so it overflowed into a horizontal scroll and got clipped. Replace it with an SVG area chart that scales the whole timeline to the panel width — every sync visible at once, nothing cut off — with start/current captions. Also rebalance the section: top-align the panels (the snapshot no longer stretches into empty space) and give the snapshot's category counts more room so it doesn't read as cramped. Refs #1
1 parent 72283ac commit 7ac1297

2 files changed

Lines changed: 49 additions & 70 deletions

File tree

site/src/scripts/techapi.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -282,16 +282,24 @@ function countUp(node, target, opts = {}) {
282282
const maxTotal = Math.max(...points.map((point) => point.total));
283283
const minTotal = Math.min(...points.map((point) => point.total));
284284
const range = Math.max(1, maxTotal - minTotal);
285-
chartEl.innerHTML = points.map((point) => {
286-
const pct = 18 + ((point.total - minTotal) / range) * 82;
287-
const deltaText = formatDelta(point.delta, point.baseline);
288-
const deltaClass = point.delta < 0 ? " is-negative" : "";
289-
return `<a class="history-bar" href="${esc(point.url)}" target="_blank" rel="noopener" style="--h:${pct.toFixed(1)}%" title="${esc(point.title)}">
290-
<span class="history-bar-fill"></span>
291-
<span class="history-bar-total">${point.total.toLocaleString()}</span>
292-
<span class="history-bar-delta${deltaClass}">${esc(deltaText)}</span>
293-
</a>`;
294-
}).join("");
285+
// Growth curve: every sync as a point on an area chart scaled to the panel
286+
// width, so the whole timeline fits with nothing clipped or scrolled.
287+
const VW = 1000, VH = 150, PAD = 6;
288+
const xs = (i) => PAD + (points.length < 2 ? 0 : (i / (points.length - 1)) * (VW - 2 * PAD));
289+
const ys = (t) => VH - PAD - ((t - minTotal) / range) * (VH - 2 * PAD);
290+
const line = points.map((p, i) => `${i ? "L" : "M"}${xs(i).toFixed(1)} ${ys(p.total).toFixed(1)}`).join(" ");
291+
const area = `${line} L${xs(points.length - 1).toFixed(1)} ${VH} L${xs(0).toFixed(1)} ${VH} Z`;
292+
const last = points[points.length - 1];
293+
chartEl.innerHTML = `<svg class="history-svg" viewBox="0 0 ${VW} ${VH}" preserveAspectRatio="none" aria-label="Dataset growth curve">
294+
<defs><linearGradient id="histfill" x1="0" y1="0" x2="0" y2="1">
295+
<stop offset="0%" stop-color="var(--accent)" stop-opacity=".34"></stop>
296+
<stop offset="100%" stop-color="var(--accent)" stop-opacity="0"></stop>
297+
</linearGradient></defs>
298+
<path d="${area}" fill="url(#histfill)"></path>
299+
<path d="${line}" fill="none" stroke="var(--accent)" stroke-width="2" vector-effect="non-scaling-stroke" stroke-linejoin="round" stroke-linecap="round"></path>
300+
</svg>
301+
<span class="history-cap history-cap-lo">${esc(points[0].when)} · ${minTotal.toLocaleString()}</span>
302+
<span class="history-cap history-cap-hi">${esc(last.when)} · ${last.total.toLocaleString()}</span>`;
295303

296304
// Show every sync (newest first), growth-first. The list scrolls (CSS
297305
// max-height) so the full history stays reachable without a giant section.

site/src/styles/techapi.css

Lines changed: 31 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -252,14 +252,17 @@ code, .mono { font-family: var(--mono); }
252252
============================================================ */
253253
.history {
254254
display: grid;
255-
grid-template-columns: .9fr 1.1fr;
255+
grid-template-columns: 1fr 1.15fr;
256256
gap: 16px;
257+
align-items: start;
257258
}
258259
.history-panel {
259260
border: 1px solid var(--border);
260261
border-radius: 8px;
261262
background: var(--surface);
262-
padding: 22px;
263+
padding: 24px;
264+
display: flex;
265+
flex-direction: column;
263266
}
264267
.history-label {
265268
font-family: var(--mono);
@@ -279,22 +282,22 @@ code, .mono { font-family: var(--mono); }
279282
.history-grid {
280283
display: grid;
281284
grid-template-columns: repeat(2, minmax(0, 1fr));
282-
gap: 8px;
285+
gap: 9px;
283286
margin-top: 22px;
284287
}
285288
.history-count {
286289
display: flex;
287290
align-items: center;
288291
justify-content: space-between;
289292
gap: 12px;
290-
padding: 10px 12px;
293+
padding: 13px 14px;
291294
border: 1px solid var(--border);
292295
border-radius: var(--radius);
293296
background: var(--surface-2);
294297
font-family: var(--mono);
295298
}
296-
.history-count span { color: var(--muted); font-size: 12px; }
297-
.history-count b { color: var(--fg); font-size: 13px; }
299+
.history-count span { color: var(--muted); font-size: 12.5px; }
300+
.history-count b { color: var(--fg); font-size: 15px; }
298301
.history-list {
299302
list-style: none;
300303
margin: 20px 0 0;
@@ -312,71 +315,39 @@ code, .mono { font-family: var(--mono); }
312315
.history-list::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; }
313316
.history-list::-webkit-scrollbar-track { background: transparent; }
314317
.history-chart {
315-
min-height: 210px;
318+
position: relative;
319+
height: 168px;
316320
margin-top: 18px;
317-
padding: 16px 12px 12px;
318-
display: grid;
319-
grid-auto-flow: column;
320-
grid-auto-columns: minmax(62px, 1fr);
321-
align-items: end;
322-
gap: 10px;
323-
overflow-x: auto;
324321
border: 1px solid var(--border);
325322
border-radius: var(--radius);
323+
overflow: hidden;
326324
background:
327-
linear-gradient(to top, var(--border) 1px, transparent 1px) 0 25% / 100% 25%,
325+
linear-gradient(to top, var(--grid-line) 1px, transparent 1px) 0 0 / 100% 25%,
328326
var(--surface-2);
329327
}
330-
.history-empty {
331-
align-self: center;
332-
justify-self: center;
333-
grid-column: 1 / -1;
328+
.history-svg { display: block; width: 100%; height: 100%; }
329+
.history-cap {
330+
position: absolute;
331+
bottom: 8px;
334332
font-family: var(--mono);
333+
font-size: 10.5px;
335334
color: var(--muted);
336-
font-size: 12px;
335+
background: color-mix(in srgb, var(--surface-2) 72%, transparent);
336+
padding: 1px 6px;
337+
border-radius: 3px;
337338
}
338-
.history-bar {
339-
min-width: 0;
340-
height: 178px;
339+
.history-cap-lo { left: 9px; }
340+
.history-cap-hi { right: 9px; color: var(--accent-text); }
341+
.history-empty {
342+
position: absolute;
343+
inset: 0;
341344
display: grid;
342-
grid-template-rows: 1fr auto auto;
343-
gap: 6px;
344-
color: var(--fg);
345-
}
346-
.history-bar-fill {
347-
align-self: end;
348-
min-height: 10px;
349-
height: var(--h);
350-
border: 1px solid color-mix(in srgb, var(--accent) 60%, var(--border));
351-
border-radius: 4px 4px 2px 2px;
352-
background:
353-
linear-gradient(180deg, color-mix(in srgb, var(--accent) 92%, white 8%), var(--accent-deep));
354-
box-shadow: 0 0 26px -12px var(--accent);
355-
transition: filter .16s, transform .16s;
356-
}
357-
.history-bar:hover .history-bar-fill {
358-
filter: brightness(1.08);
359-
transform: translateY(-2px);
360-
}
361-
.history-bar-total,
362-
.history-bar-delta {
363-
min-width: 0;
364-
overflow: hidden;
365-
text-overflow: ellipsis;
366-
white-space: nowrap;
367-
font-family: var(--mono);
345+
place-items: center;
368346
text-align: center;
369-
}
370-
.history-bar-total {
371-
font-size: 11px;
372-
font-weight: 700;
373-
}
374-
.history-bar-delta {
375-
font-size: 10px;
376-
color: var(--accent-text);
377-
}
378-
.history-bar-delta.is-negative {
379-
color: var(--err);
347+
padding: 0 16px;
348+
font-family: var(--mono);
349+
color: var(--muted);
350+
font-size: 12px;
380351
}
381352
.history-list li {
382353
display: grid;

0 commit comments

Comments
 (0)