Compare: temperature markers over slider handles; split range labels (#75)
Make the comfort/tolerance slider self-explanatory and fix its labeling. - A temperature marker sits above each of the four handles (the band borders) — the two comfort bounds and the two tolerance bounds — so the numbers read straight off the track instead of a combined caption. - The header is now two disparate labels, "Comfort lo–hi" and "Tolerance tlo–thi", rather than the old "Comfort & tolerance 16–22 · tolerance 4–27" that mislabeled the comfort range. - Distribution-bar key: on desktop each category label is positioned at its segment's midpoint (ends pinned to the bar edges) so the labels line up under the colors they describe; on mobile it stays a plain wrapping row.
This commit is contained in:
parent
9f3c2caabc
commit
bc1aac3424
3 changed files with 51 additions and 6 deletions
|
|
@ -59,7 +59,16 @@
|
|||
the date range or the location set trigger a data load. -->
|
||||
<div id="cmp-params" class="cmp-params" hidden>
|
||||
<div class="cmp-param cmp-comfort">
|
||||
<label>Comfort & tolerance <b id="cmp-range-val"></b></label>
|
||||
<div class="cmp-range-labels">
|
||||
<span class="cmp-rl cmp-rl-comfort">Comfort <b id="cmp-comfort-val"></b></span>
|
||||
<span class="cmp-rl cmp-rl-tol">Tolerance <b id="cmp-tol-val"></b></span>
|
||||
</div>
|
||||
<div class="cmp-ticks" id="cmp-ticks" aria-hidden="true">
|
||||
<span class="cmp-tick tol" id="cmp-tick-tlo"></span>
|
||||
<span class="cmp-tick" id="cmp-tick-lo"></span>
|
||||
<span class="cmp-tick" id="cmp-tick-hi"></span>
|
||||
<span class="cmp-tick tol" id="cmp-tick-thi"></span>
|
||||
</div>
|
||||
<div class="dual-range quad-range" id="cmp-slicer">
|
||||
<div class="dual-track">
|
||||
<span class="dual-fill dual-fill-tol" id="cmp-fill-tol"></span>
|
||||
|
|
@ -70,7 +79,7 @@
|
|||
<input id="cmp-hi" type="range" min="30" max="100" step="1" aria-label="Comfort range upper bound" />
|
||||
<input id="cmp-thi" type="range" min="30" max="100" step="1" class="thumb-tol" aria-label="Tolerance range upper bound" />
|
||||
</div>
|
||||
<span class="hint">Inner handles are your ideal comfort range; outer handles are the tolerance range — days there still count, ramped by how far off they land.</span>
|
||||
<span class="hint">Inner handles set your comfort range; outer handles set the tolerance range. Days in tolerance still count toward the score, scaled by how far past comfort they fall.</span>
|
||||
</div>
|
||||
|
||||
<div class="cmp-param cmp-basis-block">
|
||||
|
|
|
|||
|
|
@ -151,7 +151,12 @@ const loInput = document.getElementById("cmp-lo");
|
|||
const hiInput = document.getElementById("cmp-hi");
|
||||
const tloInput = document.getElementById("cmp-tlo");
|
||||
const thiInput = document.getElementById("cmp-thi");
|
||||
const rangeVal = document.getElementById("cmp-range-val");
|
||||
const comfortValEl = document.getElementById("cmp-comfort-val");
|
||||
const tolValEl = document.getElementById("cmp-tol-val");
|
||||
const tickEls = {
|
||||
tlo: document.getElementById("cmp-tick-tlo"), lo: document.getElementById("cmp-tick-lo"),
|
||||
hi: document.getElementById("cmp-tick-hi"), thi: document.getElementById("cmp-tick-thi"),
|
||||
};
|
||||
const fillEl = document.getElementById("cmp-fill");
|
||||
const fillTolEl = document.getElementById("cmp-fill-tol");
|
||||
const basisToggle = document.getElementById("cmp-basis");
|
||||
|
|
@ -369,9 +374,17 @@ function rankCard(loc, s, rank) {
|
|||
parts.map((p) => `<span class="cmp-seg ${p.cls}" style="width:${p.w}%" title="${p.label} — ${pct(p.w)}"></span>`).join("") +
|
||||
`</div>`;
|
||||
// Key under the bar: a color swatch + name + share for every non-empty bucket, in
|
||||
// the same cold→hot order as the bar, so small spans are still clearly labeled.
|
||||
// the same cold→hot order as the bar, so small spans are still clearly labeled. On
|
||||
// desktop each label is positioned at its segment's midpoint (the ends anchored to
|
||||
// the bar edges); on mobile it falls back to a plain wrapping row.
|
||||
let acc = 0;
|
||||
const key = `<div class="cmp-key">` +
|
||||
parts.map((p) => `<span class="cmp-key-item"><i class="cmp-sw ${p.cls}"></i>${p.label} <b>${pct(p.w)}</b></span>`).join("") +
|
||||
parts.map((p, i) => {
|
||||
const mid = acc + p.w / 2; acc += p.w;
|
||||
const pos = i === 0 ? "left:0" : i === parts.length - 1 ? "right:0" : `left:${mid}%`;
|
||||
const edge = i === 0 ? " cmp-key-first" : i === parts.length - 1 ? " cmp-key-last" : "";
|
||||
return `<span class="cmp-key-item${edge}" style="${pos}"><i class="cmp-sw ${p.cls}"></i>${p.label} <b>${pct(p.w)}</b></span>`;
|
||||
}).join("") +
|
||||
`</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`;
|
||||
|
|
@ -508,11 +521,17 @@ locList.addEventListener("click", (e) => {
|
|||
const pctPos = (v) => (v - R_MIN) / (R_MAX - R_MIN) * 100;
|
||||
function syncSlicer() {
|
||||
tloInput.value = tlo; loInput.value = lo; hiInput.value = hi; thiInput.value = thi;
|
||||
rangeVal.textContent = `${fmtTemp(lo)}–${fmtTemp(hi)} · tolerance ${fmtTemp(tlo)}–${fmtTemp(thi)}`;
|
||||
comfortValEl.textContent = `${fmtTemp(lo)}–${fmtTemp(hi)}`;
|
||||
tolValEl.textContent = `${fmtTemp(tlo)}–${fmtTemp(thi)}`;
|
||||
fillEl.style.left = `${pctPos(lo)}%`;
|
||||
fillEl.style.right = `${100 - pctPos(hi)}%`;
|
||||
fillTolEl.style.left = `${pctPos(tlo)}%`;
|
||||
fillTolEl.style.right = `${100 - pctPos(thi)}%`;
|
||||
// A temperature marker above each handle (the four band borders).
|
||||
for (const [k, v] of [["tlo", tlo], ["lo", lo], ["hi", hi], ["thi", thi]]) {
|
||||
tickEls[k].style.left = `${pctPos(v)}%`;
|
||||
tickEls[k].textContent = fmtTemp(v);
|
||||
}
|
||||
}
|
||||
function slicerChanged() {
|
||||
lsSet(CMP.lo, String(lo)); lsSet(CMP.hi, String(hi));
|
||||
|
|
|
|||
|
|
@ -864,6 +864,14 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 24px 60px; }
|
|||
.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; }
|
||||
/* Desktop: spread the labels across the bar's width, each centred under its own
|
||||
segment (first/last pinned to the ends so they never spill past the edge). */
|
||||
@media (min-width: 641px) {
|
||||
.cmp-key { position: relative; display: block; height: 18px; }
|
||||
.cmp-key-item { position: absolute; transform: translateX(-50%); white-space: nowrap; }
|
||||
.cmp-key-first { transform: none; }
|
||||
.cmp-key-last { transform: none; }
|
||||
}
|
||||
|
||||
.cmp-bands { margin: 8px 0 0; font-size: 12.5px; color: var(--muted); }
|
||||
.cmp-bands b { color: var(--text); }
|
||||
|
|
@ -922,6 +930,15 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 24px 60px; }
|
|||
.cmp-skel-badge, .cmp-skel-name, .cmp-skel-big, .cmp-skel-bar { animation: none; }
|
||||
}
|
||||
|
||||
/* Comfort vs tolerance shown as two separate labels, and a temperature marker above
|
||||
each of the four handles (the band borders). */
|
||||
.cmp-range-labels { display: flex; flex-wrap: wrap; gap: 4px 18px; margin: 0 0 6px; font-size: 13px; color: var(--muted); }
|
||||
.cmp-rl b { color: var(--text); font-size: 15px; margin-left: 2px; }
|
||||
.cmp-rl-comfort b { color: var(--accent); }
|
||||
.cmp-ticks { position: relative; height: 17px; }
|
||||
.cmp-tick { position: absolute; transform: translateX(-50%); font-size: 11px; font-weight: 700; color: var(--text); white-space: nowrap; }
|
||||
.cmp-tick.tol { color: var(--muted); font-weight: 600; }
|
||||
|
||||
/* Dual-handle comfort slicer: two range inputs overlaid on a shared track, with a
|
||||
fill marking the selected span. Only the thumbs take pointer events, so both
|
||||
handles stay grabbable even where the inputs overlap. */
|
||||
|
|
|
|||
Loading…
Reference in a new issue