diff --git a/Javascript/Weather Site/index.html b/Javascript/Weather Site/index.html index 22640db..675fc00 100644 --- a/Javascript/Weather Site/index.html +++ b/Javascript/Weather Site/index.html @@ -1,61 +1,97 @@ - - - Weather App - + + + Weather Dashboard + -
-
-

Weather App

-
- - -
-
+ +
+
+
-
-
-
- weather icon -
--°C
-
--
+
+ +
+

Weather Dashboard

+
+ + +
+
+ + +
+ + +
+

Loading...

+
+
+
+ --° + +
+
Loading weather data...
+
+ + +
+
+
🌡️
+
+

Feels Like

+
--°
+
+
+
+
💧
+
+

Humidity

+
--%
+
+
+
+
💨
+
+

Wind Speed

+
-- m/s
+
+
+
+
🎚️
+
+

Pressure

+
-- hPa
+
+
+
+
👁️
+
+

Visibility

+
-- km
+
+
+
+
☀️
+
+

UV Index

+
--
+
+
-
-
-
-
Location
-
--
-
-
-
Local Time
-
--
-
-
-
-
-
Humidity
-
--%
-
-
-
Wind
-
-- kph
-
-
-
Feels Like
-
--°C
-
-
-
Data from WeatherAPI.com
+ + +
+

5-Day Forecast

+
+ +
-
- - -
-
+ - + 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(); +}); 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; + } }