<body>
  <!-- Status Bar -->
  <div id="status">
    <p></p>
    <button class="close"><i class="fa fa-times" aria-hidden="true"></i></button>
  </div>
  <!-- Current Weather -->
  <div id="current" class="wrapper">
    <nav>
      <button id="locateBtn">
        <i class="fa fa-location-arrow" aria-hidden="true"></i>
      </button>
      <button id="unitBtn" data-units="f">f</button>
    </nav>
    <h1 class="location">Chicago, IL</h1>
    <h2 class="date">Sunday, January 1, 2017</h2>
    <div class="weatherIcon">
      <div class="sunny">
        <div class="inner"></div>
      </div>
    </div>
    <p class="temp">72</p>
    <p class="conditions">Sunny</p>
  </div>
  <!-- Future Forecast -->
  <div id="future" class="wrapper">
    <div class="container">
      <h3 class="day">Mon</h3>
      <div class="weatherIcon">
        <div class="partlycloudy">
          <div class="inner"></div>
        </div>
      </div>
      <p class="conditions">Partly Cloudy</p>
      <p class="tempRange"><span class="high">64</span> | <span class="low">48</span></p>
    </div>
    <div class="container">
      <h3 class="day">Tue</h3>
      <div class="weatherIcon">
        <div class="mostlycloudy">
          <div class="inner"></div>
        </div>
      </div>
      <p class="conditions">Mostly Cloudy</p>
      <p class="tempRange"><span class="high">57</span> | <span class="low">45</span></p>
    </div>
    <div class="container">
      <h3 class="day">Wed</h3>
      <div class="weatherIcon">
        <div class="rain">
          <div class="inner"></div>
        </div>
      </div>
      <p class="conditions">Chance of Rain</p>
      <p class="tempRange"><span class="high">63</span> | <span class="low">59</span></p>
    </div>
  </div>
  <footer>
    <p id="lastUpdated">Last updated at 12:00pm</p>
    <p>Created by <a href="http://tiffanydu.com/" title="Visit Portfolio Site" target="_blank">Tiffany Du</a> | Weather data from <a href="https://www.wunderground.com/" title="Wunderground.com" target="_blank">Wunderground</a></p>
  </footer>
</body>
/*----------------
  General
-----------------*/
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
/* Remove outline > Apply other styles for accessibility */
:focus {
  outline: none;
}
html, body {
  height: 100%;
  width: 100%;
}
body {
  color: #fff;
  font-family: 'Alegreya Sans', sans-serif;
  font-weight: 300;
  position: relative;
  letter-spacing: 0.05rem;
  line-height: 1.5;
  text-align: center;
}
a {
  color: #6D8CA0;
  font-weight: 700;
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
footer {
  color: rgba(255,255,255,0.6);
  font-size: 0.8rem;
  letter-spacing: 0.07em;
  line-height: 2;
  padding: 30px 0;
  width: 100%;
}
#lastUpdated {
  color: #fff;
  padding: 5% 0;
}
#lastUpdated:before {
  content: '-- ';
}
#lastUpdated:after {
  content: ' --';
}

/*----------------
  Containers
-----------------*/
.wrapper {
  color: #fff;
  overflow: auto;
  width: 100%;
}

/*----------------
  Status Bar
-----------------*/
#status {
  background-color: #FFECAE;
  color: rgba(0,0,0,0.5);
  display: none;
  font-size: 1rem;
  width: 100%;
  z-index: 100;
}
#status p {
  display: inline-block;
  padding: 10px 40px 6px;
}
#status a {
  color: #fff;
}
#status .close {
  background: none;
  color: rgba(0,0,0,0.5);
  float: right;
  height: 40px;
  position: absolute;
  right: 0;
  top: 0;
  width: 40px;
}
/*** Error & Success Messages ***/
#status.error {
  background-color: #EE9797;
}
#status.success {
  background-color: #82C886;
}
#status.error, #status.error .close,
#status.success, #status.success .close {
  color: #fff;
}
#status.error .fa-exclamation-triangle,
#status.success .fa-check-circle {
  margin-right: 10px;
}

/*----------------
  Buttons
-----------------*/
button {
  border: none;
  cursor: pointer;
}
nav {
  font-size: 1rem;
  margin: 0 auto;
  padding: 5% 0 10%;
}
#locateBtn, #unitBtn {
  background: none;
  border: 1px solid rgba(255,255,255,0.25);
  border-radius: 50%;
  color: #fff;
  height: 40px;
  transition: background 1s ease-in-out, border 0.2s ease-in-out;
  width: 40px;
}
#locateBtn.on, #unitBtn.on {
  background: rgba(255,255,255,0.25);
}
#locateBtn:focus, #locateBtn:hover, #unitBtn:focus, #unitBtn:hover {
  border: 1px solid rgba(255,255,255,0.75);
}
#locateBtn {
  margin-right: 10px;
}
#unitBtn {
  font-weight: 300;
  padding-right: 3px;
  padding-top: 2px;
  text-transform: uppercase;
}
#unitBtn:before {
  content: '\00b0'; /* Degree symbol */
  padding: 1px;
}
/* locateBtn Pulse Animation */
#locateBtn.pulse {
  animation: pulse 2s infinite;
}
@keyframes pulse {
  20% { background: rgba(255,255,255,0.25); }
}

