Cooking Up a Digital Feast: Tales from the Amigos Website Project

Amigos Restaurant in Juba: Savor the blend of local and global flavors at a friendly spot where every meal is a celebration of community.

Mar, 2023Completednetlify.app
Carousel image (1)
Carousel image (2)
Carousel image (3)
Carousel image (4)
Carousel image (5)

The Hunger for a Digital Presence

In the culinary jungle of Juba, a new contender emerged - Amigos Restaurant, a haven for those seeking a fusion of local and international delicacies. But in this digital age, having a killer menu just ain't enough. Nope, we needed to whip up a website that would make mouths water from miles away. Inspired by the slick design of La Barraca's site (because let's be honest, the Dutch know their stuff when it comes to stunning visuals), I set out on a quest to capture Amigos' cozy African vibes in pixel form.

Cooking Up a Storm of Challenges

Now, this project was no walk in the park (pun very much intended). As a fresh-faced Sass newbie, I found myself drowning in a sea of syntax, global variables, and mixins galore. It was like trying to cook a traditional Sudanese dish while learning a new language on the fly. But hey, what's a little chaos when you're learning, right?

Then there was the JavaScript Intersection Observer API, which introduced me to the magical world of scroll animations. Let's just say it made my website look like it was dancing to the beat of its own drum, bouncing and jiving all over the place. Fun times!

The Video Aperitif: A Tantalizing Tease

Originally, the plan was to envelop visitors in Amigos' cozy atmosphere with a hero video showcasing the restaurant's ambiance. But alas, renovations were underway, and trying to capture that vibe would've been like filming a serene nature documentary in the middle of a construction site. Not exactly the serene escape we were going for.

Plan B? Showcasing the culinary wizardry of Amigos' chefs, of course! We settled on a classic pizza, because let's be real, who doesn't love a good slice? It was a serendipitous choice too, as both the reno crew and restaurant staff were drooling over the idea on shoot day. Talk about a universal craving!

The resulting four-minute video was a masterpiece, a true ode to the art of pizza-making. But alas, it threatened to slow my website down more than a tired traveler after a long journey. Not ideal.

Bite-sized Video Buffet: A Delightful Compromise

In a stroke of genius (or so I like to think), I chopped that bad boy up into six bite-sized clips, each focusing on a key moment in the pizza-making process. But why stop there? To keep things interesting, the website was programmed to randomly serve up one of those scrumptious snippets every time someone landed on the page. It was like a delicious surprise with every visit!

Here's how we made this digital pizza roulette happen:

File:Header.jsx

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import Button from '../button/Button';
import './header.scss';

import v1 from '@/assets/vids/1.mp4';
import v2 from '@/assets/vids/2.mp4';
import v3 from '@/assets/vids/3.mp4';
import v4 from '@/assets/vids/4.mp4';
import v5 from '@/assets/vids/5.mp4';

const Header = () => {
  const vids = [v1, v2, v3, v4, v5];
  const [current, setCurrent] = useState(null);

  useEffect(() => {
    const randomNum = Math.floor(Math.random() * vids.length);
    setCurrent(vids[randomNum]);
  }, []);

  return (
    <header>
      <div className='bg-vid'>
        {current && (
          <video autoPlay={true} loop={true} muted={true} playsInline={true}>
            <source src={current} type='video/mp4' />
          </video>
        )}
      </div>
    </header>
  );
};

Let me break down this tasty code morsel for you:

It's like having a digital chef who tosses up a new culinary performance every time you visit. Bon appétit!

Animating the Appetite

With the video situation under control, it was time to add some extra pizzazz to the site. Enter: scroll animations! Now, I could've taken the easy route and used one of those fancy libraries that make animating as simple as boiling water. But where's the fun in that? Nope, I decided to dive headfirst into the Intersection Observer API and build those animations from scratch.

It was like trying to navigate a busy Juba market while blindfolded, but hey, I emerged a JavaScript ninja ready to slay any animation challenge that dared cross my path. Plus, all that hard work paid off with buttery-smooth transitions and interactions that would make even the most discerning user go "mmmm."

File:useIntersectionObserver.js

import { useEffect, useCallback } from 'react';

const useIntersectionObserver = (
  refs,
  threshold,
  animationClass,
  once = true
) => {
  const onVisibilityChanged = useCallback(
    (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          entry.target.classList.add(animationClass);
        } else if (!once) {
          entry.target.classList.remove(animationClass);
        }
      });
    },
    [animationClass, once]
  );

  useEffect(() => {
    if (refs.length === 0) return;

    const options = {
      threshold: threshold,
    };
    const observer = new IntersectionObserver(onVisibilityChanged, options);

    refs.forEach((el) => observer.observe(el?.current));

    return () => {
      refs.forEach((el) => {
        if (el.current) {
          observer.unobserve(el.current);
        }
      });
    };
  }, [refs, onVisibilityChanged]);
};

