Build an Animated Weather App in 2025: Step-by-Step Guide with Code

Introduction

Have you ever wanted to build a weather app that’s both functional and visually stunning? 🌦️ In 2025, web development is all about interactive, animated experiences. Today, I’ll show you how to build a fully animated weather app using HTML, Tailwind CSS, and JavaScript.

By the end of this tutorial, you’ll have a beautiful, responsive weather app ready to showcase on GitHub or your portfolio. Let’s bring the weather to life in your browser! ☀️🌧️❄️

Project Overview: What You’ll Build

  • Fully animated weather app with dynamic backgrounds.

  • Supports multiple weather types: sunny, cloudy, rainy, snowy, thunderstorm, mist.

  • Responsive design using Tailwind CSS.

  • Frosted glass effect container for modern UI.

  • Canvas-based animations for realistic weather visuals.

  • Placeholder for mock weather data.

  • Demo Project: Click Here to check the demo 

Step 1: Set Up Your Project

Create a folder called animated-weather-app and inside it:

				
					animated-weather-app/
│-- index.html
│-- app.js
│-- style.css

				
			

Include Tailwind CSS via CDN in your HTML and Font Awesome for icons.

Step 2: HTML Structure

				
					<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Animated Weather App</title>
    <!-- Tailwind CSS CDN -->
    <!-- Font Awesome for icons -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<body>
        <canvas id="weatherCanvas"></canvas>
            <p>Animated Weather</p>
                <input type="text" id="cityInput" placeholder="Enter weather type (e.g., sun, rain)">
                <button id="searchButton">
                     Animate
                </button>
                <h2 id="cityName"></h2>
                <p id="temperature"></p>
                <p id="description"></p>
            <p id="errorMessage"></p>
        <!-- House Image added here -->
        <img data-opt-id=266874151  data-opt-src="three-dimensional-real-estate-icon-mock-up.png"  decoding="async" src="data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%20100%%20100%%22%20width%3D%22100%%22%20height%3D%22100%%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22100%%22%20height%3D%22100%%22%20fill%3D%22transparent%22%2F%3E%3C%2Fsvg%3E" alt="House Icon">
</body>
</html>
				
			

