From fd8baf5ef018acb29bb36409190f8cacaa48f078 Mon Sep 17 00:00:00 2001 From: Fraxle Date: Thu, 25 Jun 2026 16:15:12 +0100 Subject: [PATCH] 3.0.2.4 Move At A Glance panel left. Tweak day tabs highlight and add danger level pulsing. Arrange at a glance order to make more sense and better priority. New brighter favicon. --- assets/css/table.css | 20 +++++++++++--- assets/css/ui.css | 7 +++++ assets/js/app.js | 12 +++++++-- assets/js/components/DayTabs.js | 48 ++++++++++++++++++++++++--------- assets/js/compute.js | 2 +- favicon.svg | 32 ++++++++++++---------- 6 files changed, 88 insertions(+), 33 deletions(-) diff --git a/assets/css/table.css b/assets/css/table.css index 0abd179..b66ace0 100644 --- a/assets/css/table.css +++ b/assets/css/table.css @@ -104,16 +104,30 @@ } .utci-day-tab:hover:not(.active):not(.locked) { - filter: brightness(1.14) !important; + filter: brightness(1.05) !important; } .utci-day-tab.active { color: #c8922a; - outline: 3px solid #c8922a; - outline-offset: -2px; + outline: 1px solid #000; + outline-offset: -1px; z-index: 1; } +/* Danger-band days breathe a slow dark-red inner glow */ +.utci-day-tab.danger { + animation: dayTabDangerPulse 2.4s ease-in-out infinite; +} + +@keyframes dayTabDangerPulse { + 0%, 100% { box-shadow: inset 0 0 0 0 rgba(136, 0, 0, 0); } + 50% { box-shadow: inset 0 0 34px 8px rgba(136, 0, 0, 0.6); } +} + +@media (prefers-reduced-motion: reduce) { + .utci-day-tab.danger { animation: none; } +} + /* The "Mon 12 May" date underneath each tab name */ .utci-day-date { font-family: Fraunces, serif; diff --git a/assets/css/ui.css b/assets/css/ui.css index 02f74f8..abe16d2 100644 --- a/assets/css/ui.css +++ b/assets/css/ui.css @@ -1698,6 +1698,13 @@ beneath its label instead of letting long values overflow sideways. Mobile keeps the inline label/value layout (plenty of width there). */ @media (min-width: 901px) { + /* Glance rail on the left, hourly table on the right */ + .table-with-rail { + grid-template-columns: auto minmax(0, 1fr); + } + .glance-rail { grid-column: 1; grid-row: 1; } + .table-main { grid-column: 2; grid-row: 1; } + .insight-panel--glance .insight-row { display: grid; grid-template-columns: 20px 1fr; diff --git a/assets/js/app.js b/assets/js/app.js index ddb14b1..19c0f43 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1523,10 +1523,18 @@ export function UTCIForecast() { ${label} ${value} `; + const comfortItem = glanceSummary.find(i => i.label === 'Comfortable window'); + const restSummary = glanceSummary.filter(i => i.label !== 'Comfortable window'); return [ - ...glanceSummary.flatMap(item => [ + ...restSummary.flatMap(item => [ renderRow(item), - ...(item.label === 'Peak felt temp' ? heatEvents.map(e => renderRow(e, 'ev-')) : []), + // After the peak/heat block, drop in the comfortable window + ...(item.label === 'Peak felt temp' + ? [ + ...heatEvents.map(e => renderRow(e, 'ev-')), + ...(comfortItem ? [renderRow(comfortItem)] : []), + ] + : []), ]), ...otherEvents.map(e => renderRow(e, 'ev-')), ]; diff --git a/assets/js/components/DayTabs.js b/assets/js/components/DayTabs.js index e056171..61b2e0f 100644 --- a/assets/js/components/DayTabs.js +++ b/assets/js/components/DayTabs.js @@ -69,21 +69,31 @@ function weatherGradient(r, g, b) { // colour, drawn as a radial gradient whose strong colour sits at the outer // edge and softens toward a lighter centre — so the hue reads as radiating // inward from the outside of the tab. -function weatherGradientNeutral(r, g, b) { - const blend = (c) => Math.round(c + (255 - c) * 0.58); - const [mr, mg, mb] = [blend(r), blend(g), blend(b)]; +// edgeBlend pastelises the outer edge (higher = lighter). centerBlend, when +// supplied, blends the centre straight from the raw colour for a lighter core +// (used by the Danger tier to keep a dark edge but a light centre); otherwise +// the centre is just a lightened version of the edge (original behaviour). +function weatherGradientNeutral(r, g, b, edgeBlend = 0.50, centerBlend = null) { + const blend = (c, amt) => Math.round(c + (255 - c) * amt); const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.38)); - const center = `rgb(${lighten(mr)},${lighten(mg)},${lighten(mb)})`; - const edge = `rgb(${mr},${mg},${mb})`; + const [er, eg, eb] = [blend(r, edgeBlend), blend(g, edgeBlend), blend(b, edgeBlend)]; + const [cr, cg, cb] = centerBlend != null + ? [blend(r, centerBlend), blend(g, centerBlend), blend(b, centerBlend)] + : [lighten(er), lighten(eg), lighten(eb)]; + const center = `rgb(${cr},${cg},${cb})`; + const edge = `rgb(${er},${eg},${eb})`; return `radial-gradient(circle at 50% 50%, ${center} 0%, ${center} 28%, ${edge} 100%)`; } -function weatherGradientActive(r, g, b) { - const blend = (c) => Math.round(c + (255 - c) * 0.42); - const [mr, mg, mb] = [blend(r), blend(g), blend(b)]; +function weatherGradientActive(r, g, b, edgeBlend = 0.42, centerBlend = null) { + const blend = (c, amt) => Math.round(c + (255 - c) * amt); const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.38)); - const center = `rgb(${lighten(mr)},${lighten(mg)},${lighten(mb)})`; - const edge = `rgb(${mr},${mg},${mb})`; + const [er, eg, eb] = [blend(r, edgeBlend), blend(g, edgeBlend), blend(b, edgeBlend)]; + const [cr, cg, cb] = centerBlend != null + ? [blend(r, centerBlend), blend(g, centerBlend), blend(b, centerBlend)] + : [lighten(er), lighten(eg), lighten(eb)]; + const center = `rgb(${cr},${cg},${cb})`; + const edge = `rgb(${er},${eg},${eb})`; return `radial-gradient(circle at 50% 50%, ${center} 0%, ${center} 28%, ${edge} 100%)`; } @@ -322,6 +332,7 @@ export function DayTabs({ // Base RGB for each weather condition — pastelised by weatherGradient() const [wR, wG, wB] = (daySnow >= 0.1) ? [100, 160, 230] + : (dayHi > 44) ? [136, 0, 0] : (dayHi > 38) ? [210, 60, 30] : showPrecip ? [ 50, 120, 200] : (dayHi > 32) ? [220, 110, 30] @@ -331,15 +342,23 @@ export function DayTabs({ : modalCloudCat === 'scattered' ? [160, 150, 130] : modalCloudCat === 'wispy' ? [200, 180, 130] : [220, 190, 50]; + // Danger days keep a dark, saturated edge with a lighter centre. + const isDanger = dayHi !== null && dayHi > 44; const wBg = weatherGradient(wR, wG, wB); - const wBgNeutral = weatherGradientNeutral(wR, wG, wB); - const wBgActive = weatherGradientActive(wR, wG, wB); + const wBgNeutral = isDanger + ? weatherGradientNeutral(wR, wG, wB, 0.15, 0.72) + : weatherGradientNeutral(wR, wG, wB); + const wBgActive = isDanger + ? weatherGradientActive(wR, wG, wB, 0.12, 0.72) + : weatherGradientActive(wR, wG, wB); const wText = '#2a1d10'; + // Active-tab outline: the day's weather colour, 20% darker. + const wOutline = `rgb(${Math.round(wR * 0.8)},${Math.round(wG * 0.8)},${Math.round(wB * 0.8)})`; return html`