export default useIntersectionObserver;

This custom hook is like the head chef of our animation kitchen. Here's what it does:

Animating the Appetite in Practice

The hook takes three arguments: an array of refs, a threshold, and a CSS class name. The refs are references to the DOM elements that the hook will observe. When any of these elements cross the threshold of visibility in the viewport (in this case, when 15% of the element is visible), the hook adds the specified CSS class to the element. The class triggers an animation causing the element to "slide up" into view. Just like that, a boring website can be turned into an interactive heaven, and the best part is that the hook can be reused anywhere!

File:Menu.jsx

import React, { useRef } from 'react';
import './menu.scss';
import useIntersectionObserver from '@/hooks/useIntersectionObserver';
import useWindowWidth from '@/hooks/useWindowWidth';

import breakfast from '@/assets/imgs/menu/breakfast.jpg';
import main from '@/assets/imgs/menu/main.jpg';
import pasta from '@/assets/imgs/menu/pasta.jpg';
import sandwich from '@/assets/imgs/menu/sandwich.jpg';
import main2 from '@/assets/imgs/menu/main2.jpg';
import local from '@/assets/imgs/menu/local.jpg';

export const menuShowcase = [
  {
    dish: 'Classic Beef Burger',
    category: 'Sandwich',
    img: sandwich,
  },
  {
    dish: 'Goat Meat',
    category: 'Main Course',
    img: main,
  },
  {
    dish: 'Khudra Tabikhr',
    category: 'Local Food',
    img: local,
  },
  {
    dish: 'American Breakfast',
    category: 'Breakfast',
    img: breakfast,
  },
  {
    dish: 'Chicken Shish Tawoog',
    category: 'Main Course',
    img: main2,
  },
  {
    dish: 'Pasta Chicken Alfredo',
    category: 'Pasta',
    img: pasta,
  },
];


const Menu = () => {
  {/* ... */}
  const width = useWindowWidth();
  const numRefs = 6;
  const refs = Array.from({ length: numRefs }, () => useRef(null));

  menuShowcase.forEach((item, index) => {
    item.ref = refs[index];
  });

  useIntersectionObserver(refs, 0.15, 'slide-up');

  return (
    <div className='menu'>
      {/* ... */}
      <div className='showcase'>
        {menuShowcase.map((item, index) => {
          return (
            <div
              ref={item.ref}
              key={index}
              className='item'
              style={width >= 768 ? { animationDelay: `${index * 0.3}s` } : {}}
            >
              <img
                src={item.img}
                alt={item.dish}
                title={item.dish}
                loading='eager'
              />

              <div className='item-info'>
                <h2>{item.dish}</h2>
                <h3>{item.category}</h3>
              </div>
            </div>
          );
        })}
      </div>
      {/* ... */}
    </div>
  );
};

export default Menu;

Here's the recipe for this animation feast:

It's like choreographing a culinary ballet, with each dish making its grand entrance at just the right moment. Gordon Ramsay would be proud (or at least, he wouldn't be yelling at us).

