<div class="api">
<div class="container">š This demo needs an OpenWeather API key to work. <a target="_blank" href="https://home.openweathermap.org/users/sign_up">Get yours here for free!</a>
</div>
</div>
<section class="top-banner">
<div class="container">
<h1 class="heading">Simple Weather App</h1>
<form>
<input type="text" placeholder="Search for a city" autofocus>
<button type="submit">SUBMIT</button>
<span class="msg"></span>
</form>
</div>
</section>
<section class="ajax-section">
<div class="container">
<ul class="cities"></ul>
</div>
</section>
<footer class="page-footer">
<div class="container">
<small>Made with <span>ā¤</span> by <a href="http://georgemartsoukos.com/" target="_blank">George Martsoukos</a>
</small>
</div>
</footer>
/* RESET STYLES
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */
:root {
--bg_main: #0a1f44;
--text_light: #fff;
--text_med: #53627c;
--text_dark: #1e2432;
--red: #ff1e42;
--darkred: #c3112d;
--orange: #ff8c00;
}
a {
color: inherit;
text-decoration: none;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-weight: normal;
}
button {
cursor: pointer;
}
input {
-webkit-appearance: none;
}
button,
input {
border: none;
background: none;
outline: none;
color: inherit;
}
img {
display: block;
max-width: 100%;
height: auto;
}
ul {
list-style: none;
}
body {
font: 1rem/1.3 "Roboto", sans-serif;
background: var(--bg_main);
color: var(--text_dark);
padding: 70px;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
/* SECTION #1
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */
.top-banner {
color: var(--text_light);
}
.heading {
font-weight: bold;
font-size: 4rem;
letter-spacing: 0.02em;
padding: 0 0 30px 0;
}
.top-banner form {
position: relative;
display: flex;
align-items: center;
}
.top-banner form input {
font-size: 2rem;
height: 40px;
padding: 5px 5px 10px;
border-bottom: 1px solid;
}
.top-banner form input::placeholder {
color: currentColor;
}
.top-banner form button {
font-size: 1rem;
font-weight: bold;
letter-spacing: 0.1em;
padding: 15px 20px;
margin-left: 15px;
border-radius: 5px;
background: var(--red);
transition: background 0.3s ease-in-out;
}
.top-banner form button:hover {
background: var(--darkred);
}
.top-banner form .msg {
position: absolute;
bottom: -40px;
left: 0;
max-width: 450px;
min-height: 40px;
}
/* SECTION #2
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */
.ajax-section {
margin: 70px 0 20px;
}
.ajax-section .cities {
display: grid;
grid-gap: 32px 20px;
grid-template-columns: repeat(4, 1fr);
}
.ajax-section .city {
position: relative;
padding: 40px 10%;
border-radius: 20px;
background: var(--text_light);
color: var(--text_med);
}
.ajax-section .city::after {
content: '';
width: 90%;
height: 50px;
position: absolute;
bottom: -12px;
left: 5%;
z-index: -1;
opacity: 0.3;
border-radius: 20px;
background: var(--text_light);
}
.ajax-section figcaption {
margin-top: 10px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.ajax-section .city-temp {
font-size: 5rem;
font-weight: bold;
margin-top: 10px;
color: var(--text_dark);
}
.ajax-section .city sup {
font-size: 0.5em;
}
.ajax-section .city-name sup {
padding: 0.2em 0.6em;
border-radius: 30px;
color: var(--text_light);
background: var(--orange);
}
.ajax-section .city-icon {
margin-top: 10px;
width: 100px;
height: 100px;
}
@media screen and (max-width: 1000px) {
body {
padding: 30px;
}
.ajax-section .cities {
grid-template-columns: repeat(3, 1fr);
}
}
@media screen and (max-width: 700px) {
.heading,
.ajax-section .city-temp {
font-size: 3rem;
}
.ajax-section {
margin-top: 20px;
}
.top-banner form {
flex-direction: column;
align-items: flex-start;
}
.top-banner form input,
.top-banner form button {
width: 100%;
}
.top-banner form button {
margin: 20px 0 0 0;
}
.top-banner form .msg {
position: static;
max-width: none;
min-height: 0;
margin-top: 10px;
}
.ajax-section .cities {
grid-template-columns: repeat(2, 1fr);
}
}
@media screen and (max-width: 500px) {
body {
padding: 15px;
}
.ajax-section .cities {
grid-template-columns: repeat(1, 1fr);
}
}
/* FOOTER
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */
.page-footer {
text-align: right;
font-size: 1rem;
color: var(--text_light);
margin-top: 40px;
}
.page-footer span {
color: var(--red);
}
/* API Key banner
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */
.api {
background: #fffbbc;
position: fixed;
top: 0;
left: 0;
width: 100%;
padding: 10px
}
.api a {
text-decoration: underline;
}
.api a:hover {
text-decoration: none;
}
/*SEARCH BY USING A CITY NAME (e.g. athens) OR A COMMA-SEPARATED CITY NAME ALONG WITH THE COUNTRY CODE (e.g. athens,gr)*/
const form = document.querySelector(".top-banner form");
const input = document.querySelector(".top-banner input");
const msg = document.querySelector(".top-banner .msg");
const list = document.querySelector(".ajax-section .cities");
/*PUT YOUR OWN KEY HERE - THIS MIGHT NOT WORK
SUBSCRIBE HERE: https://home.openweathermap.org/users/sign_up*/
const apiKey = "4d8fb5b93d4af21d66a2948710284366";
form.addEventListener("submit", e => {
e.preventDefault();
const listItems = list.querySelectorAll(".ajax-section .city");
const inputVal = input.value;
//ajax here
const url = `https://api.openweathermap.org/data/2.5/weather?q=${inputVal}&appid=${apiKey}&units=metric`;
fetch(url)
.then(response => response.json())
.then(data => {
const { main, name, sys, weather } = data;
const icon = `https://openweathermap.org/img/wn/${
weather[0]["icon"]
}@2x.png`;
const li = document.createElement("li");
li.classList.add("city");
const markup = `
<h2 class="city-name" data-name="${name},${sys.country}">
<span>${name}</span>
<sup>${sys.country}</sup>
</h2>
<div class="city-temp">${Math.round(main.temp)}<sup>Ā°C</sup></div>
<figure>
<img class="city-icon" src=${icon} alt=${weather[0]["main"]}>
<figcaption>${weather[0]["description"]}</figcaption>
</figure>
`;
li.innerHTML = markup;
list.appendChild(li);
})
.catch(() => {
msg.textContent = "Please search for a valid city š©";
});
msg.textContent = "";
form.reset();
input.focus();
});
This Pen doesn't use any external JavaScript resources.