How To Make a Weather App using HTML CSS & JavaScript | Weather API JavaScript Project


Welcome to my website U-GINE MEDIA. In this website I teach tutorials and share source code on some programming (HTML, CSS, JavaScript, React) tutorials. 

Before we get started, do well to subscribe to my channel to never miss out on any update that I post every single day.

You can also visit my GitHub where I upload all the code I have.

In this tutorial topic "Build a Complete Real-Time Weather Website Using HTML CSS JavaScript", we will learn how to create it following these simple easy steps.

Share this to your programmer friends. If y'all learnt something today, make sure to let me in the comments. 

Get Source Code.

If interested in watching the video before running the code below, you can click the play button to get started.



Code Begins

  1. First, create a folder with any name you like. Then, make the necessary files inside it.
  2. Create a file called index.html to serve as the main file.
  3. Create a file called style.css for the CSS code.
  4. Create a file called script.js for the JavaScript code.

To start, add the following HTML codes to your index.html file. These codes include essential HTML markup with different semantic tags, such as div, form, input, button, image, etc., to build the website layout. 


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Weather Dashboard</title>
    <link rel="stylesheet" href="style.css" />
    <link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet" />
  </head>
  <body>
    <div class="weather-app">
      <div class="main-weather" data-aos="zoom-in" data-aos-duration="2000">
        <div class="location-bar">
          <ion-icon name="location-outline"></ion-icon>
          <input value="Hanoi, Vietnam" id="location-input" />
        </div>
        <div class="temperature">28°</div>
        <div class="condition">Rainy Day</div>
        <p class="description">
          Today, expect a rainy day with temperatures reaching a maximum of
          28°C. Make sure to grab your umbrella and raincoat before heading out.
        </p>
      </div>

      <div class="details-grid">
        <div
          class="card detail-card"
          data-aos="flip-left"
          data-aos-duration="2000"
          data-aos-delay="500"
        >
          <div class="header">
            <ion-icon name="thermometer-outline"></ion-icon>
            <span>FEELS LIKE</span>
          </div>
          <div class="value">30°</div>
          <p class="subtext">Humidity is making it feel warmer</p>
        </div>
        <div
          class="card detail-card"
          data-aos="flip-left"
          data-aos-duration="2000"
          data-aos-delay="700"
        >
          <div class="header">
            <ion-icon name="rainy-outline"></ion-icon>
            <span>PRECIPITATION</span>
          </div>
          <div class="value">2.3"</div>
          <p class="subtext">2" expected in next 24h</p>
        </div>
        <div
          class="card detail-card"
          data-aos="flip-left"
          data-aos-duration="2000"
          data-aos-delay="900"
        >
          <div class="header">
            <ion-icon name="eye-outline"></ion-icon>
            <span>VISIBILITY</span>
          </div>
          <div class="value">6 mi</div>
        </div>
        <div
          class="card detail-card"
          data-aos="flip-left"
          data-aos-duration="2000"
          data-aos-delay="1100"
        >
          <div class="header">
            <ion-icon name="water-outline"></ion-icon>
            <span>HUMIDITY</span>
          </div>
          <div class="value">82%</div>
          <p class="subtext">The dew point is 25° right now</p>
        </div>
      </div>

      <!-- Hourly Forecast -->
      <div
        class="card hourly-forecast"
        data-aos="flip-up"
        data-aos-duration="2000"
        data-aos-delay="1300"
      >
        <div class="forecast-container">
          <div class="forecast-header">
            <ion-icon name="timer-outline"></ion-icon>
            <span>HOURLY FORECAST</span>
          </div>
          <div class="forecast-content">
            <div class="forecast-item">
              <div class="label">Now</div>
              <ion-icon name="rainy-outline"></ion-icon>
              <div class="temp temperature">28°</div>
            </div>
            <div class="forecast-item">
              <div class="label">15:00</div>
              <ion-icon name="rainy-outline"></ion-icon>
              <div class="temp">28°</div>
            </div>
            <div class="forecast-item">
              <div class="label">16:00</div>
              <ion-icon name="thunderstorm-outline"></ion-icon>
              <div class="temp">26°</div>
            </div>
            <div class="forecast-item">
              <div class="label">17:00</div>
              <ion-icon name="thunderstorm-outline"></ion-icon>
              <div class="temp">29°</div>
            </div>
            <div class="forecast-item">
              <div class="label">18:00</div>
              <ion-icon name="sunny-outline"></ion-icon>
              <div class="temp">32°</div>
            </div>
            <div class="forecast-item">
              <div class="label">19:00</div>
              <ion-icon name="partly-sunny-outline"></ion-icon>
              <div class="temp">24°</div>
            </div>
          </div>
        </div>
      </div>

      <!-- Ten Days Forecast -->
      <div
        class="card ten-day-forecast"
        data-aos="flip-up"
        data-aos-duration="2000"
        data-aos-delay="1500"
      >
        <div class="forecast-container">
          <div class="forecast-header">
            <ion-icon name="calendar-clear-outline"></ion-icon>
            <span>10-DAY FORECAST</span>
          </div>
          <div class="forecast-content">
            <div class="forecast-item">
              <div class="label">Today</div>
              <div class="sublabel">16/09</div>
              <ion-icon name="rainy-outline"></ion-icon>
              <div class="temp temperature">28°</div>
            </div>
            <div class="forecast-item">
              <div class="label">Thu</div>
              <div class="sublabel">17/09</div>
              <ion-icon name="rainy-outline"></ion-icon>
              <div class="temp">28°</div>
            </div>
            <div class="forecast-item">
              <div class="label">Fri</div>
              <div class="sublabel">18/09</div>
              <ion-icon name="partly-sunny-outline"></ion-icon>
              <div class="temp">24°</div>
            </div>
            <div class="forecast-item">
              <div class="label">Sat</div>
              <div class="sublabel">19/09</div>
              <ion-icon name="sunny-outline"></ion-icon>
              <div class="temp">32°</div>
            </div>
            <div class="forecast-item">
              <div class="label">Sun</div>
              <div class="sublabel">20/09</div>
              <ion-icon name="thunderstorm-outline"></ion-icon>
              <div class="temp">19°</div>
            </div>
            <div class="forecast-item">
              <div class="label">Mon</div>
              <div class="sublabel">21/09</div>
              <ion-icon name="rainy-outline"></ion-icon>
              <div class="temp">28°</div>
            </div>
          </div>
        </div>
      </div>

      <div class="other-details-grid">
        <div
          class="card uv-index"
          data-aos="flip-right"
          data-aos-duration="2000"
          data-aos-delay="1700"
        >
          <div class="header">
            <ion-icon name="thermometer-outline"></ion-icon>
            <span>UV INDEX</span>
          </div>
          <div class="value">3</div>
          <div class="subtext">Moderate</div>
          <div class="progress-bar">
            <div class="progress"></div>
          </div>
          <p class="subtext" style="margin-top: 0.5rem">
            Use sun protection until 16:00
          </p>
        </div>
        <div
          class="card wind-card"
          data-aos="flip-right"
          data-aos-duration="2000"
          data-aos-delay="1900"
        >
          <div class="header">
            <ion-icon name="speedometer-outline"></ion-icon>
            <span>WIND</span>
          </div>
          <div class="wind-details">
            <div class="wind-info">
              <div class="value">3 <span>MPH</span></div>
              <div class="subtext">Wind</div>
              <div class="value" style="margin-top: 1rem">
                9 <span>MPH</span>
              </div>
              <div class="subtext">Gusts</div>
            </div>
            <div class="compass">
              <div class="direction n">N</div>
              <div class="direction e">E</div>
              <div class="direction s">S</div>
              <div class="direction w">W</div>
              <div class="arrow"></div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
    <script
      type="module"
      src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"
    ></script>
    <script
      nomodule
      src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"
    ></script>
    <script src="script.js"></script>
  </body>
