Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
10 changes: 2 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
# Weather App

Replace this readme with your own information about your project.

Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.

## The problem

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?
The project was to build a simple weather dashboard that uses a weather API to tell today's weather and temperature and a 4-day forecast. A couple of different designers had a take on this project and it was a chance to practice to implement someone else's design.

## View it live

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
https://whats-the-weather-jenny.netlify.app/
Binary file removed assets/design-1/Group16.png
Binary file not shown.
Binary file removed assets/design-1/Group34.png
Binary file not shown.
Binary file removed assets/design-1/Group36.png
Binary file not shown.
Binary file removed assets/design-1/Group37.png
Binary file not shown.
Binary file removed assets/design-1/Group38.png
Binary file not shown.
7 changes: 0 additions & 7 deletions assets/design-2/noun_Cloud_1188486.svg

This file was deleted.

23 changes: 0 additions & 23 deletions assets/design-2/noun_Sunglasses_2055147.svg

This file was deleted.

16 changes: 0 additions & 16 deletions assets/design-2/noun_Umbrella_2030530.svg

This file was deleted.

Binary file added code/.DS_Store
Binary file not shown.
Binary file added code/assets/.DS_Store
Binary file not shown.
Binary file added code/assets/clear_sky_condition.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/assets/cloudy_condition.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/assets/default_condition.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/assets/misty_condition.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/assets/rainy_condition.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/assets/snowy_condition.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/assets/thunderstorm_condition.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions code/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Weather App</title>
<link rel="stylesheet" href="styles.css">
<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=Fredoka:wght@300..700&family=Questrial&family=Urbanist:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
</head>
<body>
<div id="imageBackground" class="image-background">
<div id="weatherApp" class="weather-app">
<main id="main-content" class="current-weather">
<div class="temp-section" tabindex="0" aria-live="polite">
<h1 id="temperature" class="temperature"></h1>
<p id="celcius" class="celcius">°c</p>
</div>
<h2 id="location" class="location" tabindex="0" aria-live="polite"></h2>
<h3 id="time" class="time" tabindex="0" aria-live="polite"></h3>
<div class="condition-container">
<h4 id="condition" class="condition" tabindex="0" aria-live="polite"></h4>
</div>
<div class="sun-times">
<h5 id="sunriseTime" class="sunrise-time" tabindex="0" aria-live="polite"></h5>
<h5 id="sunsetTime" class="sunset-time" tabindex="0" aria-live="polite"></h5>
</div>
</main>
</div>
</div>

<div id="forecast" class="forecast" role="region" aria-label="Next 5 days forecast">
</div>

<script src="script.js"></script>
</body>
</html>
183 changes: 183 additions & 0 deletions code/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Base URL
const BASE_URL = 'https://api.openweathermap.org/data/2.5/weather';

// API key
const API_KEY = '958c2b8d78ace5950d5c51dc2972950f';

// City and country for the weather forecast
let city = 'Stockholm';
let country = 'Sweden';
const units = 'metric'; // Use metric units (Celsius)

// Construct the URL for current weather data
const url = `${BASE_URL}?q=${city},${country}&units=${units}&APPID=${API_KEY}`;

// Construct the URL for forecast data
const FORECAST_URL = `https://api.openweathermap.org/data/2.5/forecast?q=${city},${country}&units=${units}&APPID=${API_KEY}`;

// DOMs
const temperatureElement = document.getElementById("temperature");
const locationElement = document.getElementById("location");
const conditionElement = document.getElementById("condition");
const sunriseElement = document.getElementById("sunriseTime");
const sunsetElement = document.getElementById("sunsetTime");
const timeElement = document.getElementById("time");
const forecastElement = document.getElementById("forecast");

// Function to fetch and display current weather data
const getWeatherData = () => {
fetch(url)
.then(response => response.json())
.then(json => {
// Get data from the API response
const temperature = Math.round(json.main.temp);
const location = json.name;
const condition = json.weather[0].description;
const iconCode = json.weather[0].icon;
const sunriseTime = new Date(json.sys.sunrise * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
const sunsetTime = new Date(json.sys.sunset * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });

// Get the current local time
const localTime = new Date();
const formattedTime = localTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });

// Update HTML elements with the weather data
temperatureElement.textContent = `${temperature}`;
temperatureElement.setAttribute('aria-label', `Current temperature is ${temperature} degrees Celsius`);
locationElement.textContent = `${location}`;
locationElement.setAttribute('aria-label', `Current location is ${location}`);
conditionElement.textContent = `${condition}`;
conditionElement.setAttribute('aria-label', `Current weather condition is ${condition}`);
sunriseElement.textContent = `Sunrise: ${sunriseTime}`;
sunriseElement.setAttribute('aria-label', `Sunrise time is ${sunriseTime}`);
sunsetElement.textContent = `Sunset: ${sunsetTime}`;
sunsetElement.setAttribute('aria-label', `Sunset time is ${sunsetTime}`);
timeElement.textContent = `Time: ${formattedTime}`;
timeElement.setAttribute('aria-label', `Current local time is ${formattedTime}`);

// Set the background based on the weather condition
setWeatherBackground(json.weather[0].main);
});
}

