
Could a Web App Feel Native?
PWAs had matured enough by 2022 that the gap between a web app and a native app was mostly perception. Offline support, home screen install, smooth animations — it was all there. I wanted to test whether you could build a weather app that someone would install on their phone and not know it wasn't from the App Store.
The visual reference was iOS Weather. Not to copy it, but to understand why it works — the way condition is communicated through color and motion, not just a text label. Rain looks like rain. Sun looks warm.
Three Things That Had to Work
OpenWeatherMap for the forecast data, the browser Geolocation API so it finds your location on first load, and a Service Worker so the last fetched forecast is available offline. Those three pieces had to work before anything else got built.
Redux handled the async data flow — fetching, loading states, error states, the current city, unit preferences. Each day of the 7-day forecast is its own slice of state, which kept the rendering logic straightforward.
Current + Forecast
The layout is two things: a large card for today with temperature, condition, feels-like, and humidity, then a horizontal scroll strip of the next seven days. City search and unit toggle live above the hero card.
Current temp, location, condition with an animated weather icon, feels-like and humidity
Horizontal scroll of 7 day cards — day name, condition icon, high/low
City search with recent searches from localStorage
C/F toggle, stored in preferences, updates all values immediately
Custom install prompt on first visit. Service worker caches the last successful fetch.
Under the Hood
Thunk async action creators call the OpenWeatherMap API. Loading and error states live in the weather slice alongside the forecast data.
Requests location on load. Falls back gracefully when permission is denied — defaults to a city, doesn't break.
Caches the last successful API response. Stale-while-revalidate — you get the cached forecast instantly, then the fresh one loads in behind it.
Weather condition animations (rain, sun, clouds) built entirely in CSS keyframes. No canvas, no JS animation library.
What Made It Hard
- OpenWeatherMap's free tier rate-limits hard. Had to add request deduplication so rapid re-renders from Redux state changes didn't fire duplicate API calls.
- Geolocation permission UX is inconsistent across browsers. Chrome, Firefox, Safari on iOS, Android WebView — each handles the prompt and the denied state differently. Tested all of them manually to get consistent fallback behavior.
- Service worker cache versioning needs to be deliberate. An old cached response with yesterday's weather is worse than no response at all. Settled on a cache key that includes the date so stale forecasts get evicted automatically.