/*----------------
  Current Weather
-----------------*/
#current {
  padding: 10% 5% 20%;
  position: relative;
}
.location {
  font-size: 2em;
  font-weight: 700;
  margin: 0;
}
.date {
  font-size: 1em;
  font-weight: 300;
  text-transform: uppercase;
}
#current .weatherIcon {
  height: 100px;
  margin: 10% auto 7%;
  width: 100px;
}
.temp {
  font-size: 3em;
  line-height: 1.3;
}
.temp:after {
  content: '\00b0';
  margin-right: -0.3em;
}
#current .conditions {
  font-size: 1.2em;
  text-transform: uppercase;
}

/*----------------
  Future Forecast
-----------------*/
#future {
  margin-bottom: 10%;
  padding: 0 10%;
}
#future > .container {
  border-bottom: 1px solid rgba(255,255,255,0.25);
  margin: 0;
  padding: 5% 10%;
  width: 100%;
}
#future > .container:first-child {
  border-top: 1px solid rgba(255,255,255,0.25);
}
#future .day {
  padding: 0;
  text-align: left;
  text-transform: uppercase;
}
#future .weatherIcon {
  float: right;
  font-size: 0.5em;
  height: 50px;
  margin-left: 10%;
  margin-top: -5%;
  width: 50px;
}
#future p { text-align: left; }
.high:after, .low:after {
  content: '\00b0';
  padding: 1px;
}

