/** * Twenty Twenty-Four functions and definitions * * @link https://developer.wordpress.org/themes/basics/theme-functions/ * * @package Twenty Twenty-Four * @since Twenty Twenty-Four 1.0 */ /** * Register block styles. */ if ( ! function_exists( 'twentytwentyfour_block_styles' ) ) : /** * Registers custom block styles. * * @since Twenty Twenty-Four 1.0 * @return void */ function twentytwentyfour_block_styles() { register_block_style( 'core/details', array( 'name' => 'arrow-icon-details', 'label' => __( 'Arrow icon', 'twentytwentyfour' ), /* * Styles for the custom Arrow icon style of the Details block */ 'inline_style' => ' .is-style-arrow-icon-details { padding-top: var(--wp--preset--spacing--10); padding-bottom: var(--wp--preset--spacing--10); } .is-style-arrow-icon-details summary { list-style-type: "\2193\00a0\00a0\00a0"; } .is-style-arrow-icon-details[open]>summary { list-style-type: "\2192\00a0\00a0\00a0"; }', ) ); register_block_style( 'core/post-terms', array( 'name' => 'pill', 'label' => __( 'Pill', 'twentytwentyfour' ), /* * Styles variation for post terms * https://github.com/WordPress/gutenberg/issues/24956 */ 'inline_style' => ' .is-style-pill a, .is-style-pill span:not([class], [data-rich-text-placeholder]) { display: inline-block; background-color: var(--wp--preset--color--base-2); padding: 0.375rem 0.875rem; border-radius: var(--wp--preset--spacing--20); } .is-style-pill a:hover { background-color: var(--wp--preset--color--contrast-3); }', ) ); register_block_style( 'core/list', array( 'name' => 'checkmark-list', 'label' => __( 'Checkmark', 'twentytwentyfour' ), /* * Styles for the custom checkmark list block style * https://github.com/WordPress/gutenberg/issues/51480 */ 'inline_style' => ' ul.is-style-checkmark-list { list-style-type: "\2713"; } ul.is-style-checkmark-list li { padding-inline-start: 1ch; }', ) ); register_block_style( 'core/navigation-link', array( 'name' => 'arrow-link', 'label' => __( 'With arrow', 'twentytwentyfour' ), /* * Styles for the custom arrow nav link block style */ 'inline_style' => ' .is-style-arrow-link .wp-block-navigation-item__label:after { content: "\2197"; padding-inline-start: 0.25rem; vertical-align: middle; text-decoration: none; display: inline-block; }', ) ); register_block_style( 'core/heading', array( 'name' => 'asterisk', 'label' => __( 'With asterisk', 'twentytwentyfour' ), 'inline_style' => " .is-style-asterisk:before { content: ''; width: 1.5rem; height: 3rem; background: var(--wp--preset--color--contrast-2, currentColor); clip-path: path('M11.93.684v8.039l5.633-5.633 1.216 1.23-5.66 5.66h8.04v1.737H13.2l5.701 5.701-1.23 1.23-5.742-5.742V21h-1.737v-8.094l-5.77 5.77-1.23-1.217 5.743-5.742H.842V9.98h8.162l-5.701-5.7 1.23-1.231 5.66 5.66V.684h1.737Z'); display: block; } /* Hide the asterisk if the heading has no content, to avoid using empty headings to display the asterisk only, which is an A11Y issue */ .is-style-asterisk:empty:before { content: none; } .is-style-asterisk:-moz-only-whitespace:before { content: none; } .is-style-asterisk.has-text-align-center:before { margin: 0 auto; } .is-style-asterisk.has-text-align-right:before { margin-left: auto; } .rtl .is-style-asterisk.has-text-align-left:before { margin-right: auto; }", ) ); } endif; add_action( 'init', 'twentytwentyfour_block_styles' ); /** * Enqueue block stylesheets. */ if ( ! function_exists( 'twentytwentyfour_block_stylesheets' ) ) : /** * Enqueues custom block stylesheets. * * @since Twenty Twenty-Four 1.0 * @return void */ function twentytwentyfour_block_stylesheets() { /** * The wp_enqueue_block_style() function allows us to enqueue a stylesheet * for a specific block. These will only get loaded when the block is rendered * (both in the editor and on the front end), improving performance * and reducing the amount of data requested by visitors. * * See https://make.wordpress.org/core/2021/12/15/using-multiple-stylesheets-per-block/ for more info. */ wp_enqueue_block_style( 'core/button', array( 'handle' => 'twentytwentyfour-button-style-outline', 'src' => get_parent_theme_file_uri( 'assets/css/button-outline.css' ), 'ver' => wp_get_theme( get_template() )->get( 'Version' ), 'path' => get_parent_theme_file_path( 'assets/css/button-outline.css' ), ) ); } endif; add_action( 'init', 'twentytwentyfour_block_stylesheets' ); /** * Register pattern categories. */ if ( ! function_exists( 'twentytwentyfour_pattern_categories' ) ) : /** * Registers pattern categories. * * @since Twenty Twenty-Four 1.0 * @return void */ function twentytwentyfour_pattern_categories() { register_block_pattern_category( 'twentytwentyfour_page', array( 'label' => _x( 'Pages', 'Block pattern category', 'twentytwentyfour' ), 'description' => __( 'A collection of full page layouts.', 'twentytwentyfour' ), ) ); } endif; add_action( 'init', 'twentytwentyfour_pattern_categories' ); Weather Web Project Using HTML CSS And Javascript | SANDIP KUMAR SINGH

Weather Web Project Using HTML CSS and Javascript

Creating a weather web Project application using HTML, CSS, and JavaScript involves integrating a weather API to fetch and display weather information

Weather Web Application

A weather web application created using HTML, CSS, and JavaScript allows users to check the weather conditions for a specific location. This application uses the OpenWeatherMap API to fetch weather data and display it on a web page. Here’s a breakdown of the components and functionality:

