From c50436474c44dafa24a5443633713920aaf98d5f Mon Sep 17 00:00:00 2001 From: Dawson <71105828+DawsonCodes@users.noreply.github.com> Date: Sat, 6 Jun 2026 23:35:34 +0000 Subject: [PATCH 1/3] Add seasonal palette system, time-of-day ambient intensity, and atmosphere/tab/scroll polish - Seasonal palette (Auto/Winter/Spring/Summer/Fall/Custom) with location-aware Auto and a read-only Auto badge; reuses the constrained hue slider for Custom - Subtle day/evening/night ambient intensity under Auto via data-daypart - Refined per-season atmosphere (spring sway, summer firefly pulse) tuned for light/dark - Settings-tab underline + panel entrance animation; smoother/subtler hourly scrolling with optional desktop arrows; clearer seasonal empty state - Bump in-app version and package.json to 1.0.0-beta.4 --- css/base.css | 1 + css/components.css | 131 +++++++++++++++++++++++++++++++++++++++++---- css/tokens.css | 33 +++++++++--- index.html | 28 ++++++++-- js/main.js | 69 ++++++++++++++++++++---- js/palette.js | 62 +++++++++++++++++++++ js/storage.js | 3 +- js/ui.js | 33 +++++++++++- package.json | 2 +- 9 files changed, 328 insertions(+), 34 deletions(-) create mode 100644 js/palette.js diff --git a/css/base.css b/css/base.css index 104dd8a..812afb9 100644 --- a/css/base.css +++ b/css/base.css @@ -32,6 +32,7 @@ body { background: radial-gradient(circle at 12% -10%, var(--bg-glow-1), transparent 45%), radial-gradient(circle at 90% 0%, var(--bg-glow-2), transparent 45%), + radial-gradient(circle at 50% 120%, hsl(30 90% 60% / var(--evening-warm, 0)), transparent 55%), var(--bg); background-attachment: fixed; line-height: 1.5; diff --git a/css/components.css b/css/components.css index 91775c8..619d054 100644 --- a/css/components.css +++ b/css/components.css @@ -720,12 +720,56 @@ input[type="range"]::-moz-range-thumb { } /* --- Hourly timeline --- */ +.timeline-row { + display: flex; + align-items: stretch; + gap: 4px; + min-width: 0; +} + +/* Optional desktop-only scroll arrows; hidden on touch/small screens. */ +.timeline-arrow { + display: none; + flex: 0 0 auto; + width: 28px; + align-items: center; + justify-content: center; + border: 1px solid var(--border); + background: var(--surface-2); + color: var(--text); + border-radius: var(--radius-sm); + cursor: pointer; + font-size: 1.1rem; + line-height: 1; +} + +.timeline-arrow:hover { + background: var(--surface-3); +} + .timeline { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 6px; + min-width: 0; + scroll-behavior: smooth; + /* Subtle, unobtrusive scrollbar. */ scrollbar-width: thin; + scrollbar-color: var(--surface-3) transparent; +} + +.timeline::-webkit-scrollbar { + height: 6px; +} + +.timeline::-webkit-scrollbar-track { + background: transparent; +} + +.timeline::-webkit-scrollbar-thumb { + background: var(--surface-3); + border-radius: 999px; } .t-hour { @@ -765,7 +809,14 @@ input[type="range"]::-moz-range-thumb { text-align: center; color: var(--muted); font-size: 0.9rem; - padding: 12px 0; + padding: 16px 0; +} + +.empty-icon { + display: block; + font-size: 1.9rem; + margin-bottom: 8px; + opacity: 0.8; } /* --- Skeleton --- */ @@ -937,6 +988,7 @@ input[type="range"]::-moz-range-thumb { } .settings-tab { + position: relative; appearance: none; border: none; background: none; @@ -945,13 +997,31 @@ input[type="range"]::-moz-range-thumb { font-size: 0.88rem; font-weight: 600; cursor: pointer; - border-bottom: 2px solid transparent; white-space: nowrap; + transition: color 0.16s ease; +} + +/* Smoothly-animated active-tab underline (scales in; zeroed under reduced motion). */ +.settings-tab::after { + content: ""; + position: absolute; + left: 8px; + right: 8px; + bottom: -1px; + height: 2px; + border-radius: 2px; + background: var(--accent-strong); + transform: scaleX(0); + transform-origin: center; + transition: transform 0.16s ease; } .settings-tab[aria-selected="true"] { color: var(--accent-strong); - border-bottom-color: var(--accent-strong); +} + +.settings-tab[aria-selected="true"]::after { + transform: scaleX(1); } .settings-tab:hover { @@ -962,6 +1032,16 @@ input[type="range"]::-moz-range-thumb { padding-top: 6px; } +/* Restrained fade + slight upward slide of a newly selected tab panel. */ +.settings-panel-tab.tab-anim { + animation: tab-in 0.16s ease both; +} + +@keyframes tab-in { + from { opacity: 0; transform: translateY(4px); } + to { opacity: 1; transform: none; } +} + .settings-group { border: none; padding: 14px 0 0; @@ -1153,7 +1233,8 @@ input[type="range"]::-moz-range-thumb { top: -6vh; font-size: calc(15px * var(--s, 1)); line-height: 1; - opacity: var(--o, 0.3); + /* Particle brightness dims subtly in the evening/night under the Auto palette. */ + opacity: calc(var(--o, 0.3) * var(--atm-bright, 1)); will-change: transform; animation: atm-fall var(--dur, 12s) linear var(--delay, 0s) infinite; } @@ -1162,20 +1243,27 @@ input[type="range"]::-moz-range-thumb { Defaults below are tuned for dark backgrounds; the light-theme block re-tints for contrast. Per-theme opacity is nudged so effects stay tasteful on both. */ .atmosphere[data-season="winter"] .atm-particle { color: #cfe0f5; } -.atmosphere[data-season="spring"] .atm-particle { color: #f3c4d8; } + +/* Spring petals drift down with a gentle side-to-side sway. */ +.atmosphere[data-season="spring"] .atm-particle { + color: #f3c4d8; + animation-name: atm-sway-fall; +} + +/* Fall leaves drift slowly and rotate (atm-fall already rotates a full turn). */ .atmosphere[data-season="fall"] .atm-particle { font-size: calc(16px * var(--s, 1)); } -/* Summer motes float upward with a soft warm glow. */ +/* Summer firefly-like motes float upward with a soft warm glow and brightness pulse. */ .atmosphere[data-season="summer"] .atm-particle { color: #ffd98a; text-shadow: 0 0 6px rgba(255, 217, 138, 0.8); - animation-name: atm-rise; + animation-name: atm-firefly; } /* Light theme: darker tints + slightly lower opacity so particles read without - shouting against a bright surface. */ -:root[data-theme="light"] .atmosphere[data-season="winter"] .atm-particle { color: #6f9fd8; opacity: calc(var(--o, 0.3) * 0.8); } -:root[data-theme="light"] .atmosphere[data-season="spring"] .atm-particle { color: #d27aa6; opacity: calc(var(--o, 0.3) * 0.8); } + shouting against a bright surface (still respecting the daypart brightness). */ +:root[data-theme="light"] .atmosphere[data-season="winter"] .atm-particle { color: #6f9fd8; opacity: calc(var(--o, 0.3) * 0.8 * var(--atm-bright, 1)); } +:root[data-theme="light"] .atmosphere[data-season="spring"] .atm-particle { color: #d27aa6; opacity: calc(var(--o, 0.3) * 0.8 * var(--atm-bright, 1)); } :root[data-theme="light"] .atmosphere[data-season="summer"] .atm-particle { color: #e08a1e; text-shadow: none; } @keyframes atm-fall { @@ -1190,6 +1278,25 @@ input[type="range"]::-moz-range-thumb { 100% { transform: translate3d(var(--x, 0), -8vh, 0); opacity: 0; } } +/* Spring: a gentle side-to-side sway while drifting down. */ +@keyframes atm-sway-fall { + 0% { transform: translate3d(0, -6vh, 0) rotate(0deg); } + 25% { transform: translate3d(16px, 27vh, 0) rotate(36deg); } + 50% { transform: translate3d(-12px, 54vh, 0) rotate(-26deg); } + 75% { transform: translate3d(14px, 81vh, 0) rotate(30deg); } + 100% { transform: translate3d(0, 108vh, 0) rotate(0deg); } +} + +/* Summer: rise slowly while pulsing brightness like a firefly. */ +@keyframes atm-firefly { + 0% { transform: translate3d(0, 112vh, 0); opacity: 0; } + 15% { opacity: calc(var(--o, 0.3) * var(--atm-bright, 1)); } + 45% { opacity: calc(var(--o, 0.3) * var(--atm-bright, 1) * 0.4); } + 65% { opacity: calc(var(--o, 0.3) * var(--atm-bright, 1)); } + 85% { opacity: calc(var(--o, 0.3) * var(--atm-bright, 1) * 0.55); } + 100% { transform: translate3d(var(--x, 0), -8vh, 0); opacity: 0; } +} + /* --- Motion polish for results / dropdown / toast --- */ .result.reveal { animation: result-reveal 0.5s cubic-bezier(0.22, 1, 0.36, 1) both; @@ -1268,6 +1375,10 @@ input[type="range"]::-moz-range-thumb { width: 118px; height: 118px; } + /* Hourly scroll arrows are a desktop-only affordance (touch users swipe). */ + .timeline-arrow { + display: flex; + } } /* Wide workspace: factor breakdown beside the secondary weather details. */ diff --git a/css/tokens.css b/css/tokens.css index 92cb6d9..62ccbd0 100644 --- a/css/tokens.css +++ b/css/tokens.css @@ -16,10 +16,16 @@ --gap: 18px; --maxw: 1080px; /* roomier desktop workspace; collapses cleanly on mobile */ - /* Accent hue (0..360). ~217 ≈ the original SnowSignal blue. */ + /* Accent hue (0..360). ~217 ≈ the original SnowSignal blue. Driven by the + Seasonal palette (Auto/season presets) or the Custom hue slider. */ --accent-h: 217; - /* fixed semantic colors — kept distinct from the accent */ + /* Time-of-day ambient intensity (set via [data-daypart] only under Auto). */ + --glow-mult: 1; /* ambient glow brightness multiplier */ + --atm-bright: 1; /* atmosphere particle brightness multiplier */ + --evening-warm: 0; /* alpha of a subtle warm evening wash */ + + /* fixed semantic colors — kept distinct from the accent and never hue-driven */ --good: #16a34a; --warn: #f59e0b; --bad: #ef4444; @@ -30,8 +36,8 @@ --accent-strong: hsl(var(--accent-h) 85% 60%); --accent-ring: hsl(var(--accent-h) 85% 53% / 0.28); --bg: #eef4fb; - --bg-glow-1: hsl(var(--accent-h) 90% 65% / 0.22); - --bg-glow-2: hsl(var(--accent-h) 83% 53% / 0.16); + --bg-glow-1: hsl(var(--accent-h) 90% 65% / calc(0.22 * var(--glow-mult))); + --bg-glow-2: hsl(var(--accent-h) 83% 53% / calc(0.16 * var(--glow-mult))); --surface: #ffffff; --surface-2: #f1f5f9; --surface-3: #e2e8f0; @@ -47,6 +53,21 @@ color-scheme: light; } +/* Time-of-day ambient intensity. Applied only under the Auto seasonal palette + (main.js sets/removes data-daypart). Subtle by design — not separate themes. */ +:root[data-daypart="day"] { + --glow-mult: 1.12; +} +:root[data-daypart="evening"] { + --glow-mult: 0.9; + --atm-bright: 0.9; + --evening-warm: 0.06; +} +:root[data-daypart="night"] { + --glow-mult: 0.66; + --atm-bright: 0.7; +} + :root[data-theme="dark"] { color-scheme: dark; /* Slightly brighter accent so any hue stays legible on the dark surface. */ @@ -54,8 +75,8 @@ --accent-strong: hsl(var(--accent-h) 85% 70%); --accent-ring: hsl(var(--accent-h) 85% 62% / 0.4); --bg: #0b1220; - --bg-glow-1: hsl(var(--accent-h) 85% 60% / 0.2); - --bg-glow-2: hsl(var(--accent-h) 80% 55% / 0.24); + --bg-glow-1: hsl(var(--accent-h) 85% 60% / calc(0.2 * var(--glow-mult))); + --bg-glow-2: hsl(var(--accent-h) 80% 55% / calc(0.24 * var(--glow-mult))); --surface: #131c2e; --surface-2: #1b2640; --surface-3: #243150; diff --git a/index.html b/index.html index 2fc7b9e..93ee59b 100644 --- a/index.html +++ b/index.html @@ -212,7 +212,11 @@

What’s driving this estimate

Weather details

Hourly outlook

- +
+ + + +
@@ -220,9 +224,10 @@

Hourly outlook

-

- Search for a location above to see your snow day estimate. -

+
+ +

Search a location to check tonight’s forecast.

+