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.
This commit is contained in:
+17
-3
@@ -104,16 +104,30 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.utci-day-tab:hover:not(.active):not(.locked) {
|
.utci-day-tab:hover:not(.active):not(.locked) {
|
||||||
filter: brightness(1.14) !important;
|
filter: brightness(1.05) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.utci-day-tab.active {
|
.utci-day-tab.active {
|
||||||
color: #c8922a;
|
color: #c8922a;
|
||||||
outline: 3px solid #c8922a;
|
outline: 1px solid #000;
|
||||||
outline-offset: -2px;
|
outline-offset: -1px;
|
||||||
z-index: 1;
|
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 */
|
/* The "Mon 12 May" date underneath each tab name */
|
||||||
.utci-day-date {
|
.utci-day-date {
|
||||||
font-family: Fraunces, serif;
|
font-family: Fraunces, serif;
|
||||||
|
|||||||
@@ -1698,6 +1698,13 @@
|
|||||||
beneath its label instead of letting long values overflow sideways.
|
beneath its label instead of letting long values overflow sideways.
|
||||||
Mobile keeps the inline label/value layout (plenty of width there). */
|
Mobile keeps the inline label/value layout (plenty of width there). */
|
||||||
@media (min-width: 901px) {
|
@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 {
|
.insight-panel--glance .insight-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 20px 1fr;
|
grid-template-columns: 20px 1fr;
|
||||||
|
|||||||
+10
-2
@@ -1523,10 +1523,18 @@ export function UTCIForecast() {
|
|||||||
<span class="insight-label">${label}</span>
|
<span class="insight-label">${label}</span>
|
||||||
<span class="insight-value">${value}</span>
|
<span class="insight-value">${value}</span>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
const comfortItem = glanceSummary.find(i => i.label === 'Comfortable window');
|
||||||
|
const restSummary = glanceSummary.filter(i => i.label !== 'Comfortable window');
|
||||||
return [
|
return [
|
||||||
...glanceSummary.flatMap(item => [
|
...restSummary.flatMap(item => [
|
||||||
renderRow(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-')),
|
...otherEvents.map(e => renderRow(e, 'ev-')),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -69,21 +69,31 @@ function weatherGradient(r, g, b) {
|
|||||||
// colour, drawn as a radial gradient whose strong colour sits at the outer
|
// 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
|
// edge and softens toward a lighter centre — so the hue reads as radiating
|
||||||
// inward from the outside of the tab.
|
// inward from the outside of the tab.
|
||||||
function weatherGradientNeutral(r, g, b) {
|
// edgeBlend pastelises the outer edge (higher = lighter). centerBlend, when
|
||||||
const blend = (c) => Math.round(c + (255 - c) * 0.58);
|
// supplied, blends the centre straight from the raw colour for a lighter core
|
||||||
const [mr, mg, mb] = [blend(r), blend(g), blend(b)];
|
// (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 lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.38));
|
||||||
const center = `rgb(${lighten(mr)},${lighten(mg)},${lighten(mb)})`;
|
const [er, eg, eb] = [blend(r, edgeBlend), blend(g, edgeBlend), blend(b, edgeBlend)];
|
||||||
const edge = `rgb(${mr},${mg},${mb})`;
|
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%)`;
|
return `radial-gradient(circle at 50% 50%, ${center} 0%, ${center} 28%, ${edge} 100%)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function weatherGradientActive(r, g, b) {
|
function weatherGradientActive(r, g, b, edgeBlend = 0.42, centerBlend = null) {
|
||||||
const blend = (c) => Math.round(c + (255 - c) * 0.42);
|
const blend = (c, amt) => Math.round(c + (255 - c) * amt);
|
||||||
const [mr, mg, mb] = [blend(r), blend(g), blend(b)];
|
|
||||||
const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.38));
|
const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.38));
|
||||||
const center = `rgb(${lighten(mr)},${lighten(mg)},${lighten(mb)})`;
|
const [er, eg, eb] = [blend(r, edgeBlend), blend(g, edgeBlend), blend(b, edgeBlend)];
|
||||||
const edge = `rgb(${mr},${mg},${mb})`;
|
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%)`;
|
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()
|
// Base RGB for each weather condition — pastelised by weatherGradient()
|
||||||
const [wR, wG, wB] = (daySnow >= 0.1) ? [100, 160, 230]
|
const [wR, wG, wB] = (daySnow >= 0.1) ? [100, 160, 230]
|
||||||
|
: (dayHi > 44) ? [136, 0, 0]
|
||||||
: (dayHi > 38) ? [210, 60, 30]
|
: (dayHi > 38) ? [210, 60, 30]
|
||||||
: showPrecip ? [ 50, 120, 200]
|
: showPrecip ? [ 50, 120, 200]
|
||||||
: (dayHi > 32) ? [220, 110, 30]
|
: (dayHi > 32) ? [220, 110, 30]
|
||||||
@@ -331,15 +342,23 @@ export function DayTabs({
|
|||||||
: modalCloudCat === 'scattered' ? [160, 150, 130]
|
: modalCloudCat === 'scattered' ? [160, 150, 130]
|
||||||
: modalCloudCat === 'wispy' ? [200, 180, 130]
|
: modalCloudCat === 'wispy' ? [200, 180, 130]
|
||||||
: [220, 190, 50];
|
: [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 wBg = weatherGradient(wR, wG, wB);
|
||||||
const wBgNeutral = weatherGradientNeutral(wR, wG, wB);
|
const wBgNeutral = isDanger
|
||||||
const wBgActive = weatherGradientActive(wR, wG, wB);
|
? 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';
|
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`
|
return html`
|
||||||
<button
|
<button
|
||||||
key=${d.key}
|
key=${d.key}
|
||||||
class=${`utci-day-tab ${isActive ? 'active' : ''}${locked ? ' locked' : ''}`}
|
class=${`utci-day-tab ${isActive ? 'active' : ''}${locked ? ' locked' : ''}${isDanger ? ' danger' : ''}`}
|
||||||
onClick=${() => {
|
onClick=${() => {
|
||||||
if (locked) {
|
if (locked) {
|
||||||
setProPromptSource('day');
|
setProPromptSource('day');
|
||||||
@@ -362,6 +381,9 @@ export function DayTabs({
|
|||||||
opacity: locked ? 0.5 : 1,
|
opacity: locked ? 0.5 : 1,
|
||||||
cursor: locked ? 'not-allowed' : 'pointer',
|
cursor: locked ? 'not-allowed' : 'pointer',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
filter: isActive ? 'saturate(1.1) brightness(1.04)' : 'none',
|
||||||
|
outline: isActive ? `2px solid ${wOutline}` : 'none',
|
||||||
|
outlineOffset: isActive ? '-2px' : '0',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
${locked && html`
|
${locked && html`
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
|
|||||||
// ── Vehicle ────────────────────────────────────────────────────────────
|
// ── Vehicle ────────────────────────────────────────────────────────────
|
||||||
if (profile === 'vehicle') {
|
if (profile === 'vehicle') {
|
||||||
const peakCabin = peakRow('vehicleT');
|
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 [
|
return [
|
||||||
...(show('vehicleT') ? [{
|
...(show('vehicleT') ? [{
|
||||||
|
|||||||
+18
-14
@@ -1,19 +1,23 @@
|
|||||||
<svg viewBox="0 0 174 173" xmlns="http://www.w3.org/2000/svg" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 174 173" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
<g transform="matrix(0.986226,0,0,0.986226,-585.724252,-110.041209)">
|
<g transform="matrix(0.986226,0,0,0.986226,-585.724252,-110.041209)">
|
||||||
<clipPath id="c">
|
<circle cx="681.984" cy="199.45" r="83.299" style="fill:rgb(245,237,214);stroke:rgb(30,18,8);stroke-width:9.13px;"/>
|
||||||
<circle cx="681.9838" cy="199.4502" r="83.2987"/>
|
<g>
|
||||||
</clipPath>
|
<clipPath id="_clip1">
|
||||||
<g clip-path="url(#c)">
|
<circle cx="681.984" cy="199.45" r="83.299"/>
|
||||||
<g transform="matrix(0.948836,0,0,0.948836,19.787839,-2.49381)">
|
</clipPath>
|
||||||
<path d="M666.992,233.0768C666.3949,230.6581 666.1785,228.6317 666.1785,226.0296C666.1785,208.6786 680.2653,194.5919 697.6163,194.5919C714.9672,194.5919 729.054,208.6786 729.054,226.0296C729.054,228.4125 728.9892,230.9346 728.4859,233.1663" fill="none" stroke="#c8922a" stroke-width="7.48"/>
|
<g clip-path="url(#_clip1)">
|
||||||
</g>
|
<g transform="matrix(0.948836,0,0,0.948836,19.787839,-2.49381)">
|
||||||
<g transform="matrix(0.520349,0,0,0.520349,304.766642,64.226716)">
|
<path d="M666.992,233.077C666.395,230.658 666.178,228.632 666.178,226.03C666.178,208.679 680.265,194.592 697.616,194.592C714.967,194.592 729.054,208.679 729.054,226.03C729.054,228.412 728.989,230.935 728.486,233.166" style="fill:none;stroke:rgb(200,146,42);stroke-width:7.48px;"/>
|
||||||
<path d="M724.9315,291.8131C777.359,291.8131 826.5862,305.6245 869.1698,329.8044" fill="none" stroke="#1e1208" stroke-width="13.64"/>
|
</g>
|
||||||
</g>
|
<g transform="matrix(0.520349,0,0,0.520349,304.766642,64.226716)">
|
||||||
<g transform="matrix(0.520349,0,0,0.520349,304.766642,64.226716)">
|
<path d="M724.932,291.813C777.359,291.813 826.586,305.625 869.17,329.804" style="fill:none;stroke:rgb(30,18,8);stroke-width:13.64px;"/>
|
||||||
<path d="M580.8436,329.719C623.3927,305.592 672.5657,291.8131 724.9315,291.8131" fill="none" stroke="#1e1208" stroke-width="13.64"/>
|
</g>
|
||||||
|
<g transform="matrix(0.520349,0,0,0.520349,304.766642,64.226716)">
|
||||||
|
<path d="M580.844,329.719C623.393,305.592 672.566,291.813 724.932,291.813" style="fill:none;stroke:rgb(30,18,8);stroke-width:13.64px;"/>
|
||||||
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<circle cx="681.9838" cy="199.4502" r="83.2987" fill="none" stroke="#1e1208" stroke-width="9.13"/>
|
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.8 KiB |
Reference in New Issue
Block a user