</html>


Next, add the following CSS codes to your style.css file to make website beautiful and user-friendly. You can customize the different CSS properties, such as color, background, font, etc., to give a personalized touch to your website. 



@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Poppins:wght@300;400;500&display=swap");

:root {
  --bg-color: #1a1a1a;
  --text-primary: #ffffff;
  --text-secondary: #b3b3b3;
  --card-bg: rgba(38, 38, 38, 0.5);
  --card-border: rgba(255, 255, 255, 0.1);
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: "Poppins", sans-serif;
  background-image: url("./bg.jpg");
  background-size: cover;
  background-position: center;
  color: var(--text-primary);
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  padding: 2rem;
}

.weather-app {
  width: 100%;
  max-width: 1200px;
  background: rgba(0, 0, 0, 0.2);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--card-border);
  border-radius: 20px;
  padding: 2rem;
  display: grid;
  grid-template-columns: 2fr 2fr;
  grid-template-rows: auto auto 1fr;
  gap: 1.5rem;
  grid-template-areas:
    "main-weather hourly-forecast"
    "main-weather ten-day-forecast"
    "details ten-day-forecast"
    "details other-details";
}

.card {
  background-color: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 16px;
  padding: 1.25rem;
  backdrop-filter: blur(10px);
}

/* --- Main Weather Display --- */
.main-weather {
  grid-area: main-weather;
  padding: 0;
  display: flex;
  flex-direction: column;
  text-align: center;
}

