Weather App Project

Project Overview

This project is a Modern Weather App built using HTML, CSS, and JavaScript. It fetches real-time weather data using a weather API and displays it in a user-friendly interface. The app supports the following features:

  • Search by City Name: Enter a city name to get real-time weather updates.
  • Detailed Weather Information: Displays Temperature: __Β°C, Wind: __ M/S, Humidity: __%, Visibility: __ meters, and Pressure: __ hPa.
  • Dynamic Animations and Icons: Animations and icons change based on the weather conditions (e.g., sunny, rainy, cloudy) and time of day (day or night).
  • Current Location-Based Weather: Automatically detects your location and provides weather updates using the Geolocation API.
  • 5-Day Forecast: Displays a detailed 5-day weather forecast with temperature, wind, humidity, visibility, and pressure for each day.
  • Modern UI: A sleek and responsive design with interactive icons, animations, and gradients for an enhanced user experience.
The app is designed to be responsive and works seamlessly on both desktop and mobile devices.

Requirements

  • Programming Language: HTML, CSS, JavaScript
  • Frameworks/Libraries: None
  • Tools: VS Code, Git
  • APIs: OpenWeatherMap API
  • Other Dependencies: Google Fonts, Font Awesome

Process/Steps

  1. Step 1: Create Project Files
    • Create a folder named weather-app-project.
    • Inside the folder, create three files:
      • index.html - For the HTML structure.
      • styles.css - For styling the weather app.
      • script.js - For handling API calls and weather data.
  2. Step 2: Write HTML Code
    • In index.html, create the basic structure of the weather app using HTML.
    • Add a search bar, a display area for weather information, and icons for weather conditions.
  3. Step 3: Style the Weather App
    • In styles.css, style the app to make it visually appealing.
    • Use gradients, shadows, and animations for a modern look.
  4. Step 4: Add JavaScript Logic
    • In script.js, write functions to fetch weather data from the API, handle user input, and update the display.
  5. Step 5: Test and Deploy
    • Test the weather app on different devices to ensure responsiveness.
    • Deploy the project using GitHub Pages or any hosting service.

Code Explanation

HTML Code


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Weather App Project</title>
  <link rel="stylesheet" href="styles.css">
  <link href="https://fonts.googleapis.com/css2?family=Digital-7&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
</head>
<body>
  <div class="weather-app">
    <!-- Search Box -->
    <div class="search-box">
      <input type="text" id="cityInput" placeholder="Enter city name">
      <button id="searchButton"><i class="fas fa-search"></i> Search</button>
      <div class="separator"></div>
      <button id="locationButton"><i class="fas fa-location-arrow"></i> Use Current Location</button>
    </div>

    <!-- Weather Info -->
    <div class="weather-info">
      <h2 id="cityName">_______ ( ______ )</h2>
      <div class="weather-details">
        <div id="temperature">Temperature: __Β°C</div>
        <div id="windSpeed">Wind: __ M/S</div>
        <div id="humidity">Humidity: __%</div>
        <div id="visibility">Visibility: __ meters</div>
        <div id="pressure">Pressure: __ hPa</div>
      </div>
      <div class="weather-icon">
        <img src="https://openweathermap.org/img/wn/01d@4x.png" alt="weather-icon">
        <h6>Clear Sky</h6>
      </div>
    </div>

    <!-- 5-Day Forecast -->
    <div class="days-forecast">
      <h2>5-Day Forecast</h2>
      <ul class="weather-cards" id="forecastCards">
        <li class="card">
          <h3>( ______ )</h3>
          <img src="https://openweathermap.org/img/wn/01d@4x.png" alt="weather-icon">
          <h6>Temp: __Β°C</h6>
          <h6>Wind: __ M/S</h6>
          <h6>Humidity: __%</h6>
          <h6>Visibility: __ meters</h6>
          <h6>Pressure: __ hPa</h6>
        </li>
        <!-- Repeat for other forecast cards -->
      </ul>
    </div>
  </div>

  <!-- Footer -->
  <footer>
    <p>Design and Developed by <strong>Hariom Soni</strong></p>
  </footer>

  <script src="script.js"></script>
</body>
</html>
                        

CSS Code


/* Import Google font - Open Sans */
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap');

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Open Sans', sans-serif;
}

