From bbfab02a131ce7559737f4fd4724e710489aa163 Mon Sep 17 00:00:00 2001 From: Malted Date: Tue, 15 Oct 2024 12:56:59 -0400 Subject: [PATCH] YSWS --- components/flag.js | 4 +- components/nav.js | 5 +- components/ysws/countdown.js | 98 +++++++ pages/ysws.js | 365 +++++++++++++++++++++++++ public/ysws/brown_prioritymail_box.png | Bin 0 -> 68704 bytes 5 files changed, 468 insertions(+), 4 deletions(-) create mode 100644 components/ysws/countdown.js create mode 100644 pages/ysws.js create mode 100644 public/ysws/brown_prioritymail_box.png diff --git a/components/flag.js b/components/flag.js index bb4863e74..97c57b6a9 100644 --- a/components/flag.js +++ b/components/flag.js @@ -28,7 +28,7 @@ const scrolled = props => height: 56px; &:hover, &:focus { - animation: ${waveFlagScaled} 0.5s linear infinite alternate; + animation: ${waveFlagScaled} 0.3s ease-in-out infinite alternate; } ` @@ -49,7 +49,7 @@ const Base = styled('a')` } &:hover, &:focus { - animation: ${waveFlag} 0.5s linear infinite alternate; + animation: ${waveFlag} 0.2s ease-in-out infinite alternate; } @media (prefers-reduced-motion: reduce) { animation: none !important; diff --git a/components/nav.js b/components/nav.js index 9c1efad7e..b106bac6f 100644 --- a/components/nav.js +++ b/components/nav.js @@ -49,6 +49,7 @@ const Root = styled(Box, { width: 100vw; z-index: 1000; ${fixed}; + transition: background 0.15s; @media print { display: none; } @@ -144,8 +145,8 @@ const Navigation = props => ( Community Scrapbook - - OnBoard + + You Ship, We Ship ) diff --git a/components/ysws/countdown.js b/components/ysws/countdown.js new file mode 100644 index 000000000..4b1f54c76 --- /dev/null +++ b/components/ysws/countdown.js @@ -0,0 +1,98 @@ +import styled from '@emotion/styled' +import { useEffect, useState } from 'react' +import { Box, Flex, Text } from 'theme-ui' + +const CountdownWrapper = styled.div` + display: grid; + grid-template-columns: repeat(8, 1fr); + + @media (max-width: 640px) { + grid-template-columns: repeat(2, 1fr); + } +` + +const Value = styled.span(props => ({ + textAlign: 'right', + marginLeft: '10px', + fontSize: props.big ? '3rem' : 'inherit', + textShadow: '0 0 0.4em #ff000090', + color: 'red' +})) + +const Label = styled.span(props => ({ + textAlign: 'left', + marginLeft: '10px', + fontSize: props.big ? '3rem' : 'inherit' +})) + +export default function Countdown({ targetDate }) { + const [timeLeft, setTimeLeft] = useState({ + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0 + }) + + useEffect(() => { + const interval = setInterval(() => { + const now = new Date().getTime() + const distance = targetDate.getTime() - now + + if (distance < 0) { + clearInterval(interval) + setTimeLeft({ + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0 + }) + } else { + setTimeLeft({ + days: Math.floor(distance / (1000 * 60 * 60 * 24)), + hours: Math.floor( + (distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60) + ), + minutes: Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)), + seconds: Math.floor((distance % (1000 * 60)) / 1000), + milliseconds: distance % 1000 + }) + } + }, 10) + + return () => clearInterval(interval) + }, [targetDate]) + + return ( + + + + + + {String(timeLeft.days)} + + + + + {String(timeLeft.hours)} + + + {String(timeLeft.minutes)} + + + {String(timeLeft.seconds)} + + + {String(timeLeft.milliseconds)} + + + + ) +} diff --git a/pages/ysws.js b/pages/ysws.js new file mode 100644 index 000000000..91fe526d9 --- /dev/null +++ b/pages/ysws.js @@ -0,0 +1,365 @@ +import { useState, useEffect, useReducer } from 'react' +import styled from '@emotion/styled' +import { keyframes } from '@emotion/react' +import { Box, Text, Flex, Image, Button } from 'theme-ui' +import Meta from '@hackclub/meta' +import Head from 'next/head' +import Nav from '../components/nav' +import ForceTheme from '../components/force-theme' +import { Zoom } from 'react-reveal' +import Countdown from '../components/ysws/countdown' + +const projects = [ + { + name: 'Sprig', + tagline: 'Every player is a creator.', + images: [ + 'https://sprig.hackclub.com/stories-tiny/sprig-front.jpeg', + 'https://sprig.hackclub.com/stories-tiny/sprig-back.jpeg', + 'https://sprig.hackclub.com/stories-tiny/play.jpeg', + 'https://sprig.hackclub.com/stories-big/develop.jpeg', + 'https://sprig.hackclub.com/stories-big/orpheus.jpeg' + ], + stickers: ['/stickers/sprig.svg', '/stickers/sprig_holographic.png'] + }, + { + name: 'Blot', + tagline: 'Drawing machine', + images: [ + 'https://blot.hackclub.com/assets/control-board.webp', + 'https://blot.hackclub.com/assets/all-parts.jpg', + 'https://blot.hackclub.com/assets/code2.webp', + 'https://blot.hackclub.com/assets/editor.png', + 'https://raw.githubusercontent.com/hackclub/blot/main/art/tree-leo/snapshots/tree.png' + ], + stickers: ['/stickers/Blot.png'] + }, + { + name: 'Bin', + tagline: 'Bin description.', + images: [ + 'https://cloud-6hdo013ly-hack-club-bot.vercel.app/0buzzer.png', + 'https://hackclub.com/bin/parts/pico.png', + 'https://hackclub.com/bin/parts/led.png', + 'https://hackclub.com/bin/parts/humidity.png', + 'https://cloud-ofybe0euz-hack-club-bot.vercel.app/00oky3527-max7219-dot-matrix-module-single-3.png' + ], + stickers: ['/stickers/sprig.svg', '/stickers/sprig_holographic.png'] + }, + { + name: 'Boba Drops', + tagline: 'Boba drops description', + images: [ + 'https://sprig.hackclub.com/stories-tiny/sprig-front.jpeg', + 'https://sprig.hackclub.com/stories-tiny/sprig-back.jpeg' + ], + stickers: ['/stickers/sprig.svg', '/stickers/sprig_holographic.png'] + }, + { + name: 'Bin', + tagline: 'Bin description.', + images: ['/stickers/sprig.svg'], + stickers: ['/stickers/sprig.svg', '/stickers/sprig_holographic.png'] + } +] + +const StyledImageBase = styled(Image)` + width: 30rem; + object-fit: cover; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5); +` + +const StyledImage = styled(StyledImageBase)` + position: absolute; +` + +const slideInFromLeft = keyframes` + from { + transform: translateX(-100%); + opacity: 0; + } + to { + transform: translateX(25%); + opacity: 1; + } +` + +const slideInFromRight = keyframes` + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(-25%); + opacity: 1; + } +` + +const AnimatedText = styled(Text)` + display: inline-block; +` + +const YouShipText = styled(AnimatedText)` + opacity: 0; + animation: ${slideInFromRight} 0.5s ease-out forwards; +` + +const WeShipText = styled(AnimatedText)` + opacity: 0; + animation: ${slideInFromLeft} 0.5s ease-out forwards 0.5s; +` + +const RotatingBox = styled(Box)` + will-change: transform; + transform-style: preserve-3d; + transition: transform 0.25s ease-in-out; +` + +const randRot = () => Math.random() * 60 - 30 + +const initialState = { + currentProjectIdx: 0, + rotAngle: 0, + imageRotations: [] +} + +function reducer(state, action) { + switch (action.type) { + case 'PROJECT_TICK': + console.log('tick!', state.imageRotations.length, state.currentProjectIdx) + return { + ...state, + currentProjectIdx: + state.imageRotations.length >= 5 + ? (state.currentProjectIdx + 1) % projects.length + : state.currentProjectIdx, + rotAngle: (360 / projects.length) * (state.currentProjectIdx + 1 - 1), + imageRotations: [...state.imageRotations, randRot()] + } + default: + return state + } +} + +export default function Component() { + const [state, dispatch] = useReducer(reducer, initialState) + + useEffect(() => { + const interval = setInterval(() => { + dispatch({ type: 'PROJECT_TICK' }) + }, 1_000) + return () => clearInterval(interval) + }, []) + + const currentProject = projects[state.currentProjectIdx] + + return ( + <> + + + +