.location-bar {
  background-color: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  padding: 0.5rem 1rem;
  color: var(--text-primary);
  margin-bottom: 2rem;
  width: 100%;
  display: inline-flex;
  gap: 2px;
  align-items: center;
  align-self: flex-start;
}

.location-bar ion-icon {
  margin-right: 0.5rem;
  font-size: 1.5rem;
}

.location-bar input {
  background: transparent;
  width: 100%;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-size: 1rem;
}

.main-weather .temperature {
  font-size: 7rem;
  font-weight: 500;
  line-height: 1;
}

.main-weather .condition {
  font-size: 2.5rem;
  font-weight: 400;
  margin-top: 0.5rem;
}

.main-weather .description {
  color: var(--text-primary);
  font-size: 1rem;
  margin-top: auto;
  padding-top: 2rem;
  margin: 0 auto;
}

/* --- Small Detail Cards --- */
.details-grid {
  grid-area: details;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
}

.detail-card {
  display: flex;
  flex-direction: column;
}

.detail-card .header {
  color: var(--text-secondary);
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  margin-bottom: 0.1rem;
  display: flex;
  align-items: center;
  gap: 0.1rem;
}

.detail-card .header ion-icon {
  margin-right: 0.1rem;
  font-size: 1.2rem;
}

.detail-card .value {
  font-size: 2rem;
  font-weight: 500;
}

.detail-card .subtext {
  color: var(--text-primary);
  font-size: 0.8rem;
  margin-top: 0.5rem;
}

/* --- Forecast Sections --- */
.hourly-forecast {
  grid-area: hourly-forecast;
}
.ten-day-forecast {
  grid-area: ten-day-forecast;
}
.other-details-grid {
  grid-area: other-details;
}

.forecast-container {
  display: flex;
  flex-direction: column;
}

.forecast-header {
  color: var(--text-secondary);
  font-size: 0.8rem;
  font-weight: 500;
  text-transform: uppercase;
  margin-bottom: 1rem;
  padding-bottom: 0.5rem;
  display: flex;
  align-items: center;
  border-bottom: 2px solid var(--card-border);
}

.forecast-header ion-icon {
  margin-right: 0.4rem;
  font-size: 1.2rem;
}

.forecast-content {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
}

.forecast-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0.5rem 0;
  color: var(--text-primary);
  font-size: 0.9rem;
  flex-grow: 1;
}

.forecast-item:nth-child(1) {
  background: var(--card-bg);
  border-radius: 10px;
}

.forecast-item .label {
  margin-bottom: 0.75rem;
}

.forecast-item .temp {
  color: var(--text-primary);
  font-size: 1.25rem;
  font-weight: 500;
  margin-top: 0.75rem;
}

.forecast-item ion-icon {
  font-size: 1.75rem;
  color: var(--text-primary);
}

.ten-day-forecast .forecast-item {
  padding: 0.75rem 0;
}

/* --- UV Index & Wind --- */
.other-details-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
}

/* UV Index */
.uv-index .header {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  margin-bottom: 0.5rem;
}

.uv-index .header ion-icon {
  font-size: 1.3rem;
}

.uv-index .progress-bar {
  width: 100%;
  height: 8px;
  background-color: rgba(255, 255, 255, 0.2);
  border-radius: 4px;
  margin-top: 0.5rem;
  overflow: hidden;
  position: relative;
}