/*----------------
  Media Queries
-----------------*/
@media (min-width: 375px) {
  nav {
    left: 5%;
    position: absolute;
    top: 10%;
  }
  #locateBtn, #unitBtn {
    display: block;
    margin-bottom: 10px;
  }
  #current { 
    padding-bottom: 15%;
    padding-top: 15%;
  }
}
@media (min-width: 550px) {
  nav { top: 8%; }
  /* Current Weather */
  #current { 
    font-size: 1.3rem;
    padding-bottom: 10%;
    padding-top: 10%;
  }
  .date, #current .conditions { font-size: 0.9em;}
  #current .weatherIcon {
    font-size: 0.95em;
    height: 120px;
    margin: 7% auto 3%;
    width: 120px;
  }
  /* Future Forecast */
  #future {
    font-size: 1.1rem;
    padding: 0;
    margin-bottom: 3%;
  }
  #future > .container {
    border-bottom: none;
    float: left;
    padding: 20px;
    width: 33.33%; 
  }
  #future > .container:first-child { border-top: none; }
  #future > .container:not(:last-child) { border-right: 1px solid rgba(255,255,255,0.25); }
  #future .day, #future p { text-align: center; }
  #future .weatherIcon {
    font-size: 0.56em;
    float: none;
    height: 60px;
    margin: 10% auto 5%;
    width: 60px;
  }
  footer { font-size: 0.9rem; }
}
@media (min-width: 880px) {
  nav { top: 5%; }
  #current {
    font-size: 1.5rem;
    padding-bottom: 7%;
    padding-top: 7%;
  }
  #current .weatherIcon {
    font-size: 0.82em;
    margin: 5% auto 2%;
  }
  #lastUpdated {
    padding: 3%;
  }
}
$(document).ready(function () {
  var currentDate,
      currentLocation = 60647, // Default to Chicago
      currentTemp = [],
      currentUnits = 'f', // Default to Fahrenheit
      forecast = [],
      $forecastDivs = $('#future .container'),
      $locateBtn = $('#locateBtn'),
      $unitBtn = $('#unitBtn');
  
  // -----------------
  // Geolocation API
  // -----------------
  function getCurrentLocation() {
    // If geolocation is not supported, output msg and exit out of function
    if (!navigator.geolocation){
      showStatus('error', 'ERROR: Geolocation is not supported by this browser');
      return;
    }
    function showPosition(position) {
      var location  = position.coords.latitude + ',' + position.coords.longitude;
      getWeather(location); // Get weather after getting position
      showStatus('success', 'Success! Location found.');
      $locateBtn.addClass('on'); // Toggle btn class to on if successful
    }
    function showError(error) {
      switch(error.code) {
        case error.PERMISSION_DENIED:
          showStatus('error', 'ERROR: Geolocation request denied. Try visiting the HTTPS site: <a href="https://codepen.io/tiffanyadu/pen/qryXBo" target="_blank">https://codepen.io/tiffanyadu/pen/qryXBo</a>');
          break;
        case error.POSITION_UNAVAILABLE:
          showStatus('error', 'ERROR: Location information is unavailable.');
          break;
        case error.TIMEOUT:
          showStatus('error', 'ERROR: The request to get user location timed out.');
          break;
        case error.UNKNOWN_ERROR:
          showStatus('error', 'ERROR: An unknown error occurred.');
          break;
      }
    }
    showStatus('', 'Locating…'); // In progress text
    navigator.geolocation.getCurrentPosition(showPosition, showError, {enableHighAccuracy: true});
  }
  
  // ---------------
  // Weather API
  // ---------------
  
  // Send request to API to get weather data
  function getWeather(location) {
    var weatherRequest = $.ajax({
      method: 'GET',
      url: 'https://api.wunderground.com/api/d6fadca18738e4ec/geolookup/conditions/forecast/q/' + location + '.json'
    });
    // If getting was successful, send data to be processed
    weatherRequest.done(function(data) {
      processData(data);
    });
    // If request fails, show error
    weatherRequest.fail(function(xhr, status, error) {
      console.warn(error.message);
    });
  }
  
  // Grab only the needed info from weather request and return
  function processData(data) {
    var current = data.current_observation;
    var daily = data.forecast.simpleforecast.forecastday;
    // Store values for current date, location, and temp
    currentDate = daily[0].date.weekday + ', ' + daily[0].date.monthname + ' ' + daily[0].date.day + ', ' + daily[0].date.year;
    currentLocation = current.display_location.city + ', ' + current.display_location.state;
    currentTemp = {
      c: current.temp_c,
      f: current.temp_f
    };
    forecast.length = 0; // Empty array first
    // Store forecast info
    daily.forEach(function(day) {
      var obj = {}; // Temporary object
      obj.weekdayShort = day.date.weekday_short;
      obj.conditions = day.conditions;
      obj.icon = day.icon;
      obj.c = {
        high: day.high.celsius,
        low: day.low.celsius
      };
      obj.f = {
        high: day.high.fahrenheit,
        low: day.low.fahrenheit
      };
      forecast.push(obj);
    });
    // Display weather ONLY after processing
    displayWeather();
  }
  
  // Display data on page
  function displayWeather() {
    // Separate today's forecast from the rest
    var today = forecast.shift();
    // Today - Print weather data
    $('#current .location').html(currentLocation);
    $('#current .date').html(currentDate);
    $('#current .weatherIcon > div').attr('class', today.icon);
    $('#current .conditions').html(today.conditions);
    $('#lastUpdated').html('Last updated at ' + getCurrentTime());
    // Add forecast data to page, don't display temps yet
    $forecastDivs.each(function(index) {
      $(this).find('.day').html(forecast[index].weekdayShort);
      $(this).find('.weatherIcon').children().attr('class', forecast[index].icon);
      $(this).find('.conditions').html(forecast[index].conditions);
    });
    // Get/update temps with current units
    updateTemps(currentUnits);
  }
  
  // Update temps and add to page
  function updateTemps(units) {
    $('#current .temp').html(Math.round(currentTemp[units]));
    $forecastDivs.each(function(index) {
      $(this).find('.high').html(forecast[index][units].high);
      $(this).find('.low').html(forecast[index][units].low);
    });
  }
  
  // ------------
  // Status Bar
  // ------------
  var $statusBar = $('#status');
  
  function showStatus(statusType, message) {
    var $statusText = $statusBar.children('p');
    var icon = '';
    // Set icon based on statusType
    if (statusType === 'error') {
      icon = '<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>';
    } else if (statusType === 'success') {
      icon = '<i class="fa fa-check-circle" aria-hidden="true"></i>';
    }
    // Set status class, icon, text, and open animation
    $statusText.html(icon + message);
    $statusBar.attr('class', statusType).slideDown('fast');
  }
  // Status bar close animation
  $statusBar.children('.close').on('click', function() {
    $statusBar.slideUp('fast'); // Slide up animation
  });
  
  // ---------------
  // Misc Functions
  // ---------------
  
  // Get and format current time
  function getCurrentTime() {
    var now = new Date();
    var hours = now.getHours();
    var mins = now.getMinutes();
    var period = 'am';
    if (hours > 11) {
      period = 'pm';
      if (hours > 12) hours -= 12; // Format for 12-hr clock
    }
    if (mins < 10) {
      mins = '0' + mins; // Format minutes
    }
    return hours + ':' + mins + period;
  }
  
  // ------------------------
  // Locate and Unit Buttons
  // ------------------------
  
  // locateBtn - click to get current location
  $locateBtn.on('click', function() {
    getCurrentLocation($(this));
    $(this).removeClass('on pulse');
  });
  
  // unitBtn - click to toggle units
  $unitBtn.on('click', function() {
    $(this).toggleClass('on')
           .attr('data-units', $(this).attr('data-units') === 'f' ? 'c' : 'f');
    currentUnits = $(this).attr('data-units');
    $(this).html(currentUnits);
    updateTemps(currentUnits);
  });
  
  // ------------------------
  // Functions to run onload
  // ------------------------ 
  window.onload = function() {
    getWeather(currentLocation); // Default to get Chicago weather
    // Suggest to share location with message and button animation
    setTimeout(function() {
      showStatus('', 'Click on the <i class="fa fa-location-arrow" aria-hidden="true"></i> button to share your current location.');
      $locateBtn.addClass('pulse');
    }, 5000);
  };
});

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css
  2. https://codepen.io/tiffanyadu/pen/OprNrV.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js