Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,43 @@
All notable changes to SnowSignal are documented here. This project is in **public
beta**; versions follow [semantic versioning](https://semver.org) with pre-release tags.

## [1.0.0-beta.4] — 2026-06-06

A focused **seasonal-polish beta** — the last planned visual-polish release for a while, and
**not** a stable production release. No engine changes, no backend, no framework, no build step,
no analytics/tracking, no new runtime dependencies. Stays fully static under GitHub Pages
`/snowsignal/`, and all beta.2/beta.3 preferences, saved/recent locations, and share links keep
working.

### Added
- **Seasonal palette** (Appearance → Seasonal palette): **Auto / Winter / Spring / Summer / Fall /
Custom**. Auto follows the selected location's latitude + local date (Southern Hemisphere
inverts; browser date as a fallback before a location is chosen) and shows a read-only
`Auto palette: <Season>` badge. Restrained seasonal accents — Winter icy blue/cyan, Spring soft
pink/lavender, Summer fresh green, Fall amber/orange — each with a matching ambient glow.
Persisted in `localStorage`. The existing constrained hue slider remains; **adjusting it switches
the palette to Custom**, and **Reset restores Auto**. Semantic warning/success/error colors stay
fixed and never follow the accent.
- **Time-of-day ambient intensity** (Auto palette only): a subtle day/evening/night variation of
the ambient glow and particle brightness based on the location's local time (browser time as a
fallback) — brighter by day, softer/slightly warmer in the evening, deeper and dimmer at night.
Not separate themes.
- **Optional desktop hourly scroll arrows** in Weather details (touch users still swipe).

### Changed
- **Atmosphere refinement** — varied winter snowflakes with slow layered drift, spring petals with
a gentle side-to-side sway, firefly-like summer motes with restrained brightness pulses, and
slowly rotating fall leaves. Low particle counts, `pointer-events: none`, no layout impact, and
fully disabled under reduced motion / Reduce animations; tuned for both Light and Dark.
- **Settings-tab motion polish** — a restrained ~160ms fade + 4px upward slide of the newly
selected panel and a smoothly animated active-tab underline, with keyboard focus, tab semantics,
Arrow/Home/End navigation, focus trapping, Escape/X/click-outside close all preserved. Switching
tabs never replays result animations; effects are zeroed under reduced motion.
- **Hourly forecast scrolling** — smooth horizontal scrolling, a subtler scrollbar, and no
horizontal page overflow.
- **Empty state** — clearer prompt ("Search a location to check tonight's forecast.") with one
restrained seasonal icon; kept lightweight.

## [1.0.0-beta.3] — 2026-06-06

Another focused **polish beta** — not a stable release. The transparent deterministic
Expand Down
32 changes: 18 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ deploys to GitHub Pages as-is. The only third-party calls are Open-Meteo (weathe
geocoding), the NWS alerts API (U.S. only), and BigDataCloud's free, keyless
reverse-geocode endpoint for friendly "My location" labels.