.uv-index .progress {
  width: 44%;
  height: 100%;
  background: linear-gradient(to right, #69d164, #f7d45c, #f75c4c);
  border-radius: 4px;
}

/* Wind Compass */
.wind-card .header {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  margin-bottom: 0.5rem;
}

.wind-card .value {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
}
.wind-card .value span {
  font-size: 1rem;
  color: var(--text-secondary);
  padding-bottom: 0.3rem;
}

.wind-details {
  display: flex;
  justify-content: space-between;
}

.wind-info {
  display: flex;
  flex-direction: column;
}

.compass {
  width: 80px;
  height: 80px;
  border: 2px solid var(--card-border);
  border-radius: 50%;
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 0.7rem;
  color: var(--text-primary);
}

.compass .direction {
  position: absolute;
}

.compass .n {
  top: 5px;
}
.compass .e {
  right: 5px;
}
.compass .s {
  bottom: 5px;
}
.compass .w {
  left: 5px;
}

.compass .arrow {
  position: relative;
  top: -0.3rem;
  right: -0.3rem;
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-bottom: 30px solid var(--text-primary);
  transform: rotate(45deg);
}

/* Responsive adjustments */
@media (max-width: 1024px) {
  body {
    padding: 1rem;
  }
  .weather-app {
    grid-template-columns: 1fr;
    grid-template-areas:
      "main-weather"
      "details"
      "hourly-forecast"
      "ten-day-forecast"
      "other-details";
  }
  .forecast-content {
    overflow-x: auto;
    padding-bottom: 1rem;
  }
  .forecast-item {
    min-width: 60px;
  }
}

@media (max-width: 768px) {
  .details-grid,
  .other-details-grid {
    grid-template-columns: 1fr;
  }
  .main-weather .temperature {
    font-size: 5rem;
  }
  .main-weather .condition {
    font-size: 1.75rem;
  }
}


Finally, add the following JavaScript code to your script.js file to make your work functional. This code handles various functions, even listeners, input handling, etc.


document.addEventListener("DOMContentLoaded", function () {
  //Tilt Weather Dashboard
  (function () {
    const scene = document.querySelector(".weather-app");
    window.addEventListener("mousemove", (e) => {
      const rect = scene.getBoundingClientRect();
      const x = (e.clientX - rect.left) / rect.width - 0.5;
      const y = (e.clientY - rect.top) / rect.height - 0.5;
      scene.style.transform = `perspective(900px) rotateY(${
        x * 2
      }deg) rotateX(${y * -1.2}deg)`;
    });
    window.addEventListener("mouseleave", () => (scene.style.transform = ""));
  })();

  const APIKey = "0783b4785d0fb3d7913e7bb878c54bff";
  const temperature = document.querySelectorAll(".temperature");
  let condition = document.querySelector(".condition");

  // Check if geolocation is supported
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(
      async (position) => {
        const latitude = position.coords.latitude;
        const longitude = position.coords.longitude;

        const weatherAPI = await fetch(
          `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${APIKey}`
        );
        const weatherResult = await weatherAPI.json();
        const data = Math.round(weatherResult.main.temp - 273.15);
        temperature.forEach((temp) => {
          temp.innerHTML = data + "&deg;";
        });
        switch (true) {
          case data >= 0 && data <= 10:
            condition.textContent = "Very Cold";
            break;

          case data >= 11 && data <= 16:
            condition.textContent = "Cloudy";
            break;

          case data >= 17 && data <= 22:
            condition.textContent = "Partly cloudy";
            break;

          case data >= 23 && data <= 28:
            condition.textContent = "Sunny Day";
            break;

          case data >= 29 && data <= 34:
            condition.textContent = "Mostly Sunny";
            break;

          case data >= 35:
            condition.textContent = "Heatwave";
            break;

          default:
            condition.textContent = "Unknown weather";
        }
      },
      (error) => {
        console.error("Error getting location:", error.message);
      }
    );
  } else {
    console.log("Geolocation is not supported by this browser.");
  }

  async function searchWeatherLocation(location) {
    const apiCall = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${APIKey}`
    );
    const apiResult = await apiCall.json();
    const data = Math.round(apiResult.main.temp - 273.15);
    temperature.forEach((temp) => {
      temp.innerHTML = data + "&deg;";
    });
    switch (true) {
      case data >= 0 && data <= 10:
        condition.textContent = "Very Cold";
        break;

      case data >= 11 && data <= 16:
        condition.textContent = "Cloudy";
        break;

      case data >= 17 && data <= 22:
        condition.textContent = "Partly cloudy";
        break;

      case data >= 23 && data <= 28:
        condition.textContent = "Sunny Day";
        break;

      case data >= 29 && data <= 34:
        condition.textContent = "Mostly Sunny";
        break;

      case data >= 35:
        condition.textContent = "Heatwave";
        break;

      default:
        condition.textContent = "Unknown weather";
    }
  }

  const locationInput = document.getElementById("location-input");
  locationInput.addEventListener("keydown", (e) => {
    e.key === "Enter" && searchWeatherLocation(e.target.value);
  });
});

AOS.init();


Conclusion and Final word

Now, we have reached the end of our code, for any information, you can comment any problem or code-not-working issues to resolve it, am always with you. You can also visit my YouTube Channel for more updates on HTML, CSS & JavaScript Designs. I believe you can create "a Complete Real-Time Weather Website Using HTML CSS JavaScript". and do so much more with codes. Go extremely far in exploring the world of HTML, CSS & JavaScript, its gonna be fun, trust me 😃.

Happy Coding Journey 🚀

Post a Comment

0 Comments

Close Menu