Premium Isla Vista sunset forecast experience built with Next.js, TypeScript, Tailwind, Framer Motion, Lenis, and a custom illustrated shoreline hero.
Live site: https://iv-sunsets.vercel.app
IV Sunsets answers a simple question:
Is tonight's Isla Vista sunset worth going out for?
Instead of a generic weather dashboard, the app presents the forecast as a cinematic product-style experience with:
- a full-screen hero for tonight's outlook
- a messenger gull animation that loops across the shoreline
- a "Should I go?" countdown section
- a 6-day sunset forecast
- an interactive sunset lab
- curated viewing spots
- a lightweight local pulse feed paired with the hero
- Production URL: https://iv-sunsets.vercel.app
- Forecast API route: https://iv-sunsets.vercel.app/api/forecast
- Forced fallback test route: https://iv-sunsets.vercel.app/api/forecast?forceFallback=1
The homepage is a scroll / swipe driven carousel with five sections:
Tonight's sunsetShould I go?6-day outlookSunset labWhere to watch
The current hero uses:
- a custom SVG shoreline illustration
- a dominant opaque foreground bluff on the left
- a distant right coastline / headland
- a restrained ocean reflection system
- a messenger gull that dives, lifts a local note, and returns to its perch
Only atmosphere is translucent:
- sky bloom
- marine haze
- sun glow
- softened water reflections
The land itself is intentionally rendered as solid mass.
The app defaults to Isla Vista, California:
- Latitude:
34.4133 - Longitude:
-119.861 - Timezone:
America/Los_Angeles
The forecast shows today plus the next 5 days.
Weather data is fetched from:
Local pulse content is fetched separately in the server route and attached to the forecast payload.
No API keys are required.
The scoring model is heuristic, opinionated, and tuned for coastal California rather than claiming meteorological precision.
Hourly inputs used in the scoring window:
cloud_covercloud_cover_lowcloud_cover_midcloud_cover_highprecipitationvisibilitytemperature_2mdew_point_2mwind_speed_10mwind_direction_10mpm2_5aerosol_optical_depth
Daily inputs used:
sunrisesunsetprecipitation_sum
- Sunset window:
2 hours before sunsetto30 minutes after sunset - Rain lookback: prior
6 to 24 hours
Each day returns:
- score
0–100 - label
- explanation
- reason chips
- factor breakdown
- normalized sunset window snapshot
- preview values for mini visual rendering
0–24→Poor25–44→Decent45–64→Good65–84→Great85–100→Unreal
Implemented in lib/scoreSunset.ts and pinned by an
executable spec (npm run sanity → scripts/score-sanity.ts).
The score multiplies a cloud canvas by a light path instead of adding bonuses:
- Cloud canvas — high cirrus and mid altocumulus, the surfaces that light up (worth up to ~68 points)
- Light path — multiplicative gates that can each collapse the night: local marine layer, offshore low cloud at a probe point ~30 km west of IV on the sunset light path, overcast ceiling, and fog saturation
- Vividness — air clarity multiplier from visibility, PM2.5, aerosol optical depth, and humidity (moderate aerosol deepens color)
- Bonuses — post-rain clearing, offshore (sundowner) flow, forecast stability
- Stabilizers — persistence pull toward yesterday's score (computed from actual past hourly data) and a blend toward the IV climatological mean for low-confidence far-out days
A clean clear sky floors out around 35 — pleasant, not fireworks. A solid marine layer collapses the score no matter how good the upper sky looks.
Forecast data is served from app/api/forecast/route.ts.
Behavior:
- primary source is Open-Meteo
- air-quality data and an offshore low-cloud probe (~30 km west of IV) are fetched in parallel
- news feed fetches carry a hard 4s timeout so a slow host can never stall the forecast
- local pulse data is attached to the payload
- API responses are normalized into a shared frontend shape
- fallback forecast data is returned if upstream fetches fail
forceFallback=1can be used to test fallback mode explicitly
Client fetching is handled in lib/fetchForecast.ts.
- Next.js 16 App Router
- React 19
- TypeScript
- Tailwind CSS 4
- Framer Motion
- Lenis
- Lucide React
- Leaflet for map-based sunset spots
d3-shapefor the current shoreline SVG path generation- Playwright for smoke tests
app/
api/forecast/route.ts Server forecast endpoint
how-it-works/page.tsx Secondary explainer page
page.tsx Main homepage carousel
components/
CircularCarousel.tsx Main section navigation shell
ForecastCard.tsx Single forecast day card
ForecastGrid.tsx 6-day forecast section
HeroShoreline.tsx SVG shoreline illustration for the hero
HeroSky.tsx Shared hero visual utility
Navbar.tsx Top-level navigation
ScoreBreakdown.tsx Factor explanation UI
ScrollyExplainer.tsx Scroll narrative section
SkyBirds.tsx Messenger gull + background bird animation
SkyCanvas.tsx Canvas sky rendering
SmoothScrollProvider.tsx Smooth scroll wrapper
SpotlightFeature.tsx Supporting spotlight UI
SunsetCountdown.tsx "Should I go?" section
SunsetMap.tsx Map rendering for spots
SunsetSimulator.tsx Interactive simulator / lab
SunsetSpots.tsx Viewing spots section
TonightCard.tsx Main hero card and shoreline composition
lib/
fetchForecast.ts Client fetch wrapper
localPulse.ts Local pulse fetch / shaping
normalizeForecast.ts Open-Meteo normalization + fallback data
scoreSunset.ts Sunset scoring heuristic
types.ts Shared payload and domain types
utils.ts General helpers
scripts/
smoke-test.mjs Playwright smoke coverage
Install dependencies:
npm installStart development:
npm run devNext.js will print the local development URL in your terminal.
npm run dev
npm run build
npm run lint
npm run typecheck
npm run smokeCurrent verification flow:
npm run lint
npm run typecheck
npm run build
npm run smokenpm run smoke checks the main flows:
- homepage loads
- desktop viewport renders
- mobile viewport renders
- reduced motion mode works
- messenger gull loop appears
- carousel navigation works
- no obvious runtime console errors appear
- The forecast model is intentionally heuristic.
- The visual design has been iterated heavily toward a premium editorial coastal illustration rather than a dashboard.
- The current production deployment is Vercel-backed and GitHub-connected.