> **Status:** polish beta (`v1.0.0-beta.3`) — not a stable release. See
> **Status:** seasonal-polish beta (`v1.0.0-beta.4`) — not a stable release. See
> [Versioning](#versioning), [CHANGELOG.md](CHANGELOG.md), and [ROADMAP.md](ROADMAP.md).
> The GitHub Pages URL is served from the `/snowsignal/` project subpath and all asset
> paths are relative so the app keeps working there.
Expand All @@ -28,10 +28,15 @@ reverse-geocode endpoint for friendly "My location" labels.
- 🛑 A **winter-weather plausibility gate**: with no meaningful winter hazard in the forecast window,
the estimate resolves to **0%** with a clear explanation — sensitivity, warm-weather wind, or the
calendar month can't create a phantom snow day, and unusual out-of-season storms still count
- 🎨 **System / Light / Dark** themes (with a "System detected" badge) plus safe **accent-hue**
customization via a constrained hue slider
- 🍃 **Seasonal atmosphere** — restrained, motion-respecting ambient effects (snow / petals / motes /
leaves) with an **Auto** mode that reads the location's hemisphere + date, tuned for light & dark
- 🎨 **System / Light / Dark** themes (with a "System detected" badge) plus a **Seasonal palette**
(Auto / Winter / Spring / Summer / Fall / Custom). **Auto** follows the location's latitude + local
date (Southern Hemisphere inverts) and shows an `Auto palette: <Season>` badge; the constrained
**hue slider** still lets you go **Custom** (Reset restores Auto). Status colors stay fixed.
- 🌗 **Time-of-day ambient intensity** under Auto — a subtle brighter-by-day / softer-evening /
deeper-night variation of the ambient glow and particle brightness (not separate themes)
- 🍃 **Seasonal atmosphere** — restrained, motion-respecting ambient effects: varied drifting snow,
swaying spring petals, firefly-like summer motes, and rotating fall leaves — tuned for light & dark
and fully disabled under reduced motion
- 📅 A separate **Schedule context** panel — clearly-labeled weekend / summer-break / winter-break
heuristics, hidden until a result exists (dismissible, never authoritative)
- 🗓️ A result **date & freshness header** (location-local date, forecast window, "Updated …" stamp) and
Expand Down Expand Up @@ -146,15 +151,14 @@ Notes:

## Versioning

This is **`v1.0.0-beta.3`** — a **polish beta**, still not a stable release. `beta.1` was the
first public beta of the rebuilt app; `beta.2` and `beta.3` keep the same transparent prediction
engine. `beta.3` adds a winter-weather plausibility gate, a wider desktop workspace, a separate
Schedule context panel, simplified System/Light/Dark themes with safe accent-hue customization, a
wider tabbed Settings modal, fewer animation replays, result date/freshness labels, a collapsible
Weather details panel, a device-local estimate counter, and friendlier loading/error states. Please
keep treating predictions as estimates while the engine is tuned against real outcomes. Full notes
are in [CHANGELOG.md](CHANGELOG.md); the road to a stable `v1.0.0` and beyond is in
[ROADMAP.md](ROADMAP.md).
This is **`v1.0.0-beta.4`** — a **seasonal-polish beta** (the last planned visual-polish release for
a while), still not a stable release. `beta.1` was the first public beta of the rebuilt app;
`beta.2`–`beta.4` keep the same transparent prediction engine. `beta.4` adds the Seasonal palette
system with location-aware Auto and safe accent ranges, subtle time-of-day ambient intensity, refined
per-season atmosphere, Settings-tab motion polish, smoother/subtler hourly-forecast scrolling, and a
clearer seasonal empty state. Please keep treating predictions as estimates while the engine is tuned
against real outcomes. Full notes are in [CHANGELOG.md](CHANGELOG.md); the road to a stable `v1.0.0`
and beyond is in [ROADMAP.md](ROADMAP.md).

## Attribution

Expand Down
9 changes: 8 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,21 @@ behavior with explicit share links, friendlier reverse-geocoded geolocation labe
reworked grouped Settings dialog with data resets, and stronger input validation. See
[CHANGELOG.md](CHANGELOG.md).

## v1.0.0-beta.3 — current
## v1.0.0-beta.3
A further polish beta (not a stable release) on the same transparent engine: a winter-weather
plausibility gate so benign weather resolves to 0%, a wider desktop workspace, a separate
Schedule context panel, simplified System/Light/Dark themes with safe accent-hue customization,
a wider tabbed Settings modal, no unnecessary animation replays, result date/freshness labels,
a collapsible Weather details panel, a device-local estimate counter, friendlier loading/error
states, and open-source (MIT) documentation. See [CHANGELOG.md](CHANGELOG.md).

## v1.0.0-beta.4 — current
The last planned visual-polish beta (not a stable release) on the same engine: a Seasonal palette
system (Auto/Winter/Spring/Summer/Fall/Custom) with location-aware Auto and a safe accent range,
subtle time-of-day ambient intensity under Auto, refined per-season atmosphere, Settings-tab motion
polish, smoother/subtler hourly-forecast scrolling with optional desktop arrows, and a clearer
seasonal empty state. See [CHANGELOG.md](CHANGELOG.md).

## v1.0.0 — stable
Promote to a stable release after bug fixes and real-world testing across a range
of locations, devices, and storms.
Expand Down
1 change: 1 addition & 0 deletions css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
131 changes: 121 additions & 10 deletions css/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 --- */
Expand Down Expand Up @@ -937,6 +988,7 @@ input[type="range"]::-moz-range-thumb {
}

.settings-tab {
position: relative;
appearance: none;
border: none;
background: none;
Expand All @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down
Loading
Loading