Sign Up for OpenWeatherMap API:

  • Go to OpenWeatherMap and sign up for a free API key.
  • Once you have your API key, you can start building your weather web application.

CSS (style.css):

  • The CSS file is responsible for styling the web page. In this example, it provides basic formatting, such as centering text and setting margins.

JavaScript (script.js):

  • The JavaScript file contains the logic for fetching weather data and updating the web page with the retrieved information.
  • It defines a getWeather function that is called when the “Get Weather” button is clicked.
  • The function uses the fetch API to make a request to the OpenWeatherMap API with the user’s entered location.
  • Upon receiving a response, it parses the JSON data and updates the content on the web page with details like city name, temperature, and weather description.
  • Error handling is implemented to handle situations where the API request fails.

Weather Web Project for Index.html

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <!-- primary meta tags  -->
    <title>weatherio</title>
    <meta name ="title" content="weatherio">
    <meta name ="description" content="weatherio is a weather app made by codewithsadee">
    <!-- favicon -->
    <link rel="shortcut icon" href="./favicon.svg" type="image/svg+xml">

    <!--google font  -->

    <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@400;600&display=swap" rel="stylesheet">

<link rel="stylesheet" href="./assets/css/style.css">
<script src="./assets/js/route.js" type="module"></script>

</head>
<body>

    <header class="header">
        <div class="container">
           <a href="#" class="logo">
            <img src="./assets/images/logo.png"  width="364"   height ="58"  alt="logo">
           </a>
           <div class="search-view " data-search-view>

            <div class="search-wrapper">
                <input type="search" name="search"  placeholder="search city..." autocomplete="off" class="search-field " data-search-field>
                <span class="m-icon leading-icon">search</span>


                <button class ="icon-btn leading-icon has-state" aria-label="close search" data-search-toggler>
                <span class="m-icon">arrow_back</span>
                </button>
            </div>

                <div class="  search-result" data-search-result></div>
               </div>


           <div class="header-actions">
            <button class=" icon-btn has-state" aria-label="open search" data-search-toggler>
                <span class="m-icon icon">search</span>
            </button>
            <a href="#/current-location" class="btn-primary has-state" data-current-location-btn  >
                <span class="m-icon">my_location</span>
                <span class="span">Current Location</span>
            </a>
           </div>
        </div>
    </header>

  <main>
    <article class="container" data-container>
        <div class="content-left">

        <section class="section current-weather" aria-label="current weather" data-current-weather> </section>

<!-- section  -->

<section class="section forecast" aria-labelledby="forecast-lable" data-5-day-forecast></section>


        </div>
<!--    -->

        <div class="content-right">
<!--           HIGHLIGHTs -->

  <section class="section highlights" aria-labelledby="highlights-label" data-highlights></section>


  <!--  per houre forecast -->


  <section class="section hourly-forecast" aria-label="hourly forecast" data-hourly-forecast></section>



  <!--
    #FOOTER
     -->
  <!-- <div class="card card-lg"> -->

  <!-- </div> -->

  <footer class="footer">
    <p class="body-3">Copyright 2023 rkcoder.tech. All Right Reserved</p>
    <p class="body-3">
        Powered by <a href="https://openweathermap.org/api" rel="noopener" title="Free Openweather Api" target="_blank">
        <img src="./assets/images/openweather.png"  width="150"  height="30"  loading="lazy" alt="Openweather"  >
        </a>
    </p>
 </footer>

        </div>

        <div class="loading" data-loading></div>

    </article>

  </main>

  <!--
    #404
   -->
<section class="error-content" data-error-content>
    <h2 class="heading">404</h2>
    <p class="body-1">Page not found !</p>
     <a href="#/weather?lat=25.4381302&lon=81.8338005" class="btn-primary">
        <span class="span"> Go Home</span>
     </a>

</section>


</body>
</html>   

Weather Web Project

Weather Web Project for Api.js

'use strict';

const api_key = '7775811b5bc22ad000d8cbdc6e86bda0';

// fetch data from server
export const fetchData = function (URL, callback) {
    fetch(`${URL}&appid=${api_key}`)
        .then(res => res.json())
        .then(data => callback(data))
}


export const url = {
    currentWeather(lat, lon) {
        return `https://api.openweathermap.org/data/2.5/weather?${lat}&${lon}&units=metric`
    },
    forecast(lat, lon) {
        return `https://api.openweathermap.org/data/2.5/forecast?${lat}&${lon}&units=metric`
    },
    airPollution(lat, lon) {
        return `https://api.openweathermap.org/data/2.5/air_pollution?${lat}&${lon}`
    },
    reverseGeo(lat, lon) {
        return `https://api.openweathermap.org/geo/1.0/reverse?${lat}&${lon}&limit=5`
     },
    /**
    *  @param {string} query Search query eg.: 'London', 'New york'
    **/
     geo(query) {
        return `https://api.openweathermap.org/geo/1.0/direct?q=${query}&limit=5`
     }
}

App.js

"use strict";

import { fetchData, url } from "./api.js";
import * as module from "./module.js";

/**
 * Add event listener on multiple elements
 * @param {NodeList} elements Elements node Array
 * @param {string} eventType event type eg: "click", "mouseover"
 * @param {fucntion} callback callback function
 */
const addEventOnElements = (elements, eventType, callback) => {
	for (const element of elements)
		element.addEventListener(eventType, callback);
};

/**
 * Toggle search in mobile devices
 */
const searchView = document.querySelector("[data-search-view]");
const searchTogglers = document.querySelectorAll("[data-search-toggler]");

const toggleSearch = () => searchView.classList.toggle("active");
addEventOnElements(searchTogglers, "click", toggleSearch);

/**
 * SEARCH INTEGRATION
 */
const searchField = document.querySelector("[data-search-field]");
const searchResult = document.querySelector("[data-search-result]");