// Function to set the background image based on weather condition
const setWeatherBackground = (weatherCondition) => {
const imageBackground = document.getElementById('imageBackground');

switch(weatherCondition.toLowerCase()) {
case 'clear':
imageBackground.style.backgroundImage = "url('assets/clear_sky_condition.jpg')";
break;
case 'clouds':
imageBackground.style.backgroundImage = "url('assets/cloudy_condition.jpg')";
break;
case 'rain':
case 'drizzle':
imageBackground.style.backgroundImage = "url('assets/rainy_condition.jpg')";
break;
case 'thunderstorm':
imageBackground.style.backgroundImage = "url('assets/thunderstorm_condition.jpg')";
break;
case 'snow':
imageBackground.style.backgroundImage = "url('assets/snowy_condition.jpg')";
break;
case 'mist':
case 'smoke':
case 'haze':
case 'fog':
imageBackground.style.backgroundImage = "url('assets/misty_condition.jpg')";
break;
default:
imageBackground.style.backgroundImage = "url('assets/default_condition.jpg')";
}
};

// Function to fetch and display forecast data
const getForecastData = () => {
fetch(FORECAST_URL)
.then(response => response.json())
.then(json => {
// Process the forecast data
let forecastData = processForecastData(json.list);
// If we don't have 5 days of forecast, fill in with additional data
if (forecastData.length < 5) {
forecastData = fillForecastData(forecastData, json.list);
}
// Display the processed forecast data
displayForecast(forecastData);
});
};

// Function to process the raw forecast data
const processForecastData = (list) => {
const today = new Date().getDate();

return list
// Transforming each item into an object with the data we need
.map(item => ({
date: new Date(item.dt * 1000),
icon: item.weather[0].icon,
temp: Math.round(item.main.temp)
}))
// Filter out today's weather and nighttime forecasts
.filter(item => {
const hour = item.date.getHours();
return item.date.getDate() !== today && hour >= 6 && hour <= 18;
})
// Reduce to max 5 items - one per day
.reduce((acc, item) => {
if (acc.length < 5 && (acc.length === 0 || item.date.getDate() !== acc[acc.length - 1].date.getDate())) {
acc.push(item);
}
return acc;
}, []);
};

// Function to fill in missing forecast days if there isn't 5
const fillForecastData = (forecast, list) => {
const today = new Date().getDate();
let i = 0;

while (forecast.length < 5 && i < list.length) {
const date = new Date(list[i].dt * 1000);
if (date.getDate() !== today && (forecast.length === 0 || date.getDate() !== forecast[forecast.length - 1].date.getDate())) {
forecast.push({
date: date,
icon: list[i].weather[0].icon,
temp: Math.round(list[i].main.temp)
});
}
i++;
}

return forecast;
};

// Function to display the forecast data in the HTML
const displayForecast = (forecastData) => {
// Clear any existing forecast
forecastElement.innerHTML = '';
const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

// Create and append a forecast item for each day
forecastData.forEach(day => {
const dayName = daysOfWeek[day.date.getDay()];
const forecastItem = document.createElement('div');
forecastItem.classList.add('forecast-item');
forecastItem.tabIndex = 0; // Make the item focusable
forecastItem.setAttribute('role', 'region'); // ARIA role for screen readers
forecastItem.setAttribute('aria-label', `Forecast for ${dayName}`); // Label for screen readers
forecastItem.innerHTML = `
<div class="forecast-section">
<span class="forecast-day">${dayName}</span>
<img src="http://openweathermap.org/img/wn/${day.icon}.png" alt="Weather icon for ${dayName}">
<span class="forecast-temp">${day.temp}°C</span>
</div>
`;
forecastElement.appendChild(forecastItem);
});
};

// Call both functions to initialize the weather app
getWeatherData();
getForecastData();
107 changes: 107 additions & 0 deletions code/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
body {
margin: 0;
padding: 0;
background-color: #ffffff;
font-family: "Questrial", sans-serif;
}

.questrial-regular {
font-family: "Questrial", sans-serif;
font-weight: 400;
font-style: normal;
}


*:focus {
outline: 2px solid #135dab;
}

h5 {
font-size: 18px;
}

.weather-app {
margin: 0 auto;
position: relative;
overflow: hidden;
max-width: 320px;
}

.sun-times {
display: flex;
justify-content: space-evenly;
max-width: 320px;
margin: 2rem 0 2rem 0;
}

.image-background {
background-size: cover;
background-position: center;
background-color: rgb(88, 159, 218);
background-image: url('assets/default_condition.jpg');
max-width: 100%;
margin: 0 auto;
border-bottom-left-radius: 50% 60px;
border-bottom-right-radius: 50% 60px;
overflow: hidden;
}

.app-header, .current-weather {
position: relative;
z-index: 1;
color: rgb(255, 255, 255);
}

.app-header {
padding: 20px;
}

.current-weather {
padding: 10px;
text-align: center;
}

.temp-section {
display: flex;
}

.temperature {
font-size: 100px;
text-align: left;
padding: 0 0 0 1.5rem;
margin-bottom: 0;
}

.celcius {
font-size: 3.5em;
margin: 4rem 0 1rem 0.5rem;
}

.location {
font-size: 34px;
text-align: left;
padding: 0 0 0 1.5rem;
}

.time, .condition {
padding: 0 1.5rem 0 1.5rem;
text-align: left;
font-size: 1.3rem;
}

.forecast-section {
display: flex;
justify-content: space-between;
padding: 0 1.5rem 0 1.5rem;
}

span {
font-size: 20px;
margin-top: 1rem;
}

.forecast {
max-width: 500px;
margin: 0 auto;
padding: 30px 30px 0 30px;
}
Loading