From a85b66453fd6a29c91fea258e095bb1d76a8d254 Mon Sep 17 00:00:00 2001 From: Lexi Mattick Date: Sat, 27 May 2023 17:39:32 -0400 Subject: [PATCH] Add OnBoard marketing page (#795) Co-authored-by: Max Wofford --- components/nav.js | 3 + package.json | 2 +- pages/_app.js | 5 +- pages/onboard.js | 766 ++++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 8 +- 5 files changed, 778 insertions(+), 6 deletions(-) create mode 100644 pages/onboard.js diff --git a/components/nav.js b/components/nav.js index 3287ee41..c3c1ace7 100644 --- a/components/nav.js +++ b/components/nav.js @@ -150,6 +150,9 @@ const Navigation = props => ( Scrapbook Workshops + + OnBoard + ) diff --git a/package.json b/package.json index 30a51d85..4ede1d16 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "react-tooltip": "^4.5.1", "react-tsparticles": "^2.9.3", "react-use-websocket": "^4.3.1", - "react-wrap-balancer": "^0.4.0", + "react-wrap-balancer": "^0.5.0", "recharts": "2.1.12", "styled-components": "^5.3.9", "swr": "^2.1.2", diff --git a/pages/_app.js b/pages/_app.js index 7dcd21c4..366fa6b5 100755 --- a/pages/_app.js +++ b/pages/_app.js @@ -7,6 +7,7 @@ import Meta from '@hackclub/meta' import '@hackclub/theme/fonts/reg-bold.css' import theme from '../lib/theme' import { ThemeProvider } from 'theme-ui' +import { Provider as BalancerProvider } from 'react-wrap-balancer' const App = ({ Component, pageProps }) => ( @@ -16,7 +17,9 @@ const App = ({ Component, pageProps }) => ( content="7zE7h5foPaxIcnv5Frq6BkcUb9-3UzVc8q3P_cexf9I" /> - + + + ) diff --git a/pages/onboard.js b/pages/onboard.js new file mode 100644 index 00000000..e814e866 --- /dev/null +++ b/pages/onboard.js @@ -0,0 +1,766 @@ +import { + Box, + Button, + Grid, + Heading, + Image, + Text, + Flex, + Link +} from 'theme-ui' +import Balancer from 'react-wrap-balancer' +import Head from 'next/head' +import Meta from '@hackclub/meta' +import Nav from '../components/nav' +import Footer from '../components/footer' +import FadeIn from '../components/fade-in' +import Sparkles from '../components/sparkles' +import Tilt from '../components/tilt' +import usePrefersReducedMotion from '../lib/use-prefers-reduced-motion' +import { useRef, useEffect, useState } from 'react' + +/** + * @type {import('theme-ui').ThemeUIStyleObject} + */ +const traceSx = { + width: 6, + bg: '#e2b747', + alignSelf: 'stretch', + mr: 100, + position: 'relative' +} + +const dimBg = '#151515' + +// Beloved classic utility function :3 +const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)) + +// "LET'S RECAP" pixel art (exported from Piskel) +// Original: https://doggo.ninja/fiK0nk.piskel +const recapWidth = 71 +const recapHeight = 10 +const recapPixels = [ + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff +] + +const slackLink = `/slack/?reason=${encodeURIComponent('I joined for OnBoard!')}` + +const stickerButtonText = 'Click 4 Stickers' +const stickerButtonFont = 'Oleo Script' +const stickerButtonFontStylesheet = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(stickerButtonFont)}&display=swap&text=${encodeURIComponent(stickerButtonText)}` + +const wandImgTraced = 'https://cloud-8lszi55ph-hack-club-bot.vercel.app/10frame_2.png' +const wandImgRendered = 'https://cloud-8lszi55ph-hack-club-bot.vercel.app/00frame_1.png' + +const ShipPage = () => { + const prefersReducedMotion = usePrefersReducedMotion() + + // Wand flicker animation + const [ wandImg, setWandImg ] = useState(wandImgTraced) + const wandAnimated = useRef(false) + useEffect(() => { + let canceled = false + + const flicker = async () => { + if (canceled) return + setWandImg(wandImgTraced) + await sleep(Math.random() * 80 + 10); if (canceled) return + setWandImg(wandImgRendered) + setTimeout(flicker, Math.random() * 4000 + 500) + } + + const animate = async () => { + if (wandAnimated.current) return + wandAnimated.current = true + + await sleep(1500); if (canceled) return + + setWandImg(wandImgRendered) + await sleep(60); if (canceled) return + setWandImg(wandImgTraced) + await sleep(340); if (canceled) return + + setWandImg(wandImgRendered) + await sleep(14); if (canceled) return + setWandImg(wandImgTraced) + await sleep(55); if (canceled) return + + setWandImg(wandImgRendered) + await sleep(10); if (canceled) return + setWandImg(wandImgTraced) + await sleep(150); if (canceled) return + + setWandImg(wandImgRendered) + setTimeout(flicker, 1200) + } + + if (prefersReducedMotion) { + setWandImg(wandImgRendered) + } else { + animate() + } + + return () => canceled = true + }, [ prefersReducedMotion ]) + + // Spotlight effect + const spotlightRef = useRef() + useEffect(() => { + const handler = (event) => { + spotlightRef.current.style.background = `radial-gradient( + circle at ${event.pageX}px ${event.pageY}px, + rgba(0, 0, 0, 0) 10px, + rgba(0, 0, 0, 0.85) 80px + )` + } + window.addEventListener('mousemove', handler) + return () => window.removeEventListener('mousemove', handler) + }, []) + + // Calculating the bus height to match the bottom left of the first connector. + const [ busHeight, setBusHeight ] = useState(null) + const containerRef = useRef() // For ResizeObserver + const connectorRef = useRef() // To get bottom left position + const busRef = useRef() // To calculate height differential + + useEffect(() => { + const observer = new ResizeObserver(() => { + const connectorRect = connectorRef.current.getBoundingClientRect() + const busRect = busRef.current.getBoundingClientRect() + setBusHeight(busRect.bottom - connectorRect.bottom + 4.5) + }) + + observer.observe(containerRef.current) + return () => observer.disconnect() + }, []) + + // Fancy lights animation + const lightsScrollTrigger = useRef() + const lightsAnimated = useRef(false) + useEffect(() => { + let canceled = false + + const setAtIndex = (i, color) => { + if (canceled) return + + // Going outside of React for performance + const el = document.getElementById(`pixel-${i}`) + if (!el) return + + if (recapPixels[i]) { + el.style.background = color + el.style.boxShadow = `0 0 10px ${color}` + } else { + el.style.background = dimBg + el.style.boxShadow = 'none' + } + } + const setAll = (color) => { + for (let i = 0; i < recapPixels.length; i++) setAtIndex(i, color) + } + + const animate = async () => { + if (lightsAnimated.current) return + lightsAnimated.current = true + + // Illuminate lights in diagonal lines starting with only top left. + for (let curColumn = 0; curColumn < recapWidth + recapHeight; curColumn++) { + for (let offset = curColumn; offset >= Math.max(0, curColumn - recapHeight); offset--) { + const i = curColumn * recapWidth + offset - offset * recapWidth + setAtIndex(i, '#ffffff') + if (!recapPixels[i]) await sleep(4); if (canceled) return + } + // await sleep(2); if (canceled) return + } + + // Flash the lights twice + await sleep(600); if (canceled) return + + setAll(dimBg) + await sleep(80); if (canceled) return + + setAll('#aaaaaa') + await sleep(20); if (canceled) return + + setAll(dimBg) + await sleep(30); if (canceled) return + + setAll('#aaaaaa') + await sleep(100); if (canceled) return + + setAll(dimBg) + await sleep(200); if (canceled) return + + // Animate rainbow 2-column increments + for (let x = 0; x < recapWidth; x++) { + const color = `hsl(${x * 360 / recapWidth}, 100%, 65%)` + for (let y = 0; y < recapHeight; y++) { + const i = y * recapWidth + x + setAtIndex(i, color) + } + if (x % 2 === 1) await sleep(35) + } + } + + if (prefersReducedMotion) { + if (!lightsAnimated.current) setAll('#ffffff') + return () => canceled = true + } else { + const observer = new IntersectionObserver(([ entry ]) => { + if (entry.isIntersecting) animate() + }, { threshold: 0.5 }) + observer.observe(lightsScrollTrigger.current) + return () => { + canceled = true + observer.disconnect() + } + } + }, [ prefersReducedMotion ]) + + return ( + <> + + + + + + + + +