let searchTimeout = null;
const searchTimeoutDuration = 500;

searchField.addEventListener("input", function () {
	searchTimeout ?? clearTimeout(searchTimeout);

	if (!searchField.value) {
		searchResult.classList.remove("active");
		searchResult.innerHTML = "";
		searchField.classList.remove("searching");
	} else {
		searchField.classList.add("searching");
	}

	if (searchField.value) {
		searchTimeout = setTimeout(() => {
			fetchData(url.geo(searchField.value), (locations) => {
				searchField.classList.remove("searching");
				searchResult.classList.add("active");
				searchResult.innerHTML = `
                    <ul class="view-list" data-search-list></ul>
                `;

				const /** {NodeList} | [] */ items = [];

				for (const { name, lat, lon, country, state } of locations) {
					const searchItem = document.createElement("li");
					searchItem.classList.add("view-item");

					searchItem.innerHTML = `
                        <span class="m-icon">location_on</span>

                        <div>
                            <p class="item-title">${name}</p>
                            <p class="label-2 item-subtitle">${state || ""} ${country}</p>
                        </div>

                        <a href="#/weather?lat=${lat}&lon=${lon}" class="item-link has-state" aria-label="${name} weather" data-search-toggler></a>
                    `;

					searchResult.querySelector("[data-search-list]").appendChild(searchItem);
					items.push(searchItem.querySelector("[data-search-toggler]"));
				}

				addEventOnElements(items, "click", () => {
					toggleSearch();
					searchResult.classList.remove("active");
				})
			});
		}, searchTimeoutDuration);
	}
});



const  container = document.querySelector("[data-container]");
const  loading = document.querySelector("[data-loading]");
const currentLocationBtn = document.querySelector("[data-current-location-btn]");
const errorContainer = document.querySelector("[data-error-content]");

/**
 *
 * @param {number} lat  latitude
 * @param {number} lon  longitude
 */

export const updateWeather =function (lat ,lon ){
    // loading.style.display= "grid";
    // container.style.overflowY= "hidden";
    // container.classList.remove("fade-in");
    errorContainer.style.display= "none";

    const currentWeatherSection = document.querySelector("[data-current-weather]");
    const highlightSection = document.querySelector("[data-highlights]");
	const hourlySection = document.querySelector("[data-hourly-forecast]");
	const forecastSection = document.querySelector("[data-5-day-forecast]");

	currentWeatherSection.innerHTML = "";
	highlightSection.innerHTML = "";
	hourlySection.innerHTML = "";
	forecastSection.innerHTML = "";

    if (window.location.hash === "#/current-location") {
		currentLocationBtn.setAttribute("disabled", "");
	} else {
		currentLocationBtn.removeAttribute("disabled");
	}

     // CURRENT WEATHER SECTION
       fetchData(url.currentWeather(lat, lon), function(currentWeather){
        const {
            weather,
            dt:dateUnix,
            sys: {sunrise: sunriseUnixUTC, sunset: sunsetUnixUTC },
            main: { temp, feels_like, pressure, humidity },
			visibility,
			timezone
        }= currentWeather;
        const [{description,icon}] =weather;
        const card = document.createElement("div");
		card.classList.add("card", "card-lg", "current-weather-card");

        card.innerHTML = `

        <h2 class="title-2 card-title">Now</h2>
            <div class="weapper">
                <p class="heading">${parseInt(temp)}ยฐc<sup></sup></p>
                <img src="./assets/images/weather_icons/${icon}.png" alt="${description}" width="64" height="64" class="weather-icon">
            </div>
            <p class="body-3">${description}</p>
            <ul class="meta-list">

                <li class="meta-item">
                    <span class="m-icon">calendar_today</span>
                       <p class="title-3 meta-text">${module.getDate(dateUnix,timezone)}</p>
                </li>
                <li class="meta-item">
                    <span class="m-icon">location_on</span>
                       <p class="title-3 meta-text" data-location></p>
                </li>
            </ul>

        `;

        fetchData(url.reverseGeo(lat, lon),  function([{ name, country }]) {
			card.querySelector("[data-location]").innerHTML = `${name}, ${country}`
		});

        currentWeatherSection.appendChild(card);

        // TODAY"S HIGHLIGHTS
		fetchData(url.airPollution(lat, lon), (airPollution) => {
			const [{
				main: {aqi},
				components: { no2, o3, so2, pm2_5}
			}] = airPollution.list;

            const card = document.createElement("div");
			card.classList.add("card", "card-lg");

            card.innerHTML = `
				<h2 class="title-2" id="highlights-label">Todays Highlights</h2>

				<div class="highlight-list">
					<div class="card card-sm highlight-card one">
						<h3 class="title-3">Air Quality Index</h3>

						<div class="wrapper">

							<span class="m-icon">air</span>

							<ul class="card-list">
								<li class="card-item">
									<p class="title-1">${pm2_5.toPrecision(3)}</p>

									<p class="label-1">PM<sub>2.5</sub></p>
								</li>

								<li class="card-item">
									<p class="title-1">${so2.toPrecision(3)}</p>

									<p class="label-1">SO<sub>2</sub></p>
								</li>

								<li class="card-item">
									<p class="title-1">${no2.toPrecision(3)}</p>

									<p class="label-1">NO<sub>2</sub></p>
								</li>

								<li class="card-item">
									<p class="title-1">${o3.toPrecision(3)}</p>

									<p class="label-1">O<sub>3</sub></p>
								</li>
							</ul>
						</div>

						<span class="badge aqi-${aqi} label-${aqi}" title="${module.aqiText[aqi].message}">
							${module.aqiText[aqi].level}
						</span>
					</div>

					<div class="card card-sm highlight-card two">
						<h3 class="title-3">Sunrise & Sunset</h3>

						<div class="card-list">
							<div class="card-item">
								<span class="m-icon">clear_day</span>

								<div>
									<p class="label-1">Sunrise</p>
									<p class="title-1">${module.getTime(sunriseUnixUTC, timezone)}</p>
								</div>
							</div>

							<div class="card-item">
								<span class="m-icon">clear_night</span>

								<div>
									<p class="label-1">Sunset</p>
									<p class="title-1">${module.getTime(sunsetUnixUTC, timezone)}</p>
								</div>
							</div>
						</div>
					</div>

					<div class="card card-sm highlight-card">
						<h3 class="title-3">Humidity</h3>

						<div class="wrapper">
							<span class="m-icon">humidity_percentage</span>

							<p class="title-1">${humidity}<sub>%</sub></p>
						</div>
					</div>

					<div class="card card-sm highlight-card">
						<h3 class="title-3">Pressure</h3>

						<div class="wrapper">
							<span class="m-icon">airwave</span>

							<p class="title-1">${pressure}<sub>hPa</sub></p>
						</div>
					</div>

					<div class="card card-sm highlight-card">
						<h3 class="title-3">Visibility</h3>

						<div class="wrapper">
							<span class="m-icon">visibility</span>

							<p class="title-1">${visibility / 1000}<sub>km</sub></p>
						</div>
					</div>

					<div class="card card-sm highlight-card">
						<h3 class="title-3">Feels Like</h3>

						<div class="wrapper">
							<span class="m-icon">thermostat</span>

							<p class="title-1">${parseInt(feels_like)}&deg;<sup>c</sup></p>
						</div>
					</div>
				</div>
			`;

			highlightSection.appendChild(card)



         });


		// 24H FORECAST
		fetchData(url.forecast(lat, lon), (forecast) => {
			const {
				list: forecastList,
				city : { timezone }
			} = forecast;

			hourlySection.innerHTML = `


				<h2 class="title-2">Today at</h2>

				<div class="slider-container">
					<ul class="slider-list" data-temp></ul>

					<ul class="slider-list" data-wind></ul>
				</div>
			`;


			for (const [index, data] of forecastList.entries()) {

				if (index > 7) break;

				const {
					dt: dateTimeUnix,
					main : { temp },
					weather,
					wind : { deg: windDirection, speed: windSpeed }
				} = data;
				const [{ icon, description }] = weather;

				const tempLi = document.createElement("li");
				tempLi.classList.add("slider-item");

                tempLi.innerHTML = `
					<div class="card card-sm slider-card">
						<p class="body-3">${module.getHours(dateTimeUnix, timezone)}</p>

						<img src="./assets/images/weather_icons/${icon}.png" alt="${description}" class="weather-icon"
						width="48" height="48" loading="lazy" title="${description}">

						<p class="body-3">${parseInt(temp)}&deg;</p>
					</div>
				`;

				hourlySection.querySelector("[data-temp]").appendChild(tempLi)

				const windLi = document.createElement("li");
				windLi.classList.add("slider-item");

				windLi.innerHTML = `
					<div class="card card-sm slider-card">
						<p class="body-3">${module.getHours(dateTimeUnix, timezone)}</p>

						<img src="./assets/images/weather_icons/direction.png" alt="direction" class="weather-icon"
							width="48" height="48" loading="lazy" title="" style="transform: rotate(${windDirection - 180}deg)">

						<p class="body-3">${parseInt(module.mps_to_kmh(windSpeed))} km/h</p>
					</div>
				`;

				hourlySection.querySelector("[data-wind]").appendChild(windLi)



            }


            // 5 DAY FORECAST
			forecastSection.innerHTML = `
            <h2 class="title-2" id="forecast-label">5 Days Forecast</h2>

            <div class="card card-lg forecast-card">
                <ul data-forecast-list></ul>
            </div>
        `;

        for (let i = 7, len = forecastList.length; i < len; i += 8) {
            const {
                main: { temp_max },
                weather,
                dt_txt
            } = forecastList[i];
            const [{ icon, description }] = weather;
            const date = new Date(dt_txt);

            const li = document.createElement("li");
            li.classList.add("card-item");

            li.innerHTML = `
                <div class="icon-wrapper">
                    <img src="./assets/images/weather_icons/${icon}.png" alt="${description}"
                        class="weather-icon" width="36" height="36" title="${description}">

                    <span class="span">
                        <p class="title-2">${parseInt(temp_max)}&deg;</p>
                    </span>
                </div>

                <p class="label-1">${date.getDate()} ${module.monthNames[date.getUTCMonth()]}</p>

                <p class="label-1">${module.weekDayNames[date.getUTCDay()]}</p>
            `;

            forecastSection.querySelector("[data-forecast-list]").appendChild(li);
        }



        loading.style.display = "none";
        container.style.overflowY = "overlay";
        container.classList.add("fade-in");





        });

});

}

export const error404 = () => errorContent.style.display = "flex";

Module.js


'use strict';

export const weekDayNames = [
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday"
]

export const monthNames = [
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec"
]

/**
 *
 * @param {number} dateUnix Unix date in seconds
 * @param {number} timezone timezone shift from UTC in seconds
 * @returns {string} Date string, format: 'Sunday 10, Jan'
 */
export const getDate = function (dateUnix, timezone) {
    const date = new Date((dateUnix + timezone) * 1000)
    const weekDayName = weekDayNames[date.getUTCDay()]
    const monthName = monthNames[date.getUTCMonth()]

    return `${weekDayName} ${date.getUTCDate()}, ${monthName}`
}

/**
 * @param {number} timeUnix Unix time in seconds
 * @param {number} timezone timezone shift from UTC in seconds
 * @returns {string} Time string, format: 'HH:MM AM?PM'
 */
export const getTime = function (timeUnix, timezone) {
    const date = new Date((timeUnix + timezone) * 1000)
    const hours = date.getUTCHours()
    const minutes = date.getUTCMinutes()
    const period = hours >= 12 ? "PM" : "AM"

    return `${hours % 12 || 12}:${minutes} ${period}`
}