body {
    background: linear-gradient(135deg, #1e3c72, #2a5298);
    color: #fff;
    min-height: 100vh;
    overflow-x: hidden;
}

.background-animation {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(135deg, rgba(30, 60, 114, 0.8), rgba(42, 82, 152, 0.8));
    z-index: -1;
    animation: moveBackground 10s infinite alternate;
}

@keyframes moveBackground {
    0% {
        transform: scale(1);
    }
    100% {
        transform: scale(1.1);
    }
}

h1 {
    background: rgba(83, 114, 240, 0.9);
    font-size: 2rem;
    text-align: center;
    padding: 20px 0;
    color: #fff;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
    margin-bottom: 30px;
}

.container {
    display: flex;
    gap: 35px;
    padding: 30px;
    flex-wrap: wrap;
    max-width: 1200px;
    margin: 0 auto;
}

.weather-input {
    width: 550px;
    background: rgba(255, 255, 255, 0.1);
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
    backdrop-filter: blur(10px);
}

.weather-input input {
    height: 46px;
    width: 100%;
    outline: none;
    font-size: 1.07rem;
    padding: 0 17px;
    margin: 10px 0 20px 0;
    border-radius: 4px;
    border: 1px solid rgba(255, 255, 255, 0.3);
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
    transition: all 0.3s ease;
}

.weather-input input:focus {
    border: 2px solid #5372F0;
    background: rgba(255, 255, 255, 0.2);
}

.weather-input .separator {
    height: 1px;
    width: 100%;
    margin: 25px 0;
    background: rgba(255, 255, 255, 0.3);
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
}

.weather-input .separator::before {
    content: "or";
    color: rgba(255, 255, 255, 0.7);
    font-size: 1.18rem;
    padding: 0 15px;
    background: rgba(30, 60, 114, 0.8);
    position: absolute;
    top: -10px;
}

/* Buttons */
.weather-input button {
    width: 100%;
    padding: 10px 0;
    cursor: pointer;
    outline: none;
    border: none;
    border-radius: 4px;
    font-size: 1rem;
    color: #fff;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
}

.weather-input .search-btn {
    background: #5372F0;
}

.weather-input .search-btn:hover {
    background: #2c52ed;
    transform: translateY(-2px);
}

.weather-input .location-btn {
    background: #6C757D;
}

.weather-input .location-btn:hover {
    background: #5c636a;
    transform: translateY(-2px);
}

/* Weather Data */
.weather-data {
    width: 100%;
}

.weather-data .current-weather {
    color: #fff;
    background: rgba(83, 114, 240, 0.9);
    border-radius: 10px;
    padding: 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
    backdrop-filter: blur(10px);
}

.current-weather h2 {
    font-weight: 700;
    font-size: 1.7rem;
}

.weather-data h6 {
    margin-top: 12px;
    font-size: 1rem;
    font-weight: 500;
}

.current-weather .icon {
    text-align: center;
}

.current-weather .icon img {
    max-width: 120px;
    margin-top: -15px;
}

.current-weather .icon h6 {
    margin-top: -10px;
    text-transform: capitalize;
}

/* Forecast Section */
.days-forecast h2 {
    margin: 20px 0;
    font-size: 1.5rem;
}

.days-forecast .weather-cards {
    display: flex;
    gap: 20px;
    flex-wrap: wrap;
}

.weather-cards .card {
    color: #fff;
    padding: 20px;
    list-style: none;
    width: calc(100% / 5 - 15px);
    background: rgba(108, 117, 125, 0.9);
    border-radius: 10px;
    text-align: center;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
    backdrop-filter: blur(10px);
    transition: all 0.3s ease;
}

.weather-cards .card:hover {
    transform: translateY(-5px);
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
}

.weather-cards .card h3 {
    font-size: 1.3rem;
    font-weight: 600;
}

.weather-cards .card img {
    max-width: 70px;
    margin: 5px 0 -12px 0;
}

/* Responsive Design */
@media (max-width: 1400px) {
    .weather-data .current-weather {
        padding: 20px;
    }

    .weather-cards .card {
        width: calc(100% / 4 - 15px);
    }
}

@media (max-width: 1200px) {
    .weather-cards .card {
        width: calc(100% / 3 - 15px);
    }
}

@media (max-width: 950px) {
    .weather-input {
        width: 450px;
    }

    .weather-cards .card {
        width: calc(100% / 2 - 10px);
    }
}

@media (max-width: 750px) {
    h1 {
        font-size: 1.45rem;
        padding: 16px 0;
    }

    .container {
        flex-wrap: wrap;
        padding: 15px;
    }

    .weather-input {
        width: 100%;
    }

    .weather-data h2 {
        font-size: 1.35rem;
    }

    /* Adjust current weather section for mobile */
    .current-weather {
        flex-direction: column;
        text-align: center;
    }

    .current-weather .icon {
        margin-top: 15px;
    }

    /* Adjust forecast cards for mobile */
    .days-forecast .weather-cards {
        gap: 10px; /* Reduce gap between cards */
    }

    .weather-cards .card {
        width: calc(50% - 10px); /* 2 cards per row */
        padding: 15px; /* Reduce padding */
        border-radius: 15px; /* Add rounded corners */
    }

    .weather-cards .card h3 {
        font-size: 1.2rem; /* Reduce font size */
    }

    .weather-cards .card img {
        max-width: 50px; /* Reduce icon size */
    }

    .weather-cards .card h6 {
        font-size: 0.9rem; /* Reduce font size */
    }
}

@media (max-width: 480px) {
    /* Further adjustments for very small screens */
    .weather-cards .card {
        width: calc(100% - 10px); /* 1 card per row */
    }
}

/* Weather Icons and Animations */
.weather-icon {
    font-size: 3rem;
    margin-bottom: 10px;
    display: inline-block;
}

.sunny {
    animation: sunny 5s infinite;
}

@keyframes sunny {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

.cloudy {
    animation: cloudy 5s infinite;
}

@keyframes cloudy {
    0% { transform: translateX(-10px); }
    50% { transform: translateX(10px); }
    100% { transform: translateX(-10px); }
}

.rainy {
    animation: rainy 3s infinite;
}

@keyframes rainy {
    0% { transform: translateY(0); }
    50% { transform: translateY(10px); }
    100% { transform: translateY(0); }
}

.snowy {
    animation: snowy 5s infinite;
}

@keyframes snowy {
    0% { transform: translateY(0); }
    50% { transform: translateY(10px); }
    100% { transform: translateY(0); }
}

.stormy {
    animation: stormy 2s infinite;
}

@keyframes stormy {
    0% { transform: scale(1); }
    50% { transform: scale(1.1); }
    100% { transform: scale(1); }
}

.drizzly {
    animation: drizzly 4s infinite;
}

@keyframes drizzly {
    0% { opacity: 1; }
    50% { opacity: 0.5; }
    100% { opacity: 1; }
}

.misty {
    animation: misty 6s infinite;
}

@keyframes misty {
    0% { opacity: 0.8; }
    50% { opacity: 0.4; }
    100% { opacity: 0.8; }
}

/* Loading Spinner */
.loading-spinner {
    border: 4px solid rgba(255, 255, 255, 0.3);
    border-radius: 50%;
    border-top: 4px solid #5372F0;
    width: 40px;
    height: 40px;
    animation: spin 1s linear infinite;
    margin: 20px auto;
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

/* Day/Night Animations */
.sunny {
    animation: sunny 5s infinite;
}

@keyframes sunny {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

.night {
    animation: night 5s infinite;
}

@keyframes night {
    0% { opacity: 1; }
    50% { opacity: 0.7; }
    100% { opacity: 1; }
}

.cloudy {
    animation: cloudy 5s infinite;
}

@keyframes cloudy {
    0% { transform: translateX(-10px); }
    50% { transform: translateX(10px); }
    100% { transform: translateX(-10px); }
}

.cloudy-night {
    animation: cloudy-night 5s infinite;
}

@keyframes cloudy-night {
    0% { opacity: 1; }
    50% { opacity: 0.7; }
    100% { opacity: 1; }
}
/* Footer Styles */
footer {
    background: rgba(83, 114, 240, 0.9);
    color: #fff;
    text-align: center;
    padding: 15px 0;
    margin-top: 30px;
    box-shadow: 0 -4px 15px rgba(0, 0, 0, 0.2);
    backdrop-filter: blur(10px);
    position: relative;
    z-index: 1;
}

footer p {
    margin: 0;
    font-size: 1rem;
    font-weight: 500;
}

footer strong {
    color: #ffdd57; /* Highlight the developer's name */
}
                        

JavaScript Code


const cityInput = document.querySelector(".city-input");
const searchButton = document.querySelector(".search-btn");
const locationButton = document.querySelector(".location-btn");
const currentWeatherDiv = document.querySelector(".current-weather");
const weatherCardsDiv = document.querySelector(".weather-cards");
const API_KEY = "YOUR_API_KEY"; // API key for OpenWeatherMap API, Replace with your API KEY

// Function to show loading spinner
const showLoading = () => {
    currentWeatherDiv.innerHTML = `<div class="loading-spinner"></div>`;
    weatherCardsDiv.innerHTML = "";
};

// Function to hide loading spinner
const hideLoading = () => {
    const spinner = document.querySelector(".loading-spinner");
    if (spinner) spinner.remove();
};

// Function to get weather-specific icon, animation, and background
const getWeatherDetailsWithEffects = (weatherCondition, iconCode) => {
    const isDay = iconCode.includes("d"); // Check if it's day or night

    const weatherEffects = {
        Clear: {
            icon: isDay ? "β˜€οΈ" : "πŸŒ™",
            animation: isDay ? "sunny" : "night",
            background: isDay ? "linear-gradient(135deg, #ff9a9e, #fad0c4)" : "linear-gradient(135deg, #0f0c29, #302b63)",
        },
        Clouds: {
            icon: isDay ? "☁️" : "☁️",
            animation: isDay ? "cloudy" : "cloudy-night",
            background: isDay ? "linear-gradient(135deg, #bdc3c7, #2c3e50)" : "linear-gradient(135deg, #1e3c72, #2a5298)",
        },
        Rain: {
            icon: "🌧️",
            animation: "rainy",
            background: "linear-gradient(135deg, #4da0b0, #d39d38)",
        },
        Snow: {
            icon: "❄️",
            animation: "snowy",
            background: "linear-gradient(135deg, #e6e9f0, #eef1f5)",
        },
        Thunderstorm: {
            icon: "β›ˆοΈ",
            animation: "stormy",
            background: "linear-gradient(135deg, #0f0c29, #302b63)",
        },
        Drizzle: {
            icon: "🌦️",
            animation: "drizzly",
            background: "linear-gradient(135deg, #89f7fe, #66a6ff)",
        },
        Mist: {
            icon: "🌫️",
            animation: "misty",
            background: "linear-gradient(135deg, #d3cce3, #e9e4f0)",
        },
        default: {
            icon: "🌈",
            animation: "default",
            background: "linear-gradient(135deg, #1e3c72, #2a5298)",
        },
    };

    return weatherEffects[weatherCondition] || weatherEffects.default;
};

// Function to create weather card
const createWeatherCard = (cityName, weatherItem, index) => {
    const weatherCondition = weatherItem.weather[0].main;
    const iconCode = weatherItem.weather[0].icon; // Get the icon code (e.g., "01d" or "01n")
    const { icon, animation, background } = getWeatherDetailsWithEffects(weatherCondition, iconCode);

    if (index === 0) {
        return `<div class="details" style="background: ${background};">
            <h2>${cityName} (${weatherItem.dt_txt.split(" ")[0]})</h2>
            <h6>Temperature: ${(weatherItem.main.temp - 273.15).toFixed(2)}Β°C</h6>
            <h6>Wind: ${weatherItem.wind.speed} M/S</h6>
            <h6>Humidity: ${weatherItem.main.humidity}%</h6>
            <h6>Visibility: ${(weatherItem.visibility / 1000).toFixed(1)} km</h6>
            <h6>Pressure: ${weatherItem.main.pressure} hPa</h6>
        </div>
        <div class="icon">
            <span class="weather-icon ${animation}">${icon}</span>
            <h6>${weatherItem.weather[0].description}</h6>
        </div>`;
    } else {
        return `<li class="card" style="background: ${background};">
            <h3>(${weatherItem.dt_txt.split(" ")[0]})</h3>
            <span class="weather-icon ${animation}">${icon}</span>
            <h6>Temp: ${(weatherItem.main.temp - 273.15).toFixed(2)}Β°C</h6>
            <h6>Wind: ${weatherItem.wind.speed} M/S</h6>
            <h6>Humidity: ${weatherItem.main.humidity}%</h6>
            <h6>Visibility: ${(weatherItem.visibility / 1000).toFixed(1)} km</h6>
            <h6>Pressure: ${weatherItem.main.pressure} hPa</h6>
        </li>`;
    }
};

// Function to fetch weather details
const getWeatherDetails = (cityName, latitude, longitude) => {
    const WEATHER_API_URL = `https://api.openweathermap.org/data/2.5/forecast?lat=${latitude}&lon=${longitude}&appid=${API_KEY}`;
    
    showLoading(); // Show loading spinner

    fetch(WEATHER_API_URL)
        .then(response => {
            if (!response.ok) throw new Error("Network response was not ok");
            return response.json();
        })
        .then(data => {
            console.log("API Response:", data); // Debugging
            const uniqueForecastDays = [];
            const fiveDaysForecast = data.list.filter(forecast => {
                const forecastDate = new Date(forecast.dt_txt).getDate();
                if (!uniqueForecastDays.includes(forecastDate)) {
                    return uniqueForecastDays.push(forecastDate);
                }
            });
            
            cityInput.value = "";
            currentWeatherDiv.innerHTML = "";
            weatherCardsDiv.innerHTML = "";
            
            fiveDaysForecast.forEach((weatherItem, index) => {
                const html = createWeatherCard(cityName, weatherItem, index);
                if (index === 0) {
                    currentWeatherDiv.insertAdjacentHTML("beforeend", html);
                } else {
                    weatherCardsDiv.insertAdjacentHTML("beforeend", html);
                }
            });
        })
        .catch(error => {
            console.error("Error fetching weather data:", error); // Debugging
            alert("An error occurred while fetching the weather forecast!");
        })
        .finally(() => {
            hideLoading(); // Hide loading spinner
        });
};

// Function to fetch city coordinates
const getCityCoordinates = () => {
    const cityName = cityInput.value.trim();
    if (cityName === "") return alert("Please enter a city name");
    
    showLoading(); // Show loading spinner

    const API_URL = `https://api.openweathermap.org/geo/1.0/direct?q=${cityName}&limit=1&appid=${API_KEY}`;
    
    fetch(API_URL)
        .then(response => {
            if (!response.ok) throw new Error("Network response was not ok");
            return response.json();
        })
        .then(data => {
            if (!data.length) return alert(`No coordinates found for ${cityName}`);
            const { lat, lon, name } = data[0];
            getWeatherDetails(name, lat, lon);
        })
        .catch(error => {
            console.error("Error fetching coordinates:", error); // Debugging
            alert("An error occurred while fetching the coordinates!");
        })
        .finally(() => {
            hideLoading(); // Hide loading spinner
        });
};

// Function to fetch user coordinates
const getUserCoordinates = () => {
    showLoading(); // Show loading spinner

    navigator.geolocation.getCurrentPosition(
        position => {
            const { latitude, longitude } = position.coords;
            const API_URL = `https://api.openweathermap.org/geo/1.0/reverse?lat=${latitude}&lon=${longitude}&limit=1&appid=${API_KEY}`;
            
            fetch(API_URL)
                .then(response => {
                    if (!response.ok) throw new Error("Network response was not ok");
                    return response.json();
                })
                .then(data => {
                    const { name } = data[0];
                    getWeatherDetails(name, latitude, longitude);
                })
                .catch(error => {
                    console.error("Error fetching city name:", error); // Debugging
                    alert("An error occurred while fetching the city name!");
                })
                .finally(() => {
                    hideLoading(); // Hide loading spinner
                });
        },
        error => {
            if (error.code === error.PERMISSION_DENIED) {
                alert("Geolocation request denied. Please reset location permission to grant access again.");
            } else {
                alert("Geolocation request error. Please reset location permission.");
            }
            hideLoading(); // Hide loading spinner
        }
    );
};

// Event listeners
searchButton.addEventListener("click", getCityCoordinates);
locationButton.addEventListener("click", getUserCoordinates);
    

Screenshots/Output

Weather App Screenshot