A sophisticated, data-driven weather application demonstrating advanced vanilla JavaScript architecture with strict Model-View-Controller separation of concerns, procedural celestial geometry, and dynamic UI context binding.
Live Demo β’ Documentation β’ Contributing
The Day & Night Weather Dashboard is a real-time weather forecasting application that transcends traditional weather data visualization by introducing contextual UI generation and procedural astronomical tracking. Built entirely in Vanilla JavaScript without frameworks, the application demonstrates enterprise-grade architectural patterns while maintaining a minimal, composable codebase.
This is not merely a weather displayβit's an intelligent information system that:
- Transforms raw meteorological data into UI-ready models via a centralized view model
- Procedurally renders celestial motion using SVG coordinate geometry and real-time astronomical calculations
- Dynamically adapts visual context (backgrounds, map styles, typography) based on solar cycles and atmospheric conditions
- Manages complex state through a centralized controller with zero global mutation
- Maintains architectural purity via strict MVC boundaries and chart-level Model-Renderer-Config isolation
The application adheres to three foundational architectural principles:
- Separation of Concerns (SoC): Each module has a single, well-defined responsibility with minimal cross-module coupling.
- Unidirectional Data Flow: Data flows strictly from Models β Views β Controller event handlers, preventing circular dependencies.
- Immutable Configuration: API keys, chart dimensions, and UI thresholds are frozen constants to prevent accidental runtime mutations.
src/
βββ index.html # Application shell (semantic HTML5)
βββ index.js # Entry point
β
βββ api/ # External API integration layer
β βββ weather.js # Visual Crossing weather API client
β βββ dynamicBackground.js # Unsplash image fetching
β βββ geolocation.js # Browser Geolocation API wrapper
β βββ map.js # Geoapify static map URL builder
β
βββ models/ # Data transformation layer (MVC Models)
β βββ weatherViewModel.js # Raw API β UI-ready data structure
β
βββ components/ # UI rendering layer (MVC Views)
β βββ index.js # Component export barrel
β βββ header.js # Header date display
β βββ sidebar.js # Weather metrics + air quality
β βββ weatherHero.js # Main weather display
β βββ insights.js # Wind stats + astronomy toggle
β βββ nextdaysForecast.js # 5-day forecast cards
β
βββ charts/ # Specialized visualization modules
β βββ windChart/
β β βββ config.js # Chart dimensions & scaling
β β βββ windChartModel.js # Hourly wind β SVG coordinates
β β βββ windChartRenderer.js # SVG histogram + trendline
β β
β βββ forecastChart/
β β βββ config.js
β β βββ forecastChartModel.js # Temperature data β coordinates
β β βββ forecastChartRenderer.js # Quadratic bezier trendlines
β β
β βββ astroObjectPosition/
β βββ computePosition.js # Sun/moon trajectory computation
β βββ renderAstroObject.js # SVG circle positioning + phase
β βββ index.js # Orchestration entry point
β
βββ ui/ # Application controller (MVC Controller)
β βββ controller.js # Event binding + data pipeline orchestration
β
βββ utils/ # Pure utility functions
β βββ weatherUnit.js # Unit system management (metric/US/UK)
β βββ weatherLevels.js # Metric β interpretation mapping
β βββ time-fns.js # Timezone-aware time utilities + "vibe" engine
β βββ customDropDown.js # Accessible dropdown component
β βββ capitalizeFirstLetter.js # String utility
β
βββ styles/ # Component-scoped and utility CSS
βββ main.css # Entry point (import order = cascade priority)
βββ base.css # Global reset + typography
βββ layout.css # App grid layout
βββ components/ # Component-specific styles
β βββ header.css
β βββ sidebar.css
β βββ weatherHero.css
β βββ insights.css
β βββ nextdaysForecast.css
βββ utilities/ # Reusable utility classes
βββ glass-panel.css # Glassmorphism effect
βββ customDropdown.css # Dropdown styling
βββ toggleSwitch.css # Sun/moon mode toggle
βββ accessibility.css # Screen reader utilitiesThe Model acts as the "brain" of the application, transforming raw Visual Crossing API responses into structured, component-aware objects.
Responsibilities:
- Normalize nested API response into flat, UI-optimized structure
- Extract component-specific data subsets (header, sidebar, weatherHero, insights)
- Compute derived values (e.g., centered 39-hour window from hourly data)
- Generate simplified forecast objects for card rendering
Key Pattern:
// Raw API response β component-indexed model
processWeatherData(rawAPIData) β {
conditions,
header: { date },
sidebar: { humidity, uvIndex, ... },
weatherHero: { temperature, location, ... },
insights: { centered39hWindow, astronomy { sun, moon } },
nextFiveDays: [ { dayName, highTemp, icon } ]
}Benefits:
- Single source of truth for data transformation
- Components consume only their required data slice
- API schema changes isolated to one module
- Easy to extend with new data fields
Views are stateless, pure rendering functions that accept processed models and update DOM.
Component Structure:
renderXXX(processedData) β DOM mutations onlyChart Structure (Model-Renderer-Config Pattern):
config.js β Immutable dimensions, thresholds
chartModel.js β Raw data β SVG coordinates
chartRenderer.js β Coordinates β DOM SVG elements
index.js β Orchestrate model + rendererExample: Wind Chart
config.js: SVG height=70px, visualMax=30m/s, barCap=0.7windChartModel.js: Convert hourly wind speeds to bar heights and gust line positionswindChartRenderer.js: Create SVG<rect>elements and<polyline>trendline- Result: Composable, testable, dimension-agnostic visualization
The Controller orchestrates the complete data pipeline: events β models β views.
Orchestration Flow:
User Input (form/button)
β
bindFormEvents() / bindLocationButton()
β
getWeather(location, unitSystem) [API call]
β
processWeatherData() [Model transformation]
β
updateBackgroundImage() [Dynamic context]
β
renderAll() [Component rendering]
βββ renderHeader()
βββ renderSideBar()
βββ renderWeatherHero()
βββ renderInsights()
βββ renderNextDaysForecast()Key Patterns:
- Lazy DOM node caching via IIFE pattern to avoid null reference errors
- Single
isInitializedguard prevents event listener duplication - Centralized state management (currentProcessedData, currentTimeVibe)
- Promise chaining for sequential API β transform β render pipeline
The application renders the sun and moon trajectories in real-time using procedural geometry rather than pre-computed sprites.
How It Works:
- SVG Arc Definition: An SVG
<path>element defines an elliptical arc representing the celestial object's path above the horizon. - Time-Based Progress: Current time (in seconds since midnight) is normalized to a 0β1 progress value between rise and set times.
- Coordinate Extraction:
track.getPointAtLength()returns the (x, y) position at the specified progress along the arc. - Dynamic Positioning: The circle element (
<circle>) is continuously updated with new coordinates as time advances.
Mathematical Foundation:
// Normalized progress: 0 = rise, 1 = set
progress = (currentTimeInSeconds - riseTimeInSeconds) / (setTimeInSeconds - riseTimeInSeconds)
// Arc traversal: SVG path length β screen coordinates
[x, y] = track.getPointAtLength(progress * totalArcLength)
// Result: Sun/moon appears to move along the arc in real-timeEdge Case Handling:
- Midnight Crossing: Objects that rise before midnight and set after correctly compute elapsed time across the day boundary.
- Polar Regions: Handles scenarios where celestial objects never set or never rise (constant visibility).
The application renders real-time lunar phase visualization using a calculated offset shadow.
Implementation:
- Phase-Based Offset: Moon phase (0 = new, 0.5 = full, 1 = new) is mapped to a horizontal shadow offset.
- Crescent Effect: A dark SVG circle (
moon-shroud) slides across the visible moon circle to create crescent, gibbous, and full phase appearances. - Offset Calculation:
const radius = 6; // Moon circle radius offset = (phase - 0.5) * (radius * 4) // phase 0 β offset = -12 (full dark) // phase 0.5 β offset = 0 (no shadow, full moon) // phase 1 β offset = +12 (full light)
Visual Result:
- New Moon: Shroud completely covers the moon
- Crescent: Shroud partially offset, creating thin crescent
- Full Moon: Shroud hidden (offset = 0), moon fully visible
- Gibbous: Shroud progressively offset, showing intermediate phases
The application dynamically adapts its visual context (backgrounds, map styles, text hierarchy) based on the time of day relative to solar cycles.
"Vibe" Categories:
| Vibe | Time Window | Background Theme | Map Style | UI Intent |
|---|---|---|---|---|
night moody |
Before dawn OR after dusk (+30min buffer) | Dark, stormy | dark-matter | Calm, reduced contrast |
sunrise dawn |
30min before to 1hr after sunrise | Golden, warm | klokantech-basic | Awakening energy |
morning light |
1hr after sunrise to noon | Bright, clear | osm-bright | Vibrant, optimistic |
afternoon sun |
Noon to 1hr before sunset | Vivid, saturated | osm-bright | Peak energy |
sunset golden hour |
1hr before to 30min after sunset | Golden, warm | dark-matter | Romantic, reflective |
Implementation:
// Solar cycle calculation
getTimeVibe(timezone, sunriseTime, sunsetTime) β vibe string
// Context binding
currentTimeVibe β Unsplash image search query
currentTimeVibe β Geoapify map style selection
currentTimeVibe β CSS variable values (future extensibility)Real-World Impact:
- A weather query at 6:30 AM automatically fetches "sunrise dawn" imagery and applies warm map styling.
- The same location at 3 PM renders "afternoon sun" context with bright map styles and saturated backgrounds.
- User never explicitly selects contextβit's derived from solar physics.
The application dynamically switches between three standardized unit systems without requiring page reload.
Supported Systems:
- Metric: Β°C, km/h, km
- US: Β°F, mph, miles
- UK: Β°C, mph, miles
Implementation:
- Centralized
weatherUnit.jsmanages active unit system setUnitSystem(unitSystem)updates global state before data fetch- All chart renderers query
formatUnit(measurement)at render time - Wind speed models apply unit-specific conversion factors
Real-time pollutant concentration display with color-coded risk levels.
Monitored Pollutants:
- PMβ.β : Fine particulate matter (Β΅g/mΒ³)
- PMββ: Coarse particulate matter (Β΅g/mΒ³)
- Oβ: Ozone (ppb)
- NOβ: Nitrogen dioxide (ppb)
AQI Categories:
- 0β50: Good (green)
- 51β100: Moderate (yellow)
- 101β150: Unhealthy (Sensitive Groups) (orange)
- 151β200: Unhealthy (red)
- 201β300: Very Unhealthy (purple)
- 300+: Hazardous (maroon)
39-Hour Centered Window: Wind chart displays hourly data centered on the current hour (19.5 hours past + current + 19 hours future).
Temperature Trendlines: Forecast chart renders high/low temperature curves using quadratic BΓ©zier splines for smooth interpolation between daily points.
The application implements a "Smart Start" sequence to ensure the user is never met with a blank state:
- Primary Action: Attempts to utilize the Browser Geolocation API to provide hyper-local weather immediately upon arrival.
- Graceful Fallback: If location access is denied or unavailable, the Controller automatically initializes with a high-context default city (e.g., Dubai).
The application implements a dedicated Loading Lifecycle to bridge the gap between user intent and API resolution.
-
UX Consistency: Employs a custom-animated "Thematic Pulse" overlay that maintains visual continuity with the application's astronomical theme while preventing "stale data" confusion.
-
Performance Testing: Engineered to maintain UI responsiveness even under significant network latency, verified using Chrome DevTools' Slow 3G throttling.
-
Fail-Safe Logic: Utilizes try...catch...finally blocks within the Controller to ensure the UI state resets correctly even if API requests are interrupted or exhausted.
- Vanilla ES6+: No frameworks; pure JavaScript modules with native
import/export - Native Web APIs: Fetch, Geolocation, Date (date-fns for timezone support)
- CSS3: Grid, Flexbox, Custom Properties, Glassmorphism backdrop filter
- Component Architecture: BEM-inspired naming with scoped component stylesheets
- Intrinsically Responsive Design: Utilizes CSS Grid and Flexbox to maintain layout integrity across varying viewport widths without heavy reliance on fixed breakpoints.
- Webpack 5: Module bundling, asset management, dev server
- Babel: (implicit via webpack) for ES6 transpilation
- ESLint + Prettier: Code quality and formatting enforcement
- Husky + lint-staged: Pre-commit hooks for linting/formatting
| Service | Purpose | Rate Limit | Documentation |
|---|---|---|---|
| Visual Crossing | Weather & astronomy data | 40 queries/day | Weather API Documentation |
| Unsplash | Dynamic background imagery | 50 reqs/hour | Unsplash Developers |
| Geoapify | Static map rendering | 3k reqs/day | Geoapify APIs Documentation |
| Browser Geolocation | User coordinates | Built-in | MDN |
Note: Rate limits are based on the current API configuration used in this project (development/free-tier credentials). If you use your own API keys or different subscription tiers, these limits may differ.
| Library | Purpose | Size |
|---|---|---|
| date-fns | Timezone-aware date manipulation | ~13KB (modular) |
| @fontsource | Self-hosted typography (Inter, Plus Jakarta Sans) | ~50KB total |
- Node.js 16+ and npm 8+
- Environment Variables: Create
.envin project rootWEATHER_API_KEY=your_visual_crossing_api_key UNSPLASH_KEY=your_unsplash_access_key GEOAPIFY_KEY=your_geoapify_api_key
git clone https://github.com/nishadnp/weather-app.git
cd weather-app
npm installnpm run dev
# Opens http://localhost:8080 with hot reloadnpm run build
# Outputs optimized bundle to dist/npm run lint # Run ESLint
npm run lint:fix # Fix linting issues
npm run format # Run Prettierβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β USER INTERACTION β
β (Search form / Current location button) β
ββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββ
β UI CONTROLLER β
β ui/controller.js β
β β’ Event binding β
β β’ Unit system selection β
ββββββββββββββ¬ββββββββββββββββ
β
ββββββββββββββββββββββββββββββ
β API INTEGRATION LAYER β
β api/weather.js β
β β’ Fetch raw API data β
β β’ Handle errors β
ββββββββββββββ¬ββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββββββ
β DATA TRANSFORMATION (MODEL) β
β models/weatherViewModel.js β
β β’ Normalize nested structures β
β β’ Extract component-specific data β
β β’ Compute derived values β
ββββββββββββββ¬ββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CONTEXT DERIVATION β
β β’ Time vibe calculation β
β β’ Background image fetch β
β β’ Map style selection β
ββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββ
β VIEW RENDERING LAYER β
β βββ Header (date display) β
β βββ Sidebar (metrics + AQI) β
β βββ Weather Hero (conditions) β
β βββ Insights (wind + astro) β
β βββ Next 5 Days Forecast (cards + chart) β
β β
β CHART SUBSYSTEMS: β
β βββ Wind Chart (bars + trendline) β
β βββ Forecast Chart (bezier curves) β
β βββ Astro Chart (arc + shadows) β
ββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββ
β RENDERED DOM β
β (User sees result) β
ββββββββββββββββββββββββββ- Each module has one reason to change
weatherViewModel.jschanges only if API schema changeswindChartModel.jschanges only if chart math changes- Components change only if DOM structure changes
- Data flows: Controller β Model β Views
- Views never modify Model; only Controller does
- No circular dependencies or mutual modification
- Easier to trace data mutations for debugging
- Chart dimensions live in
config.jsfiles - Thresholds for level mapping in
weatherLevels.js - API endpoints frozen in constants
- Non-developers can adjust UI without touching logic
- Utility functions are pure, stateless
- Components are small, focused, reusable
- Charts are self-contained with model/renderer/config
- Easy to add new features without modifying existing code
- Static HTML provides baseline functionality (no JS)
- CSS handles layout and basic styling
- JavaScript layers interactivity and real-time updates
- Graceful degradation if APIs fail
- Add field to Visual Crossing API request
- Normalize in
models/weatherViewModel.js - Create new component:
components/newMetric.js - Call renderer in
controller.js'srenderAll()function
- Create folder:
src/charts/newChart/ - Implement:
config.js,newChartModel.js,newChartRenderer.js,index.js - Import and orchestrate in relevant component
- Chart is instantly testable, reusable, dimension-agnostic
- Add entry to
weatherUnit.jsUNIT_MAP - Update conversion factors in relevant chart models
- UI automatically adapts on dropdown selection
- Lazy DOM Queries: Nodes cached on first access via
??=operator - Event Delegation: Single listener on
documentfor dropdowns - Efficient Rerenders: Only modified data sections trigger DOM updates
- SVG Optimization: Chart paths computed once, reused for scaling
- Async Image Loading: Background images fetch without blocking UI
- Minimal Bundle: No frameworks; ~100KB minified + gzipped
- Chrome/Edge: 90+
- Firefox: 88+
- Safari: 14+
- Mobile: iOS Safari 14+, Chrome Android
(Requires ES6 support: arrow functions, destructuring, template literals, fetch API)
src/
βββ api/ β External API clients (no DOM access)
βββ models/ β Pure data transformations (no side effects)
βββ components/ β View functions (DOM mutations only)
βββ charts/ β Visualization subsystems (MRC pattern)
βββ ui/ β Application controller (event orchestration)
βββ utils/ β Pure utility functions (reusable across modules)
βββ styles/ β Component + utility CSS- Naming: camelCase for variables/functions, BEM for CSS classes
- Comments: JSDoc for public APIs; inline for complex logic
- Modules: One default export per file; named exports for utilities
- Immutability: Use
Object.freeze()for config; avoid direct mutation
Currently no test framework, but architecture enables easy testing:
// Model is pure function β easy to unit test
processWeatherData(mockAPIResponse) β assert output structure
// Chart model is isolated β test without rendering
getWindChartModel(mockHours, config) β assert coordinate precision
// Chart renderer is predictable β mock DOM and assert SVG attributes- Real-Time Updates: Application doesn't auto-refresh; requires manual location re-entry
- Offline Mode: No service worker; requires internet connectivity
- Historical Data: Only current forecast; no historical data archive
- Accessibility: Charts are primarily visual; however, core weather status and loading states utilize ARIA live regions and roles for screen reader compatibility.
- Mobile: Responsive design present but not fully optimized for touch interactions
-
The application is architected for modern cloud environments and is currently deployed via Vercel.
-
Automated Pipeline: Integrated with GitHub to trigger automated builds and deployments on every git push.
-
Environment Security: API keys (Visual Crossing, Unsplash, Geoapify) are managed through Vercel Environment Variables, ensuring sensitive credentials are never exposed in the client-side source code or version control.
-
Production Optimization: Leverages Vercelβs edge network for fast asset delivery and optimized performance of the bundled Webpack output.
See LICENSE file for details.
-
Weather Icons: Created by Dr. Lex (Alexander Thomas), based on the original Dark Sky icon set. Used under Creative Commons - Attribution license. (Source: dr-lex.be).
-
Astronomy Icons:
-
Sunrise/Sunset: Sourced from Bootstrap Icons.
-
Moonrise/Moonset: Sourced from Carbon Design System (Licensed under Apache 2.0).
-
-
General UI Icons: Curated from Google Material Symbols for high-density dashboard clarity.
-
Typography: Inter and Plus Jakarta Sans self-hosted via Fontsource.
UI Reference: Based on the Weathrly Weather Forecasting Web Design on Dribbble.
Technical Execution: The static high-fidelity mockup was translated into a functional, state-driven dashboard. The implementation prioritizes intrinsic responsiveness using CSS Grid and Flexbox, custom glassmorphism effects, and real-time SVG coordinate geometry for celestial tracking.
- Author: nishadnp
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Built with βοΈ and π by nishadnp
A celebration of Vanilla JavaScript, MVC architecture, and the poetry of procedural geometry.