<main>
    <div :class="display">
      <div class="frontFace">
        <h1 v-show="show">Trying to get your location...</h1>
      </div>
      <div class="backFace">
        <i :class="weatherIcons"></i>
        <h1>{{ weather }}</h1>
        <h2>{{ animateTemp }}{{ temp.unit }}</h2>
        <small @click="C2F">Deg. F <span>&harr;</span> Deg. C</small>
        <h4>Location: <strong>{{ cityName }}, {{ country }}</strong>.</h4>
      </div>
    </div>
  </main>
  <footer>
    <a href="https://twitter.com/yagoestevez" class="link">
      coded with <span class="love">&#9825;</span> by Yago Estévez
    </a>
  </footer>
@import 'https://cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons-wind.min.css';
@import 'https://cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.min.css';
@import 'https://cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/font/weathericons-regular-webfont.ttf';
@import 'https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300';

html {
  box-sizing : border-box;
}

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

html,
body {
  width      : 100vw;
  height     : 100vh;
}

body {
  background      : url(https://www.dropbox.com/s/gbd8upnanqjj2z0/BG.jpeg?raw=1) no-repeat center center fixed;
  background-size : cover;
  display         : flex;
  flex-direction  : column;
  justify-content : center;
  align-items     : center;
  font-family     : 'Open Sans Condensed', sans-serif;
  font-size       : 16px;
  color           : #43545c;
  line-height     : 1.8rem;
}

main {
  position        : relative;
  height          : 70vh;
  width           : 70vh;
}

  .content {
    position        : absolute;
    background      : linear-gradient(
                      to bottom,
                      #ffffff 0%,
                      #ffffff00 100%
                    );
    border-radius   : 100%;
    border          : #43545c 1px solid;
    height          : 70vh;
    width           : 70vh;
    transform-style : preserve-3d;
    transition      : all 500ms ease;
  }

    .rotate {
      transform     : rotateY(180deg);
    }

  .frontFace {
    position            : absolute;
    display             : flex;
    flex-direction      : column;
    justify-content     : center;
    align-items         : center;
    width               : 100%;
    height              : 100%;
    backface-visibility : hidden;
    text-align          : center;
  }

    .frontFace h1 {
      line-height : 2.5rem;
      animation   : blinker 1s linear infinite;
    }

    @keyframes blinker {
      50% {
        opacity: 0;
      }
    }

  .backFace {
    position            : absolute;
    display             : flex;
    flex-direction      : column;
    justify-content     : center;
    align-items         : center;
    width               : 100%;
    height              : 100%;
    backface-visibility : hidden;
    transform           : rotateY(180deg);
    text-align          : center;
  }

    .backFace i {
      font-size       : 5rem;
      margin-bottom   : 1rem;
    }

    .backFace h1 {
      font-size       : 2.2rem;
    }

    .backFace h2 {
      font-size       : 2.2rem;
      margin          : 1rem;
    }

    .backFace small {
      font-size       : 1.2rem;
      padding         : 0.4rem 1.5rem;
      margin          : 0.2rem;
      cursor          : pointer;
      border-top      : #43545c 1px solid;
      border-bottom   : #43545c 1px solid;
      border-radius   : 10px;
    }
      .backFace small:hover {
        background-color  : #43545c;
        color             : #ffffff;
      }

      .backFace small span {
        font-size      : 1.5rem;
        vertical-align : top;
      }

    .backFace h4 {
      font-size     : 0.9rem;
      font-weight   : normal;
    }

      .backFace h4 strong {
        font-style  : italic;
      }

footer {
  font-size     : 1rem;
  text-align    : center;
  padding-top   : 1rem;
}

  .love {
    color           : #d33333;
    font-size       : 1.3rem;
    vertical-align  : middle;
  }

  footer a:link,
  footer a:visited {
    text-decoration : none;
    border-radius   : 100px;
    color           : #43545c;
  }

  footer a:hover {
    font-weight     : bold;
  }


@media ( max-height : 350px ){
  body {
    line-height   : 1rem;
  }
    .frontFace h1 {
      font-size   : 0.8rem;
      line-height : 2.5rem;
    }

    .backFace i {
      font-size       : 2rem;
      margin-bottom   : 0.3rem;
    }

    .backFace h1, .backFace h2 {
      font-size       : 1rem;
      margin          : 0.2rem;
    }

    .backFace small {
      font-size       : 0.5rem;
      font-weight     : bold;
      padding         : 0.2rem 0.5rem;
      margin          : 0 0 0.2rem 0;
    }
      .backFace small:hover {
        background-color  : #43545c;
        color             : #ffffff;
      }

      .backFace small span {
        font-size         : 0.7rem;
      }
      
    .backFace h4 {
      font-size   : 0.4rem;
    }

  footer {
    font-size     : 0.5rem;
    padding-top   : 0.5rem;
  }
    footer.love {
      font-size   : 0.5rem;
    }
}
/**
 *  Coded with ♥ by Yago Estévez
 * 
 *  Many things to polish, like:
 *  - The weird stuff going on when the virtual keyboard shows up on the phone.
 *  - Cross browser testing
 *  - Etc.
 * 
 */

new Vue( {
  el   : 'main',
  data : {
    position  : {
      lat     : 0,
      lon     : 0,
    },
    weather   : '',
    code      : 0,
    temp      : {
      kelvin  : 0.00,
      degrees : 0.00,
      unit    : 'ºC',
    },
    cityName  : '',
    country   : '',
    show      : true,
    display   : 'content'
  },
  // Tries to get the browser's location on start up.
  mounted ( ) {
    this.getLocation( )
  },
  methods : {
    // Gets the browser's global position and iniciates the AJAX call. If it can't, sets a flag.
    getLocation ( ) {
      const success = ( position ) => {
        this.position.lat = position.coords.latitude,
        this.position.lon = position.coords.longitude;
        this.getWeather( );
      }
  
      const error = ( e ) => {
        if ( e.code === 1 ) console.error( 'PERMISSION DENIED'    );
        if ( e.code === 2 ) console.error( 'POSITION UNAVAILABLE' );
        if ( e.code === 3 ) console.error( 'TIME OUT'             );
        if ( e.code === 0 ) console.error( 'UNKNOWN ERROR'        );
        // Gets the IP location if the user won't give permissions.
        axios.get( 'https://ipapi.co/json' )
          .then( results => {
            this.position.lat = results.data.latitude,
            this.position.lon = results.data.longitude;
            this.getWeather( );        
          } );
        this.show = false;
      }
  
      const OPTIONS = {
        enableHighAccuracy  : true,
        timeout             : 5000,
        maximumAge          : 0
      };
  
      const position = navigator.geolocation.getCurrentPosition( success,error,OPTIONS );
    },
    // Establishes the AJAX call, fetches results and does the initial set up.
    getWeather ( ) {  
      const URL  = `//api.openweathermap.org/data/2.5/weather?lat=${this.position.lat}&lon=${this.position.lon}&appid=a7e8689c16bddca198ae1d762f5049cd`;

      axios.get( URL )
      .then( results => {
        this.weather      = results.data.weather[0].main + ' (' + results.data.weather[0].description + ')';
        this.code         = results.data.weather[0].id;
        this.temp.kelvin  = ( results.data.main.temp ).toFixed( 1 );              // Kelvin
        this.temp.degrees = ( results.data.main.temp - 273.15 ).toFixed( 1 );     // K -> Celsius
        this.cityName     = results.data.name;
        this.country      = results.data.sys.country;
        this.display     += ' rotate';
        this.show         = false;
      } )
      .catch( err => console.log( err ) );
    },
    // Changes from Celsius to Fahrenheit and viceversa. Also, animate their transitions.
    C2F ( ) { 
      if ( this.temp.unit == 'ºC' ) {
        const celsius = ( this.temp.kelvin * 9 / 5 - 459.67 ).toFixed( 1 );
        TweenLite.to( this.$data.temp, 0.5, { degrees : celsius });
        this.temp.unit    = 'ºF';
      } else {
        const fahrenheit = ( this.temp.kelvin - 273.15 ).toFixed( 1 );
        TweenLite.to( this.$data.temp, 0.5, { degrees : fahrenheit });
        this.temp.unit    = 'ºC';
      }
    }
  },
  computed: {
    // Animates the degrees between celsius and fahrenheit.
    animateTemp ( ) {
      return Number( this.temp.degrees ).toFixed( 1 );
    },
    // Chooses a Weather Icon for each weather ID.
    weatherIcons ( ) {
      if ( this.code >= 200 && this.code <= 232 )
        return 'wi wi-thunderstorm';
      else if ( this.code >= 300 && this.code <= 321 )
        return 'wi wi-rain-mix';
      else if ( this.code >= 500 && this.code <= 531 )
        return 'wi wi-rain';
      else if ( this.code >= 600 && this.code <= 622 )
        return 'wi wi-snowflake-cold';
      else if ( this.code >= 701 && this.code <= 781 )
        return 'wi wi-fog';
      else if ( this.code >= 801 && this.code <= 804 )
        return 'wi wi-cloudy';
      else if ( this.code == 800 )
        return 'wi wi-day-sunny';
    }
  }
} );

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js
  3. https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js