Show a metric's unit once atop its distribution strip (#195)
The distribution strip printed each bar's average with its full unit inline, so
humidity read "4.0 g/m³" in a ~38px phone column and clipped on every bar —
temperature never did, because it shows a bare "40°". Wind ("5 mph") and precip
were heading the same way.
The unit now appears once, top-right of the strip, and every bar value is bare:
"4.0" under a "g/m³" label, "5" under "mph", "0.00" under "in". It reacts to the
unit toggle like the values do (°F↔°C, mm↔in, mph↔km/h). Temperature keeps its
degree glyph on the value since it is iconic and never clipped.
With the unit no longer on the humidity label, its calendar title drops the
"(g/m³ of water vapor)" parenthetical that would otherwise repeat it — every
metric's title is unitless now, the strip's own label carries it.
Also fixes a pre-existing crash: calendar.js referenced DRY_CAP without importing
it from shared.js, so selecting the dry-streak metric threw and left its legend
unrendered. Exported it.
Verified at 390 and 1440 in light and dark, imperial and metric, across all five
calendar metrics and the compare strips: no label clips, no console errors.
Claude-Session: https://claude.ai/code/session_013dRZmX9D3JEntfMKWMTWZ8
This commit is contained in:
parent
57dfbe109e
commit
ef61308a9e
3 changed files with 49 additions and 6 deletions
|
|
@ -11,7 +11,7 @@ import { TIER_COLORS, PRECIP_COLORS, SCALE_TEMP, SCALE_RAIN, drynessColor, pctOr
|
|||
CHUNK_MONTHS, buildChunks, clickOpensPicker, metricBuckets, distStrip,
|
||||
seasonFilterDropdown, seasonSummaryText, syncSeasonChecks,
|
||||
applySeasonMonthChange, initSeasonExpand, allMonths, monthsToMask, maskToMonths,
|
||||
weatherType as wxType } from "./shared.js";
|
||||
weatherType as wxType, DRY_CAP } from "./shared.js";
|
||||
import { initFilterSheet } from "./filtersheet.js";
|
||||
|
||||
// The diverging-temperature-scale metrics (colored exactly like High/Low), with a
|
||||
|
|
@ -20,7 +20,9 @@ const TEMP_METRIC_INFO = {
|
|||
tmax: { name: "High", label: "daily high", fmt: fmtTemp },
|
||||
tmin: { name: "Low", label: "daily low", fmt: fmtTemp },
|
||||
feels: { name: "Feels", label: "feels like (apparent temperature)", fmt: fmtTemp },
|
||||
humid: { name: "Humid", label: "absolute humidity (g/m³ of water vapor)", fmt: fmtHumid },
|
||||
// The unit lives in the strip's own top-right label now, so the metric name
|
||||
// doesn't repeat it (every other metric's title is unitless too).
|
||||
humid: { name: "Humid", label: "absolute humidity", fmt: fmtHumid },
|
||||
wind: { name: "Wind", label: "wind speed", fmt: fmtWind },
|
||||
gust: { name: "Gust", label: "wind gust", fmt: fmtWind },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// constant and helper here previously existed as a copy in app.js, calendar.js,
|
||||
// day.js, compare.js and/or legend.html; pages import what they need so a tier
|
||||
// color, scale label or formatter has exactly one home.
|
||||
import { fmtTemp, fmtPrecip, fmtWind, fmtHumid, getUnit,
|
||||
import { fmtTemp, fmtPrecip, fmtWind, fmtHumid, getUnit, windUnit, precipUnit,
|
||||
toUnit, toWind, toPrecip } from "./units.js";
|
||||
// ---- tier colors ----
|
||||
// style.css's :root custom properties are the source of truth (they're not
|
||||
|
|
@ -59,7 +59,8 @@ export const SCALE_RAIN = [
|
|||
// ---- dry-streak ramp ----
|
||||
// "Days since last rain": rain days are blue; each dry day warms from a light
|
||||
// base toward dark red, saturating at 14 consecutive dry days.
|
||||
const DRY_BASE = [247, 217, 176], DRY_MAX = [122, 13, 26], DRY_CAP = 14;
|
||||
const DRY_BASE = [247, 217, 176], DRY_MAX = [122, 13, 26];
|
||||
export const DRY_CAP = 14;
|
||||
const lerpRgb = (a, b, t) =>
|
||||
`rgb(${a.map((x, i) => Math.round(x + (b[i] - x) * t)).join(",")})`;
|
||||
export function drynessColor(dsr) {
|
||||
|
|
@ -128,6 +129,31 @@ function fmtMetricVal(unit, v) {
|
|||
return `${Math.round(v)}`;
|
||||
}
|
||||
|
||||
// The value of a bucket's average with no unit — the unit is shown once at the top
|
||||
// of the strip (stripUnitLabel), not repeated on every bar. Temperature keeps its
|
||||
// degree glyph, which is iconic and narrow; the wordy units (" g/m³", " mph") are
|
||||
// exactly what made these labels clip on a phone.
|
||||
function fmtMetricBare(unit, v) {
|
||||
if (v == null || isNaN(v)) return "";
|
||||
if (unit === "temp") return fmtTemp(v); // "40°"
|
||||
if (unit === "humid") return v.toFixed(1); // "4.0"
|
||||
if (unit === "wind") return fmtWind(v, false); // "5"
|
||||
if (unit === "precip") return fmtPrecip(v, false);// "0.00" / "5"
|
||||
if (unit === "dsr") return `${Math.round(v)}`; // "9", header supplies "days"
|
||||
return `${Math.round(v)}`;
|
||||
}
|
||||
|
||||
// The unit label for a whole strip, shown once at its top. Reacts to the active
|
||||
// unit system, so it flips with the toggle alongside the values.
|
||||
function stripUnitLabel(unit) {
|
||||
if (unit === "temp") return `°${getUnit()}`;
|
||||
if (unit === "humid") return "g/m³";
|
||||
if (unit === "wind") return windUnit();
|
||||
if (unit === "precip") return precipUnit();
|
||||
if (unit === "dsr") return "days";
|
||||
return "";
|
||||
}
|
||||
|
||||
// The low–high span for a tier, as bare numbers. No unit: this line sits directly
|
||||
// under the average, which carries it, and a column is ~38px wide on a phone —
|
||||
// repeating " g/m³" on both ends is what made the old in-bar label clip.
|
||||
|
|
@ -192,13 +218,18 @@ export function distStrip(buckets, showCount) {
|
|||
return `<div class="ct-col${b.cls ? ` ct-${b.cls}` : ""}" style="--c:${b.color}" ` +
|
||||
`title="${b.label} · ${pctText(b.n, b.group)} (${b.n})${avg != null ? ` · avg ${fmtMetricVal(b.unit, avg)}` : ""}` +
|
||||
`${lo != null ? ` · ${fmtMetricVal(b.unit, lo)} to ${fmtMetricVal(b.unit, hi)}` : ""}">` +
|
||||
`<span class="ct-top">${avg != null ? fmtMetricVal(b.unit, avg) : ""}</span>` +
|
||||
`<span class="ct-top">${avg != null ? fmtMetricBare(b.unit, avg) : ""}</span>` +
|
||||
`<span class="ct-range">${range}</span>` +
|
||||
bar +
|
||||
`<span class="ct-cap"><span class="ct-num${b.n ? "" : " ct-num-zero"}">${valText(b.n, b.group)}</span>` +
|
||||
`<span class="ct-label">${b.label}</span></span></div>`;
|
||||
}).join("");
|
||||
return `<div class="ct-strip ct-connected">${cols}</div>`;
|
||||
// The unit once, at the top of the strip — not repeated on every bar, where the
|
||||
// wordy ones ("g/m³", "km/h") clipped a ~38px phone column. Bare on dry-streak
|
||||
// and any unitless metric.
|
||||
const unit = stripUnitLabel(buckets[0] ? buckets[0].unit : "");
|
||||
const cap = unit ? `<div class="ct-unit">${unit}</div>` : "";
|
||||
return `<div class="ct-plot">${cap}<div class="ct-strip ct-connected">${cols}</div></div>`;
|
||||
}
|
||||
|
||||
// ---- formatters & tiny utils ----
|
||||
|
|
|
|||
|
|
@ -1050,6 +1050,16 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 24px 60px; }
|
|||
baseline and nearly touching so they read as one chart. The tier's average value
|
||||
sits above its bar, the min–max range inside it, and the share + name below.
|
||||
Bar height encodes frequency (within the metric's scale group). */
|
||||
/* Wraps the unit label + the scrolling strip. The label sits outside .ct-strip's
|
||||
overflow so it stays put when a wide (precip) strip scrolls. */
|
||||
.ct-plot { position: relative; }
|
||||
/* The metric's unit, shown once at the top-right of the strip — the values on the
|
||||
bars are bare so they don't clip a narrow phone column. */
|
||||
.ct-unit {
|
||||
text-align: right; margin: 6px 2px -2px; height: 1em;
|
||||
font-size: 11px; font-weight: 700; line-height: 1;
|
||||
color: var(--muted); font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.ct-strip {
|
||||
display: flex; align-items: flex-end; gap: 1px; margin: 10px 0 6px; overflow-x: auto;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue