<div class="container">
  <div class="phone">

  </div>
</div>

<template id="factor">
  <div class="factor">
    <div class="factor__color"></div>
    <div class="">
      <div class="factor__percentage">26%</div>
      <div class="factor__text">Food</div>
    </div>
  </div>
</template>

<template id="demo">
  <div class="page-grid">
    <section class="title">
      <div class="title__label">March 2021</div>
      <h1 class="title__heading">Budget</h1>
    </section>

    <section class="main">
      <div class="pie"></div>
    </section>

    <section class="legend fullbleed">

      <div class="factor">
        <div class="factor__color factor__color--food"></div>
        <div class="">
          <div class="factor__percentage">26%</div>
          <div class="factor__text">Food</div>
        </div>
      </div>

      <div class="factor">
        <div class="factor__color factor__color--services"></div>
        <div class="factor__text">
          <div class="factor__percentage">12%</div>
          <div class="factor__text">Services</div>
        </div>
      </div>

      <div class="factor">
        <div class="factor__color factor__color--rent"></div>
        <div class="factor__text">
          <div class="factor__percentage">30%</div>
          <div class="factor__text">Rent</div>
        </div>
      </div>

      <div class="factor">
        <div class="factor__color factor__color--oops"></div>
        <div class="factor__text">
          <div class="factor__percentage">38%</div>
          <div class="factor__text">Oops</div>
        </div>
      </div>

    </section>

    <form class="action-section convert hidden">

      <h3>
        Want to see your own expenses?
      </h3>

      <button type="submit" class="btn-primary">
        Create an account
      </button>
    </form>

  </div>
</template>

<template id="signIn">
  <div class="sign-in-grid">
    <section class="title--logo">
      <div class="title--logo__svg">
        <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
          <rect width="36" height="36" rx="12" fill="#6E6EC4" />
          <path d="M17.2533 7.41421C18.0343 6.63316 19.3006 6.63317 20.0817 7.41421L22.203 9.53553C22.9841 10.3166 22.9841 11.5829 22.203 12.364L21.4959 13.0711C19.9338 14.6332 17.4012 14.6332 15.8391 13.0711L15.1319 12.364C14.3509 11.5829 14.3509 10.3166 15.1319 9.53553L17.2533 7.41421Z" fill="white" />
          <path d="M10.5358 14.1317C11.3168 13.3507 12.5831 13.3507 13.3642 14.1317L15.4855 16.253C16.2665 17.0341 16.2665 18.3004 15.4855 19.0815L14.7784 19.7886C13.2163 21.3507 10.6836 21.3507 9.12154 19.7886L8.41443 19.0815C7.63338 18.3004 7.63338 17.0341 8.41443 16.253L10.5358 14.1317Z" fill="white" />
          <path d="M17.9604 21.5563C18.7414 20.7753 20.0078 20.7753 20.7888 21.5563L22.9101 23.6777C23.6912 24.4587 23.6912 25.725 22.9101 26.5061L22.203 27.2132C20.6409 28.7753 18.1083 28.7753 16.5462 27.2132L15.8391 26.5061C15.058 25.725 15.058 24.4587 15.8391 23.6777L17.9604 21.5563Z" fill="white" />
        </svg>
      </div>

      <h1 class="title--logo__heading">
        Login
      </h1>

      <div class="title__logo__subtitle">
        <span>Aren't a member yet?</span>
        <a href="#">Create an account</a>
      </div>

    </section>

    <form class="sign-in-social">

      <button class="btn-primary">
        Sign in with Google
      </button>

    </form>

    <section>
      <div class="thin-border"></div>
    </section>

    <form class="sign-in-guest">

      <div data-control="name" class="form-control">
        <label for="name">Name</label>
        <input type="text" required placeholder="Sparky McFirebase" name="name" />
      </div>

      <button type="submit" class="btn-primary">
        Sign in as Guest
      </button>
    </form>

  </div>
</template>
:root {
  --font-stack: arboria, sans-serif;
  --primary-hue: 240;
  --money-hue: 120;
  --primary-light: hsl(var(--primary-hue), 67%, 96%); 
  --primary: hsl(var(--primary-hue), 32%, 57%);
  --money: hsl(var(--money-hue), 32%, 57%);
  --white: white;
  --gray: hsla(240, 10%, 48%, 1);
  --blue: hsl(212, 62%, 57%);
  
  --scale: 1.5;
  
  --pie-1__color: var(--money);
  --pie-1__value: 26%;
  --pie-1__computed: calc(var(--pie-1__value) + 0%);

  --pie-2__color: var(--gray);
  --pie-2__value: 12%;
  --pie-2__computed: calc(var(--pie-1__value) + var(--pie-2__value));
  
  --pie-3__color: var(--blue);
  --pie-3__value: 30%;
  --pie-3__computed: calc(var(--pie-2__computed) + var(--pie-3__value));
  
  --pie-4__color: var(--primary);
  --pie-4__value: 38%;
  --pie-4__computed: 0%; 
  
  --pie-1: var(--pie-1__color) var(--pie-1__computed);
  --pie-2: var(--pie-2__color) 0 var(--pie-2__computed); 
  --pie-3: var(--pie-3__color) 0 var(--pie-3__computed);
  --pie-4: var(--pie-4__color) 0 var(--pie-4__computed);
 
}