/**
 * @param {number} timeUnix Unix time in seconds
 * @param {number} timezone timezone shift from UTC in seconds
 * @returns {string} Time string, format: 'HH AM/PM'
 */
export const getHours = function (timeUnix, timezone) {
    const date = new Date((timeUnix + timezone) * 1000)
    const hours = date.getUTCHours()
    const period = hours >= 12 ? "PM" : "AM"

    return `${hours % 12 || 12} ${period}`
}

/**
 * @param {number} mps Metre per second
 * @returns {number} kilometre per hour
 */
export const mps_to_kmh = mps => {
    // mph = mps * 3600

    return (mps * 3600) / 1000
}

export const aqiText = {
    1: {
        level: "Good",
        message: "Air quality is considered satisfactory, and air pollution poses little or no risk."
    },
    2: {
        level: "Fair",
        message: "Air quality is acceptable; however, for some pollutants there may be a moderate health concern for a very small number of people who are unusually sensitive to air pollution."
    },
    3: {
        level: "Moderate",
        message: "Members of sensitive groups may experience health effects. The general public is not likely to be affected"
    },
    4: {
        level: "Poor",
        message: "Everyone may begin to experience health effects; members of sensitive groups may experience more serious health effects."
    },
    5: {
        level: "Very Poor",
        message: "Health warnings of emergency conditions. The entire population is more likely to be affected."
    }
}

Route.js

‘use strict’;

// import { query } from ‘express’;
import {updateWeather, error404 } from ‘./app.js’;
const defaultLocation = “#/weather?lat=25.4381302&lon=81.8338005”

const currentLocation = function() {

window.navigator.geolocation.getCurrentPosition(res=> {
    const { latitude, longitude } = res.coords;
    updateWeather(`lat=${latitude}`,`lon=${longitude}`);
}, err => {
    window.location.hash = defaultLocation;
});

}

Route.js

'use strict';

// import { query } from 'express';
import {updateWeather, error404 } from './app.js';
 const defaultLocation = "#/weather?lat=25.4381302&lon=81.8338005"

 const currentLocation = function() {

    window.navigator.geolocation.getCurrentPosition(res=> {
        const { latitude, longitude } = res.coords;
        updateWeather(`lat=${latitude}`,`lon=${longitude}`);
    }, err => {
        window.location.hash = defaultLocation;
    });

 }

 /**
  *
  * @param {string} query  Searched query
  */

 const searcheLocation = query => updateWeather(...query.split('&'));

 const routes = new Map([
    ["/current-location", currentLocation],
    ["/weather", searcheLocation]
 ]);

 const checkHash = function () {
    const requestURL = window.location.hash.slice(1);

    const [route, query] = requestURL.includes ? requestURL.split('?') : [requestURL];

    routes.get(route) ? routes.get(route)(query) : error404();

 }

 window.addEventListener("hashchange", checkHash);

 window.addEventListener("load", function () {
    if(!window.location.hash) {
        window.location.hash = "#/current-location";
    } else {
        checkHash();
    }
 });
Weather Web Project for

Style.css

:root {
	/* COLOR */
	--primary: #b5a1e5;
	--on-primary: #100e17;
	--background: #131214;
	--on-background: #eae6f2;
	--surface: #1d1c1f;
	--on-surface: #dddae5;
	--on-surface-variant: #7b7980;
	--on-surface-variant-2: #b9b6bf;
	--outline: #3e3d40;
	--bg-aqi-1: #89e589;
	--on-bg-aqi-1: #1f331f;
	--bg-aqi-2: #e5dd89;
	--on-bg-aqi-2: #33311f;
	--bg-aqi-3: #e5c089;
	--on-bg-aqi-3: #332b1f;
	--bg-aqi-4: #e58989;
	--on-bg-aqi-4: #331f1f;
	--bg-aqi-5: #e589b7;
	--on-bg-aqi-5: #331f29;
	--white: hsl(0, 0%, 100%);
	--white-alpha-4: hsla(0, 0%, 100%, 0.04);
	--white-alpha-8: hsla(0, 0%, 100%, 0.08);
	--black-alpha-10: hsla(0, 0%, 0%, 0.1);



	/* gradient */
	--gradient-1: linear-gradient(
		180deg,
		hsla(270, 5%, 7%, 0) 0%,
		hsla(270, 5%, 7%, 0.8) 65%,
		hsl(270, 5%, 7%) 100%
	);
	--gradient-2: linear-gradient(
		180deg,
		hsla(260, 5%, 12%, 0) 0%,
		hsla(260, 5%, 12%, 0.8) 65%,
		hsl(260, 5%, 12%) 100%
	);



	/* TYPOGRAPHY */
  /* font family */
	--ff-nunito-sans: "Nunito Sans", sans-serif;

  /* font size */
	--heading: 5.6rem;
	--title-1: 2rem;
	--title-2: 1.8rem;
	--title-3: 1.6rem;
	--body-1: 2.2rem;
	--body-2: 2rem;
	--body-3: 1.6rem;
	--label-1: 1.4rem;
	--label-2: 1.2rem;

  /* font weight */
	--weight-regular: 400;
	--weight-semiBold: 600;



	/* BOX SHADOW */
	--shadow-1: 0px 1px 3px hsla(0, 0%, 0%, 0.5);
	--shadow-2: 0px 3px 6px hsla(0, 0%, 0%, 0.4);



	/* BORDER RADIUS */
	--radius-28: 28px;
	--radius-16: 16px;
	--radius-pill: 500px;
	--radius-circle: 50%;



	/* TRANSITION */
	--transition-short: 100ms ease;
}

/*-----------------------------------*\
  #RESET
\*-----------------------------------*/

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

li {list-style: none;}

a,
img,
span,
input,
button { display: block; }

a { color: inherit; text-decoration: none;}

