-
-
--°C
-
--
+
+
+
+
+
+
+
+
+
+
Loading...
+
+
⏳
+
+ --°
+ °F
+
+
Loading weather data...
+
+
+
+
-
-
-
-
-
+
-
+
From b792fb86a227cca387ccaaa42cd4cd17c91d07a3 Mon Sep 17 00:00:00 2001
From: Aaditya Agarwal <146380217+psy-duck1@users.noreply.github.com>
Date: Wed, 22 Oct 2025 01:07:35 +0530
Subject: [PATCH 2/3] Refactor CSS for improved styling and responsiveness
Updated styles for the weather site with new variables, animations, and responsive design.
---
Javascript/Weather Site/style.css | 413 +++++++++++++++++++++++-------
1 file changed, 318 insertions(+), 95 deletions(-)
diff --git a/Javascript/Weather Site/style.css b/Javascript/Weather Site/style.css
index e4c1897..dc4aa33 100644
--- a/Javascript/Weather Site/style.css
+++ b/Javascript/Weather Site/style.css
@@ -1,130 +1,353 @@
:root {
- --bg: #0f1724;
- --glass: rgba(255, 255, 255, 0.04);
- --accent: #60a5fa;
- --muted: #9aa7b2;
+ --glass-bg: rgba(255, 255, 255, 0.1);
+ --glass-border: rgba(255, 255, 255, 0.2);
+ --text-primary: #ffffff;
+ --text-secondary: rgba(255, 255, 255, 0.8);
+ --shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ --border-radius: 20px;
+ --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
-* { box-sizing: border-box; }
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
body {
- margin: 0;
- font-family: Inter, system-ui;
- min-height: 100vh;
- background: linear-gradient(180deg, #071025 0%, #0b1626 100%);
- color: #e6eef6;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 24px;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ min-height: 100vh;
+ background: linear-gradient(135deg, #1e3a8a 0%, #7c3aed 50%, #ec4899 100%);
+ background-size: 400% 400%;
+ animation: gradientShift 15s ease infinite;
+ color: var(--text-primary);
+ overflow-x: hidden;
+}
+
+@keyframes gradientShift {
+ 0% { background-position: 0% 50%; }
+ 50% { background-position: 100% 50%; }
+ 100% { background-position: 0% 50%; }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+/* Header Styles */
+.header {
+ text-align: center;
+ margin-bottom: 30px;
+ animation: fadeIn 0.8s ease-out;
+}
+
+.header h1 {
+ font-size: clamp(2rem, 4vw, 3.5rem);
+ font-weight: 700;
+ margin-bottom: 20px;
+ text-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
-.app {
- max-width: 720px;
- width: 100%;
- background: rgba(255, 255, 255, 0.02);
- border-radius: 16px;
- padding: 20px;
- box-shadow: 0 10px 30px rgba(2, 6, 23, 0.6);
+.search-container {
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+ margin-bottom: 20px;
}
-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 16px;
+.search-input {
+ padding: 15px 20px;
+ border: none;
+ border-radius: 25px;
+ background: var(--glass-bg);
+ backdrop-filter: blur(10px);
+ border: 1px solid var(--glass-border);
+ color: var(--text-primary);
+ font-size: 16px;
+ width: 300px;
+ max-width: 100%;
+ transition: var(--transition);
}
-header h1 {
- font-size: 20px;
- margin: 0;
+.search-input::placeholder {
+ color: var(--text-secondary);
}
-.controls {
- display: flex;
- gap: 8px;
+.search-input:focus {
+ outline: none;
+ border-color: rgba(255, 255, 255, 0.4);
+ transform: scale(1.02);
}
-.controls input {
- padding: 10px 12px;
- border-radius: 10px;
- border: 1px solid rgba(255, 255, 255, 0.06);
- background: var(--glass);
- color: inherit;
- width: 220px;
+.search-btn {
+ padding: 15px 25px;
+ border: none;
+ border-radius: 25px;
+ background: var(--glass-bg);
+ backdrop-filter: blur(10px);
+ border: 1px solid var(--glass-border);
+ color: var(--text-primary);
+ cursor: pointer;
+ font-size: 16px;
+ transition: var(--transition);
}
-.controls button {
- padding: 10px 14px;
- border-radius: 10px;
- border: none;
- background: var(--accent);
- color: #06243a;
- font-weight: 600;
- cursor: pointer;
+.search-btn:hover {
+ background: rgba(255, 255, 255, 0.2);
+ transform: scale(1.05);
}
-.card {
- display: flex;
- gap: 20px;
- align-items: center;
- padding: 18px;
- border-radius: 12px;
- background: rgba(255, 255, 255, 0.02);
+/* Glass Card Base Style */
+.glass-card {
+ background: var(--glass-bg);
+ backdrop-filter: blur(10px);
+ border: 1px solid var(--glass-border);
+ border-radius: var(--border-radius);
+ box-shadow: var(--shadow);
+ padding: 25px;
+ transition: var(--transition);
+ animation: fadeIn 0.8s ease-out;
}
-.left { text-align: center; }
-.icon { width: 96px; height: 96px; margin: auto; }
-.temp { font-size: 36px; font-weight: 700; }
-.cond { font-size: 14px; color: var(--muted); }
+.glass-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
+}
-.details { flex: 1; }
-.row { display: flex; justify-content: space-between; }
-.meta { display: flex; gap: 12px; margin-top: 10px; }
+/* Main Weather Card */
+.main-weather {
+ text-align: center;
+ margin-bottom: 30px;
+ animation-delay: 0.2s;
+}
-.meta .m {
- background: rgba(255, 255, 255, 0.02);
- padding: 10px;
- border-radius: 8px;
- min-width: 100px;
- text-align: center;
+.city-name {
+ font-size: clamp(1.5rem, 3vw, 2.5rem);
+ font-weight: 600;
+ margin-bottom: 10px;
}
-.muted { color: var(--muted); font-size: 13px; }
-.small { font-size: 12px; }
+.date-time {
+ color: var(--text-secondary);
+ font-size: 1.1rem;
+ margin-bottom: 20px;
+}
-.loader {
- height: 6px;
- background: rgba(255, 255, 255, 0.04);
- border-radius: 6px;
- overflow: hidden;
- margin-top: 12px;
+.weather-icon {
+ font-size: clamp(4rem, 8vw, 8rem);
+ margin: 20px 0;
+ display: block;
}
-.loader > i {
- display: block;
- height: 100%;
- width: 0;
- background: linear-gradient(90deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0.2));
- animation: load 1.2s linear infinite;
+.temperature {
+ font-size: clamp(3rem, 6vw, 5rem);
+ font-weight: 700;
+ margin: 20px 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 20px;
}
-@keyframes load {
- 0% { width: 0; }
- 50% { width: 60%; }
- 100% { width: 0; }
+.temp-toggle {
+ padding: 8px 15px;
+ border: none;
+ border-radius: 15px;
+ background: rgba(255, 255, 255, 0.2);
+ color: var(--text-primary);
+ cursor: pointer;
+ font-size: 1rem;
+ transition: var(--transition);
}
-.error {
- background: #4c1f1f;
- color: #ffdede;
- padding: 10px;
- border-radius: 8px;
- margin-top: 12px;
+.temp-toggle:hover {
+ background: rgba(255, 255, 255, 0.3);
+ transform: scale(1.05);
}
-@media (max-width: 560px) {
- .controls { flex-direction: column; }
- .controls input { width: 100%; }
- .card { flex-direction: column; align-items: flex-start; }
+.weather-description {
+ font-size: 1.3rem;
+ color: var(--text-secondary);
+ text-transform: capitalize;
+}
+
+/* Weather Details Grid */
+.weather-details {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 20px;
+ margin-bottom: 30px;
+}
+
+.detail-card {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ animation-delay: 0.4s;
+}
+
+.detail-icon {
+ font-size: 2rem;
+}
+
+.detail-info h3 {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ margin-bottom: 5px;
+}
+
+.detail-info .value {
+ font-size: 1.3rem;
+ font-weight: 600;
+}
+
+/* Forecast Section */
+.forecast-section {
+ animation-delay: 0.6s;
+}
+
+.forecast-section h2 {
+ text-align: center;
+ margin-bottom: 20px;
+ font-size: 1.8rem;
+}
+
+.forecast-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 15px;
+ overflow-x: auto;
+ padding-bottom: 10px;
+}
+
+.forecast-card {
+ text-align: center;
+ padding: 20px 15px;
+ animation-delay: 0.8s;
+}
+
+.forecast-day {
+ font-weight: 600;
+ margin-bottom: 10px;
+ font-size: 1rem;
+}
+
+.forecast-icon {
+ font-size: 2.5rem;
+ margin: 10px 0;
+}
+
+.forecast-temps {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 10px;
+}
+
+.high-temp {
+ font-weight: 600;
+}
+
+.low-temp {
+ color: var(--text-secondary);
+}
+
+/* Loading Spinner */
+.loading {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ display: none;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+}
+
+.spinner {
+ width: 50px;
+ height: 50px;
+ border: 3px solid rgba(255, 255, 255, 0.3);
+ border-top: 3px solid #ffffff;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+/* Error Message */
+.error-message {
+ background: rgba(239, 68, 68, 0.2);
+ border: 1px solid rgba(239, 68, 68, 0.4);
+ border-radius: 15px;
+ padding: 15px;
+ margin: 20px 0;
+ text-align: center;
+ color: #fef2f2;
+ display: none;
+ animation: fadeIn 0.5s ease-out;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .container {
+ padding: 15px;
+ }
+
+ .search-container {
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .search-input {
+ width: 100%;
+ margin-bottom: 10px;
+ }
+
+ .weather-details {
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ }
+
+ .forecast-container {
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+ }
+
+ .temperature {
+ flex-direction: column;
+ gap: 10pax;
+ }
+}
+
+@media (max-width: 480px) {
+ .weather-details {
+ grid-template-columns: 1fr;
+ }
+
+ .forecast-container {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .detail-card {
+ justify-content: center;
+ text-align: center;
+ flex-direction: column;
+ gap: 10px;
+ }
}
From 70c7c6930593babed6093f04abc390b4f75eb633 Mon Sep 17 00:00:00 2001
From: Aaditya Agarwal <146380217+psy-duck1@users.noreply.github.com>
Date: Wed, 22 Oct 2025 01:08:39 +0530
Subject: [PATCH 3/3] Integrate OpenWeather API and enhance UI features
Refactored weather fetching logic and added support for OpenWeather API. Introduced temperature unit toggle and improved error handling.
---
Javascript/Weather Site/script.js | 290 +++++++++++++++++++++++++-----
1 file changed, 245 insertions(+), 45 deletions(-)
diff --git a/Javascript/Weather Site/script.js b/Javascript/Weather Site/script.js
index d870f40..b1b8158 100644
--- a/Javascript/Weather Site/script.js
+++ b/Javascript/Weather Site/script.js
@@ -1,61 +1,261 @@
-const API_KEY = "f45d51406f914de6899151327251010";
-const BASE = "https://api.weatherapi.com/v1/current.json"; // Use HTTPS to avoid browser blocks
+let currentUnit = 'celsius';
+let currentWeatherData = null;
+let currentForecastData = null;
+const API_KEY = 'bd5e378503939ddaee76f12ad7a97608';
+const WEATHER_API_URL = 'https://api.openweathermap.org/data/2.5/weather';
+const FORECAST_API_URL = 'https://api.openweathermap.org/data/2.5/forecast';
-const $ = (id) => document.getElementById(id);
-const searchBtn = $("search");
-const qInput = $("q");
-const loadingEl = $("loading");
-const errorEl = $("error");
+// Weather condition to icon mapping
+const weatherIcons = {
+ 'Clear': '☀️',
+ 'Clouds': '☁️',
+ 'Rain': '🌧️',
+ 'Drizzle': '🌦️',
+ 'Thunderstorm': '⛈️',
+ 'Snow': '🌨️',
+ 'Mist': '🌫️',
+ 'Fog': '🌫️',
+ 'Haze': '🌫️'
+};
-async function fetchWeather(q) {
- errorEl.style.display = "none";
- loadingEl.style.display = "block";
+// Gradient colors for different weather conditions
+const weatherGradients = {
+ 'Clear': 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 50%, #ea580c 100%)',
+ 'Clouds': 'linear-gradient(135deg, #6b7280 0%, #4b5563 50%, #374151 100%)',
+ 'Rain': 'linear-gradient(135deg, #3b82f6 0%, #2563eb 50%, #1e40af 100%)',
+ 'Drizzle': 'linear-gradient(135deg, #3b82f6 0%, #2563eb 50%, #1e40af 100%)',
+ 'Thunderstorm': 'linear-gradient(135deg, #1f2937 0%, #374151 50%, #4b5563 100%)',
+ 'Snow': 'linear-gradient(135deg, #e5e7eb 0%, #d1d5db 50%, #9ca3af 100%)',
+ 'default': 'linear-gradient(135deg, #1e3a8a 0%, #7c3aed 50%, #ec4899 100%)'
+};
- try {
- const url = `${BASE}?key=${API_KEY}&q=${encodeURIComponent(q)}&aqi=yes`;
- const res = await fetch(url);
+// DOM elements
+const elements = {
+ loading: document.getElementById('loading'),
+ errorMessage: document.getElementById('errorMessage'),
+ cityInput: document.getElementById('cityInput'),
+ searchBtn: document.getElementById('searchBtn'),
+ cityName: document.getElementById('cityName'),
+ dateTime: document.getElementById('dateTime'),
+ weatherIcon: document.getElementById('weatherIcon'),
+ temperature: document.getElementById('temperature'),
+ tempToggle: document.getElementById('tempToggle'),
+ weatherDescription: document.getElementById('weatherDescription'),
+ feelsLike: document.getElementById('feelsLike'),
+ humidity: document.getElementById('humidity'),
+ windSpeed: document.getElementById('windSpeed'),
+ pressure: document.getElementById('pressure'),
+ visibility: document.getElementById('visibility'),
+ uvIndex: document.getElementById('uvIndex'),
+ forecastContainer: document.getElementById('forecastContainer')
+};
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
+// Utility functions
+function showLoading() {
+ elements.loading.style.display = 'flex';
+}
+
+function hideLoading() {
+ elements.loading.style.display = 'none';
+}
+
+function showError(message) {
+ elements.errorMessage.textContent = message;
+ elements.errorMessage.style.display = 'block';
+ setTimeout(() => {
+ elements.errorMessage.style.display = 'none';
+ }, 5000);
+}
+
+function getWeatherIcon(condition) {
+ return weatherIcons[condition] || '🌤️';
+}
+
+function convertTemp(temp, unit) {
+ if (unit === 'fahrenheit') {
+ return Math.round((temp * 9/5) + 32);
+ }
+ return Math.round(temp);
+}
- const data = await res.json();
- render(data);
- } catch (err) {
- showError(err.message);
- } finally {
- loadingEl.style.display = "none";
- }
+function formatDate() {
+ const now = new Date();
+ const options = {
+ weekday: 'long',
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ };
+ return now.toLocaleDateString('en-US', options);
}
-function render(data) {
- if (!data || !data.location) return showError("Invalid response");
+function updateBackground(weatherCondition) {
+ const gradient = weatherGradients[weatherCondition] || weatherGradients.default;
+ document.body.style.background = gradient;
+ document.body.style.backgroundSize = '400% 400%';
+}
- $("location").textContent = `${data.location.name}, ${data.location.country}`;
- $("localtime").textContent = data.location.localtime || "--";
- $("temp").textContent = `${data.current.temp_c}°C`;
- $("condition").textContent = data.current.condition.text;
- $("humidity").textContent = `${data.current.humidity}%`;
- $("wind").textContent = `${data.current.wind_kph} kph`;
- $("feelslike").textContent = `${data.current.feelslike_c}°C`;
+// API functions
+async function fetchWeather(city) {
+ try {
+ const response = await fetch(`${WEATHER_API_URL}?q=${city}&appid=${API_KEY}&units=metric`);
+ if (!response.ok) {
+ throw new Error(`Weather data not found for "${city}". Please check the city name and try again.`);
+ }
+ const data = await response.json();
+ return data;
+ } catch (error) {
+ throw new Error(error.message || 'Failed to fetch weather data. Please try again.');
+ }
+}
- const iconUrl = data.current.condition.icon.startsWith("//")
- ? "https:" + data.current.condition.icon
- : data.current.condition.icon;
+async function fetchForecast(city) {
+ try {
+ const response = await fetch(`${FORECAST_API_URL}?q=${city}&appid=${API_KEY}&units=metric`);
+ if (!response.ok) {
+ throw new Error('Failed to fetch forecast data');
+ }
+ const data = await response.json();
+ return data;
+ } catch (error) {
+ throw new Error('Failed to fetch forecast data. Please try again.');
+ }
+}
- $("icon").src = iconUrl;
- $("icon").alt = data.current.condition.text;
+// Display functions
+function displayWeather(data) {
+ currentWeatherData = data;
+
+ elements.cityName.textContent = `${data.name}, ${data.sys.country}`;
+ elements.dateTime.textContent = formatDate();
+
+ const condition = data.weather[0].main;
+ elements.weatherIcon.textContent = getWeatherIcon(condition);
+
+ const temp = convertTemp(data.main.temp, currentUnit);
+ const unit = currentUnit === 'celsius' ? '°C' : '°F';
+ elements.temperature.textContent = `${temp}${unit}`;
+
+ elements.weatherDescription.textContent = data.weather[0].description;
+
+ // Update details
+ const feelsLikeTemp = convertTemp(data.main.feels_like, currentUnit);
+ elements.feelsLike.textContent = `${feelsLikeTemp}${unit}`;
+ elements.humidity.textContent = `${data.main.humidity}%`;
+ elements.windSpeed.textContent = `${Math.round(data.wind.speed * 10) / 10} m/s`;
+ elements.pressure.textContent = `${data.main.pressure} hPa`;
+ elements.visibility.textContent = data.visibility ? `${Math.round(data.visibility / 1000)} km` : 'N/A';
+
+ // UV Index is not available in the free API, so we'll show N/A
+ elements.uvIndex.textContent = 'N/A';
+
+ // Update background based on weather
+ updateBackground(condition);
}
-function showError(msg) {
- errorEl.style.display = "block";
- errorEl.className = "error";
- errorEl.textContent = msg;
+function displayForecast(data) {
+ currentForecastData = data;
+ elements.forecastContainer.innerHTML = '';
+
+ // Group forecast data by day (every 8th item represents a new day as data is every 3 hours)
+ const dailyForecasts = [];
+ for (let i = 0; i < data.list.length; i += 8) {
+ if (dailyForecasts.length >= 5) break;
+ dailyForecasts.push(data.list[i]);
+ }
+
+ dailyForecasts.forEach((forecast, index) => {
+ const forecastCard = document.createElement('div');
+ forecastCard.className = 'glass-card forecast-card';
+
+ const date = new Date(forecast.dt * 1000);
+ const dayName = index === 0 ? 'Today' : date.toLocaleDateString('en-US', { weekday: 'long' });
+
+ const condition = forecast.weather[0].main;
+ const icon = getWeatherIcon(condition);
+ const highTemp = convertTemp(forecast.main.temp_max, currentUnit);
+ const lowTemp = convertTemp(forecast.main.temp_min, currentUnit);
+ const unit = currentUnit === 'celsius' ? '°C' : '°F';
+
+ forecastCard.innerHTML = `
+
${dayName}
+
${icon}
+
${forecast.weather[0].description}
+
+ ${highTemp}${unit}
+ ${lowTemp}${unit}
+
+ `;
+
+ elements.forecastContainer.appendChild(forecastCard);
+ });
}
-searchBtn.addEventListener("click", () =>
- fetchWeather(qInput.value || "London")
-);
-qInput.addEventListener("keydown", (e) => {
- if (e.key === "Enter") fetchWeather(qInput.value || "London");
+function updateTemperatureDisplay() {
+ if (currentWeatherData) {
+ displayWeather(currentWeatherData);
+ }
+ if (currentForecastData) {
+ displayForecast(currentForecastData);
+ }
+}
+
+// Main functions
+async function loadWeatherData(city) {
+ showLoading();
+ elements.errorMessage.style.display = 'none';
+
+ try {
+ const [weatherData, forecastData] = await Promise.all([
+ fetchWeather(city),
+ fetchForecast(city)
+ ]);
+
+ displayWeather(weatherData);
+ displayForecast(forecastData);
+ } catch (error) {
+ showError(error.message);
+ } finally {
+ hideLoading();
+ }
+}
+
+function handleSearch() {
+ const city = elements.cityInput.value.trim();
+ if (city) {
+ loadWeatherData(city);
+ elements.cityInput.value = '';
+ }
+}
+
+function toggleTemperatureUnit() {
+ currentUnit = currentUnit === 'celsius' ? 'fahrenheit' : 'celsius';
+ elements.tempToggle.textContent = currentUnit === 'celsius' ? '°F' : '°C';
+ updateTemperatureDisplay();
+}
+
+// Update time every minute
+function updateTime() {
+ elements.dateTime.textContent = formatDate();
+}
+
+// Event listeners
+elements.searchBtn.addEventListener('click', handleSearch);
+elements.cityInput.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter') {
+ handleSearch();
+ }
});
+elements.tempToggle.addEventListener('click', toggleTemperatureUnit);
-fetchWeather(qInput.value || "London");
+// Initialize app
+document.addEventListener('DOMContentLoaded', () => {
+ // Load default city (London)
+ loadWeatherData('London');
+
+ // Update time every minute
+ setInterval(updateTime, 60000);
+ updateTime();
+});