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.
This commit is contained in:
Fraxle
2026-06-25 16:15:12 +01:00
parent 87907f50d6
commit fd8baf5ef0
6 changed files with 88 additions and 33 deletions
+17 -3
View File
@@ -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;
+7
View File
@@ -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;
+10 -2
View File
@@ -1523,10 +1523,18 @@ export function UTCIForecast() {
<span class="insight-label">${label}</span>
<span class="insight-value">${value}</span>
</div>`;
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-')),
];
+35 -13
View File
@@ -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`
<button
key=${d.key}
class=${`utci-day-tab ${isActive ? 'active' : ''}${locked ? ' locked' : ''}`}
class=${`utci-day-tab ${isActive ? 'active' : ''}${locked ? ' locked' : ''}${isDanger ? ' danger' : ''}`}
onClick=${() => {
if (locked) {
setProPromptSource('day');
@@ -362,6 +381,9 @@ export function DayTabs({
opacity: locked ? 0.5 : 1,
cursor: locked ? 'not-allowed' : 'pointer',
position: 'relative',
filter: isActive ? 'saturate(1.1) brightness(1.04)' : 'none',
outline: isActive ? `2px solid ${wOutline}` : 'none',
outlineOffset: isActive ? '-2px' : '0',
}}
>
${locked && html`
+1 -1
View File
@@ -479,7 +479,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
// ── Vehicle ────────────────────────────────────────────────────────────
if (profile === 'vehicle') {
const peakCabin = peakRow('vehicleT');
const dangerFrom = todayRows.find(r => r.vehicleT != null && r.vehicleT >= 35);
const dangerFrom = todayRows.find(r => r.vehicleT != null && r.vehicleT >= 29);
return [
...(show('vehicleT') ? [{