Adapting to a Shifting Menu

Just when I thought I had everything figured out, the project took an unexpected turn. The grand vision of an ordering system and delivery app? Gone like a mirage in the desert. Financial constraints meant we had to pivot to a good ol' fashioned showcase website.

Now, I could've switched to a snazzier backend or CMS to make content management a breeze. But with time ticking away faster than a chef's order during a dinner rush, I decided to stick with our trusty Firebase setup. Sure, it might not have been the most elegant solution, but sometimes you gotta work with what you've got and make it sing, you know?

Savoring the Lessons

This project was a crash course in all things Sass, intersection observers, and CSS animation wizardry. But perhaps the most eye-opening lesson? React's not-so-stellar SEO performance out of the box. Cue the exploration of solutions like React Helmet to give those search engine crawlers something to sink their teeth into.

Beyond the technical know-how, this project was a masterclass in collaboration and adaptability. Pivoting alongside Amigos' team as the scope shifted was like a choreographed dance, gracefully adjusting to the rhythm of the ever-changing beat.

File:Home.jsx

import { Helmet } from 'react-helmet-async';
import { pages } from '@/Constants';
// Other imports


const Home = () => {
  //....
  return (
    <>
      <Helmet>
        <meta charSet='utf-8' />
        <title>{pages[0].title}</title>
        <link rel='canonical' href='/' />
        <meta name='description' content={pages[0].description} />
        <meta name='theme-color' content={pages[0].theme} />

        <meta name='image' content={pages[0].image} />

        <meta name='msapplication-TileImage' content={pages[0].image} />

        <meta property='og:title' content={pages[0].title} />
        <meta property='og:description' content={pages[0].description} />
        <meta property='og:image' content={pages[0].image} />
        <meta property='og:url' content={pages[0].url} />
        <meta property='og:type' content='article' />

        <meta name='twitter:title' content={pages[0].title} />
        <meta name='twitter:description' content={pages[0].description} />
        <meta name='twitter:image' content={pages[0].image} />
        <meta name='twitter:card' content='summary_large_image' />

        <meta name='msapplication-square150x150logo' content={pages[0].image} />
        <meta name='msapplication-square310x310logo' content={pages[0].image} />

        <link rel='apple-touch-icon' href='/src/assets/imgs/logo/amigos.png' />

        <link rel='image_src' href='/src/assets/imgs/logo/amigos.png' />
      </Helmet>

      <Header />

      {/* ... */}
    </>
  );
};

export default Home;

This Helmet component is like the menu board for search engines. It tells them exactly what's cooking on our page:

It's like leaving a trail of digital breadcrumbs for search engines to follow, leading them (and hungry customers) right to our virtual doorstep.

A Bittersweet Farewell

Alas, all good things must come to an end, and Amigos has since closed its doors. But fear not, for their digital footprint lives on in the form of this delectable website. It's a monument to the power of food in bringing people together, a reminder that even in the digital realm, we can capture the essence of a vibrant local business.

The journey with Amigos was a wild ride, full of valuable lessons about web development, capturing vibes in pixels, and the unpredictable nature of the restaurant biz. Sure, the project may be over, but the memories (and newly acquired skills) will forever fuel my passion for crafting digital experiences that tantalize the senses and leave a lasting impression.

The Final Course

The Amigos website project was a true culinary adventure in the digital realm, complete with technical curveballs, creative problem-solving, and a heartwarming tale to top it all off. It taught me the value of adaptability, the power of captivating visuals, and the importance of storytelling in connecting with an audience.

As I move on to new gastronomic (and digital) conquests, the lessons from this project will forever shape my approach, reminding me that every challenge is an opportunity to cook up something truly innovative and delectable.

So, my fellow foodies and web aficionados, I invite you to dig in and share your own tales of culinary (or digital) adventures. Let's swap stories, trade tips, and continue this delicious conversation about the endless possibilities that await when we blend our passions for food, technology, and creativity. Bon appétit!

Share Project