img { height: auto;}

input,
button {
  background: none;
  border: none;
  color: inherit;
  font: inherit;
}

input { width: 100%; }

button { cursor: pointer; }

sub { vertical-align: baseline; }

sup { vertical-align: top; }

sub, sup { font-size: 0.75em;}

html {
  font-family: var(--ff-nunito-sans);
  font-size: 10px;
  scroll-behavior: smooth;
}

body {
  background-color: var(--background);
  color: var(--on-background);
  font-size: var(--body-3);
  overflow: hidden;
}

:focus-visible {
  outline: 2px solid var(--white);
  outline-offset: 2px;
}

::selection { background-color: var(--white-alpha-8);}

::-webkit-scrollbar {
  width: 6px;
  height: 6px;  /*for horizontal scrollbar*/
}

::-webkit-scrollbar-thumb {
  background-color: var(--white-alpha-8);
  border-radius: var(--radius-pill);
}

/*-----------------------------------*\
  #MATERIAL ICON
\*-----------------------------------*/

@font-face {
  font-family: 'Material Symbols Rounded';
  font-style: normal;
  font-weight: 400;
  src: url('../font/material-symbol-rounded.woff2') format('woff2');
}

.m-icon {
  font-family: 'Material Symbols Rounded';
  font-style: normal;
  font-weight: normal;
  font-size: 2.4rem;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  font-feature-settings: 'liga';
  -webkit-font-feature-settings: 'liga';
  -webkit-font-smoothing: antialiased;
  height: 1em;
  width: 1em;
  overflow: hidden;
}







/*-----------------------------------*\
  #REUSED STYLE
\*-----------------------------------*/

.container {
  max-width: 1600px;
  width: 100%;
  margin-inline: auto;
  padding: 16px;
}

.icon-btn {
  background-color: var(--white-alpha-8);
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-circle);
}

.has-state { position: relative; }

.has-state:hover { box-shadow: var(--shadow-1); }

.has-state:is(:focus, :focus-visible) { box-shadow: none; }

.has-state::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  clip-path: circle(100% at 50% 50%);
  transition: var(--transition-short);
}

.has-state:hover::before { background-color: var(--white-alpha-4); }

.has-state:is(:focus, :focus-visible)::before {
  background-color: var(--white-alpha-8);
  animation: ripple 250ms ease forwards;
}

.btn-primary {
  background-color: var(--primary);
  color: var(--on-primary);
  height: 48px;
  line-height: 48px;
  max-width: max-content;
  display: flex;
  align-items: center;
  gap: 16px;
  padding-inline: 16px;
  border-radius: var(--radius-pill);
}

.btn-primary .span { font-weight: var(--weight-semiBold); }

.btn-primary[disabled] {
  background-color: var(--outline);
  color: var(--on-surface-variant);
  cursor: not-allowed;
}

.btn-primary[disabled]::before { display: none; }

.card {
  background-color: var(--surface);
  color: var(--on-surface);
}

.card-lg {
  border-radius: var(--radius-28);
  padding: 20px;
}

.card-sm {
  border-radius: var(--radius-16);
  padding: 16px;
}

.heading {
  color: var(--white);
  font-size: var(--heading);
  line-height: 1.1;
}

.title-1 { font-size: var(--title-1); }

.title-2 {
  font-size: var(--title-2);
  margin-block-end: 12px;
}

.title-3 {
  font-size: var(--title-3);
  font-weight: var(--weight-semiBold);
}

.body-1 { font-size: var(--body-1); }

.body-2 {
  font-size: var(--body-2);
  font-weight: var(--weight-semiBold);
}

.body-1 { font-size: var(--body-3); }

.label-1 { font-size: var(--label-1); }

.label-2 { font-size: var(--label-2); }
/*
.fade-in { animation: fade-in 25ms ease forwards; }


@keyframes fade-in{
  0%{ opacity:0; }
  100%{ opacity: 1;}

} */








/*-----------------------------------*\
  #HEADER
\*-----------------------------------*/

.header .btn-primary .span { display: none; }

.logo img { width: 158px; }

.header .container,
.header-actions {
  display: flex;
  align-items: center;
}

.header .container { justify-content: space-between; }

.header-actions { gap: 16px; }

.header .btn-primary { padding-inline: 12px; }

.search-view {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  height: 100svh;   /*for mobile browser*/
  background-color: var(--surface);
  color: var(--on-surface);
  clip-path: circle(4% at calc(100% - 102px) 5%);
  opacity: 0;
  visibility: hidden;
  z-index: 4;
  transition: clip-path 500ms ease;
}

.search-view.active {
  opacity: 1;
  visibility: visible;
  clip-path: circle(130% at 73% 5%);
}

.search-wrapper {
  position: relative;
  border-block-end: 1px solid var(--outline);
}

.search-wrapper::before {
  content: "";
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  right: 16px;
  width: 24px;
  height: 24px;
  border: 3px solid var(--on-surface);
  border-block-start-color: transparent;
  border-radius: var(--radius-circle);
  animation: loading 500ms linear infinite;
  display: none;
}

.search-wrapper:has(.searching)::before { display: block; }

.search-field {
  height: 80px;
  line-height: 80px;
  padding-inline: 56px 16px;
  outline: none;
}

.search-field::placeholder { color: var(--on-surface-variant-2); }

.search-field::-webkit-search-cancel-button { display: none; }

.search-wrapper .leading-icon {
  position: absolute;
  top: 50%;
  left: 28px;
  transform: translate(-50%, -50%);
}

.search-wrapper > .m-icon { display: none; }

.search-wrapper .icon-btn {
  background-color: transparent;
  box-shadow: none;
}

.search-view .view-list { padding-block: 8px 16px; }

.search-view .view-item {
  position: relative;
  height: 56px;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  gap: 16px;
  padding-inline: 16px 24px;
}