* {
  box-sizing: border-box;
}

html, body {
  background-color: var(--primary-light);
  font-family: var(--font-stack);
  height: 100vh;
  display: grid;
  padding: 1rem;
}

.container {
  display: grid;
  place-content: center;
}

.phone {
  background: var(--white);
  border-radius: calc(50px / var(--scale));
  height: calc(972px / var(--scale));
  width: calc(452px / var(--scale));
  box-shadow: 2px 2px 24px -8px hsla(240, 40%, 33%, .45);
  position: relative;
  overflow: hidden;
}

.page-grid {
  padding-top: 2rem;
  display: flex;
  flex-direction: column;
  row-gap: 2rem;  
}

.page-grid section:not(.fullbleed) {
  padding-left: 1rem;
  padding-right: 1rem;
}

.sign-in-grid {
  display: flex;
  flex-direction: column;
  padding-left: 1rem;
  padding-right: 1rem;
  place-content: center;
  height: 100%;
  row-gap: 2rem;
}

.title {
  display: grid;
  row-gap: .5rem;
}

.title__heading {
  font-size: 2.25rem;
  font-weight: 400;
}

.title__label {
  color: var(--primary);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0.04ch;
  text-transform: uppercase;
}

.title--logo {
  display: flex;
  flex-direction: column;
  align-items: center;
  row-gap: 1rem;
}

.title--logo__heading {
  font-size: 32px;
  font-weight: 500;
  margin-top: .5rem;
}

.title__logo__subtitle {
  font-size: 12px;
  text-align: center;
}

.btn-primary {
  background: hsl(240, 81%, 73%);
  box-shadow: 4px 4px 16px -4px hsla(0, 0%, 0%, 0.25);
  border-radius: 6px;
  border: none;
  color: white;
  width: 100%;
  height: 44px;
  text-align: center;
}

.btn-primary:hover {
  background: hsl(240, 83%, 75%);
  color: hsl(0, 0, 96%);
}

.thin-border {
  background-color: hsl(0, 0%, 90%);
  height: 2px;
  border-radius: 2px;
}

.form-control {
  display: grid;
  row-gap: .25rem;
  margin-bottom: 1rem;
}

.form-control label {
  display: block;
}

.form-control input[type="text"] {
  display: block;
  height: 44px;
  border: none;
  box-shadow: 4px 4px 16px -4px rgba(0, 0, 0, 0.25);
  border-radius: 6px;
  width: 100%;
  padding: .5rem;
}

.main {
  background-color: hsl(240, 77%, 86%);
  height: 16rem;
  display: flex;
  justify-content: center;
  align-items: center;
  perspective: 1000px;
}

.main--light {
  background-color: var(--white);
}

.legend {
  display: flex;
  justify-content: space-evenly;
}

.factor__color {
  height: .75rem;
  width: .75rem;
  border-radius: 100%;
}

.factor {
  display: flex;
  align-items: center;
  column-gap: .25rem;
}

.factor__color--food {
  background-color: var(--money);
}

.factor__color--services {
  background-color: var(--gray);
}

.factor__color--rent {
  background-color: var(--blue);
}

.factor__color--oops {
  background-color: var(--primary);
}

.factor__text, .factor__percentage {
  font-size: .5rem;
  text-transform: uppercase;
  letter-spacing: .04ch;
  font-weight: bold;
  color: var(--gray);
}

.factor__percentage {
  font-size: .65rem;
  color: hsla(240, 100%, 6%);
}

.pie {
  background: 
    conic-gradient(
      var(--pie-1), 
      var(--pie-2), 
      var(--pie-3),
      var(--pie-4)
    );
  border-radius: 50%;
  width: 65%;
  padding-top: 65%;
  box-shadow: 2px 2px 24px -8px hsla(240, 40%, 33%, .75);
}

.action-section {
  display: grid;
  row-gap: 2rem;
  place-content: center;
  height: 100%;
  padding-top: 2rem;
  padding-bottom: 2rem;
}

.hidden {
  display: none;
}
let firebaseConfig = {
  apiKey: "AIzaSyANCgyffqqOMepBntmZ3SdyGOkqXSSBiwQ",
  authDomain: "codepenfire.firebaseapp.com",
  projectId: "codepenfire",
  storageBucket: "codepenfire.appspot.com",
  messagingSenderId: "263874418526",
  appId: "1:263874418526:web:34b33bf61d8d7b9e7821cf",
  measurementId: "G-29DTC3DBD8"
};
let firebaseApp = window.firebase.initializeApp(firebaseConfig);
let rootView = document.querySelector(".phone");
let session = redirectSession();
let root = document.documentElement;

firebaseApp.auth().onAuthStateChanged((user) => {
  if (user != null) {
    routeTo("demo", rootView, user);
  } else {
    routeTo("signIn", rootView);
  }
});

checkForRedirect(session);

