English | Русский
Try it live → https://gradus.website
A lightweight, high-performance, offline-capable Progressive Web Application (PWA) for weather forecasts inspired by the clean visual density and information hierarchy of modern weather apps. Built with SvelteKit (Svelte 5 runes), the open Open-Meteo API, and vector Meteocons.
| Home | Forecast | Favorites | Settings |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Engineered as a cross-platform Progressive Web Application (PWA). It provides an excellent standalone experience on iOS Safari, Android (Chrome), and Desktop environments (Mac, Windows) via Chrome, where it can be installed as a native-like app. It is completely static (zero server-side runtime footprint), with resilient offline caching, and deployable to GitHub Pages or any static web server.
- Home Screen Hero: Large temperature display in °C with Unicode minus (
−), weather conditions, feels-like temperature, wind speed in m/s with 8-point compass direction, and atmospheric pressure in mmHg. - Interactive Hour Scrubber: Select any hour on the hourly rail to temporarily preview conditions in the hero block with an instant
✕ Nowreset button. - Native Mobile Pull-to-Refresh: Smooth gesture with resistance, haptic feedback (
vibrate), and animated status pill to refresh forecasts on the fly. - Astronomy (Sun & Moon): Dynamic SVG sun arc with sunset/sunrise countdown and polar day/night handling, plus Moon Phase with illumination % and days to next full/new moon (hemisphere-aware).
- Detailed Air Quality & Pollen: European AQI score, individual pollutant gauges (PM2.5, PM10, NO₂, SO₂, O₃, CO) with WHO 24h targets, and 6 pollen species under a collapsible accordion.
- Coastal Water Temperature: Sea surface temperature and wave height for coastal cities (Open-Meteo Marine API with negative caching for inland locations).
- Home Screen Layout Customization: Rearrange sections using up/down controls or toggle visibility in Settings.
- Expanded Weather Metrics: UV index, humidity, dew point, and air quality index in a dedicated details card.
- Near-Term Precipitation Heuristic: Deterministic 2-hour rain/snow probability heuristic based on hourly data with direct map link, plus an interactive 24-hour precipitation SVG chart.
- Hourly Forecast Rail: Smooth horizontal scrolling forecast («Now» + 24+ hours) with current-hour highlighting, day/night weather icons, and precipitation probability badges.
- 10-Day Forecast: Daily summary on Home and detailed breakdown on the
/forecast/page. - City Search & Geocoding: Powered by Open-Meteo Geocoding API (300 ms debounce, queries starting at 2 characters, up to 8 results with region/country, automatic request cancellation, localized queries).
- Favorites: Fast local storage of favourite locations (
localStorage), quick switching, and cached temperature previews on/favorites/. - Settings: City selection, GPS geolocation request, last updated status, theme (System / Light / Dark), language switcher (English / Russian), section customizer, weather alerts configuration, server push alerts toggle, and iOS installation guidance.
- Bilingual Interface (i18n): English by default with instant Russian switch; localized WMO descriptions, units, compass points, and date/time formatting.
- Smart Weather Alerts & Background Web Push: In-app alerts when the tab is active, plus autonomous background Web Push delivery via lightweight Cloudflare Workers + D1 for locked/closed devices (iOS 16.4+ standalone / Android / Desktop) with quiet hours, rate limiting, and 3-hour cooldown.
- Privacy-First Analytics Dashboard (
/stats/): 100% anonymous telemetry with token-protected management — device platform and language breakdowns, active installations over 7 days, a 30-day activity chart (app opens vs dispatched alerts), a push onboarding funnel (installs → opt-ins → live subscriptions), alert type breakdown, subscription health metrics (stale / auto-removed / never-alerted), top subscriber cities, and alert history. The admin token persists inlocalStorageuntil explicitly locked. - Dynamic Favicon & App Badging: Live temperature rendered in the browser tab favicon (Canvas API) and Home Screen icon badge via
navigator.setAppBadge(iOS 16.4+ standalone / Android). - Platform-Specific PWA Prompts & Onboarding: Native 1-click install banner on Android (
beforeinstallprompt), top banner with animated step-by-step instructions for iOS Safari, and smart push alert onboarding banner on first standalone launch. - Interactive Radar Map: MapLibre GL JS map on
/map/with RainViewer precipitation radar animation (past + forecast frames) and layer switching. For cities in Russia and Belarus, the map link is automatically omitted because the provider (RainViewer / MeteoLab) unilaterally stopped collecting and serving radar data for these regions. - Offline Mode & Caching: Two-layer cache (in-memory
Map+ versionedlocalStoragewith LRU eviction, 8-city cap, and 1 MB budget), SWR strategy (fresh < 15 min, stale < 6 hours, offline fallback with timestamp). - Vector Icons: High-quality SVG Meteocons with dynamic day/night switching based on wall-time solar calculations (sunrise/sunset).
- Framework: SvelteKit 2 with Svelte 5 runes (
$state,$derived,$effect.root) - Language: TypeScript (strict mode)
- Bundler & PWA: Vite + @vite-pwa/sveltekit (Workbox Service Worker, offline fallback)
- Adapter:
@sveltejs/adapter-static(full static prerender of all routes withtrailingSlash: 'always') - Styling: Handcrafted CSS with CSS variables, iOS Safe Areas (
env(safe-area-inset-*)), and zero UI bloat (no Tailwind / bulky component libraries) - Weather Data, Geocoding & Air Quality: Open-Meteo API (no API keys required)
- Radar Data: RainViewer public radar API (the provider unilaterally blocked radar aggregation and maps for Russia and Belarus; the app automatically suppresses map links for unsupported regions)
- Maps: MapLibre GL JS with OpenStreetMap / CARTO basemap tiles
- Weather Icons: Meteocons (SVG, MIT License)
- App Icons: Original vector SVG + automated PNG generation via
sharp
- Node.js 22+ (see
.nvmrc) - npm 10+
# Install dependencies
npm install
# Start Vite dev server
npm run dev
# Or start with automatic browser launch
npm run dev -- --open# Typecheck TypeScript and Svelte diagnostics
npm run check
# Run Vitest test suites (37 suites, 360+ tests)
npm test
# Verify PWA icon dimensions and maskable safe-zone invariant (radius <= 0.4 * size)
npm run check:icons
# Regenerate PNG icons from SVG source (static/icons/app/icon-source.svg)
npm run icons
# Build static production bundle (outputs to build/)
npm run buildLive applications: https://gradus.website/ and https://zeklop.github.io/weather/
Both deployments are updated automatically on every push to main: one workflow builds two variants (root for the custom domain, /weather subpath for GitHub Pages) and deploys each to its target.
To build for a repository subpath deployment:
PUBLIC_BASE_PATH=/weather npm run buildAutomated CI/CD is configured in .github/workflows/deploy.yml. It runs on pushes to main or manual trigger (workflow_dispatch), validates checks and tests, generates static assets, and deploys to GitHub Pages.
Repository configuration: Settings → Pages → Build and deployment → Source: GitHub Actions.
When serving from the root of a domain (e.g. https://weather.example.com/):
PUBLIC_BASE_PATH='' npm run build
# or simply:
npm run buildSince the app is 100% static, no Node.js runtime is required on the server. A production-ready Caddyfile is included in the repository.
For autonomous background push delivery on locked devices and self-hosted analytics:
cd serverless
npx wrangler login
# Create D1 database, then paste the printed database_id into wrangler.toml
npx wrangler d1 create weather_pwa
# Execute schema migrations (on the remote database, not local)
npx wrangler d1 execute weather_pwa --remote --file=schema.sql
# Generate VAPID keypair (raw base64url format is supported as-is)
npx web-push generate-vapid-keys
# Paste the public key into wrangler.toml (PUBLIC_VAPID_KEY) and set secrets:
npx wrangler secret put VAPID_PRIVATE_KEY
npx wrangler secret put ADMIN_TOKEN # protects the /stats/ dashboard
# Deploy worker
npx wrangler deployAlso set APP_ORIGIN in wrangler.toml to the frontend origin (e.g. https://<user>.github.io) so CORS only allows the app, and keep APP_BASE_PATH in sync with PUBLIC_BASE_PATH (/weather on GitHub Pages, "" for root deploys).
Then build the frontend with PUBLIC_PUSH_WORKER_URL and PUBLIC_VAPID_KEY environment variables. If these variables are omitted, the application runs in 100% static zero-backend mode with graceful fallback.
- Open
https://gradus.website/in Safari on iPhone. - Tap the Share button (box with an upward arrow in the bottom toolbar).
- Scroll down and select «Add to Home Screen».
- Tap «Add» in the top right corner.
- Launch the app from your Home Screen — the icon is named «Gradus» — for a native standalone fullscreen experience with offline support. On iOS 16.4+ the icon also shows a live temperature badge (enable in Settings).
- Open
https://gradus.website/in Chrome on Android. - Tap the menu button (⋮) and choose “Add to Home screen” / “Install app” — or tap the install banner that appears at the top of the app.
- Confirm the installation.
- Launch the app from your Home Screen — the icon is named «Gradus» — for a native standalone fullscreen experience with offline support and a live temperature badge on the icon (badge rendering also depends on your launcher).
- Open
https://gradus.website/in Google Chrome on your Mac or Windows PC. - In the right side of the address bar, click the install icon (looks like a screen with a down arrow) or select "Install Gradus..." from the Chrome menu.
- Confirm the installation in the dialog box.
- The application will be added to your system (Launchpad on Mac, or Start Menu on Windows) and run in its own dedicated window without browser UI.
- Weather data, geocoding & air quality: Open-Meteo — CC BY 4.0.
- Weather icons: Meteocons by Bas Milius — MIT License.
- Interactive maps: MapLibre GL JS — BSD 3-Clause; basemap tiles © OpenStreetMap contributors and CARTO.
- Radar precipitation data: RainViewer (unilaterally blocked Doppler radar feeds and precipitation maps for Russia and Belarus).
- Reverse geocoding (GPS → city name): BigDataCloud free client-side API.
Detailed license information is available in THIRD_PARTY_NOTICES.md.



