1.8.2
New table temperature colour gradients
This commit is contained in:
+26
-27
@@ -433,7 +433,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.utci-table tbody tr {
|
.utci-table tbody tr {
|
||||||
border-bottom: 1px solid #ede4cc;
|
border-bottom: 1px solid #ffffff;
|
||||||
transition: background 0.1s;
|
transition: background 0.1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,7 +460,7 @@
|
|||||||
container border. */
|
container border. */
|
||||||
.utci-table th:not(:last-child),
|
.utci-table th:not(:last-child),
|
||||||
.utci-table td:not(:last-child) {
|
.utci-table td:not(:last-child) {
|
||||||
border-right: 1px solid #ede4cc;
|
border-right: 1px solid #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The Dir column is centered on both desktop and mobile so the
|
/* The Dir column is centered on both desktop and mobile so the
|
||||||
@@ -715,23 +715,23 @@
|
|||||||
The hover state darkens slightly. The col-unit underneath inherits
|
The hover state darkens slightly. The col-unit underneath inherits
|
||||||
a matching accent colour. */
|
a matching accent colour. */
|
||||||
|
|
||||||
.utci-table th.grp-felt { background: #dff3ec; color: #075c3a; }
|
.utci-table th.grp-felt { background: rgba(223,243,236,0.50); color: #075c3a; }
|
||||||
.utci-table th.grp-surface { background: #ddeaf8; color: #0b3e72; }
|
.utci-table th.grp-surface { background: rgba(221,234,248,0.50); color: #0b3e72; }
|
||||||
.utci-table th.grp-ambient { background: #eceae3; color: #3c3a35; }
|
.utci-table th.grp-ambient { background: rgba(236,234,227,0.50); color: #3c3a35; }
|
||||||
.utci-table th.grp-precip { background: #f7e4ee; color: #6b1e3a; }
|
.utci-table th.grp-precip { background: rgba(247,228,238,0.50); color: #6b1e3a; }
|
||||||
.utci-table th.grp-sky { background: #eae8fc; color: #352e7a; }
|
.utci-table th.grp-sky { background: rgba(234,232,252,0.50); color: #352e7a; }
|
||||||
.utci-table th.grp-wind { background: #fae6e0; color: #6a2210; }
|
.utci-table th.grp-wind { background: rgba(250,230,224,0.50); color: #6a2210; }
|
||||||
.utci-table th.grp-airqual { background: #e5f0d8; color: #244408; }
|
.utci-table th.grp-airqual { background: rgba(229,240,216,0.50); color: #244408; }
|
||||||
.utci-table th.grp-solar { background: #faeacc; color: #5a3004; }
|
.utci-table th.grp-solar { background: rgba(250,234,204,0.50); color: #5a3004; }
|
||||||
|
|
||||||
.utci-table th.grp-felt.col-info-th:hover { background: #c8ead9; }
|
.utci-table th.grp-felt.col-info-th:hover { background: rgba(213,233,226,0.80); }
|
||||||
.utci-table th.grp-surface.col-info-th:hover { background: #c5d9f0; }
|
.utci-table th.grp-surface.col-info-th:hover { background: rgba(211,224,238,0.80); }
|
||||||
.utci-table th.grp-ambient.col-info-th:hover { background: #d8d6ce; }
|
.utci-table th.grp-ambient.col-info-th:hover { background: rgba(226,224,217,0.80); }
|
||||||
.utci-table th.grp-precip.col-info-th:hover { background: #eecfdf; }
|
.utci-table th.grp-precip.col-info-th:hover { background: rgba(237,218,228,0.80); }
|
||||||
.utci-table th.grp-sky.col-info-th:hover { background: #d4d0f5; }
|
.utci-table th.grp-sky.col-info-th:hover { background: rgba(224,222,242,0.80); }
|
||||||
.utci-table th.grp-wind.col-info-th:hover { background: #f0cfc5; }
|
.utci-table th.grp-wind.col-info-th:hover { background: rgba(240,220,214,0.80); }
|
||||||
.utci-table th.grp-airqual.col-info-th:hover { background: #d0e4bc; }
|
.utci-table th.grp-airqual.col-info-th:hover { background: rgba(219,230,206,0.80); }
|
||||||
.utci-table th.grp-solar.col-info-th:hover { background: #f0d5a8; }
|
.utci-table th.grp-solar.col-info-th:hover { background: rgba(240,224,194,0.80); }
|
||||||
|
|
||||||
.utci-table th.grp-felt .col-unit { color: #0f9e63; }
|
.utci-table th.grp-felt .col-unit { color: #0f9e63; }
|
||||||
.utci-table th.grp-surface .col-unit { color: #2672c8; }
|
.utci-table th.grp-surface .col-unit { color: #2672c8; }
|
||||||
@@ -758,15 +758,14 @@
|
|||||||
border-bottom: 1px solid rgba(0,0,0,0.06);
|
border-bottom: 1px solid rgba(0,0,0,0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.grp-label-hour { background: #f0e4c4; }
|
.utci-table .grp-label-row th.grp-felt { background: rgba(223,243,236,0.50); border-left: 2px solid rgba(74,50,24,0.18); border-bottom: 1px solid rgba(74,50,24,0.18);}
|
||||||
.grp-label-felt { background: #dff3ec; color: #075c3a; border-left: 2px solid rgba(74,50,24,0.18); }
|
.utci-table .grp-label-row th.grp-surface { background: rgba(221,234,248,0.50); border-left: 2px solid rgba(74,50,24,0.18); border-bottom: 1px solid rgba(74,50,24,0.18);}
|
||||||
.grp-label-surface { background: #ddeaf8; color: #0b3e72; border-left: 2px solid rgba(74,50,24,0.18); }
|
.utci-table .grp-label-row th.grp-ambient { background: rgba(236,234,227,0.50); border-left: 2px solid rgba(74,50,24,0.18); border-bottom: 1px solid rgba(74,50,24,0.18);}
|
||||||
.grp-label-ambient { background: #eceae3; color: #3c3a35; border-left: 2px solid rgba(74,50,24,0.18); }
|
.utci-table .grp-label-row th.grp-precip { background: rgba(247,228,238,0.50); border-left: 2px solid rgba(74,50,24,0.18); border-bottom: 1px solid rgba(74,50,24,0.18);}
|
||||||
.grp-label-precip { background: #f7e4ee; color: #6b1e3a; border-left: 2px solid rgba(74,50,24,0.18); }
|
.utci-table .grp-label-row th.grp-sky { background: rgba(234,232,252,0.50); border-left: 2px solid rgba(74,50,24,0.18); border-bottom: 1px solid rgba(74,50,24,0.18);}
|
||||||
.grp-label-sky { background: #eae8fc; color: #352e7a; border-left: 2px solid rgba(74,50,24,0.18); }
|
.utci-table .grp-label-row th.grp-wind { background: rgba(250,230,224,0.50); border-left: 2px solid rgba(74,50,24,0.18); border-bottom: 1px solid rgba(74,50,24,0.18);}
|
||||||
.grp-label-wind { background: #fae6e0; color: #6a2210; border-left: 2px solid rgba(74,50,24,0.18); }
|
.utci-table .grp-label-row th.grp-airqual { background: rgba(229,240,216,0.50); border-left: 2px solid rgba(74,50,24,0.18); border-bottom: 1px solid rgba(74,50,24,0.18);}
|
||||||
.grp-label-airqual { background: #e5f0d8; color: #244408; border-left: 2px solid rgba(74,50,24,0.18); }
|
.utci-table .grp-label-row th.grp-solar { background: rgba(250,234,204,0.50); border-left: 2px solid rgba(74,50,24,0.18); border-bottom: 1px solid rgba(74,50,24,0.18);}
|
||||||
.grp-label-solar { background: #faeacc; color: #5a3004; border-left: 2px solid rgba(74,50,24,0.18); }
|
|
||||||
|
|
||||||
/* ── FILTER PROFILES + COLUMN TOGGLES — mobile fixes ───────────────── */
|
/* ── FILTER PROFILES + COLUMN TOGGLES — mobile fixes ───────────────── */
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
|
|||||||
+33
-44
@@ -23,7 +23,7 @@
|
|||||||
import { h, render, Fragment } from '../vendor/preact.js';
|
import { h, render, Fragment } from '../vendor/preact.js';
|
||||||
import htm from '../vendor/htm.js';
|
import htm from '../vendor/htm.js';
|
||||||
import {
|
import {
|
||||||
utciCategory, UTCI_BANDS,
|
utciCategory, UTCI_BANDS, bandGradient,
|
||||||
SKIN_TYPES, sunburnMinutes, burnLabel,
|
SKIN_TYPES, sunburnMinutes, burnLabel,
|
||||||
VEHICLE_TYPES, BUILDING_TYPES,
|
VEHICLE_TYPES, BUILDING_TYPES,
|
||||||
confidenceBand, moonGlyph,
|
confidenceBand, moonGlyph,
|
||||||
@@ -128,7 +128,7 @@ export function UTCIForecast() {
|
|||||||
const span = GROUP_ORDER[g].filter(isColVisible).length;
|
const span = GROUP_ORDER[g].filter(isColVisible).length;
|
||||||
if (span === 0) continue;
|
if (span === 0) continue;
|
||||||
const labels = { felt: 'Felt', surface: 'Surface', ambient: 'Ambient', precip: 'Precip', sky: 'Sky', wind: 'Wind', airqual: 'Air quality', solar: 'Solar' };
|
const labels = { felt: 'Felt', surface: 'Surface', ambient: 'Ambient', precip: 'Precip', sky: 'Sky', wind: 'Wind', airqual: 'Air quality', solar: 'Solar' };
|
||||||
cells.push(html`<th class=${`grp-label grp-label-${g}`} colspan=${span} scope="colgroup">${labels[g]}</th>`);
|
cells.push(html`<th class=${`grp-label grp-label-${g} grp-${g}`} colspan=${span} scope="colgroup">${labels[g]}</th>`);
|
||||||
}
|
}
|
||||||
return html`<tr class="grp-label-row">${cells}</tr>`;
|
return html`<tr class="grp-label-row">${cells}</tr>`;
|
||||||
};
|
};
|
||||||
@@ -468,7 +468,7 @@ ${isPro && (activeProfile === 'custom' || activeCols['utciP']) && html`<button c
|
|||||||
</label>
|
</label>
|
||||||
<label class="display-opt">
|
<label class="display-opt">
|
||||||
<input type="checkbox" checked=${showDegree} onChange=${toggleShowDegree} />
|
<input type="checkbox" checked=${showDegree} onChange=${toggleShowDegree} />
|
||||||
° symbol
|
° symbol
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -538,41 +538,28 @@ ${isPro && (activeProfile === 'custom' || activeCols['utciP']) && html`<button c
|
|||||||
const fmt = (v, dp = 1) => v == null ? '—' : (showDecimals ? v.toFixed(dp) : String(Math.round(v)));
|
const fmt = (v, dp = 1) => v == null ? '—' : (showDecimals ? v.toFixed(dp) : String(Math.round(v)));
|
||||||
const deg = showDegree ? '°' : '';
|
const deg = showDegree ? '°' : '';
|
||||||
// Returns a background tint based on temperature value - all share the same
|
// Returns a background tint based on temperature value - all share the same
|
||||||
// Thermal Stress Bands colors with 50% alpha for consistency. Uses exact legend ranges.
|
// Thermal Stress Bands - solid colours matching the legend exactly.
|
||||||
// Converts a hex colour - r,g,b components for rgba().
|
// All band-coloured cells use the full bg/fg from UTCI_BANDS.
|
||||||
const hexToRgb = (hex) => {
|
|
||||||
const n = parseInt(hex.replace('#',''), 16);
|
|
||||||
return [(n>>16)&255, (n>>8)&255, n&255];
|
|
||||||
};
|
|
||||||
// Air + UTCI columns - 35% alpha light tint.
|
|
||||||
const tempBg = (t) => {
|
const tempBg = (t) => {
|
||||||
if (t == null) return 'transparent';
|
if (t == null) return 'transparent';
|
||||||
const band = utciCategory(t);
|
const band = utciCategory(t);
|
||||||
if (!band) return 'transparent';
|
return band ? bandGradient(band.bg, band.darkenAmt) : 'transparent';
|
||||||
const [r,g,b] = hexToRgb(band.bg);
|
|
||||||
return `rgba(${r},${g},${b},0.35)`;
|
|
||||||
};
|
};
|
||||||
// All other coloured columns - 50% alpha.
|
const tempBgStrong = tempBg;
|
||||||
const tempBgStrong = (t) => {
|
const tempFontColor = (t) => {
|
||||||
if (t == null) return 'transparent';
|
if (t == null) return '#1a1200';
|
||||||
const band = utciCategory(t);
|
const band = utciCategory(t);
|
||||||
if (!band) return 'transparent';
|
return band ? band.fg : '#1a1200';
|
||||||
const [r,g,b] = hexToRgb(band.bg);
|
|
||||||
return `rgba(${r},${g},${b},0.5)`;
|
|
||||||
};
|
};
|
||||||
// Returns perceived luminance 0..1 from a hex colour.
|
const tempFontColorStrong = tempFontColor;
|
||||||
const luminance = (hex) => {
|
// Returns any extra band styles (fontWeight, textShadow) for a given temp.
|
||||||
const [r,g,b] = hexToRgb(hex);
|
const bandExtras = (t) => {
|
||||||
return (0.299*r + 0.587*g + 0.114*b) / 255;
|
|
||||||
};
|
|
||||||
// Light tint (35% alpha) - always dark text, background is never dark enough.
|
|
||||||
const tempFontColor = (t) => '#4a3218';
|
|
||||||
// Medium tint (50% alpha) - use light text on dark bands, dark text on light bands.
|
|
||||||
const tempFontColorStrong = (t) => {
|
|
||||||
if (t == null) return '#4a3218';
|
|
||||||
const band = utciCategory(t);
|
const band = utciCategory(t);
|
||||||
if (!band) return '#4a3218';
|
if (!band) return {};
|
||||||
return luminance(band.bg) < 0.35 ? '#ffffff' : '#4a3218';
|
return {
|
||||||
|
...(band.fontWeight ? { fontWeight: band.fontWeight } : {}),
|
||||||
|
...(band.textShadow ? { textShadow: band.textShadow } : {}),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
const scaleBg = (v, min, max, rgb, maxAlpha = 0.14) => {
|
const scaleBg = (v, min, max, rgb, maxAlpha = 0.14) => {
|
||||||
if (v == null || isNaN(v)) return 'transparent';
|
if (v == null || isNaN(v)) return 'transparent';
|
||||||
@@ -619,19 +606,19 @@ ${isPro && (activeProfile === 'custom' || activeCols['utciP']) && html`<button c
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
${visibleCols.utciP && html`
|
${visibleCols.utciP && html`
|
||||||
<td class=${groupStart('utciP')} style=${{ background: hexToRgba(adjCat.bg, 0.50), color: tempFontColorStrong(r.utciAdj), fontWeight: 700, fontSize: '13px' }}>
|
<td class=${groupStart('utciP')} style=${{ background: bandGradient(adjCat.bg, adjCat.darkenAmt), color: adjCat.fg, fontSize: '13px', ...bandExtras(r.utciAdj) }}>
|
||||||
${fmt(r.utciAdj)}${deg}
|
${fmt(r.utciAdj)}${deg}
|
||||||
</td>`}
|
</td>`}
|
||||||
${visibleCols.vehicleT && html`
|
${visibleCols.vehicleT && html`
|
||||||
<td class=${groupStart('vehicleT')} style=${{ color: tempFontColorStrong(r.vehicleT), fontWeight: 'bold', background: tempBgStrong(r.vehicleT) }}>
|
<td class=${groupStart('vehicleT')} style=${{ color: tempFontColorStrong(r.vehicleT), background: tempBgStrong(r.vehicleT), ...bandExtras(r.vehicleT) }}>
|
||||||
${fmt(r.vehicleT)}${deg}
|
${fmt(r.vehicleT)}${deg}
|
||||||
</td>`}
|
</td>`}
|
||||||
${indoorMode === 'on' && !indoorManaged && html`
|
${indoorMode === 'on' && !indoorManaged && html`
|
||||||
<td class=${groupStart('indoorT')} style=${{ color: tempFontColorStrong(r.indoorT), fontWeight: 'bold', background: tempBgStrong(r.indoorT) }}>
|
<td class=${groupStart('indoorT')} style=${{ color: tempFontColorStrong(r.indoorT), background: tempBgStrong(r.indoorT), ...bandExtras(r.indoorT) }}>
|
||||||
${fmt(r.indoorT)}${deg}
|
${fmt(r.indoorT)}${deg}
|
||||||
</td>`}
|
</td>`}
|
||||||
${indoorMode === 'on' && indoorManaged && html`
|
${indoorMode === 'on' && indoorManaged && html`
|
||||||
<td class=${groupStart('managedT')} style=${{ color: tempFontColorStrong(r.managedT), fontWeight: 'bold', background: tempBgStrong(r.managedT) }}>
|
<td class=${groupStart('managedT')} style=${{ color: tempFontColorStrong(r.managedT), background: tempBgStrong(r.managedT), ...bandExtras(r.managedT) }}>
|
||||||
${fmt(r.managedT)}${deg}
|
${fmt(r.managedT)}${deg}
|
||||||
</td>`}
|
</td>`}
|
||||||
${visibleCols.burn && html`
|
${visibleCols.burn && html`
|
||||||
@@ -639,31 +626,30 @@ ${isPro && (activeProfile === 'custom' || activeCols['utciP']) && html`<button c
|
|||||||
${burnLabel(burnMins)}
|
${burnLabel(burnMins)}
|
||||||
</td>`}
|
</td>`}
|
||||||
${visibleCols.utci && html`
|
${visibleCols.utci && html`
|
||||||
<td class=${groupStart('utci')} style=${{ background: hexToRgba(cat.bg, 0.35), color: tempFontColorStrong(r.utci), fontWeight: 600 }}>
|
<td class=${groupStart('utci')} style=${{ background: bandGradient(cat.bg, cat.darkenAmt), color: cat.fg, ...bandExtras(r.utci) }}>
|
||||||
${fmt(r.utci)}${deg}
|
${fmt(r.utci)}${deg}
|
||||||
</td>`}
|
</td>`}
|
||||||
${visibleCols.delta && html`
|
${visibleCols.delta && html`
|
||||||
<td class=${groupStart('delta')} style=${{
|
<td class=${groupStart('delta')} style=${{
|
||||||
color: delta > 3 ? '#c8601a' : delta < -3 ? '#3f73c4' : '#4a3218',
|
color: delta > 3 ? '#c8601a' : delta < -3 ? '#3f73c4' : '#4a3218',
|
||||||
fontWeight: 600,
|
|
||||||
background: deltaBg(delta),
|
background: deltaBg(delta),
|
||||||
}}>
|
}}>
|
||||||
${delta > 0 ? '+' : ''}${fmt(delta)}
|
${delta > 0 ? '+' : ''}${fmt(delta)}
|
||||||
</td>`}
|
</td>`}
|
||||||
${visibleCols.tmrt && html`<td class=${groupStart('tmrt')} style=${{ background: tempBgStrong(r.Tmrt), color: tempFontColorStrong(r.Tmrt) }}>${fmt(r.Tmrt)}${deg}</td>`}
|
${visibleCols.tmrt && html`<td class=${groupStart('tmrt')} style=${{ background: tempBgStrong(r.Tmrt), color: tempFontColorStrong(r.Tmrt), ...bandExtras(r.Tmrt) }}>${fmt(r.Tmrt)}${deg}</td>`}
|
||||||
${visibleCols.concreteT && html`
|
${visibleCols.concreteT && html`
|
||||||
<td class=${groupStart('concreteT')} style=${{ color: tempFontColorStrong(r.concreteT), fontWeight: 'bold', background: tempBgStrong(r.concreteT) }}>
|
<td class=${groupStart('concreteT')} style=${{ color: tempFontColorStrong(r.concreteT), background: tempBgStrong(r.concreteT), ...bandExtras(r.concreteT) }}>
|
||||||
${fmt(r.concreteT)}${deg}
|
${fmt(r.concreteT)}${deg}
|
||||||
</td>`}
|
</td>`}
|
||||||
${visibleCols.soilT && html`
|
${visibleCols.soilT && html`
|
||||||
<td class=${groupStart('soilT')} style=${{ color: tempFontColorStrong(r.soilT0), background: tempBgStrong(r.soilT0) }}>${fmt(r.soilT0)}${deg}</td>`}
|
<td class=${groupStart('soilT')} style=${{ color: tempFontColorStrong(r.soilT0), background: tempBgStrong(r.soilT0), ...bandExtras(r.soilT0) }}>${fmt(r.soilT0)}${deg}</td>`}
|
||||||
${visibleCols.soilT6 && html`
|
${visibleCols.soilT6 && html`
|
||||||
<td class=${groupStart('soilT6')} style=${{ color: tempFontColorStrong(r.soilT6), background: tempBgStrong(r.soilT6) }}>${fmt(r.soilT6)}${deg}</td>`}
|
<td class=${groupStart('soilT6')} style=${{ color: tempFontColorStrong(r.soilT6), background: tempBgStrong(r.soilT6), ...bandExtras(r.soilT6) }}>${fmt(r.soilT6)}${deg}</td>`}
|
||||||
${visibleCols.soilM && html`
|
${visibleCols.soilM && html`
|
||||||
<td class=${groupStart('soilM')} style=${{ color: '#2a6a90', background: scaleBg(r.soilM, 0.12, 0.45, '50,125,190') }}>${r.soilM != null ? fmt(r.soilM * 100, 1) + '%' : '—'}</td>`}
|
<td class=${groupStart('soilM')} style=${{ color: '#2a6a90', background: scaleBg(r.soilM, 0.12, 0.45, '50,125,190') }}>${r.soilM != null ? fmt(r.soilM * 100, 1) + '%' : '—'}</td>`}
|
||||||
${visibleCols.air && html`<td class=${groupStart('air')} style=${{ background: tempBg(r.Ta), color: tempFontColorStrong(r.Ta) }}>${fmt(r.Ta)}${deg}</td>`}
|
${visibleCols.air && html`<td class=${groupStart('air')} style=${{ background: tempBg(r.Ta), color: tempFontColorStrong(r.Ta), ...bandExtras(r.Ta) }}>${fmt(r.Ta)}${deg}</td>`}
|
||||||
${visibleCols.rh && html`<td class=${groupStart('rh')} style=${{ background: scaleBg(r.RH, 30, 100, '70,145,200') }}>${Math.round(r.RH)}</td>`}
|
${visibleCols.rh && html`<td class=${groupStart('rh')} style=${{ background: scaleBg(r.RH, 30, 100, '70,145,200') }}>${Math.round(r.RH)}</td>`}
|
||||||
${visibleCols.dew && html`<td class=${groupStart('dew')} style=${{ background: tempBgStrong(r.dew), color: tempFontColorStrong(r.dew) }}>${fmt(r.dew)}${deg}</td>`}
|
${visibleCols.dew && html`<td class=${groupStart('dew')} style=${{ background: tempBgStrong(r.dew), color: tempFontColorStrong(r.dew), ...bandExtras(r.dew) }}>${fmt(r.dew)}${deg}</td>`}
|
||||||
${visibleCols.precip && html`
|
${visibleCols.precip && html`
|
||||||
<td class=${groupStart('precip')} style=${{ background: r.snow > 0 ? scaleBg(r.snow, 0, 4, '90,140,210') : scaleBg(r.precip, 0, 8, '70,145,200') }}>
|
<td class=${groupStart('precip')} style=${{ background: r.snow > 0 ? scaleBg(r.snow, 0, 4, '90,140,210') : scaleBg(r.precip, 0, 8, '70,145,200') }}>
|
||||||
<span style=${{ display: 'inline-flex', alignItems: 'center', gap: '5px', verticalAlign: 'middle' }}>
|
<span style=${{ display: 'inline-flex', alignItems: 'center', gap: '5px', verticalAlign: 'middle' }}>
|
||||||
@@ -824,12 +810,15 @@ ${isPro && (activeProfile === 'custom' || activeCols['utciP']) && html`<button c
|
|||||||
</div>`;
|
</div>`;
|
||||||
})()}
|
})()}
|
||||||
|
|
||||||
|
<div class="utci-legend">
|
||||||
|
<span class="utci-legend-label">Thermal stress bands</span>
|
||||||
<div class="utci-legend-row">
|
<div class="utci-legend-row">
|
||||||
${UTCI_BANDS.map((b, i) => html`
|
${UTCI_BANDS.map((b, i) => html`
|
||||||
<span key=${i} class="utci-legend-item" style=${{ background: b.bg, color: b.fg }}>
|
<span key=${i} class="utci-legend-item" style=${{ background: bandGradient(b.bg), color: b.fg, ...(b.fontWeight ? { fontWeight: b.fontWeight } : {}), ...(b.textShadow ? { textShadow: b.textShadow } : {}) }}>
|
||||||
${b.label}
|
${b.label}
|
||||||
</span>`)}
|
</span>`)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
${(() => {
|
${(() => {
|
||||||
|
|||||||
+28
-13
@@ -34,19 +34,19 @@
|
|||||||
// utciCategory() reads from this array - do not duplicate elsewhere.
|
// utciCategory() reads from this array - do not duplicate elsewhere.
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
export const UTCI_BANDS = [
|
export const UTCI_BANDS = [
|
||||||
{ max: -20, label: 'Extreme cold', bg: '#0e1a50', fg: '#c8d4f0' },
|
{ max: -20, label: 'Extreme cold', bg: '#5e668a', fg: '#ffffff' },
|
||||||
{ max: -10, label: 'Arctic', bg: '#2a52b0', fg: '#dce8ff' },
|
{ max: -10, label: 'Arctic', bg: '#708bca', fg: '#ffffff' },
|
||||||
{ max: 0, label: 'Freezing', bg: '#5c90cc', fg: '#ffffff' },
|
{ max: 0, label: 'Freezing', bg: '#92b5dd', fg: '#1a1200' },
|
||||||
{ max: 5, label: 'Very cold', bg: '#90bcd8', fg: '#08182e' },
|
{ max: 5, label: 'Very cold', bg: '#b5d2e5', fg: '#1a1200' },
|
||||||
{ max: 10, label: 'Cold', bg: '#b8d4dc', fg: '#08182e' },
|
{ max: 10, label: 'Cold', bg: '#cfe2e8', fg: '#1a1200' },
|
||||||
{ max: 15, label: 'Chilly', bg: '#c8dfc0', fg: '#082010' },
|
{ max: 15, label: 'Chilly', bg: '#daead5', fg: '#1a1200' },
|
||||||
{ max: 19, label: 'Cool', bg: '#8aca78', fg: '#082010' },
|
{ max: 19, label: 'Cool', bg: '#b1dba5', fg: '#1a1200' },
|
||||||
{ max: 24, label: 'Comfortable', bg: '#157018', fg: '#b8f0b8' },
|
{ max: 24, label: 'Comfortable', bg: '#90d090', fg: '#157a15', fontWeight: 700 },
|
||||||
{ max: 29, label: 'Warm', bg: '#e8d840', fg: '#201800' },
|
{ max: 29, label: 'Warm', bg: '#f8e554', fg: '#1a1200' },
|
||||||
{ max: 34, label: 'Hot', bg: '#eca030', fg: '#201800' },
|
{ max: 34, label: 'Hot', bg: '#f5aa54', fg: '#1a1200' },
|
||||||
{ max: 39, label: 'Very hot', bg: '#d84818', fg: '#ffffff' },
|
{ max: 39, label: 'Very hot', bg: '#f07f6a', fg: '#1a1200' },
|
||||||
{ max: 44, label: 'Extremely hot', bg: '#a81810', fg: '#ffc8b8' },
|
{ max: 44, label: 'Extremely hot', bg: '#dd5454', fg: '#000000' },
|
||||||
{ label: 'Danger', bg: '#3a0404', fg: '#ffffff' },
|
{ label: 'Danger', bg: '#880000', fg: '#ffffff', fontWeight: 700, darkenAmt: 0.18, textShadow: '-1px -1px 0 #3a0000, 1px -1px 0 #3a0000, -1px 1px 0 #3a0000, 1px 1px 0 #3a0000', solid: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function utciCategory(u) {
|
export function utciCategory(u) {
|
||||||
@@ -55,6 +55,21 @@ export function utciCategory(u) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Diagonal sweep gradient for a band background hex colour.
|
||||||
|
// Pre-blends with 50% white to sit harmoniously alongside lighter table cells,
|
||||||
|
// then applies a light→mid→dark sweep for depth.
|
||||||
|
export function bandGradient(hex, darkenAmt = 0.04) {
|
||||||
|
const n = parseInt(hex.replace('#',''), 16);
|
||||||
|
const wb = (c) => Math.min(255, Math.round(c + (255 - c) * 0.50));
|
||||||
|
const [r, g, b] = [wb((n>>16)&255), wb((n>>8)&255), wb(n&255)];
|
||||||
|
const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.30));
|
||||||
|
const darken = (c) => Math.round(c * (1 - darkenAmt));
|
||||||
|
const light = '#' + [lighten(r), lighten(g), lighten(b)].map(x => x.toString(16).padStart(2,'0')).join('');
|
||||||
|
const mid = '#' + [r, g, b].map(x => x.toString(16).padStart(2,'0')).join('');
|
||||||
|
const dark = '#' + [darken(r), darken(g), darken(b) ].map(x => x.toString(16).padStart(2,'0')).join('');
|
||||||
|
return `linear-gradient(135deg, ${light} 0%, ${mid} 60%, ${dark} 100%)`;
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
// SOAK-FACTOR - SunScope's original rain/snow penalty.
|
// SOAK-FACTOR - SunScope's original rain/snow penalty.
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|||||||
+1
-1
@@ -29,5 +29,5 @@
|
|||||||
|
|
||||||
@import "./css/base.css";
|
@import "./css/base.css";
|
||||||
@import "./css/header.css";
|
@import "./css/header.css";
|
||||||
@import "./css/table.css";
|
@import "./css/table.css?v=2";
|
||||||
@import "./css/ui.css";
|
@import "./css/ui.css";
|
||||||
|
|||||||
+2
-2
@@ -78,10 +78,10 @@
|
|||||||
href="https://fonts.bunny.net/css2?family=Fraunces:ital,wght@0,700;0,900;1,300;1,700&family=JetBrains+Mono:wght@400;600&family=Manrope:wght@400;500;600;700&display=swap" />
|
href="https://fonts.bunny.net/css2?family=Fraunces:ital,wght@0,700;0,900;1,300;1,700&family=JetBrains+Mono:wght@400;600&family=Manrope:wght@400;500;600;700&display=swap" />
|
||||||
</noscript>
|
</noscript>
|
||||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☀️</text></svg>" />
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☀️</text></svg>" />
|
||||||
<link rel="stylesheet" href="./assets/sunscope.css?v=15a">
|
<link rel="stylesheet" href="./assets/sunscope.css?v=15b">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="./assets/js/main.js?v=15a"></script>
|
<script type="module" src="./assets/js/main.js?v=15b"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user