async function checkForRedirect(session) {
  if (!session.isRedirected()) {
    return;
  }
  session.removeLink();
  try {
    const result = await firebaseApp.auth().getRedirectResult();
  } catch(error) {
    switch(error.code) {
      case 'auth/credential-already-in-use': {
        // You can check for the provider it uses
        // firebaseApp.auth().fetchProvidersForEmail(error.email)
        // But in this case we don't want the user to link with 
        // an existing account we would prompt them about an
        // error and prompt them to log in with their account
      }
    }
  }  
}

function routeTo(view, rootView, user) {
  let clone, listeners;
  switch (view) {
    case "signIn": {
      clone = cloneNode("#signIn");
      // This is mostly to keep the code pen simple
      // ideally you'd detach listeners
      listeners = attachSignInListeners;
      break;
    }
    case "demo": {
      clone = cloneNode("#demo");
      // This is mostly to keep the code pen simple
      // ideally you'd detach listeners
      listeners = attachDemoListeners;
      break;
    }
    default:
      throw new Error("???");
      break;
  }
  rootView.innerHTML = "";
  rootView.appendChild(clone);
  listeners(document, firebaseApp, user);
}

function cloneNode(templateSelector) {
  let template = document.querySelector(templateSelector);
  return template.content.cloneNode(true);
}

// This is a performance optimization. Calling the method
// getRedirectResult() will load in code. We can avoid
// loading it before its needed by using sessionStorage
// to signal if the user initiated a redirect
function redirectSession() {
  // Simple helpers for using sessionStorage to
  // indicate if a user is returning from a redirect
  return {
    setLink() {
      sessionStorage.setItem('link', 'true');
    },
    removeLink() {
      sessionStorage.setItem('link', '');
    },
    isRedirected() {
      return !!sessionStorage.getItem('link');
    }
  };
}

function signInGoogle({ link } = { link: false }) {
  let provider = new firebase.auth.GoogleAuthProvider();
  if(link) {
    session.setLink();
    firebaseApp.auth().currentUser.linkWithRedirect(provider);
  } else {
    firebaseApp.auth().signInWithRedirect(provider);
  }
}

function attachSignInListeners(document, firebaseApp) {
  let socialForm = document.querySelector("form.sign-in-social");
  let guestForm = document.querySelector("form.sign-in-guest");

  guestForm.addEventListener("submit", async (submitEvent) => {
    submitEvent.preventDefault();
    let formData = new FormData(guestForm);
    let displayName = formData.get("name");
    let photoURL = await getRandomPhotoURL();
    // Firebase specific code
    let { user } = await firebaseApp.auth().signInAnonymously();
    await user.updateProfile({ displayName, photoURL });
    // End Firebase specific code
  });

  socialForm.addEventListener("submit", (submitEvent) => {
    submitEvent.preventDefault();
    // Firebase specific code
    signInGoogle();
    // End Firebase specific code
  });

  async function getRandomPhotoURL() {
    let response = await fetch("https://picsum.photos/128?blur");
    return response.url;
  }
}

function attachDemoListeners(document, firebaseApp, user) {
  let convertForm = document.querySelector('form.convert');
  let legend = document.querySelector('.legend');
  let marchDoc = firebaseApp.firestore()
    .collection('users')
    .doc(user.uid)
    .collection('expenses')
    .doc('3-2021');
  
  if(user.isAnonymous) {
    convertForm.classList.toggle('hidden');
  }
  convertForm.addEventListener("submit", (submitEvent) => {
    submitEvent.preventDefault();
    signInGoogle({ link: true });
  });
  marchDoc.onSnapshot(snap => {
    legend.innerHTML = '';
    let { factors } = snap.data();
    factors.forEach((factor, index) => {
      let template = cloneNode('#factor');
      let textEl = template.querySelector('.factor__text');
      let percentageEl = template.querySelector('.factor__percentage');
      let factorColorEl = template.querySelector('.factor__color');
      root.style.setProperty(`--pie-${index+1}__value`, `${factor.value}%`);
      textEl.textContent = factor.label;
      percentageEl.textContent = `${factor.value}%`;
      factorColorEl.classList.add(`factor__color--${factor.label.toLowerCase()}`);
      legend.appendChild(template);
    });
    
  });
}

// Creates helpful hot-keys for debugging
// CTRL+O - Sign out
// CTRL+U - Log the current user to the console
document.addEventListener("keydown", async (event) => {
  if (event.ctrlKey && event.key === "o") {
    await firebaseApp.auth().signOut();
    console.log("Signed out!");
  }
  if (event.ctrlKey && event.key === "u") {
    let currentUser = firebaseApp.auth().currentUser;
    console.log(currentUser.toJSON());
  }
});

External CSS

  1. https://use.typekit.net/cti8iku.css
  2. https://unpkg.com/[email protected]/modern-normalize.css
  3. https://meyerweb.com/eric/tools/css/reset/reset.css

External JavaScript

  1. https://www.gstatic.com/firebasejs/8.2.10/firebase-app.js
  2. https://www.gstatic.com/firebasejs/8.2.10/firebase-auth.js
  3. https://www.gstatic.com/firebasejs/8.2.10/firebase-firestore.js