From de1f2f5a372c4ae62865f93c4c17b0dedf518305 Mon Sep 17 00:00:00 2001 From: fraxle Date: Sat, 16 May 2026 09:21:06 +0100 Subject: [PATCH] Google Page Insights Tweaks --- assets/css/base.css | 3 +- assets/css/table.css | 10 +-- assets/css/ui.css | 36 +++++++---- assets/js/app.js | 137 +++++++++++++++++++++------------------- assets/js/components.js | 6 +- assets/js/utils.js | 2 +- index.html | 11 +++- 7 files changed, 114 insertions(+), 91 deletions(-) diff --git a/assets/css/base.css b/assets/css/base.css index d07fc6e..9b077c3 100644 --- a/assets/css/base.css +++ b/assets/css/base.css @@ -34,7 +34,8 @@ /* ── 1. FONTS + RESET ───────────────────────────────────────────────── */ -@import "https://fonts.bunny.net/css2?family=Fraunces:ital,wght@0,300;0,500;0,700;0,900;1,300;1,500;1,700;1,900&family=JetBrains+Mono:wght@400;600&family=Manrope:wght@400;500;600;700&display=swap"; +/* Font loading is handled in index.html via a non-blocking + to avoid render-blocking. The @import has been intentionally removed. */ *, *:before, diff --git a/assets/css/table.css b/assets/css/table.css index 6fa3657..1145891 100644 --- a/assets/css/table.css +++ b/assets/css/table.css @@ -141,7 +141,7 @@ font-size: 12px; text-transform: uppercase; letter-spacing: 0.18em; - color: #b09870; + color: #7a5c2a; margin-right: 4px; white-space: nowrap; } @@ -151,7 +151,7 @@ cursor: pointer; border: 1.5px solid #c9b08a; background: #f5edd6; - color: #9a7d5a; + color: #6b4228; font-family: JetBrains Mono, monospace; font-size: 13px; text-transform: uppercase; @@ -192,7 +192,7 @@ font-size: 12px; text-transform: uppercase; letter-spacing: 0.18em; - color: #b09870; + color: #7a5c2a; margin-right: 4px; white-space: nowrap; } @@ -203,7 +203,7 @@ cursor: pointer; border: 1.5px solid #c9b08a; background: #f5edd6; - color: #9a7d5a; + color: #6b4228; font-family: JetBrains Mono, monospace; font-size: 12px; text-transform: uppercase; @@ -295,7 +295,7 @@ border-left: 1px solid #d4c0a0; /* softer join line */ border-radius: 0 20px 20px 0; background: #fef8ee; - color: #7a5c30; + color: #6b4228; user-select: none; transition: background 0.15s, color 0.15s, border-color 0.15s; white-space: nowrap; diff --git a/assets/css/ui.css b/assets/css/ui.css index 3a837b2..8077bd4 100644 --- a/assets/css/ui.css +++ b/assets/css/ui.css @@ -20,9 +20,9 @@ cursor: pointer; border: 1.5px solid #c9b08a; background: #f5edd6; - color: #9a7d5a; + color: #6b4228; font-family: JetBrains Mono, monospace; - font-size: 12px; + font-size: 13px; text-transform: uppercase; letter-spacing: 0.1em; transition: all 0.15s; @@ -36,26 +36,36 @@ } /* Active state — brass border + brass text, parchment background */ .cs-btn.on { + background: #c8922a; border-color: #c8922a; - color: #c8922a; - background: #f5edd6; + color: #fff; font-weight: 600; } .cs-btn.on:hover { - background: #fef3dc; - color: #a06e18; + background: #a06e18; border-color: #a06e18; + color: #fff; } /* The chevron arrow inside the button */ .cs-arrow { - font-size: 9px; - opacity: 0.6; - margin-left: 2px; - transition: transform 0.15s; display: inline-block; + width: 6px; + height: 6px; + border-right: 2px solid currentColor; + border-bottom: 2px solid currentColor; + transform: rotate(45deg); + margin-left: 5px; + margin-bottom: 3px; + opacity: 1; + transition: transform 0.15s; + vertical-align: middle; } .cs-wrap.open .cs-arrow { + transform: rotate(225deg); + margin-bottom: -1px; +} +.cs-wrap.open .cs-arrow-old { transform: rotate(180deg); } @@ -140,7 +150,7 @@ padding-left: 11px; border-radius: 0 20px 20px 0; background: #f5edd6; - color: #9a7d5a; + color: #6b4228; user-select: none; transition: background 0.15s, color 0.15s, border-color 0.15s; white-space: nowrap; @@ -215,7 +225,7 @@ font-size: 10px; text-transform: uppercase; letter-spacing: 0.18em; - color: #b09870; + color: #7a5c2a; margin-bottom: 12px; display: block; font-weight: 600; @@ -301,7 +311,7 @@ font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; - color: #c0a880; + color: #9a7d5a; line-height: 2.2; } diff --git a/assets/js/app.js b/assets/js/app.js index a6db99a..6888c33 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -139,12 +139,6 @@ export function UTCIForecast() { icon: '🌡️', cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false }, }, - outdoors: { - label: 'Places', - icon: '🌤️', - // Default cols match the first variant (urban). Switching variant updates visibleCols. - cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false }, - }, home: { label: 'Home', icon: '🏠', @@ -160,11 +154,11 @@ export function UTCIForecast() { icon: '🌾', cols: { hour: true, air: true, rh: true, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: false, uvB: false, burn: false, utciP: false, precip: true, soilT: true, soilT6: true, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false }, }, - sailing: { - label: 'Sailing', - icon: '⛵', - proOnly: true, - cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false }, + outdoors: { + label: 'Places', + icon: '🌤️', + // Default cols match the first variant (urban). Switching variant updates visibleCols. + cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false }, }, alltemps: { label: 'Temps', @@ -189,6 +183,7 @@ export function UTCIForecast() { festival: { name: 'Festival', proOnly: true, cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false } }, wintersports: { name: 'Winter Sports', proOnly: true, cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: true, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } }, naturist: { name: 'Naturist', proOnly: true, cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: false, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } }, + sailing: { name: 'Sailing', proOnly: true, cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } }, }; // Current active filter profile @@ -692,7 +687,7 @@ export function UTCIForecast() {
-
+
@@ -1025,6 +1020,41 @@ export function UTCIForecast() { Profile: ${Object.entries(FILTER_PROFILES).map(([key, profile]) => { const locked = profile.proOnly && !isPro; + + // The 'outdoors' profile becomes a CustomSelect dropdown + if (key === 'outdoors') { + return html` + <${CustomSelect} + key="outdoors" + value=${activeProfile === 'outdoors' ? outdoorsVariant : 'off'} + isOn=${activeProfile === 'outdoors'} + noHide=${true} + hideLabel=${`${profile.icon} ${profile.label}`} + options=${Object.entries(OUTDOORS_VARIANTS).map(([k, v]) => ({ + value: k, + label: v.name + (v.proOnly && !isPro ? ' 🔒' : ''), + disabled: false, + }))} + onChange=${(v) => { + if (v === 'off') { + setActiveProfile('basic'); + setVisibleCols({ ...FILTER_PROFILES.basic.cols }); + setIndoorMode('off'); + return; + } + if (OUTDOORS_VARIANTS[v]?.proOnly && !isPro) { + setProPromptSource(`variant:${v}`); + setProPromptDay(0); + return; + } + setActiveProfile('outdoors'); + setOutdoorsVariant(v); + setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols }); + setIndoorMode('off'); + }} + />`; + } + return html` `} +${isPro && (activeProfile === 'custom' || FILTER_PROFILES[activeProfile].cols['air']) && html``} ${isPro && (activeProfile === 'custom' || FILTER_PROFILES[activeProfile].cols['rh']) && html``} ${isPro && (activeProfile === 'custom' || FILTER_PROFILES[activeProfile].cols['dew']) && html``} ${isPro && (activeProfile === 'custom' || FILTER_PROFILES[activeProfile].cols['soilT']) && html``} @@ -1214,31 +1219,31 @@ export function UTCIForecast() { - - ${visibleCols.air && html``} - ${visibleCols.rh && html``} - ${visibleCols.dew && html``} - ${visibleCols.soilT && html``} - ${visibleCols.soilT6 && html``} - ${visibleCols.soilM && html``} - ${visibleCols.concreteT && html``} - ${visibleCols.wind && html``} - ${visibleCols.dir && html``} - ${visibleCols.cloud && html``} - ${visibleCols.sun && html``} - ${visibleCols.direct && html``} - ${visibleCols.diffuse && html``} - ${visibleCols.tmrt && html``} - ${visibleCols.delta && html``} - ${visibleCols.utci && html``} - ${visibleCols.uvA && html``} - ${visibleCols.uvB && html``} - ${visibleCols.burn && html``} - ${visibleCols.vehicleT && html``} - ${indoorMode === 'on' && !indoorManaged && html``} - ${indoorMode === 'on' && indoorManaged && html``} - ${visibleCols.precip && html``} - ${visibleCols.utciP && html``} + + ${visibleCols.air && html``} + ${visibleCols.rh && html``} + ${visibleCols.dew && html``} + ${visibleCols.soilT && html``} + ${visibleCols.soilT6 && html``} + ${visibleCols.soilM && html``} + ${visibleCols.concreteT && html``} + ${visibleCols.wind && html``} + ${visibleCols.dir && html``} + ${visibleCols.cloud && html``} + ${visibleCols.sun && html``} + ${visibleCols.direct && html``} + ${visibleCols.diffuse && html``} + ${visibleCols.tmrt && html``} + ${visibleCols.delta && html``} + ${visibleCols.utci && html``} + ${visibleCols.uvA && html``} + ${visibleCols.uvB && html``} + ${visibleCols.burn && html``} + ${visibleCols.vehicleT && html``} + ${indoorMode === 'on' && !indoorManaged && html``} + ${indoorMode === 'on' && indoorManaged && html``} + ${visibleCols.precip && html``} + ${visibleCols.utciP && html``}
handleThClick('hour', e)}>Hour handleThClick('air', e)}>Air °C handleThClick('rh', e)}>RH % handleThClick('dew', e)}>Dew °C handleThClick('soilT', e)}>Soil °C surface handleThClick('soilT6', e)}>Soil 6cm °C root handleThClick('soilM', e)}>Soil moist m³/m³ handleThClick('concreteT', e)}>Concrete °C surface handleThClick('wind', e)}>Wind m/s (gust) handleThClick('dir', e)}>Dir - handleThClick('cloud', e)}>Cloud % handleThClick('sun', e)}>Sun elev° handleThClick('direct', e)}>Direct W/m² handleThClick('diffuse', e)}>Diffuse W/m² handleThClick('tmrt', e)}>Tmrt °C handleThClick('delta', e)}>Δ UTCI−Air handleThClick('utci', e)}>UTCI °C felt handleThClick('uvA', e)}>UV-A est. idx handleThClick('uvB', e)}>UV-B est. idx handleThClick('burn', e)}>Burn to MED handleThClick('vehicleT', e)}>Vehicle °C peak handleThClick('indoorT', e)}>Indoors °C est. handleThClick('managedT', e)}>Managed °C est. handleThClick('precip', e)}>Pcpt mm/h handleThClick('utciP', e)}>UTCI+P °C adj. handleThClick('hour', e)}>Hour handleThClick('air', e)}>Air °C handleThClick('rh', e)}>RH % handleThClick('dew', e)}>Dew °C handleThClick('soilT', e)}>Soil °C surface handleThClick('soilT6', e)}>Soil 6cm °C root handleThClick('soilM', e)}>Soil moist m³/m³ handleThClick('concreteT', e)}>Concrete °C surface handleThClick('wind', e)}>Wind m/s (gust) handleThClick('dir', e)}>Dir - handleThClick('cloud', e)}>Cloud % handleThClick('sun', e)}>Sun elev° handleThClick('direct', e)}>Direct W/m² handleThClick('diffuse', e)}>Diffuse W/m² handleThClick('tmrt', e)}>Tmrt °C handleThClick('delta', e)}>Δ UTCI−Air handleThClick('utci', e)}>UTCI °C felt handleThClick('uvA', e)}>UV-A est. idx handleThClick('uvB', e)}>UV-B est. idx handleThClick('burn', e)}>Burn to MED handleThClick('vehicleT', e)}>Vehicle °C peak handleThClick('indoorT', e)}>Indoors °C est. handleThClick('managedT', e)}>Managed °C est. handleThClick('precip', e)}>Pcpt mm/h handleThClick('utciP', e)}>UTCI+P °C adj.
@@ -1420,7 +1425,7 @@ export function UTCIForecast() { { l: '-13 to 0 Freezing', bg: '#7eb0e0', fg: '#111' }, { l: '0 to 9 Cold', bg: '#bcd9ec', fg: '#111' }, { l: '9 to 18 Chilled', bg: '#c8dcc0', fg: '#111' }, - { l: '18 to 26 Comfortable', bg: '#6ab05a', fg: '#fff' }, + { l: '18 to 26 Comfortable', bg: '#4a8a3a', fg: '#fff' }, { l: '26 to 32 Mod heat', bg: '#e8c547', fg: '#111' }, { l: '32 to 38 Strong heat', bg: '#dc8a3a', fg: '#111' }, { l: '38 to 46 V. strong', bg: '#c44a3a', fg: '#fff' }, @@ -1460,6 +1465,6 @@ export function UTCIForecast() { On clear sunny days this gap can exceed 10 °C even at modest air temperatures.
-
+
`; } diff --git a/assets/js/components.js b/assets/js/components.js index 8904e78..4947e2f 100644 --- a/assets/js/components.js +++ b/assets/js/components.js @@ -36,7 +36,7 @@ const html = htm.bind(h); // groupedLeft — bool — fuse right side with a VentPill // isLastChild — bool — restore right border-radius when no sibling follows // ═══════════════════════════════════════════════════════════════════ -export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel, isOn, groupedLeft, isLastChild }) { +export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel, isOn, noHide, groupedLeft, isLastChild }) { const [open, setOpen] = useState(false); const wrapRef = useRef(null); @@ -78,11 +78,11 @@ export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel, type="button" > ${btnLabel} - + ${open && html`
- ${isOn && html` + ${isOn && !noHide && html` - - + + + +