Step 3: CSS Style

				
					    <style>
        /* Custom styles for the app */
        body {
            font-family: 'Inter', sans-serif;
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            /* Transparent gradient background for the body */
            background: linear-gradient(135deg, rgba(135, 206, 235, 0.7) 0%, rgba(106, 128, 154, 0.7) 100%);
            transition: background 0.8s ease-in-out; /* Slower transition for background */
            overflow: hidden; /* Prevent scroll for canvas animations */
            padding-top: 50px; /* At least 50px space from top */
            padding-bottom: 50px; /* At least 50px space from bottom */
            box-sizing: border-box; /* Include padding in height calculation */
        }
        .weather-container {
            position: relative;
            width: 400px; /* Set a fixed width for desktop */
            aspect-ratio: 9 / 16; /* Enforce a 9:16 aspect ratio (makes it a vertical box) */
            max-height: calc(100vh - 100px); /* Constrain height to fit within 50px top/bottom padding */
            background: rgba(255, 255, 255, 0.2); /* Semi-transparent background for the container */
            backdrop-filter: blur(10px); /* Frosted glass effect */
            border-radius: 20px;
            box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3); /* More prominent shadow */
            padding: 30px;
            text-align: center;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: space-between; /* Distribute space vertically within the container */
            animation: fadeInScale 0.8s ease-out forwards; /* Initial container animation */
            border: 1px solid rgba(255, 255, 255, 0.3); /* Subtle white border */
        }
        @keyframes fadeInScale {
            from { opacity: 0; transform: scale(0.9); }
            to { opacity: 1; transform: scale(1); }
        }
        canvas {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 0; /* Behind other content */
            pointer-events: none; /* Allow clicks to pass through */
        }
        .content-wrapper {
            position: relative;
            z-index: 1; /* Above canvas */
            width: 100%;
            display: flex;
            flex-direction: column;
            align-items: center;
            color: white; /* Ensure text is visible on transparent background */
            text-shadow: 0 1px 3px rgba(0,0,0,0.2);
            flex-grow: 1; /* Allow content to grow and push elements apart */
            justify-content: space-around; /* Distribute space within content */
            padding-bottom: 80px; /* Add padding to make space for the house image */
        }
        .input-section {
            display: flex;
            gap: 10px;
            margin-bottom: 25px;
            width: 100%;
            justify-content: center;
        }
        .input-section input {
            flex-grow: 1;
            padding: 12px 18px;
            border: 1px solid rgba(255, 255, 255, 0.4); /* Lighter border for transparency */
            background: rgba(255, 255, 255, 0.1); /* Transparent input background */
            color: white; /* Input text color */
            border-radius: 10px;
            font-size: 1rem;
            outline: none;
            transition: border-color 0.3s ease, box-shadow 0.3s ease, background 0.3s ease;
        }
        .input-section input::placeholder {
            color: rgba(255, 255, 255, 0.7); /* Placeholder color */
        }
        .input-section input:focus {
            border-color: rgba(255, 255, 255, 0.8); /* Brighter border on focus */
            box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.3); /* Focus ring */
            background: rgba(255, 255, 255, 0.2); /* Slightly less transparent on focus */
        }
        .input-section button {
            padding: 12px 20px;
            background-color: rgba(99, 102, 241, 0.8); /* Indigo-500 with transparency */
            color: white;
            border-radius: 10px;
            border: none;
            cursor: pointer;
            font-size: 1rem;
            transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
        }
        .input-section button:hover {
            background-color: rgba(79, 70, 229, 0.9); /* Indigo-600 with transparency */
            transform: translateY(-3px); /* More pronounced lift */
            box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3);
        }
        .input-section button:active {
            transform: translateY(0);
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }
        .weather-info {
            margin-top: 20px;
            color: white; /* Ensure text is visible */
            text-shadow: 0 1px 3px rgba(0,0,0,0.3);
            animation: fadeInSlideUp 0.6s ease-out forwards; /* Animate weather info appearance */
            opacity: 0; /* Start hidden for animation */
            transform: translateY(20px);
        }
        @keyframes fadeInSlideUp {
            from { opacity: 0; transform: translateY(20px); }
            to { opacity: 1; transform: translateY(0); }
        }
        .city-name {
            font-size: 2.8rem; /* Slightly larger */
            font-weight: 700;
            margin-bottom: 5px;
            color: white;
        }
        .temperature {
            font-size: 4.5rem; /* Larger temperature */
            font-weight: 800;
            margin-bottom: 10px;
            color: white;
            animation: pulseTemp 2s infinite alternate; /* Pulsing temperature */
        }
        @keyframes pulseTemp {
            from { transform: scale(1); }
            to { transform: scale(1.03); }
        }
        .description {
            font-size: 1.6rem; /* Slightly larger */
            font-weight: 500;
            margin-bottom: 20px;
            text-transform: capitalize;
            color: rgba(255, 255, 255, 0.9);
        }
        .error-message {
            color: #ffdddd; /* Lighter red for transparency */
            font-size: 1.1rem;
            margin-top: 20px;
            font-weight: 600;
            animation: shake 0.5s ease-in-out; /* Shake on error */
            text-shadow: none; /* No text shadow for error */
        }
        @keyframes shake {
            0%, 100% { transform: translateX(0); }
            20%, 60% { transform: translateX(-5px); }
            40%, 80% { transform: translateX(5px); }
        }
        .loading-spinner {
            border: 4px solid rgba(255, 255, 255, 0.3); /* Lighter spinner */
            border-left-color: rgba(255, 255, 255, 0.8); /* White spinner part */
            border-radius: 50%;
            width: 35px; /* Slightly larger spinner */
            height: 35px;
            animation: spin 1s linear infinite;
            margin: 20px auto;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        .house-image {
            position: absolute;
            bottom: -20px; /* Adjust as needed to position the house */
            left: 50%;
            transform: translateX(-50%);
            width: 90%; /* Adjust width for responsiveness */
            max-width: 300px; /* Max width to prevent it from being too big */
            z-index: -1; /* Changed z-index to send it to the back */
            pointer-events: none; /* Allow clicks to pass through */
            filter: drop-shadow(0 10px 15px rgba(0,0,0,0.3)); /* Add a subtle shadow */
        }
        /* Responsive adjustments */
        @media (max-width: 600px) {
            .weather-container {
                width: 95%; /* Still responsive width */
                height: auto; /* Let height adjust based on aspect-ratio */
                aspect-ratio: 9 / 16; /* Maintain 9:16 on small screens */
                max-height: calc(100vh - 100px); /* Apply max-height here too */
                padding: 20px; /* Keep responsive padding */
            }
            .city-name {
                font-size: 2.2rem;
            }
            .temperature {
                font-size: 3.8rem;
            }
            .description {
                font-size: 1.3rem;
            }
            .input-section {
                flex-direction: column;
                gap: 15px;
            }
            .input-section button {
                width: 100%;
            }
            .house-image {
                width: 80%; /* Adjust house size for smaller screens */
                bottom: -10px; /* Adjust position for smaller screens */
            }
        }
        /* Dynamic background colors based on weather - now applied to the body's gradient */
        body.sunny { background: linear-gradient(135deg, rgba(135, 206, 235, 0.7) 0%, rgba(255, 215, 0, 0.7) 100%); } /* Sky Blue to Gold */
        body.clouds { background: linear-gradient(135deg, rgba(176, 196, 222, 0.7) 0%, rgba(106, 128, 154, 0.7) 100%); } /* Light Steel Blue to Darker Blue-Gray */
        body.rain { background: linear-gradient(135deg, rgba(106, 128, 154, 0.7) 0%, rgba(74, 84, 98, 0.7) 100%); } /* Darker Blue-Gray to even darker */
        body.snow { background: linear-gradient(135deg, rgba(224, 255, 255, 0.7) 0%, rgba(176, 196, 222, 0.7) 100%); } /* Light Cyan to Light Steel Blue */
        body.thunderstorm { background: linear-gradient(135deg, rgba(74, 74, 74, 0.7) 0%, rgba(20, 20, 20, 0.7) 100%); } /* Dark Gray to very dark */
        body.drizzle { background: linear-gradient(135deg, rgba(169, 188, 207, 0.7) 0%, rgba(106, 128, 154, 0.7) 100%); } /* Medium Blue-Gray to Darker Blue-Gray */
        body.mist, body.smoke, body.haze, body.dust, body.fog, body.sand, body.ash, body.squall, body.tornado { background: linear-gradient(135deg, rgba(178, 190, 181, 0.7) 0%, rgba(120, 130, 135, 0.7) 100%); } /* Ash Gray to a darker gray */
    </style>
				
			

Step 4: JavaScript Animations

				
					    
				
			

Step 05: Styling Tips

  • Dynamic backgrounds: Change the gradient based on weather type using JS.

  • Responsive design: Tailwind handles responsiveness automatically.

  • Frosted glass effect: Already applied in .content-wrapper for a modern look.

Conclusion

Congratulations! 🎉 You’ve built an animated weather app in 2025 with HTML, Tailwind CSS, and JavaScript.

Key takeaways:

  • Canvas animations for sun, rain, and snow.

  • Frosted glass container for modern UI.

  • Responsive layout with Tailwind CSS.

  • Easy to expand with APIs for live weather data.

Share your project on GitHub and let others experience your interactive weather app! ☀️🌧️❄️

SHARE

Leave a Reply

Your email address will not be published. Required fields are marked *