repos

isaacbythewood.com-nextjs

mirror archived upstream

Animation-heavy personal portfolio on Next.js: a custom cursor, a page-transition loader, and canvas backgrounds.

canvas-animationscss-modulesdark-themehandcodednextjspersonal-websiteportfolioreact

4.7 KB · 154 lines · JavaScript Raw History
  1import React, { useState, useRef } from "react";
  2
  3import { CSSTransition } from "react-transition-group";
  4import Image from "next/image";
  5import Link from "next/link";
  6import styles from "@styles/components/menu.module.css";
  7
  8const Menu = () => {
  9  const [open, setOpen] = useState(false);
 10  const overlayRef = useRef(null);
 11  const gridLeftRef = useRef(null);
 12  const gridRightRef = useRef(null);
 13  const pages = [
 14    { num: "000", href: "/", title: "Home" },
 15    { num: "001", href: "/about", title: "About" },
 16    { num: "002", href: "/code", title: "Code" },
 17    { num: "003", href: "/art", title: "Art" },
 18    { num: "004", href: "/contact", title: "Contact" },
 19  ];
 20
 21  const toggleMenu = () => {
 22    if (open === false) {
 23      setOpen(true);
 24      document.body.style.overflowY = "hidden";
 25    } else {
 26      setOpen(false);
 27      document.body.style.overflowY = "scroll";
 28    }
 29  };
 30
 31  return (
 32    <>
 33      <button
 34        className={styles.hamburger}
 35        onClick={toggleMenu}
 36        aria-label="Hamburger"
 37      >
 38        <div className={styles.patty} style={{ width: "15px" }} />
 39        <div className={styles.patty} />
 40        <div className={styles.patty} style={{ width: "20px" }} />
 41      </button>
 42      <CSSTransition
 43        in={open}
 44        timeout={500}
 45        classNames="menu"
 46        appear
 47        nodeRef={overlayRef}
 48        onEnter={() => {
 49          if (typeof document !== "undefined") {
 50            document.body.style.overflowY = "hidden";
 51          }
 52        }}
 53        onExited={() => {
 54          if (typeof document !== "undefined") {
 55            document.body.style.overflowY = "scroll";
 56          }
 57        }}
 58      >
 59        <div className={styles.overlay} ref={overlayRef}>
 60          <div className={styles.overlayGrid}>
 61            <CSSTransition
 62              in={open}
 63              timeout={500}
 64              classNames="menu"
 65              appear
 66              nodeRef={gridLeftRef}
 67            >
 68              <div className={styles.overlayGridLeft} ref={gridLeftRef}>
 69                <div className={styles.topBar}>
 70                  <a
 71                    className={styles.topLink}
 72                    href="https://blog.bythewood.me/"
 73                    target="_blank"
 74                    rel="noopener noreferrer"
 75                  >
 76                    Blog
 77                  </a>
 78                  <a
 79                    className={styles.topLink}
 80                    href="https://status.bythewood.me/a40ff31d-18b0-49c3-9a36-deb02c909204"
 81                    target="_blank"
 82                    rel="noopener noreferrer"
 83                  >
 84                    Status
 85                  </a>
 86                  <a
 87                    className={styles.topLink}
 88                    href="https://analytics.bythewood.me/30e69c06-9beb-4283-8919-8c7a686ab013"
 89                    target="_blank"
 90                    rel="noopener noreferrer"
 91                  >
 92                    Analytics
 93                  </a>
 94                  <a
 95                    className={styles.topLink}
 96                    href="https://github.com/overshard"
 97                    target="_blank"
 98                    rel="noopener noreferrer"
 99                  >
100                    GitHub
101                  </a>
102                  <div className={styles.bar} />
103                </div>
104                {pages.map((page) => {
105                  return (
106                    <Link
107                      key={page.href}
108                      href={page.href}
109                      className={styles.overlayLink}
110                      data-text={page.title}
111                      onClick={toggleMenu}
112                    >
113                      {page.title}
114                    </Link>
115                  );
116                })}
117                <div className={styles.bottomBar}>
118                  <Image
119                    src="/static/images/avatar.webp"
120                    alt="Isaac Bythewood"
121                    width={50}
122                    height={50}
123                  />
124                  <div className={styles.bar} />
125                </div>
126              </div>
127            </CSSTransition>
128            <CSSTransition
129              in={open}
130              timeout={500}
131              classNames="menu"
132              appear
133              nodeRef={gridRightRef}
134            >
135              <div className={styles.overlayGridRight} ref={gridRightRef}>
136                <Image
137                  src="/static/images/art/acrylic-pours/006.webp"
138                  alt="#006 Molten Copper"
139                  loading="lazy"
140                  fill
141                  sizes="40vw"
142                  style={{ objectFit: "cover", objectPosition: "center" }}
143                />
144              </div>
145            </CSSTransition>
146          </div>
147        </div>
148      </CSSTransition>
149    </>
150  );
151};
152
153export default Menu;