Compare: label every bar segment with a key; drop the colored caption (#73)
The bar showed each segment's share but not what it meant, leaning on the coloured below/above-comfort text to explain it. Replace that text with a per-card key directly under the bar: a colour swatch + plain name + share for every bucket present (Too cold / Cool / Comfortable / Warm / Too hot), in the same cold→hot order as the bar. Every span is spelled out, so a 1% sliver is as clear as a 60% block, and small segments no longer need to fit a label inside them. Bar segments also carry a hover title. The redundant top legend is removed (each card now names its own colours), and the leftover summary line keeps just the tolerance share and typical miss.
This commit is contained in:
parent
46c5006ce0
commit
9f3c2caabc
2 changed files with 38 additions and 50 deletions
|
|
@ -331,17 +331,6 @@ const pct = (v) => `${v < 10 ? v.toFixed(1) : Math.round(v)}%`;
|
||||||
// Temperature *differences* (avg miss, typical miss) in the active unit.
|
// Temperature *differences* (avg miss, typical miss) in the active unit.
|
||||||
const deg = (v) => fmtDelta(v);
|
const deg = (v) => fmtDelta(v);
|
||||||
|
|
||||||
// Names the five bar colors once, above the ranked cards.
|
|
||||||
const LEGEND =
|
|
||||||
`<div class="cmp-legend" aria-hidden="true">` +
|
|
||||||
`<span><i class="cmp-sw cmp-seg-vcold"></i>Too cold</span>` +
|
|
||||||
`<span><i class="cmp-sw cmp-seg-cool"></i>Cool</span>` +
|
|
||||||
`<span><i class="cmp-sw cmp-seg-comf"></i>Comfortable</span>` +
|
|
||||||
`<span><i class="cmp-sw cmp-seg-warm"></i>Warm</span>` +
|
|
||||||
`<span><i class="cmp-sw cmp-seg-vhot"></i>Too hot</span>` +
|
|
||||||
`<span class="cmp-legend-note">each bar = 100% of days · “cool”/“warm” fall within your tolerance</span>` +
|
|
||||||
`</div>`;
|
|
||||||
|
|
||||||
function renderLocList() {
|
function renderLocList() {
|
||||||
locList.innerHTML = locations.map((l, i) => {
|
locList.innerHTML = locations.map((l, i) => {
|
||||||
let label, cls = "cmp-chip", lead = "";
|
let label, cls = "cmp-chip", lead = "";
|
||||||
|
|
@ -358,29 +347,33 @@ function renderLocList() {
|
||||||
}).join("");
|
}).join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The five day-buckets, ordered cold → hot: css color class, plain-language name,
|
||||||
|
// and the stat key holding its share of days. Drives both the bar and its key.
|
||||||
|
const BUCKETS = [
|
||||||
|
["cmp-seg-vcold", "Too cold", "belowOutPct"],
|
||||||
|
["cmp-seg-cool", "Cool", "belowTolPct"],
|
||||||
|
["cmp-seg-comf", "Comfortable", "comfortPct"],
|
||||||
|
["cmp-seg-warm", "Warm", "aboveTolPct"],
|
||||||
|
["cmp-seg-vhot", "Too hot", "aboveOutPct"],
|
||||||
|
];
|
||||||
|
|
||||||
// A ranked place: the 0–100 match score up top, then one full-width bar showing how
|
// A ranked place: the 0–100 match score up top, then one full-width bar showing how
|
||||||
// this place's days split across five temperature buckets, ordered cold → hot using
|
// the place's days split across the five temperature buckets (cold → hot, in the
|
||||||
// the site's own temperature colors — too cold, cool (within tolerance), comfortable,
|
// site's own colors), and directly beneath it a key that names every bucket and its
|
||||||
// warm (within tolerance), too hot. The whole bar is 100% of the days, so every
|
// share — so even a 1% sliver on the bar reads clearly. The whole bar is 100% of the
|
||||||
// segment is a real share and a place that runs cold vs one that runs hot read at a
|
// days, so more blue = runs cold, more red = runs hot.
|
||||||
// glance (more blue vs more red). The legend above the cards names each color.
|
|
||||||
function rankCard(loc, s, rank) {
|
function rankCard(loc, s, rank) {
|
||||||
const win = rank === 1;
|
const win = rank === 1;
|
||||||
// Label a segment inline when it's wide enough to fit the number legibly.
|
const parts = BUCKETS.map(([cls, label, key]) => ({ cls, label, w: s[key] })).filter((p) => p.w > 0);
|
||||||
const seg = (cls, w) => (w > 0
|
const bar = `<div class="cmp-bar" role="img" aria-label="of all days: ${parts.map((p) => `${pct(p.w)} ${p.label.toLowerCase()}`).join(", ")}">` +
|
||||||
? `<span class="cmp-seg ${cls}" style="width:${w}%">${w >= 10 ? `<span class="cmp-seg-n">${Math.round(w)}%</span>` : ""}</span>`
|
parts.map((p) => `<span class="cmp-seg ${p.cls}" style="width:${p.w}%" title="${p.label} — ${pct(p.w)}"></span>`).join("") +
|
||||||
: "");
|
|
||||||
const bar = `<div class="cmp-bar" role="img" aria-label="of all days: ${pct(s.belowOutPct)} too cold, ${pct(s.belowTolPct)} cool, ${pct(s.comfortPct)} comfortable, ${pct(s.aboveTolPct)} warm, ${pct(s.aboveOutPct)} too hot">` +
|
|
||||||
seg("cmp-seg-vcold", s.belowOutPct) + seg("cmp-seg-cool", s.belowTolPct) +
|
|
||||||
seg("cmp-seg-comf", s.comfortPct) +
|
|
||||||
seg("cmp-seg-warm", s.aboveTolPct) + seg("cmp-seg-vhot", s.aboveOutPct) +
|
|
||||||
`</div>`;
|
`</div>`;
|
||||||
const cap =
|
// Key under the bar: a color swatch + name + share for every non-empty bucket, in
|
||||||
`<div class="cmp-cap-row">` +
|
// the same cold→hot order as the bar, so small spans are still clearly labeled.
|
||||||
`<span class="cmp-cap-below">${pct(s.belowPct)} below comfort${s.below ? ` · avg ${deg(s.avgBelow)} colder` : ""}</span>` +
|
const key = `<div class="cmp-key">` +
|
||||||
`<span class="cmp-cap-above">${s.above ? `avg ${deg(s.avgAbove)} hotter · ` : ""}${pct(s.abovePct)} above comfort</span>` +
|
parts.map((p) => `<span class="cmp-key-item"><i class="cmp-sw ${p.cls}"></i>${p.label} <b>${pct(p.w)}</b></span>`).join("") +
|
||||||
`</div>`;
|
`</div>`;
|
||||||
const bands = `<div class="cmp-bands"><b>${pct(s.comfortPct)}</b> comfortable · <b>${pct(s.tolPct)}</b> within tolerance · typically <b>${deg(s.mad)}</b> off</div>`;
|
const bands = `<div class="cmp-bands"><b>${pct(s.tolPct)}</b> within tolerance · typically <b>${deg(s.mad)}</b> off comfort</div>`;
|
||||||
const sub = win ? `<span class="cmp-best">★ Best match</span> · ${s.n} days` : `${s.n} days`;
|
const sub = win ? `<span class="cmp-best">★ Best match</span> · ${s.n} days` : `${s.n} days`;
|
||||||
return `<div class="cmp-card${win ? " cmp-win" : ""}">` +
|
return `<div class="cmp-card${win ? " cmp-win" : ""}">` +
|
||||||
`<div class="cmp-card-top">` +
|
`<div class="cmp-card-top">` +
|
||||||
|
|
@ -388,7 +381,7 @@ function rankCard(loc, s, rank) {
|
||||||
`<div class="cmp-card-name"><span class="cmp-name">${loc.name}</span><span class="cmp-daycount">${sub}</span></div>` +
|
`<div class="cmp-card-name"><span class="cmp-name">${loc.name}</span><span class="cmp-daycount">${sub}</span></div>` +
|
||||||
`<div class="cmp-big"><b>${Math.round(s.score)}</b><span>match / 100</span></div>` +
|
`<div class="cmp-big"><b>${Math.round(s.score)}</b><span>match / 100</span></div>` +
|
||||||
`</div>` +
|
`</div>` +
|
||||||
bar + cap + bands +
|
bar + key + bands +
|
||||||
`</div>`;
|
`</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -463,7 +456,7 @@ function renderResults() {
|
||||||
`<p class="meta">By ${BASIS_LABEL[basis]} · comfort ${fmtTemp(lo)}–${fmtTemp(hi)}, tolerance ${fmtTemp(tlo)}–${fmtTemp(thi)} · ${span}` +
|
`<p class="meta">By ${BASIS_LABEL[basis]} · comfort ${fmtTemp(lo)}–${fmtTemp(hi)}, tolerance ${fmtTemp(tlo)}–${fmtTemp(thi)} · ${span}` +
|
||||||
`${loading ? ` · <span class="cmp-loading-inline"><span class="cmp-spinner"></span>loading ${loading} more…</span>` : ""}</p>`;
|
`${loading ? ` · <span class="cmp-loading-inline"><span class="cmp-spinner"></span>loading ${loading} more…</span>` : ""}</p>`;
|
||||||
|
|
||||||
results.innerHTML = LEGEND + scored.map((x, i) => rankCard(x.loc, x.s, i + 1)).join("") +
|
results.innerHTML = scored.map((x, i) => rankCard(x.loc, x.s, i + 1)).join("") +
|
||||||
(loading ? Array.from({ length: loading }, skeletonRow).join("") : "");
|
(loading ? Array.from({ length: loading }, skeletonRow).join("") : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -847,28 +847,24 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 24px 60px; }
|
||||||
/* Diverging comfort bar: a fixed center marks the comfort zone; colder days grow
|
/* Diverging comfort bar: a fixed center marks the comfort zone; colder days grow
|
||||||
left of it, warmer days grow right. Both halves share one 0–100% scale, so a
|
left of it, warmer days grow right. Both halves share one 0–100% scale, so a
|
||||||
cold-skewed place and a hot-skewed one mirror each other and compare at a glance. */
|
cold-skewed place and a hot-skewed one mirror each other and compare at a glance. */
|
||||||
/* Legend naming the five bar colors once, above the cards. */
|
/* Full-width day-distribution bar: five temperature buckets, cold to hot, together
|
||||||
.cmp-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; margin: 2px 0 4px; font-size: 12px; color: var(--muted); }
|
100% of the days, in the site's own temperature colors. min-width keeps a thin
|
||||||
.cmp-legend > span { display: inline-flex; align-items: center; gap: 6px; }
|
sliver visible; the key below spells out every color and share. */
|
||||||
.cmp-sw { width: 12px; height: 12px; border-radius: 3px; flex: 0 0 auto; }
|
.cmp-bar { display: flex; height: 20px; border-radius: 6px; overflow: hidden; margin: 14px 0 10px; background: var(--surface-2); }
|
||||||
.cmp-legend-note { font-style: italic; opacity: .85; }
|
.cmp-seg { height: 100%; min-width: 2px; }
|
||||||
|
|
||||||
/* Full-width day-distribution bar: five temperature buckets, cold → hot, together
|
|
||||||
100% of the days, in the site's own temperature colors. Every segment is a real
|
|
||||||
share, so more blue = runs cold, more red = runs hot — no empty space to decode. */
|
|
||||||
.cmp-bar { display: flex; height: 22px; border-radius: 6px; overflow: hidden; margin: 14px 0 8px; background: var(--surface-2); }
|
|
||||||
.cmp-seg { height: 100%; display: flex; align-items: center; justify-content: center; min-width: 0; }
|
|
||||||
.cmp-seg-n { font-size: 10.5px; font-weight: 700; color: rgba(10, 15, 20, .68); }
|
|
||||||
.cmp-seg-vcold .cmp-seg-n, .cmp-seg-comf .cmp-seg-n, .cmp-seg-vhot .cmp-seg-n { color: rgba(255, 255, 255, .95); }
|
|
||||||
.cmp-seg-vcold { background: var(--very-cold); }
|
.cmp-seg-vcold { background: var(--very-cold); }
|
||||||
.cmp-seg-cool { background: var(--cool); }
|
.cmp-seg-cool { background: var(--cool); }
|
||||||
.cmp-seg-comf { background: var(--normal); }
|
.cmp-seg-comf { background: var(--normal); }
|
||||||
.cmp-seg-warm { background: var(--warm); }
|
.cmp-seg-warm { background: var(--warm); }
|
||||||
.cmp-seg-vhot { background: var(--very-hot); }
|
.cmp-seg-vhot { background: var(--very-hot); }
|
||||||
|
|
||||||
.cmp-cap-row { display: flex; justify-content: space-between; gap: 10px; font-size: 12px; }
|
/* Key under the bar: swatch + plain name + share for every bucket the place has, in
|
||||||
.cmp-cap-below { color: var(--cold); }
|
the same cold-to-hot order as the bar — so a 1% span is named as clearly as a 60% one. */
|
||||||
.cmp-cap-above { color: var(--hot); text-align: right; }
|
.cmp-key { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 12.5px; color: var(--muted); }
|
||||||
|
.cmp-key-item { display: inline-flex; align-items: center; gap: 6px; }
|
||||||
|
.cmp-key-item b { color: var(--text); font-weight: 700; }
|
||||||
|
.cmp-sw { width: 12px; height: 12px; border-radius: 3px; flex: 0 0 auto; }
|
||||||
|
|
||||||
.cmp-bands { margin: 8px 0 0; font-size: 12.5px; color: var(--muted); }
|
.cmp-bands { margin: 8px 0 0; font-size: 12.5px; color: var(--muted); }
|
||||||
.cmp-bands b { color: var(--text); }
|
.cmp-bands b { color: var(--text); }
|
||||||
|
|
||||||
|
|
@ -1065,6 +1061,5 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 24px 60px; }
|
||||||
.cmp-bl-row { grid-template-columns: 84px 1fr; gap: 8px; }
|
.cmp-bl-row { grid-template-columns: 84px 1fr; gap: 8px; }
|
||||||
.cmp-bl-name { font-size: 12px; }
|
.cmp-bl-name { font-size: 12px; }
|
||||||
.cmp-bl-axis { margin-left: 92px; }
|
.cmp-bl-axis { margin-left: 92px; }
|
||||||
.cmp-cap-row { font-size: 11px; }
|
.cmp-key { font-size: 11.5px; gap: 5px 12px; }
|
||||||
.cmp-legend { font-size: 11px; gap: 5px 10px; }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue