mirror of
https://github.com/System-End/site.git
synced 2026-04-19 16:28:21 +00:00
YSWS
This commit is contained in:
parent
619ddf67a6
commit
bbfab02a13
5 changed files with 468 additions and 4 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 => (
|
|||
</NextLink>
|
||||
<Link href="/slack">Community</Link>
|
||||
<Link href="https://scrapbook.hackclub.com/">Scrapbook</Link>
|
||||
<NextLink href="/onboard" passHref>
|
||||
<Link>OnBoard</Link>
|
||||
<NextLink href="/ysws" passHref>
|
||||
<Link>You Ship, We Ship</Link>
|
||||
</NextLink>
|
||||
</NavBar>
|
||||
)
|
||||
|
|
|
|||
98
components/ysws/countdown.js
Normal file
98
components/ysws/countdown.js
Normal file
|
|
@ -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 (
|
||||
<Box
|
||||
sx={{
|
||||
fontFamily: "'7Seg', monospace",
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
>
|
||||
<Box sx={{ marginY: 3 }}>
|
||||
<Label>Ends in</Label>
|
||||
|
||||
<Box sx={{ lineHeight: 1 }}>
|
||||
<Value big={true}>{String(timeLeft.days)}</Value>
|
||||
<Label big={true}>DAY{timeLeft.days === 1 ? '' : 's'}</Label>
|
||||
</Box>
|
||||
</Box>
|
||||
<CountdownWrapper>
|
||||
<Value>{String(timeLeft.hours)}</Value>
|
||||
<Label>HOUR{timeLeft.hours === 1 ? '' : 's'}</Label>
|
||||
|
||||
<Value>{String(timeLeft.minutes)}</Value>
|
||||
<Label>MINUTE{timeLeft.minutes === 1 ? '' : 's'}</Label>
|
||||
|
||||
<Value>{String(timeLeft.seconds)}</Value>
|
||||
<Label>SECOND{timeLeft.seconds === 1 ? '' : 's'}</Label>
|
||||
|
||||
<Value>{String(timeLeft.milliseconds)}</Value>
|
||||
<Label>MILLISECONDS</Label>
|
||||
</CountdownWrapper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
365
pages/ysws.js
Normal file
365
pages/ysws.js
Normal file
|
|
@ -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 (
|
||||
<>
|
||||
<Box as="main">
|
||||
<Meta
|
||||
as={Head}
|
||||
title="You Ship, We Ship"
|
||||
description="You Ship, We Ship."
|
||||
/>
|
||||
<style>{`
|
||||
@font-face {
|
||||
font-family: '7Seg';
|
||||
src: url('https://cloud-6okxw4gi2-hack-club-bot.vercel.app/0seven_segment.ttf') format('truetype');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
html { scroll-behavior: smooth }
|
||||
`}</style>
|
||||
<Nav light />
|
||||
<ForceTheme theme={'light'} />
|
||||
|
||||
<Flex
|
||||
as="header"
|
||||
sx={{
|
||||
pt: 5,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
bg: 'blue',
|
||||
height: '80vh'
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
as="h1"
|
||||
sx={{
|
||||
position: 'relative',
|
||||
fontSize: ['4rem', '5rem'],
|
||||
lineHeight: 1.2,
|
||||
color: 'white'
|
||||
}}
|
||||
>
|
||||
<Zoom duration={500}>
|
||||
<Image
|
||||
src="/arcade/o3.png"
|
||||
alt=""
|
||||
width={128}
|
||||
sx={{ position: 'absolute', left: '-75%' }}
|
||||
/>
|
||||
</Zoom>
|
||||
<YouShipText sx={{ position: 'relative' }}>You Ship,</YouShipText>
|
||||
<br />
|
||||
<WeShipText>We Ship</WeShipText>
|
||||
|
||||
<Zoom delay={500} duration={500}>
|
||||
<Image
|
||||
src="/ysws/brown_prioritymail_box.png"
|
||||
alt="USPS priority mail cardboard box"
|
||||
width={128}
|
||||
sx={{ position: 'absolute', right: '-60%', bottom: 0 }}
|
||||
/>
|
||||
</Zoom>
|
||||
</Text>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
marginX: 'auto',
|
||||
width: '100%',
|
||||
maxWidth: '60vw',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
marginY: 4
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 2fr',
|
||||
height: '100%',
|
||||
perspective: '50px'
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
color: 'white',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<p className="p-3 text-red-500 z-50">
|
||||
{currentProject.name} - {state.currentProjectIdx}
|
||||
</p>
|
||||
<RotatingBox
|
||||
data-angle={state.rotAngle}
|
||||
sx={{ transform: `rotate3d(1, 0, 0, ${state.rotAngle}deg)` }}
|
||||
>
|
||||
{projects.toReversed().map((p, idx) => {
|
||||
const radius = 120
|
||||
|
||||
const theta = idx * ((2 * Math.PI) / projects.length)
|
||||
const x = radius * Math.cos(theta)
|
||||
const y = radius * Math.sin(theta)
|
||||
|
||||
const angle = (theta * 180) / Math.PI + 360
|
||||
|
||||
return (
|
||||
<Flex
|
||||
key={idx}
|
||||
sx={{
|
||||
backfaceVisibility: 'hidden',
|
||||
position: 'absolute',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
color: 'white',
|
||||
translate: '0 -50% 0',
|
||||
transform: `translate3d(0, ${y}px, ${x}px) rotate3d(1, 0, 0, ${angle}deg)`,
|
||||
width: '100%',
|
||||
willChange: 'transform'
|
||||
}}
|
||||
>
|
||||
<Text sx={{ fontSize: '4rem', fontWeight: 'bold' }}>
|
||||
{p.name}
|
||||
</Text>
|
||||
<Text sx={{ fontSize: '2rem' }}>{p.tagline}</Text>
|
||||
</Flex>
|
||||
)
|
||||
})}
|
||||
</RotatingBox>
|
||||
|
||||
{/* <Image
|
||||
src={currentProject.stickers[0]}
|
||||
alt=""
|
||||
width={64}
|
||||
height={64}
|
||||
sx={{ rotate: '-15deg', position: 'absoulute', top: 0 }}
|
||||
/>
|
||||
|
||||
<Image
|
||||
src={currentProject.stickers[1]}
|
||||
alt=""
|
||||
width={64}
|
||||
height={64}
|
||||
sx={{ rotate: '30deg' }}
|
||||
/> */}
|
||||
</Flex>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
position: 'relative',
|
||||
height: '100%'
|
||||
}}
|
||||
>
|
||||
{state.imageRotations.map((rot, idx) => (
|
||||
<StyledImage
|
||||
key={idx}
|
||||
src={
|
||||
currentProject.images[idx % currentProject.images.length]
|
||||
}
|
||||
alt=""
|
||||
sx={{
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: `rotate(${rot}deg)`,
|
||||
translate: '-50% -50%',
|
||||
zIndex: idx,
|
||||
opacity: (idx + 1) / state.imageRotations.length,
|
||||
transition: 'opacity 0.5s'
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
maxWidth: '80ch',
|
||||
marginX: 'auto',
|
||||
textAlign: 'center',
|
||||
alignItems: 'center',
|
||||
py: 4
|
||||
}}
|
||||
>
|
||||
<Text as="h2" sx={{ lineHeight: 1.2 }}>
|
||||
LIMITED TIME EVENT
|
||||
<br />
|
||||
<Text sx={{ fontSize: '1.8em' }}>LLM Workshop</Text>
|
||||
</Text>
|
||||
|
||||
<Countdown targetDate={new Date('2024-10-12T23:59:59')} />
|
||||
|
||||
<Flex sx={{ flexDirection: 'column', marginY: 4, gap: 3 }}>
|
||||
<Button sx={{ width: '100%', fontSize: '2em' }}>Get a book</Button>
|
||||
<StyledImageBase
|
||||
src="https://sprig.hackclub.com/stories-tiny/sprig-front.jpeg"
|
||||
width="50%"
|
||||
alt=""
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
BIN
public/ysws/brown_prioritymail_box.png
Normal file
BIN
public/ysws/brown_prioritymail_box.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
Loading…
Add table
Reference in a new issue