advice-generator-app-frontendmentor

Advice Generator App

Coded with HTML, SCSS, JavaScript

ℹ️ About

This is a solution to the Advice generator app challenge on Frontend Mentor.

The challenge is to build out this advice generator app using the Advice Slip API and get it looking as close to the design as possible.

See Task.md for more details about the task.

Users should be able to:

⚙️ Tools

📁 File Structure

Path Description
public Folder with .html and .css files
public / index.html Main HTML file
public / index.css Main CSS file, generated with SCSS transpiler
src Source files needed for application development
src / assets Images and other media used on the webpage
src / index.scss Main SCSS file, used to later generate CSS
src / index.js Main file with JS
src / scripts Folder with JS functions imported by index.js
docs Folder with additional information, documentation
docs / images Folder with the application screenshots
docs / images / goals Folder with images from Frontendmentor that show how the page should look like
docs / images / results Folder with screenshots of how the application works after being fully developed
docs / README-template.md Template for README.md, don't use it (from FrontendMentor)
docs / style-guide.md Style information: color palette, fonts, etc. (from FrontendMentor)
docs / task.md Detailed task description (from FrontendMentor)
docs / advice-generator-app-main.zip Initial archive provided by Frontend Mentor

💡 Details

Working with API

try {
  const result = await fetch(API_URL, {
    method: "GET",
  });
  const resultJSON = await result.json();

  // ...
} catch (err) {
  // ...
}

Button animation

There is a limitation described in the docs of API: it can generate new quotes only once in 2 seconds. Thus I disabled the button after the pressing, then enable it in 3 seconds. During these 2 seconds I show spinning animation

&__button[disabled] &__icon-dice {
  animation: rotate-360 3s infinite alternate ease-in-out;
}

&__button[disabled] {
  animation: shade-fade 3s forwards infinite alternate ease-in-out;
}

@keyframes rotate-360 {
  100% {
    transform: rotate(360deg);
  }
}

@keyframes shade-fade {
  0% {
    box-shadow: 0 0 32px 4px var(--primary-1);
  }

  100% {
    box-shadow: 0 0 0px 0px var(--primary-1);
  }
}

Quote and advice text animation

I achieved by toggling classes and adding a little setTimeout

setTimeout(() => {
  removeClassFromElement(adviceText, "card__advice--out");
  removeClassFromElement(adviceId, "card__id--out");

  displayData(adviceId, resultJSON?.slip?.id ?? "-");
  displayData(
    adviceText,
    resultJSON?.slip?.advice ?? "Unable to get the quote."
  );

  addClassToElement(adviceText, "card__advice--in");
  addClassToElement(adviceId, "card__id--in");
}, 600); // 600ms helps to make all animations smooth - result of experiments
&__id--out {
  animation: shrink-out 0.8s forwards;
}

&__id--in {
  animation: shrink-in 0.8s forwards;
}

&__advice--out {
  animation: fade-out 0.8s forwards;
}

&__advice--in {
  animation: fade-in 0.8s forwards;
}

@keyframes fade-out {
  0% {
    transform: scale(1);
  }
  100% {
    transform: scale(0);
  }
}

@keyframes fade-in {
  0% {
    transform: scale(0);
  }
  100% {
    transform: scale(1);
  }
}

@keyframes shrink-out {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

@keyframes shrink-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

Offline and error notifications

If user goes offline, notification shows up and disappears when he is back online. Notifications fade in and fade out with animation.

When I add a notification to the page, it starts with 0 opacity, then by the end of animation the opacity is 1.

When I remove the notification from the page, launch the animation that slowly turns the opacity to 0, after some delay I delete the element from the page.

addEventListener("offline", (event) => {
  disableElement(generateQuoteButton);
  const errorElement = displayError("You are offline");
  const checkOnline = setInterval(() => {
    const isOnline = window.navigator.onLine;
    if (isOnline) {
      enableElement(generateQuoteButton);
      clearInterval(checkOnline);
      errorElement.style.animation = "shrink-out 0.8s forwards";
      setTimeout(() => {
        removeElementFromPage(errorElement);
      }, 800);
    }
  }, 1000);
});
try {
  ...
} catch (err) {
    const errorElement = displayError(
      "Something went wrong with receiving a quote"
    );
    setTimeout(() => {
      errorElement.style.animation = "shrink-out 0.8s forwards";
      setTimeout(() => {
        removeElementFromPage(errorElement);
      }, 800);
    }, 3000);
  }
@keyframes shrink-out {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

@keyframes shrink-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

Nothing special, thanks to https://tobiasahlin.com/blog/css-trick-animating-link-underlines/

&--link::before {
  content: "";
  position: absolute;
  display: block;
  width: 100%;
  height: 2px;
  border-radius: 2px;
  bottom: -2px;
  left: 0;
  background-color: var(--primary-2);
  transform: scaleX(0);
  transition: transform 0.5s ease;
}

&--link:hover::before {
  transform: scaleX(1);
}

&--description {
  font-size: 14px;
}

Generate quotes from keyboard

const generateQuoteButton = document.querySelector(".card__button");

/// ....

document.addEventListener("keydown", (e) => {
  switch (e.key) {
    case "Enter":
      generateQuoteButton.focus();
      generateQuoteButton.dispatchEvent("click");
      return;
  }
});

Figma prototype

I use a free version of FrontendMentor. Thus I don’t have access to Figma prototypes. But that’s not a problem - I made my own one

Here it is: https://www.figma.com/file/gtbZrKQzY4Ovlz9kQWeBPR/Advice-Generator-App?node-id=0%3A1

That helped me to make the application as close to the photos as possible

🔗 Useful resources

👤 Author