diff --git a/static/calendar.js b/static/calendar.js index 37ed0cd..4a68271 100644 --- a/static/calendar.js +++ b/static/calendar.js @@ -534,20 +534,26 @@ function renderTotals(visible) { ["14+ dry", drynessColor(14), (d) => d >= 14], ]; const vals = days.map((r) => r.dsr).filter((d) => d != null); - buckets = B.map(([label, color, fn]) => [label, color, vals.filter(fn).length]); + // Group (index 3) sets which categories share a height scale: the lone "Rain day" + // (wet) is scaled apart from the dry-streak buckets so it can't flatten them. + buckets = B.map(([label, color, fn], i) => + [label, color, vals.filter(fn).length, i === 0 ? "wet" : "dry"]); title = "dry streak"; } else if (metric === "precip") { const classes = days.map((r) => r.precip && r.precip.c).filter(Boolean); - buckets = [["Dry", drynessColor(7), classes.filter((c) => c === "dry").length]] + // Dry days scale independently of the rain-intensity buckets (wet) so a big dry + // count doesn't crush the rain bars — and vice versa. + buckets = [["Dry", drynessColor(7), classes.filter((c) => c === "dry").length, "dry"]] .concat(SCALE_RAIN.map((t) => - [t.label, PRECIP_COLORS[t.c], classes.filter((c) => c === t.c).length])); + [t.label, PRECIP_COLORS[t.c], classes.filter((c) => c === t.c).length, "wet"])); title = "rain intensity"; } else { const classes = days .map((r) => { const g = r[metric]; return g && g.c; }) .filter(Boolean); + // Temperature tiers all share one scale group, so they scale together as before. buckets = SCALE_TEMP.map((t) => - [t.label, TIER_COLORS[t.c], classes.filter((c) => c === t.c).length, t.c === "normal"]); + [t.label, TIER_COLORS[t.c], classes.filter((c) => c === t.c).length, "temp"]); title = (TEMP_METRIC_INFO[metric] || {}).label || "value"; } @@ -573,11 +579,15 @@ function renderTotals(visible) { // own color. Every category — including the neutral "Normal" center of the diverging // temperature scale — draws a line, so the strip reads as a full distribution. // Category names live in the color key below the grid too, so this stays terse. - // Line heights scale to the tallest category. - const maxLine = Math.max(1, ...buckets.map((b) => b[2])); - const LINE_MAX = 30; // px — height of the most common category - const cols = buckets.map(([label, color, n]) => { - const h = !n ? 0 : Math.max(2, Math.round(LINE_MAX * n / maxLine)); + // Line heights scale to the tallest category *within each scale group* (index 3): + // for precip/dry-streak, wet and dry days scale independently, so a lopsided count + // on one side doesn't shrink the opposing side's bars into invisibility. Temperature + // tiers all sit in one group, so they still scale against each other as before. + const maxByGroup = {}; + for (const b of buckets) maxByGroup[b[3]] = Math.max(maxByGroup[b[3]] || 1, b[2]); + const LINE_MAX = 30; // px — height of the tallest category in its group + const cols = buckets.map(([label, color, n, group]) => { + const h = !n ? 0 : Math.max(2, Math.round(LINE_MAX * n / maxByGroup[group])); const line = h ? `` : ""; // Caption stacks the category name over its value; the name reserves two lines // so the value figures stay aligned across columns whether a name wraps or not.