.search-view .view-item :is(.m-icon, .item-subtitle) {
  color: var(--on-surface-variant);
}

.search-view .view-item .item-link {
  position: absolute;
  inset: 0;
  box-shadow: none;

}








/*-----------------------------------*\
  #MAIN
\*-----------------------------------*/

main {
  height: calc(100vh - 80px);
  height: calc(100svh - 80px);   /*for mobile browsers*/
  overflow: hidden;
}

article.container {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
  height: 100%;
  overflow-y: auto;  /*for firefox*/
  overflow-y: overlay;
}

article.container::-webkit-scrollbar-thumb { background-color: transparent; }

article.container:is(:hover, :focus-within)::-webkit-scrollbar-thumb {
  background-color: var(--white-alpha-8);
}

article.container::-webkit-scrollbar-button { height: 10px;}

article.container::before {
  content: "";
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 40px;
  background-image: var(--gradient-1);
  pointer-events: none;
  z-index: 1;
}

.section:not(:last-child) { margin-block-end: 16px;}








/*-----------------------------------*\
  #CURRENT WEATHER
\*-----------------------------------*/

.current-weather-card .wrapper {
  margin-block: 12px;
  display: flex;
  gap: 8px;
  align-items: center;
}

.current-weather-card .weather-icon { margin-inline: auto; }

.current-weather-card > .body-3 { text-transform: capitalize; }

.current-weather-card .meta-list {
  margin-block-start: 16px;
  padding-block-start: 16px;
  border-block-start: 1px solid var(--outline);
}

.current-weather-card .meta-item {
  display: flex;
  align-items: center;
  gap: 8px;
}

.current-weather-card .meta-item:not(:last-child) { margin-block-end: 12px; }

.current-weather-card .meta-text { color: var(--on-surface-variant);}








/*-----------------------------------*\
  #HIGHLIGHTS
\*-----------------------------------*/

.forecast-card .title-2 { margin-block-end: 0; }

.forecast-card :is(.card-item, .icon-wrapper) {
  display: flex;
  align-items: center;
}

.forecast-card .card-item:not(:last-child) {margin-block-end: 12px;}

.forecast-card .icon-wrapper { gap: 8px; }

.forecast-card .label-1 {
  color: var(--on-surface-variant);
  font-weight: var(--weight-semiBold);
}

.forecast-card .card-item > .label-1 {
  width: 100%;
  text-align: right;
}








/*-----------------------------------*\
  #HOURLY FORECAST
\*-----------------------------------*/

.highlights .m-icon { font-size: 3.2rem; }

.highlight-list {
  display: grid;
  gap: 20px;
}

.highlight-list .title-3 {
  color: var(--on-surface-variant);
  margin-block-end: 20px;
}

.highlights .card-sm {
  background-color: var(--black-alpha-10);
  position: relative;
}

.highlight-card :is(.wrapper, .card-list, .card-item) {
  display: flex;
  align-items: center;
}

.highlight-card .wrapper {
  justify-content: space-between;
  gap: 16px;
}

.highlight-card .card-list {
  flex-wrap: wrap;
  flex-grow: 1;
  row-gap: 8px;
}

.highlight-card .card-item {
  width: 50%;
  justify-content: flex-end;
  gap: 4px;
}

.highlight-card .label-1 { color: var(--on-surface-variant); }

.badge {
  position: absolute;
  top: 16px;
  right: 16px;
  padding: 2px 12px;
  border-radius: var(--radius-pill);
  font-weight: var(--weight-semiBold);
  cursor: help;
}

.badge.aqi-1 {
  background-color: var(--bg-aqi-1);
  color: var(--on-bg-aqi-1);
}

.badge.aqi-2 {
  background-color: var(--bg-aqi-2);
  color: var(--on-bg-aqi-2);
}

.badge.aqi-3 {
  background-color: var(--bg-aqi-3);
  color: var(--on-bg-aqi-3);
}

.badge.aqi-4 {
  background-color: var(--bg-aqi-4);
  color: var(--on-bg-aqi-4);
}

.badge.aqi-5 {
  background-color: var(--bg-aqi-5);
  color: var(--on-bg-aqi-5);
}

.highlight-card.two .card-item {
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 8px 16px;
}

.highlight-card.two .label-1 { margin-block-end: 4px;}








/*-----------------------------------*\
  #FORECAST
\*-----------------------------------*/

.slider-container {
  overflow-x: auto;
  margin-inline: -16px;
}

.slider-container::-webkit-scrollbar { display: none; }

.slider-list {
  display: flex;
  gap: 12px;
}

.slider-list:first-child { margin-block-end: 16px; }

.slider-list::before,
.slider-list::after {
  content: "";
  min-width: 4px;
}

.slider-item {
  min-width: 110px;
  flex: 1 1 100%;
}

.slider-card { text-align: center; }

.slider-item .weather-icon {
  margin-inline: auto;
  margin-block: 10px;
}








/*-----------------------------------*\
  #LOADING
\*-----------------------------------*/

.loading {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: var(--background);
  display: grid;
  place-items: center;
  z-index: 1;
  display: none;
}

.loading::before {
  content: "";
  width: 48px;
  height: 48px;
  border: 4px solid var(--on-background);
  border-block-start-color: transparent;
  border-radius: var(--radius-circle);
  animation: loading 500ms linear infinite;
}








/*-----------------------------------*\
  #ERROR SECTION
\*-----------------------------------*/

.error-content {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  height: 100svh;  /* for mobile browsers*/
  background-color: var(--background);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  display: none;
  z-index: 8;
}

.error-content .btn-primary { margin-block-start: 20px; }







/*-----------------------------------*\
  #FOOTER
\*-----------------------------------*/

.footer,
.footer .body-3:last-child {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
}

.footer {
  color: var(--on-surface-variant);
  text-align: center;
  gap: 12px 24px;
  margin-block-start: 20px;
  display: none;
}

