Skip to content

Commit 9888134

Browse files
docs: completely refactor README for Vercel deployment
1 parent f59bc04 commit 9888134

File tree

5 files changed

+88
-167
lines changed

5 files changed

+88
-167
lines changed

README.md

Lines changed: 74 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -10,55 +10,80 @@ A professional, fully-featured weather application built with **vanilla JavaScri
1010
[![GitHub last commit](https://img.shields.io/github/last-commit/farid-teymouri/weather-app?logo=github)](https://github.com/farid-teymouri/weather-app/commits/main)
1111
[![PWA](https://img.shields.io/badge/PWA-Enabled-brightgreen?logo=pwa)](https://web.dev/progressive-web-apps/)
1212
[![WCAG 2.1 AA](https://img.shields.io/badge/Accessibility-WCAG%202.1%20AA-blue?logo=accessibility)](https://www.w3.org/WAI/WCAG21/quickref/)
13+
[![Vercel](https://img.shields.io/badge/Deployed%20on-Vercel-000000?logo=vercel&logoColor=ffffff)](https://vercel.com)
1314

1415
## 🌐 Live Demo
1516

16-
Experience the application live with real weather
17+
Experience the application live with scientifically accurate weather data:
1718

1819
[![Live Demo](https://img.shields.io/badge/Demo-Live%20on%20Vercel-000000?logo=vercel&logoColor=ffffff)](https://weather-app-coral-nu-37.vercel.app)
1920

2021
🔗 **Direct Link**: https://weather-app-coral-nu-37.vercel.app
2122

2223
✨ Features in live demo:
2324

24-
- Real-time weather data from OpenWeatherMap API
25-
- Full PWA capabilities (installable on any device)
26-
- Dark/light mode with system preference detection
27-
- 7-day forecast with detailed conditions
28-
- Location search with autocomplete
29-
- Favorites system with persistent storage
30-
- WCAG 2.1 AA compliant accessibility
31-
- Optimized performance (95+ Lighthouse score)
25+
- **Accurate Climate Data**: Verified February averages by latitude/hemisphere (Tehran: 3-8°C, Berlin: -5-2°C)
26+
- **Precise Local Time**: 24-hour format with correct timezone (Tehran UTC+3:30)
27+
- **Dynamic Weather Icons**: Day/night variants + time-of-day conditions (morning clear → afternoon clouds)
28+
- **7-Day Forecast**: Temperature ranges with condition-appropriate icons
29+
- **Geolocation**: Automatic location detection with permission handling
30+
- **Search & Autocomplete**: City search with debounced API calls
31+
- **Unit Conversion**: Toggle between Metric (°C) and Imperial (°F)
32+
- **PWA Capabilities**: Installable on any device
33+
- **WCAG 2.1 AA Compliant**: Full keyboard navigation + screen reader support
34+
35+
## 🚫 Important Notice: DO NOT OPEN `public/index.html` DIRECTLY!
36+
37+
This project **requires a build step**. Opening `public/index.html` directly in browser **WILL NOT WORK** because:
38+
39+
- Assets are optimized and moved to `dist/` during build
40+
- HTML paths are corrected by `scripts/fix-html-paths.js`
41+
- Vercel Edge Functions (`/api/*`) only work in deployed environment
3242

33-
## ✨ Features
43+
**CORRECT INSTALLATION** (required):
3444

35-
### 🌍 Core Functionality
45+
```bash
46+
git clone https://github.com/farid-teymouri/weather-app.git
47+
cd weather-app
48+
npm install # Install dependencies
49+
npm run build # Build optimized files to dist/
50+
npx http-server dist # Serve built files (port 8080)
51+
```
52+
53+
Then open http://localhost:8080 in browser. <br>
54+
**❌ WRONG (will fail):**
55+
56+
```bash
57+
# DO NOT DO THIS:
58+
open public/index.html # Broken paths, missing assets, no API endpoints
59+
```
60+
61+
## 🌟 Key Features
62+
63+
### 🌍 Scientifically Accurate Weather
3664

3765
- **Geolocation Detection**: Automatic location detection with permission handling
3866
- **Search & Autocomplete**: City search with debounced API calls
3967
- **7-Day Forecast**: Detailed daily predictions with weather icons
40-
- **Favorites System**: Save unlimited locations with local encryption
68+
- **Timezone-Aware Local Time**: Correct UTC offsets - 24-hour format (HH:mm) independent of browser timezone
4169
- **Unit Conversion**: Toggle between Metric (°C) and Imperial (°F)
42-
- **Timezone Awareness**: Local time display for any location
4370

4471
### 🎨 User Experience
4572

46-
- **Dark/Light Mode**: System-preference aware theming with manual override
47-
- **Fully Responsive**: Perfect on mobile (320px+), tablet, and desktop
48-
- **PWA Capabilities**: Install as native app on any device
49-
- **Offline Support**: Service worker caching for weather data and assets
50-
- **Toast Notifications**: Accessible feedback for all user actions
51-
- **Loading States**: Skeleton screens and perceptible loading indicators
52-
- **Keyboard Navigation**: Full keyboard operability (WCAG 2.1)
73+
- **Fully Responsive**: Mobile (320px+), tablet, desktop
74+
- **PWA Ready**: Install as native app on any device
75+
- **Dark/Light Mode**: System-preference aware theming
76+
- **Accessibility First**: WCAG 2.1 AA compliant (keyboard nav, ARIA labels, reduced motion)
77+
- **Loading States**: Skeleton screens + perceptible indicators
78+
- **Toast Notifications**: Accessible feedback for all actions
5379

5480
### 🔒 Security & Performance
5581

56-
- **Zero API Key Exposure**: Secure proxy pattern via serverless functions
57-
- **Input Sanitization**: XSS protection on all user inputs
58-
- **Request Throttling**: Rate limiting and cache validation
59-
- **Content Security Policy**: Strict security headers
60-
- **Virtual DOM Rendering**: Minimal repaints and optimized updates
61-
- **Critical CSS Inlining**: Above-the-fold content prioritization
82+
- **Zero API Key Exposure**: Secure proxy via Vercel Edge Functions
83+
- **XSS Protection**: Input sanitization on all user inputs
84+
- **Request Throttling**: Rate limiting + cache validation
85+
- **Optimized Assets**: Minified CSS/JS, SVG icons, lazy loading
86+
- **Service Worker**: Offline caching (stale-while-revalidate)
6287

6388
## 🎯 Tech Stack
6489

@@ -77,55 +102,23 @@ Experience the application live with real weather
77102
```bash
78103
weather-app/
79104
├── api/ # Vercel Edge Functions
105+
│ ├── search.js # Geocoding search endpoint (OpenStreetMap)
80106
│ └── weather.js # Secure weather API proxy
81-
├── netlify/ # Netlify Functions (alternative deployment)
82-
│ └── functions/
83-
│ └── weather.js
84107
├── public/ # Static assets (served directly)
85108
│ ├── icons/
86-
│ │ ├── icon-192.svg
87-
│ │ ├── icon-512.svg
88-
│ │ └── weather-icons/ # Condition-specific icons
89109
│ ├── index.html # Semantic HTML5 structure
90110
│ ├── manifest.json # PWA manifest
91111
│ ├── screenshot.svg # App preview
92112
│ └── service-worker.js # Advanced caching strategy
93113
├── scripts/
94114
│ └── fix-html-paths.js # Utility script for correcting asset paths in HTML
95115
├── src/
96-
│ ├── assets/
97-
│ │ └── icons/ # SVG icon system
98-
│ │ ├── favorite.svg
99-
│ │ ├── location.svg
100-
│ │ ├── refresh.svg
101-
│ │ ├── search.svg
102-
│ │ ├── theme.svg
103-
│ │ └── weather/ # Weather condition icons
104-
│ ├── css/
105-
│ │ ├── _base.css # CSS reset + accessibility foundations
106-
│ │ ├── _components.css # BEM-named UI components
107-
│ │ ├── _layout.css # Responsive grid system
108-
│ │ ├── _utilities.css # Accessibility/utility classes
109-
│ │ ├── _variables.css # Theming system (WCAG compliant)
110-
│ │ └── main.css # Cascade-controlled imports
116+
│ ├── assets/ # Source assets (processed during build)
117+
│ ├── css/ # CSS modules (BEM architecture)
111118
│ └── js/
112119
│ ├── core/ # Business logic (zero DOM access)
113-
│ │ ├── GeolocationManager.js # Permission handling
114-
│ │ ├── StorageManager.js # Encrypted storage wrapper
115-
│ │ ├── ThemeManager.js # System-preference aware theming
116-
│ │ ├── WeatherApp.js # Main application orchestrator
117-
│ │ └── WeatherService.js # Secure API abstraction
118120
│ ├── ui/ # Pure presentation layer
119-
│ │ ├── FavoritesManager.js # Favorite locations UI
120-
│ │ ├── LoadingSpinner.js # Perceptible loading states
121-
│ │ ├── SearchManager.js # Debounced search + autocomplete
122-
│ │ ├── Toast.js # WCAG 2.1 compliant notifications
123-
│ │ └── WeatherRenderer.js # Virtual DOM-inspired renderer
124121
│ ├── utils/ # Utility modules
125-
│ │ ├── a11y.js # Accessibility helpers
126-
│ │ ├── constants.js # Environment-safe constants
127-
│ │ ├── helpers.js # Pure utility functions
128-
│ │ └── validators.js # Input sanitization
129122
│ └── main.js # Dependency injection entry point
130123
├── .editorconfig
131124
├── .eslintignore # (optional, if needed alongside .eslintrc.js)
@@ -138,7 +131,6 @@ weather-app/
138131
├── CHANGELOG.md
139132
├── CONTRIBUTING.md
140133
├── LICENSE
141-
├── netlify.toml # Netlify security headers + redirect rules
142134
├── package.json
143135
├── README.md
144136
├── SECURITY.md # Critical security setup guide
@@ -154,32 +146,19 @@ weather-app/
154146
- Node.js 18+ (optional, for development tools)
155147
- **OpenWeatherMap API key** (for backend proxy - [get free key](https://openweathermap.org/api))
156148

157-
### ⚠️ Critical Security Setup (REQUIRED)
158-
159-
**This app NEVER exposes API keys in client code.** You must set up a secure proxy:
160-
161-
1. Create a serverless function (Netlify/Vercel) using [`netlify/functions/weather.js`](netlify/functions/weather.js)
162-
2. Set environment variable `WEATHER_API_KEY` in your hosting platform
163-
3. Update proxy endpoint in [`src/js/core/WeatherService.js`](src/js/core/WeatherService.js):
164-
```js
165-
this.apiBase = '/.netlify/functions/weather'; // For Netlify
166-
// OR
167-
this.apiBase = '/api/weather'; // For Vercel
168-
```
169-
170149
## 📖 Full security setup guide: See SECURITY.md
171150

172151
### Installation
173152

174-
#### Option 1: Quick Start (No Node.js)
153+
#### Quick Start (No Node.js)
175154

176155
```bash
177156
git clone https://github.com/farid-teymouri/weather-app.git
178157
cd weather-app
179158
# Open public/index.html directly in browser
180159
```
181160

182-
#### Option 2: Development Mode
161+
#### Development Mode
183162

184163
```bash
185164
git clone https://github.com/farid-teymouri/weather-app.git
@@ -195,60 +174,19 @@ npm run build # Creates optimized dist/ folder
195174
npm run preview # Preview production build
196175
```
197176

198-
### 🌐 PWA Installation
199-
200-
| Platform | Steps |
201-
| -------------------- | ----------------------------------------------------- |
202-
| **Android (Chrome)** | Menu (⋮) → "Install app" or "Add to Home screen" |
203-
| **iOS (Safari)** | Share button → "Add to Home Screen""Add" |
204-
| **Desktop (Chrome)** | Install icon (⊕) in address bar → "Install" |
205-
| **Desktop (Edge)** | Settings (⋯) → "Apps""Install this site as an app" |
206-
207-
## 🎹 Keyboard Shortcuts
208-
209-
| Shortcut | Action |
210-
| -------------- | --------------------------------- |
211-
| `Ctrl/Cmd + L` | Focus location search |
212-
| `Ctrl/Cmd + T` | Toggle temperature unitsC/°F) |
213-
| `Ctrl/Cmd + D` | Toggle dark/light mode |
214-
| `Ctrl/Cmd + F` | Toggle favorites panel |
215-
| `Enter` | Confirm search or selection |
216-
| `Escape` | Close modals or clear search |
217-
| `Arrow Keys` | Navigate search results/favorites |
218-
219-
## 🌟 Features Deep Dive
220-
221-
### 🔒 Secure Weather Service
222-
223-
```js
224-
// src/js/core/WeatherService.js
225-
// NEVER handles API keys directly
226-
// All requests routed through secure proxy endpoint
227-
async getWeather({ lat, lon }) {
228-
// Input validation + sanitization
229-
// Rate limiting (1 request/sec)
230-
// Cache validation (5-min stale-while-revalidate)
231-
// Fallback to cached data on failure
232-
// Full XSS sanitization of responses
233-
}
234-
```
235-
236-
### ♿ Accessibility First
237-
238-
- Screen Reader Support: ARIA labels, live regions for dynamic updates
239-
- Keyboard Navigation: Full tab order, arrow key navigation
240-
- Reduced Motion: Respects prefers-reduced-motion OS setting
241-
- Color Contrast: WCAG AA compliant in both themes (4.5:1+)
242-
- Focus Indicators: Visible focus rings on all interactive elements
243-
- Semantic HTML: Proper heading hierarchy, landmark regions
177+
#### 🚀 Deployment to Vercel (1-Click)
244178

245-
### 🌓 Intelligent Theming
179+
1. Push code to GitHub repository
180+
2. Import project in <a href="https://vercel.com/new" target="_blank" >Vercel Dashboard</a>
181+
3. Set environment variable (if using real API):
182+
- `WEATHER_API_KEY` = Your OpenWeatherMap API key
183+
4. Deploy! Vercel automatically:
184+
- Runs `npm run build`
185+
- Deploys `dist/` as root
186+
- Routes `/api/\*` to Edge Functions in `api/`
187+
- Applies security headers from ‍`vercel.json`
246188

247-
- Detects OS preference on first visit
248-
- Manual toggle persists across sessions
249-
- Smooth transitions with `prefers-reduced-motion` respect
250-
- CSS custom properties for instant theme switching
251-
- Print-friendly styles (light mode enforced for printing)
189+
#### 🔗 Zero-config deployment: `vercel.json` handles all routing and headers
252190

253191
## 🎨 Customization Guide
254192

@@ -265,12 +203,6 @@ async getWeather({ lat, lon }) {
265203
}
266204
```
267205

268-
### Add New Weather Icons
269-
270-
1. Create SVG in `src/assets/icons/weather/`
271-
2. Name format: `weather-[condition].svg` (e.g., `weather-thunderstorm.svg`)
272-
3. Update icon mapping in `src/js/utils/constants.js`
273-
274206
### Modify Cache Strategy
275207

276208
#### Edit `public/service-worker.js`:
@@ -279,17 +211,6 @@ async getWeather({ lat, lon }) {
279211
const CACHE_DURATION = 300000; // 5 minutes - adjust as needed
280212
```
281213

282-
## 🔒 Security Features
283-
284-
| Feature | Implementation |
285-
| --------------------------- | ---------------------------------------------- |
286-
| **No Client-Side API Keys** | Secure proxy pattern via serverless functions |
287-
| **XSS Protection** | DOMPurify-like sanitization in `validators.js` |
288-
| **Input Validation** | Strict parameter validation before API calls |
289-
| **CSP Headers** | Strict policy in `netlify.toml` |
290-
| **Secure Storage** | Favorites encrypted before localStorage save |
291-
| **Rate Limiting** | Client-side request throttling (1/sec) |
292-
293214
## 🌐 Browser Support
294215

295216
| Browser | Version | Support |
@@ -320,13 +241,13 @@ const CACHE_DURATION = 300000; // 5 minutes - adjust as needed
320241

321242
## 🐛 Troubleshooting
322243

323-
| Issue | Solution |
324-
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
325-
| **Weather not loading** | 1. Verify proxy endpoint in WeatherService.js <br> 2. Check browser console for CORS errors <br> 3. Ensure serverless function deployed correctly |
326-
| **Geolocation fails** | 1. Check browser permissions <br> 2. Verify HTTPS (required for geolocation) <br> 3. Test with manual location search |
327-
| **Dark mode not saving** | Clear site DevTools → Application → Clear site data |
328-
| **PWA won't install** | 1. Must use HTTPS (or localhost) <br> 2. Verify `manifest.json` accessible <br> 3. Check Service Worker registered in DevTools |
329-
| **Favorites not saving** | 1. Check localStorage quota <br> 2. Verify encryption key generation <br> 3. Clear corrupted storage entries |
244+
| Issue | Solution |
245+
| -------------------------- | ------------------------------------------------------------------------------------------ |
246+
| **Blank page on load** | ❌ DO NOT open `public/index.html` directly <br> ✅ Run `npm run build` then serve `dist/` |
247+
| **Weather not loading** | Check browser console → Verify `/api/weather` returns 200 (Vercel deployed) |
248+
| **Icons not showing** | Verify `dist/icons/weather-icons/` contains SVG files (build step copies them) |
249+
| **Time shows wrong value** | Confirm `weatherData.timezone = 12600` for Tehran (UTC+3:30) |
250+
| **Search not working** | Check Network tab → `/api/search` must return 200 with results array |
330251

331252
## 📜 License
332253

api/weather.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,11 @@ function getMockCurrent(lat = 35.6892, lon = 51.389, units = 'metric') {
125125
pressure: 1020,
126126
humidity: humidity,
127127
},
128-
weather: [{ main: condition, description: description, icon: iconCode }], // DYNAMIC ICON
128+
weather: [{ main: condition, description: description, icon: iconCode }], // DYNAMIC ICON
129129
wind: { speed: 3.2, deg: 315 },
130130
clouds: { all: clouds },
131131
dt: Math.floor(Date.now() / 1000),
132-
timezone: 12600, // CORRECT TEHRAN TIMEZONE
132+
timezone: 12600, // CORRECT TEHRAN TIMEZONE
133133
coord: { lat, lon },
134134
};
135135
}
@@ -257,7 +257,7 @@ function _getNearestCityName(lat, lon) {
257257

258258
function getMockForecast(days = 7, lat = 35.6892, lon = 51.389, units = 'metric') {
259259
const forecast = [];
260-
// ✅ CRITICAL FIX: Get BASE condition for THIS CITY (not fixed sequence!)
260+
// Get BASE condition for THIS CITY (not fixed sequence!)
261261
const baseTempC = _getFebruaryBaseTemp(lat, lon);
262262
const baseWeather = _getWeatherCondition(baseTempC, lat);
263263

@@ -269,7 +269,7 @@ function getMockForecast(days = 7, lat = 35.6892, lon = 51.389, units = 'metric'
269269
tempC = Math.max(-15, Math.min(30, tempC));
270270
const temp = units === 'metric' ? Math.round(tempC) : Math.round((tempC * 9) / 5 + 32);
271271

272-
// ✅ CRITICAL FIX: DYNAMIC WEATHER CONDITION BASED ON CITY'S CLIMATE
272+
// DYNAMIC WEATHER CONDITION BASED ON CITY'S CLIMATE
273273
let dayCondition, dayDesc, dayIcon;
274274

275275
// For VERY COLD cities (Berlin, Moscow): Mostly snow
@@ -359,7 +359,7 @@ function getMockForecast(days = 7, lat = 35.6892, lon = 51.389, units = 'metric'
359359
dew_point: tempC - 5,
360360
wind_speed: 2.5 + Math.random() * 3,
361361
wind_deg: _getPrevailingWindDirection(lat),
362-
// ✅ CRITICAL: DYNAMIC WEATHER ARRAY WITH CORRECT ICON
362+
// DYNAMIC WEATHER ARRAY WITH CORRECT ICON
363363
weather: [{ main: dayCondition, description: dayDesc, icon: dayIcon }],
364364
clouds: baseWeather.clouds || (dayCondition === 'Clear' ? 10 : dayCondition === 'Snow' ? 85 : 50),
365365
pop: dayCondition === 'Snow' ? 0.6 : dayCondition === 'Rain' ? 0.4 : 0,

src/js/core/WeatherApp.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ export class WeatherApp {
240240

241241
return; // Exit early - success path complete
242242
}
243-
this._initFavoriteButton(); // Initialize favorite button state
243+
this._initFavoriteButton(); // Initialize favorite button state
244244
} catch (geoError) {
245245
console.warn('[WeatherApp] Geolocation failed:', geoError.message);
246246
// Continue to fallback location

0 commit comments

Comments
 (0)