# Wayfarer A black-and-gold weather station: pixel-art skies, a heart-based comfort meter, hourly and 14-day forecasts you can switch between, an animated radar map, official alerts and air quality, and a switchable data source. Built with Next.js 16 (App Router), TypeScript, Tailwind CSS v4 and Leaflet. ## Run it ```bash npm install cp .env.example .env.local # add whichever keys you have npm run dev # http://localhost:3000 ``` With no keys at all, everything except the OpenWeather map layers still works, on Open-Meteo. ## What it shows | Section | What's in it | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Alerts | Official NWS watches and warnings for US places; provider alerts elsewhere (WeatherAPI only). Each opens to show the full text. | | Hero | Temperature, conditions, today's high/low, a precipitation outlook for the next 24 h, comfort hearts. | | Conditions | Feels like, dew point, humidity, wind with direction and gusts, UV index, air quality (US AQI + PM2.5), pressure, visibility, cloud cover, precipitation, sun times, moon phase. | | Hourly | 24 h / 48 h / 7-day chart (temperature, feels like, chance of precipitation) with a crosshair readout of any hour, or a full table. | | Daily | Up to 14 days with a range bar per day. Tap a day for precipitation, wind, UV, humidity, sunrise, sunset and daylight. | | Radar and maps | Animated radar (worldwide or high-detail US), plus OpenWeather precipitation, cloud, temperature, wind and pressure maps. | A reading a provider doesn't offer is simply hidden. Nothing shows a fake zero. ## Providers and what each delivers | Provider | Env vars | Hourly | Daily | Notes | | ----------- | ---------------------------------------- | ----------------- | ----------------- | ------------------------------------------------------------------- | | Open-Meteo | none | 7 days, 1 h steps | **14 days** | Free, no key. The deepest data here. | | OpenWeather | `OPENWEATHER_API_KEY`, `OPENWEATHER_PLAN`| 5 days, 3 h steps | 5 days | `free` plan. No dew point or UV. | | | | 48 h, 1 h steps | 8+ days | `onecall` plan (One Call 4.0). Separate subscription, 1,000 calls/day free. | | WeatherAPI | `WEATHERAPI_API_KEY`, `WEATHERAPI_PLAN` | up to 3 days | 3 days (14 paid) | Includes its own alerts. | | Tomorrow.io | `TOMORROW_API_KEY` | 5 days | 6 days | Free plan: 25 calls/hour. Responses are cached for 10 minutes. | Every provider also gets these, from free sources: - **Air quality and time zone:** Open-Meteo's air-quality API (CAMS data), so the AQI scale is the same whichever provider you pick. - **US alerts:** the National Weather Service. Set `NWS_CONTACT` to an email or URL, because NWS asks apps to identify themselves. - **Moon phase:** calculated locally. ## Radar sources | Layer | Source | Coverage | Frames | Limits | | ------------- | ------------------------------ | --------- | ----------------------- | ------------------------------------------------------------------------- | | Radar | RainViewer | Worldwide | Last hour, 10-min steps | Max zoom 7, past frames only, **personal/educational use only** (since Jan 2026) | | US radar | NWS NEXRAD via Iowa State IEM | US only | Last 50 min, 5-min steps | Shared academic service: fine for personal use, not for thousands of users | | OpenWeather × 5 | OpenWeather map tiles | Worldwide | Current only | Needs `OPENWEATHER_API_KEY`; tiles are proxied by `/api/tiles` so the key stays secret | US places open on the US radar layer automatically. The basemap is CARTO's dark style; set `CARTO_API_KEY` (free at `carto.com/basemaps/apikey`) so `/api/tiles` can strip CARTO's watermark while keeping the key server-side. ## How it fits together ``` Browser Server (Next.js route handlers) Outside ─────── ─────────────────────────────── ─────── WeatherApp ── /api/providers ──▶ which keys exist + each plan's limits ── /api/geocode ──▶ lib/weather/geocode.ts ─────────────────────▶ OpenWeather / Open-Meteo ── /api/weather ──▶ in parallel: providers/ (required) ──────────▶ chosen provider extras/air-quality (optional) ──────────▶ Open-Meteo AQ extras/nws-alerts (optional) ──────────▶ api.weather.gov RadarPanel ── /api/radar ──▶ RainViewer frame index ─────────────────────▶ RainViewer ── /api/tiles/… ──▶ OpenWeather + CARTO tile proxy ─────────────▶ OpenWeather / CARTO CDN ── NEXRAD + RainViewer tiles load straight from their servers (no keys involved) ``` If an optional source fails, its section just disappears; only a failed forecast shows an error. - `lib/weather/types.ts`: the one normalized shape. Units are metric, times are UTC, and missing readings are `null`. - `lib/weather/providers/`: one adapter per provider. OpenWeather has a folder with `free.ts` and `onecall.ts`. - `lib/weather/extras/`: air quality and NWS alerts. - `lib/radar/layers.ts`: every map layer and how to load its frames. - `lib/display.ts`: unit conversion, time-zone-aware formatting, UV/AQI labels, moon phase, precipitation outlook. - `components/forecast/`: `ForecastPanel` (the switch), `HourlyView` + `HourlyChart`, `DailyView`. - `components/radar/`: `RadarPanel` (controls) and `RadarMap` (Leaflet, browser-only). - `components/ui/ChoiceChips.tsx`: the chip group used for providers, views, ranges and map layers. ### Adding a provider 1. Write `lib/weather/providers/foo.ts` exporting a `WeatherProvider`, with `getForecast()` and `limits()`. 2. Add `"foo"` to `ProviderId` in `types.ts`. 3. Register it in `providers/index.ts`, then add its key to `.env.example`. ## Styling (Tailwind v4) There is no `tailwind.config.js`; v4 is configured in CSS. - `app/globals.css` holds the theme tokens in `@theme`. Colors like `--color-gold` become `bg-gold`, `text-gold` and so on, and the fonts become `font-display`, `font-pixel` and `font-sans`. It also holds one custom utility, `chamfer` (the cut corners), Leaflet control overrides, and a few base rules. - Every other style is a utility class in the component that uses it. Layout is mobile-first, with `sm:` (640px and up) for the desktop arrangement. - `lib/styles.ts` holds the class strings used by more than one component. - To resize a frame's corners, add `[--cut:22px]` to that `Slot`'s `className`. Put fixed heights on the `Slot` itself, not its inner element, which is already `h-full`. - The Leaflet overrides sit outside any `@layer` and are prefixed with `.leaflet-container`. Leaflet's own CSS is unlayered and loads later, so anything weaker loses. - The hourly chart is hand-written SVG with no chart library. Temperature and chance of precipitation sit in separate bands on one time axis rather than sharing two y-axes. ## Mobile - Uses `viewport-fit=cover` with safe-area insets, 44px tap targets and 16px inputs (so iOS doesn't zoom on focus). - Chips, the hourly chart and the forecast strip scroll sideways; the page itself never does. - The map ignores the scroll wheel, so scrolling the page doesn't zoom it; use pinch or the +/− buttons. - "Use my location" requires HTTPS on phones. To test on your Android over LAN, use a Vercel preview deploy. - Radar autoplay and the floating icon stop when the OS asks for reduced motion. ## Deploy & Legal Compliance - **Vercel**: import the repo and add your environment variables (`OPENWEATHER_API_KEY`, `CARTO_API_KEY`, `NWS_CONTACT`, plus any optional provider keys) in Project Settings. - **PM2**: `npm run build && pm2 start npm --name wayfarer -- start`. - **CARTO domain lock**: once deployed, restrict `CARTO_API_KEY` to your production domain at `dashboard.basemaps.carto.com`. All `/api/*` routes also reject cross-site `Sec-Fetch-Site: cross-site` hotlinking automatically. - **Built-in attributions**: `SiteFooter`, `PlaceSearch`, `AlertsBanner`, and `RadarMap` render the required live backlinks and license credits for OpenWeather (`CC BY-SA 4.0` / `ODbL`), Open-Meteo (`CC BY 4.0`), Copernicus Atmosphere Monitoring Service (`CAMS`), GeoNames (`CC BY 4.0`), WeatherAPI.com, Tomorrow.io, OpenStreetMap, CARTO, RainViewer, Iowa State IEM, and NOAA/NWS. - **Non-commercial vs. commercial publishing**: - **Free, ad-free personal / portfolio site**: compliant across every source out of the box. - **Commercial site (ads, paid tiers, or business use)**: OpenWeather, WeatherAPI.com, NWS, and CARTO (`CARTO_API_KEY` includes 1M commercial tiles/month) permit commercial use within plan quotas, whereas **Open-Meteo** (forecast, geocoding, air quality), **RainViewer**, and **Tomorrow.io** free tiers are **non-commercial only** and require paid commercial plans or disabling before monetizing.