.fade-in .footer { display: flex; }

.footer .body-3:last-child { gap: 6px; }








/*-----------------------------------*\
  #ANIMATION
\*-----------------------------------*/

@keyframes ripple {
  0% {
    clip-path: circle(0% at 50% 50%);
  }
  100% {
    clip-path: circle(100% at 50% 50%);
  }
}

@keyframes loading {
  0% {
    transform: translateY(-50%) rotate(0);
  }
  100% {
    transform: translateY(-50%) rotate(1turn);
  }
}

@keyframes fade-in {
  0% { opacity: 0; }
  100% { opacity: 1; }
}







/*-----------------------------------*\
  #MEDIA QUERIES
\*-----------------------------------*/

/* for >768px screens */
@media (min-width: 769px) {

  /* REUSED STYLE */
  .container { padding: 24px; }

  .title-1 {--title-1: 2.4rem}

  .section > .title-2 { margin-block-end: 16px; }

  .card-lg { padding: 24px; }

  .card-sm {
    padding: 20px;
    display: grid;
    grid-template-rows: min-content 1fr;
  }

  .badge {
    top: 20px;
    right: 20px;
  }



  /* HEADER */
  .header-actions { gap: 24px; }

  .header .btn-primary { padding-inline: 16px 24px; }

  .header .btn-primary .span { display: block; }

  .search-view { clip-path: circle(3% at calc(100% - 273px) 6%);}



  /* MAIN */
  main {
    height: calc(100vh - 96px);
    height: calc(100svh - 96px);
  }

  article.container {
    padding-block-start: 0;
    grid-template-columns: 280px minmax(0, 1fr);
    align-items: flex-start;
    gap: 24px;
  }

  .content-left {
    position: sticky;
    top: 0;
  }

  .section:not(:last-child) { margin-block: 20px; }

  .forecast-card .card-item:not(:last-child) { margin-block-end: 16px;}

  .highlight-list { grid-template-columns: 1fr 1fr;}

  .highlight-card:nth-child(-n+2) {
    grid-column: span 2;
    height: 160px;
  }

  .highlight-card:nth-child(n+3) { height: 120px; }

  .highlights .m-icon {font-size: 3.6rem; }

  .highlight-card.one .card-item {
    width: 25%;
    flex-direction: column-reverse;
    gap: 8px;
  }

  .slider-container {
    margin-inline: 0 -24px;
    border-bottom-left-radius: var(--radius-16);
    border-top-left-radius: var(--radius-16);
  }

  .slider-list::before { display: none; }

  .slider-list::after { min-width: 12px; }

  .hourly-forecast .card-sm { padding: 16px; }
}


/* for >1200px screens */
@media (min-width: 1200px) {
  /* CUSTOM PROPERTY */
  :root {

    /* font-size */
    --heading: 8rem;
    --title-2: 2rem;
  }


  /* REUSED STYLES */
  .container { padding: 40px; }

  .card-lg { padding: 36px; }

  .card-sm { padding: 24px; }

  .title-1 { --title-1: 3.6rem; }

  .highlight-card.two .card-item { column-gap: 24px; }



  /* HEADER */
  .header .icon-btn { display: none; }

  .logo img { width: 200px; }

  .header {
    position: relative;
    height: 120px;
    z-index: 4;
  }

  .header .container {
    padding-block: 0;
    height: 100%;
  }

  .search-view,
  .search-view.active {
    all: unset;
    display: block;
    position: relative;
    width: 500px;
    animation: none;
  }

  .search-wrapper { border-block-end: none; }

  .search-wrapper > .m-icon { display: block; }

  .search-field,
  .search-view .view-list { background-color: var(--surface);}

  .search-field {
    height: 56px;
    border-radius: var(--radius-28);
  }

  .search-result,
  .search-view:not(:focus-within) .search-result { display: none; }

  .search-view:focus-within .search-result.active { display: block; }

  .search-view:has(.search-result.active):focus-within .search-field {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }

  .search-view .view-list {
    position: absolute;
    top: 100%;
    left: 0;
    width: 100%;
    max-height: 360px;
    border-radius: 0 0 var(--radius-28) var(--radius-28);
    border-block-start: 1px solid var(--outline);
    overflow-y: auto;   /* for firefox */
    overflow-y: overlay;
  }

  .search-view .view-list:empty { min-height: 120px; }

  .search-view .view-list::-webkit-scrollbar-button { height: 20px; }

  .search-view :is(:hover, :has(.view-list):hover) {
    filter: drop-shadow(var(--shadow-1));
  }

  .search-view :is(:focus-within, :has(.view-list):focus-within) {
    filter: drop-shadow(var(--shadow-2));
  }


  /* MAIN */
  main {
    height: calc(100vh - 120px);
    height: calc(100svh - 120px);
  }

  article.container {
    grid-template-columns: 360px minmax(0, 1fr);
    gap: 40px;
  }

  .currrent-weather .weather-icon { width: 80px; }

  .forecast-card .title-2 { --title-2: 2.2rem; }

  .highlight-card:nth-child(-n+2) { height: 200px; }

  .highlight-card:nth-child(n+3) { height: 150px; }

  .highlight-card .m-icon { font-size: 4.8rem; }

  .slider-list { gap: 16px; }
}


/* for >1400px screens */
@media (min-width: 1400px) {
  .highlight-list { grid-template-columns: repeat(4, 1fr); }
}
80 / 100 SEO Score

Comments

2 responses to “Weather Web Project Using HTML CSS and Javascript”

  1. Fantastic web site. Lots of useful information here. Iโ€™m sending it to several friends ans also sharing in delicious. And of course, thanks for your effort!

  2. Excellent beat ! I wish to apprentice whilst you amend your web site, how could i subscribe for a weblog site? The account aided me a acceptable deal. I have been a little bit familiar of this your broadcast provided brilliant clear idea

Leave a Reply

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