Winterwinterwinterwinterwinterwinter (#618)

init
This commit is contained in:
Ella 2022-12-05 10:59:40 -05:00 committed by GitHub
parent 3a88bebd83
commit 1294bf96ef
47 changed files with 1562 additions and 9 deletions

View file

@ -14,7 +14,7 @@ const handleClick = () => {
}
})
}
const ScrollHint = () => (
const ScrollHint = ({...props}) => (
<Box
sx={{
display: 'block',
@ -33,6 +33,7 @@ const ScrollHint = () => (
'&:active': { transform: ' translateY(6px) rotate(45deg)' }
}}
onClick={handleClick}
{...props}
></Box>
)

View file

@ -0,0 +1,80 @@
import {
Box,
Card,
Flex,
Container,
Heading,
Text,
Badge,
Link
} from 'theme-ui'
import { Zoom } from 'react-reveal'
import Icon from '@hackclub/icons'
function BreakdownBox({ subtitle, icon, text, description, delay, href, color, bg }) {
return (
<Zoom delay={delay}>
<Card
sx={{
color: 'white',
background: 'rgba(225,225,225,0.2)',
height: '100%',
cursor: `${href ? 'pointer' : 'default'}`,
display: 'flex',
flexDirection: 'column'
// justifyContent: 'flex-end'
}}
variant="interactive"
as={href ? 'a' : 'div'}
href={href}
>
{subtitle ? (
<Text
as="h1"
sx={{
fontSize: [2, 3, 4]
}}
>
{subtitle}
</Text>
) : (
<Box
as="span"
sx={{
width: 'fit-content',
bg: bg || 'white',
borderRadius: 18,
lineHeight: 0,
p: 2,
mb: 1,
display: 'inline-block',
transform: ['scale(0.75)', 'none'],
transformOrigin: 'bottom left',
boxShadow:
'inset 2px 2px 6px rgba(255,255,255,0.2), inset -2px -2px 6px rgba(0,0,0,0.1), 0 1px 4px rgba(0,0,0,0.1), 0 4px 8px rgba(0,0,0,0.1)'
}}
>
<Icon glyph={icon} size={48} color={color || 'white'} />
</Box>
)}
<Heading
sx={{
fontSize: [3, 4, 5]
}}
>
{text}
</Heading>
<Text
as="p"
sx={{
fontSize: [2, 3]
}}
>
{description}
</Text>
</Card>
</Zoom>
)
}
export default BreakdownBox

View file

@ -0,0 +1,106 @@
import {
Box,
Flex,
Container,
Heading,
Grid,
Text,
Badge,
Link
} from 'theme-ui'
import { Fade, Slide } from 'react-reveal'
import BreakdownBox from './breakdown-box'
function Breakdown() {
return (
<>
<Box
sx={{
pt: 3,
pb: 5,
background: 'linear-gradient(180deg, #579AC9 0%, #338EDA 100%)'
}}
>
<Container>
<Slide>
<Heading
variant="headline"
sx={{
textShadow: '0px 0px 21px #E1F1FF',
color: 'white',
fontStyle: 'italic'
}}
>
<Fade>
Dear high school hacker, we have a challenge for you:
</Fade>
</Heading>
<Heading
variant="headline"
sx={{
textShadow: '0px 0px 21px #E1F1FF',
color: 'white',
fontSize: [4, 5, 6],
pb: 4,
mt: 0
}}
>
<Fade>
What will you make this winter?
</Fade>
</Heading>
</Slide>
<Fade>
<Heading
variant="headline"
sx={{
// textShadow: '0px 0px 21px #E1F1FF',
color: '#338eda',
background: 'white',
px: 3,
py: 2,
borderRadius: '10px',
width: 'fit-content'
}}
>
Join Hack Clubbers in a winter of making with
</Heading>
</Fade>
<Grid gap={[2, 2, 3]} columns={[1, 1, 3, 3]} py={3}>
<BreakdownBox
icon="friend"
color="#5bc0de"
text="Friends"
description="Find support from our community of 20k+ teenagers in the Hack Club Slack."
delay="300"
href="/slack"
/>
<BreakdownBox
icon="event-code"
color="#5bc0de"
text="Daily progress"
description={
<>
From <strong>Feb 15-25</strong>, work on your project, share
short photo/video updates each day.
</>
}
delay="100"
href="https://scrapbook.hackclub.com/r/10daysinpublic"
/>
<BreakdownBox
icon="settings"
color="#5bc0de"
text="Free hardware"
description="We'll pay for up to $250 of your hardware to build your project."
delay="200"
/>
</Grid>
</Container>
</Box>
</>
)
}
export default Breakdown

162
components/winter/form.js Normal file
View file

@ -0,0 +1,162 @@
import { Box, Input, Label, Button, Select, Text, Grid } from 'theme-ui'
import { useEffect, useRef, useState } from 'react'
import theme from '@hackclub/theme'
import Icon from '../icon'
import { keyframes } from '@emotion/react'
import debounce from 'lodash/debounce'
const hideAnimation = keyframes({
from: { display: 'flex' },
to: { display: 'none', opacity: 0, padding: 0, position: 'absolute' }
})
const spinAnimation = keyframes({
from: { transform: 'rotate(0deg)' },
to: { transform: 'rotate(360deg)' }
})
function Base({ children, action, target, method, onSubmit, id }) {
return (
<Box
as="form"
sx={{ display: 'grid', gridTemplateColumns: '1fr' }}
id={id}
action={action}
target={target}
method={method}
onSubmit={onSubmit}
>
{children}
</Box>
)
}
function Field({
placeholder,
label,
name,
type,
value,
onChange,
required = true,
loading = false
}) {
return (
<Box sx={{ my: 2 }}>
<Label htmlFor={name} sx={{ color: 'muted', fontSize: 18 }}>
{label}
</Label>
<Box sx={{ position: 'relative' }}>
{loading && (
<Box
sx={{
position: 'absolute',
top: 10,
right: 10,
width: 20,
height: 20,
border: '1px solid white',
borderRightStyle: 'none',
animation: `${spinAnimation} 1s linear infinite`,
borderRadius: '50%'
}}
></Box>
)}
<Input
id={name}
placeholder={placeholder}
name={name}
type={type}
sx={{
bg: 'light'
}}
onChange={onChange}
value={value}
required={required}
/>
</Box>
</Box>
)
}
export default function Signup() {
const [submitted, setSubmitted] = useState(false)
const [eventName, setEventName] = useState('')
const [userEmail, setUserEmail] = useState('')
const handleSubmit = async e => {
e.preventDefault()
await fetch('/api/bank/demo', {
method: 'POST',
body: JSON.stringify({
eventName,
userEmail
})
})
setSubmitted(true)
// clear form
setEventName('')
setUserEmail('')
}
return (
<>
<Base
id="form"
method="POST"
action="/api/bank/demo"
onSubmit={handleSubmit}
>
<Field
label="Name"
name="eventName"
placeholder="Fiona's Hardware Fund"
value={eventName}
onChange={e => setEventName(e.target.value)}
/>
<Field
label="Email address"
name="userEmail"
placeholder="fiona@hackclub.com"
type="email"
value={userEmail}
onChange={e => setUserEmail(e.target.value)}
/>
<Button
sx={{
backgroundImage: theme.util.gx('green', 'blue'),
mt: [2, 3],
py: 2
}}
type="submit"
>
Create new account
</Button>
</Base>
{submitted && (
<Box
sx={{
mt: 2,
px: 2,
py: 2,
borderRadius: 'default',
color: 'white',
bg: 'green',
display: 'flex',
flexDirection: 'row',
alignItems: 'start',
animation: `${hideAnimation} 0s ease-in 15s`,
animationFillMode: 'forwards'
}}
>
<Icon glyph="send" size={24} />
<Text>Submitted! Check your email for a sign in link.</Text>
</Box>
)}
</>
)
}

246
components/winter/info.js Normal file
View file

@ -0,0 +1,246 @@
import {
Card,
Grid,
Box,
Button,
Container,
Heading,
Text,
Flex,
Avatar
} from 'theme-ui'
import Icon from '../icon'
import Tilt from '../tilt'
import { Zoom } from 'react-reveal'
export default function InfoGrid() {
return (
<Container py={4}>
<Grid
sx={{
gridTemplateColumns: ['1fr', '1fr', '1fr 1fr', '1fr 1fr 1fr'],
gap: 3,
h: 'fit-content'
}}
>
<Zoom>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
height: ['100%']
}}
>
<Card
variant="translucent"
sx={{ borderRadius: 'default', px: 4, py: [2, 3, 5] }}
>
{/* <Icon glyph="sam" size={64} color="#5BC0DE" /> */}
<Text variant="lead" as="p">
A deeper look at
</Text>
<Heading as="h2" variant="title">
Free hardware for your project
</Heading>
<br />
<Button
as="a"
href="https://github.com/hackclub/wom"
variant="ctaLg"
sx={{ mt: 4 }}
>
Apply
</Button>
</Card>
</Box>
</Zoom>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
height: '100%'
}}
>
<Zoom delay={200}>
<Card
variant="translucent"
sx={{
borderRadius: 'default',
px: 4,
py: 3,
mb: 3
}}
>
<Heading variant="headline">To qualify:</Heading>
{/* <BulletItem iconGlyph="checkmark" iconColor="#5BC0DE">
Up to $250 grant per person
</BulletItem> */}
<BulletItem iconGlyph="checkmark" iconColor="#5BC0DE">
Be a high schoolers (& younger)
</BulletItem>
{/* <BulletItem iconGlyph="checkmark" iconColor="#5BC0DE">
Share a plan for what you want to build and how much you need (up to $250)
</BulletItem> */}
<Text sx={{ color: 'muted' }}>
If you qualify, share your idea! We're giving out as many grants
as possible!
</Text>
</Card>
</Zoom>
<Zoom delay={400}>
<Card
variant="translucent"
sx={{
borderRadius: 'default',
px: 4,
py: 4,
display: 'flex',
flexDirection: 'column',
height: 'fit-content'
}}
>
<Heading variant="headline">
Once you have a project idea,
</Heading>
<Text as="p">
figure out the hardware you need and where you can buy it. Share
that with us and we'll give you a grant of up to $250.
</Text>
<Text as="p" mt={3}>
It could be your first ever electronics project or your tenth,
we want to support you in building whatever you want.
</Text>
{/* <Text>
(& see what other Hack Clubbers, like{' '}
<UserMention user="bellesea" />, are building)
todo: link a random pr from the repo that is tagged with "accepted"
</Text> */}
{/* <Button
as="a"
href="https://github.com/hackclub/wom"
variant="outline"
sx={{
color: 'text',
mt: 3
}}
>
See projects &#10138;
</Button> */}
</Card>
</Zoom>
</Box>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
height: '100%'
}}
>
<Zoom delay={600}>
<Card
variant="translucent"
sx={{
borderRadius: 'default',
px: 4,
py: 3,
mb: 3,
}}
>
<Heading variant="headline">
Receive and spend the grant through Hack Club Bank.
</Heading>
<BulletItem iconGlyph="bank-account" iconColor="#5BC0DE">
Full history and balance stuff on powerful web dashbaord
</BulletItem>
<BulletItem iconGlyph="card" iconColor="#5BC0DE">
Issue yourself a debit card to spend the funds
</BulletItem>
<BulletItem iconGlyph="explore" iconColor="#5BC0DE">
Use transparency mode to spend it in public
</BulletItem>
</Card>
</Zoom>
<Zoom delay={800}>
<Tilt>
<Card
as="div"
sx={{
backgroundColor: 'transparent',
backgroundImage:
'url("https://cloud-ehtgzdn7u-hack-club-bot.vercel.app/0card.png")',
height: ['300px', '500px', '100%', '230px'],
backgroundSize: '100%',
backgroundRepeat: 'no-repeat',
boxShadow: '0 8px 32px rgba(255, 255, 255, 0.0625)',
display: ['block', 'block', 'none', 'block']
}}
/>
</Tilt>
</Zoom>
</Box>
<Tilt>
<Card
as="div"
sx={{
backgroundColor: 'transparent',
backgroundImage:
'url("https://cloud-ehtgzdn7u-hack-club-bot.vercel.app/0card.png")',
height: ['300px', '500px', '100%', '230px'],
backgroundSize: '100%',
backgroundRepeat: 'no-repeat',
boxShadow: '0 8px 32px rgba(255, 255, 255, 0.0625)',
display: ['none', 'none', 'block', 'none']
}}
/>
</Tilt>
</Grid>
</Container>
)
}
function BulletItem({ children, iconGlyph, iconColor, iconSize }) {
return (
<Flex
sx={{
flexDirection: 'row',
alignItems: 'flex-start',
alignItems: 'center',
my: 2
}}
>
<Icon
glyph={iconGlyph}
size={iconSize || 36}
color={iconColor || 'text'}
sx={{
flexShrink: '0'
}}
/>
<Text sx={{ ml: 1 }}>{children}</Text>
</Flex>
)
}
function UserMention({ user }) {
return (
<Box
as="span"
sx={{
whiteSpace: 'nowrap',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Text as="a" href={`https://github.com/${user}`}>
@{user}
</Text>
<Avatar
src={`https://github.com/${user}.png`}
height="24px"
width="24px"
sx={{ backgroundColor: 'white', ml: 2 }}
/>
</Box>
)
}

View file

@ -0,0 +1,124 @@
import { Box, Heading, Button, Text, Container } from 'theme-ui'
// import { gsap } from 'gsap'
import { useEffect } from 'react'
// import ScrollTrigger from 'gsap/dist/ScrollTrigger'
/** @jsxImportSource theme-ui */
import Snowfall from 'react-snowfall'
import { Fade } from 'react-reveal'
import Rsvp from './rsvp'
import ScrollHint from '../scroll-hint'
import useSWR from 'swr'
import fetcher from '../../lib/fetcher'
export default function Landing({ rsvpCount }) {
// useEffect(() => {
// if (typeof window !== 'undefined') {
// gsap.registerPlugin(ScrollTrigger)
// gsap.to('.box', {
// scrollTrigger: {
// trigger: '.box',
// start: 'top center',
// end: 'top 100%',
// scrub: true,
// markers: true,
// }, // start the animation when ".box" enters the viewport (once)
// x: 600,
// ease: 'none',
// duration: 5
// })
// }
// })
return (
<Box sx={{ position: 'relative' }}>
{/* <img
src="https://cloud-mvlym308h-hack-club-bot.vercel.app/0cloud_2.png"
width="200px"
height="auto"
class="box"
sx={{ zIndex: 3, position: 'absolute', top: '10%', left: '60%' }}
/>
<img
src="https://cloud-mvlym308h-hack-club-bot.vercel.app/1cloud_1.png"
width="200px"
height="auto"
class="box"
sx={{ zIndex: 3, position: 'absolute', top: '20%', left: '80%' }}
/>
<img
src="https://cloud-mvlym308h-hack-club-bot.vercel.app/1cloud_1.png"
width="200px"
height="auto"
class="box"
sx={{ zIndex: 3, position: 'absolute', top: '40%', left: '40%' }}
/> */}
<Box
sx={{
background:
'url(https://cloud-6h53svh6x-hack-club-bot.vercel.app/0group_5.png), linear-gradient(0deg, #3561A4 0%, #338EDA 100%)',
backgroundPosition: 'bottom center',
backgroundSize: ['200%', '150%', '100%'],
backgroundRepeat: 'no-repeat',
width: '100vw',
height: '85vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center',
zIndex: 2
}}
>
<Snowfall />
<Box>
<Box
sx={{
backdropFilter: 'blur(1.5px)',
maxWidth: 'container'
}}
>
<Fade left cascade>
<Heading
variant="eyebrow"
sx={{
color: 'sunken'
// fontSize: ['18px', '20px', '24px']
}}
>
{rsvpCount} more RSVPs till the start of a hacker's
</Heading>
</Fade>
<Fade left cascade>
<Heading
as="h1"
variant="ultratitle"
sx={{
color: 'white',
textShadow: '0 0 16px rgba(0, 0, 0, 0.2)'
}}
>
Winter Hardware <br />
Wonderland
</Heading>
</Fade>
{/* <Container variant="copy">
<Text
variant="subtitle"
as="p"
sx={{
color: 'white',
textShadow: '2px 2px 10px rgba(0, 0, 0, 1)',
pt: 2
}}
>
Get up to $250 in grant money build a hardware project this
winter alongside hundreds of other hackers.
</Text>
</Container> */}
<Rsvp />
</Box>
{/* <ScrollHint sx={{mt: 3}} /> */}
</Box>
</Box>
</Box>
)
}

View file

@ -0,0 +1,308 @@
import React, { useState } from 'react'
import styled from '@emotion/styled'
import {
Box,
Button,
Container,
Flex,
Heading,
Card,
Grid,
Link as A,
Text,
Avatar,
Image
} from 'theme-ui'
import Photo from '../../components/photo'
import NextImage from 'next/image'
import Marquee from 'react-marquee-slider'
import Photo1 from '../../public/winter/1.jpeg'
import Photo2 from '../../public/winter/2.png'
import Photo3 from '../../public/winter/3.jpeg'
import Photo4 from '../../public/winter/4.jpeg'
import Photo5 from '../../public/winter/5.jpeg'
import Photo6 from '../../public/winter/6.jpeg'
import Photo7 from '../../public/winter/7.jpeg'
import Photo8 from '../../public/winter/8.jpeg'
import Photo9 from '../../public/winter/9.jpeg'
import Photo10 from '../../public/winter/10.jpeg'
import Photo12 from '../../public/winter/12.jpeg'
import Photo13 from '../../public/winter/13.jpeg'
import Photo14 from '../../public/winter/14.jpeg'
import Photo15 from '../../public/winter/15.jpeg'
import Photo16 from '../../public/winter/16.jpeg'
import Photo17 from '../../public/winter/17.jpeg'
import Photo18 from '../../public/winter/18.jpeg'
import Photo19 from '../../public/winter/19.jpeg'
import Photo20 from '../../public/winter/20.jpeg'
import Photo21 from '../../public/winter/21.jpeg'
import Photo22 from '../../public/winter/22.jpeg'
import Photo23 from '../../public/winter/23.jpeg'
import Photo24 from '../../public/winter/24.jpeg'
import Photo25 from '../../public/winter/25.jpeg'
import Photo26 from '../../public/winter/26.jpeg'
import Photo27 from '../../public/winter/27.jpeg'
import Photo28 from '../../public/winter/28.jpeg'
import Photo29 from '../../public/winter/29.jpeg'
import Photo30 from '../../public/winter/30.jpeg'
import Photo31 from '../../public/winter/31.png'
/** @jsxImportSource theme-ui */
const Header = styled(Box)`
background: url('/pattern.svg');
`
const Sheet = styled(Card)`
position: relative;
overflow: hidden;
border-radius: 8px;
width: 100%;
color: white;
`
Sheet.defaultProps = {
sx: {
bg: 'rgba(255, 255, 255, 0.875)',
p: [3, 4],
color: 'black',
width: 1,
mb: 4
}
}
Sheet.defaultProps = {
sx: {
maxWidth: '52rem',
fontSize: 3,
p: [4, 5],
color: 'white'
}
}
const PhotoRow = ({ photos }) => (
<Box sx={{ height: '200px', overflow: 'hidden' }}>
<Box sx={{ display: ['block', 'block', 'block', 'block', 'none'] }}>
<Marquee velocity={12}>
{photos.map((photo, index) => (
<NextImage
placeholder="blur"
src={photo}
objectFit="cover"
className="next-image"
height="200px"
width="300px"
alt="Hack Club students"
key={'image-' + index}
/>
))}
</Marquee>
</Box>
<Box sx={{ display: ['none', 'none', 'none', 'none', 'block'] }}>
<Marquee velocity={12}>
{photos.map((photo, index) => (
<NextImage
placeholder="blur"
src={photo}
objectFit="cover"
className="next-image"
height="200px"
width="600px"
key={'image-' + index}
alt="Hack Club students"
/>
))}
</Marquee>
</Box>
</Box>
)
const Cards = ({ avatar, username, description, image }) => {
return (
<Card
className="post"
sx={{ p: [3, 3], width: '100%', background: 'rgba(225,225,225,0.9)' }}
>
<Flex
as="a"
href={username != 'cjmika110'? `https://scrapbook.hackclub.com/${username}`: 'https://scrapbook.hackclub.com' }
sx={{
color: 'inherit',
textDecoration: 'none',
alignItems: 'center',
mb: 2
}}
>
<Avatar loading="lazy" src={avatar} alt="" mr={2} />
<Box>
<Text variant="subheadline" my={0} fontSize={[1, 1]}>
@{username}
</Text>
</Box>
</Flex>
<Text
as="p"
sx={{
fontSize: 1,
textAlign: 'left',
mb: 2,
a: {
color: 'primary',
wordBreak: 'break-all',
wordWrap: 'break-word'
},
'> div': { width: 18, height: 18 }
}}
>
{description}
</Text>
<Box
sx={{
position: 'relative',
width: '100%',
height: '200px',
overflow: 'hidden',
backgroundImage: `url('${image}')`,
backgroundSize: '100%',
backgroundPosition: 'bottom center',
backgroundRepeat: 'no-repeat'
}}
>
{/* <img src={image} sx={{ width: '100%' }} /> */}
</Box>
</Card>
)
}
export default function Projects() {
const [count, setCount] = useState(0)
let list = [
'mechanical keyboard',
'3D printer',
'drone',
'CNC machine',
'pixel art display',
'camera'
]
if (count == list.length - 1) {
setCount(0)
}
let project_idea = list[count]
return (
<Box>
<Header sx={{ position: 'relative' }}>
<Box
sx={{
background: 'rgba(0,0,0, 0.8)',
zIndex: 1,
position: 'relative',
color: 'white!important',
height: ['auto', '800px', '800px']
}}
pt={[5, 5, '50px']}
pb={[5, 0, 0]}
>
<Box
sx={{
maxWidth: '64rem',
mx: 'auto',
zIndex: 1,
position: 'relative'
}}
align="center"
py={2}
px={[1, 3]}
>
<Container sx={{ maxWidth: '48rem' }}>
<Heading
variant="title"
sx={{
textShadow: '0px 0px 21px #E1F1FF',
color: 'white',
fontSize: [4, 5, 6]
}}
>
You could be building a{' '}
<Text
sx={{
backgroundColor: '#406BAB',
py: 2,
px: 3,
borderRadius: 10,
position: 'relative',
lineHeight: '1.5'
}}
onClick={() => setCount(count + 1)}
>
<Box
as="span"
sx={{ whiteSpace: 'nowrap', cursor: 'pointer' }}
>
{project_idea}
</Box>
<Image
src="https://cloud-ohzuz4m3t-hack-club-bot.vercel.app/0click_me.svg"
alt="click me"
sx={{
position: 'absolute',
top: -3,
right: -4
}}
/>
</Text>
</Heading>
<Grid columns={[1, 3, 3]} mt={4} mx={['5vw', 'auto', 'auto']}>
<Cards
avatar="https://scrapbook.hackclub.com/_next/image?url=https://avatars.slack-edge.com/2022-12-04/4449277732855_bc5a70015c4b2146cdec_192.jpg&w=640&q=75"
username="sampoder"
description="today i presented.. the *CLIMATATOR*! its a 4D interactive media experience / climate change simulator that showcases the effects of climate change to a younger audience..."
image="https://cloud-lwd22jmab-hack-club-bot.vercel.app/420210303_154846.jpg"
/>
<Cards
avatar="https://scrapbook.hackclub.com/_next/image?url=https://avatars.slack-edge.com/2022-07-26/3865494839057_a471d7e9c871ca9121ea_192.png&w=640&q=75"
username="maggie"
description="working on something…"
image="https://image.mux.com/50200NTyKdeFIayts00oUjauhMTTfQiOPgVn9Xm00mE1aS8/thumbnail.jpg?width=512&fit_mode=pad&time=0"
/>
<Cards
avatar="https://www.gravatar.com/avatar/04484b2178b8fd17c5a529c54614e14c?d=identicon&r=pg"
username="cjmika110"
description="Tired of QWERTY keyboards? Is typing too intuitive for you? Karl the Keyboard is a portable, squishable, fun, easy- to-use binary keyboard! Instead of pressing keys, you move a joystick up and down to represent ones..."
image="https://assemble-images.hackclub.com/a34cbf72-7023-424a-8239-abf4146529e8-Untitled%20drawing%20(1).png"
/>
</Grid>
<Button
as="a"
variant="cta"
href="https://scrapbook.hackclub.com/"
sx={{
background:
'linear-gradient(32deg, rgba(51, 142, 218, 0.9) 0%, rgba(51, 214, 166, 0.9) 100%)',
mt: 2
}}
>
Keep exploring
</Button>
</Container>
</Box>
</Box>
<Box
sx={{
position: 'absolute',
top: 0,
zIndex: 0,
width: '100%',
display: ['none', 'block', 'block']
}}
>
<PhotoRow photos={[Photo1, Photo2, Photo3, Photo4, Photo5]} />
<PhotoRow photos={[Photo6, Photo7, Photo8, Photo9, Photo10]} />
<PhotoRow photos={[Photo21, Photo12, Photo13, Photo14, Photo15]} />
<PhotoRow photos={[Photo16, Photo17, Photo18, Photo19, Photo20]} />
</Box>
</Header>
</Box>
)
}

100
components/winter/recap.js Normal file
View file

@ -0,0 +1,100 @@
import { Box, Button, Container, Heading, Grid, Card, Text } from 'theme-ui'
import { Slide, Zoom } from 'react-reveal'
import BreakdownBox from './breakdown-box'
import Signup from './form'
function Recap() {
return (
<>
<Container sx={{ py: 5 }}>
<Slide>
<Heading
variant="headline"
sx={{
textShadow: '0px 0px 21px #E1F1FF',
color: 'white',
fontSize: [3, 4, 5],
pb: 4,
maxWidth: '90%'
}}
>
Make your ideas real this winter, with electronics and Hack Club
friends.{' '}
</Heading>
</Slide>
<Grid gap={[2, 2, 3]} columns={[1, 1, 3, 3]} pb={4}>
<BreakdownBox
icon="event-code"
text="10 days"
description="of building with other teenagers around the world"
delay="100"
bg="blue"
/>
<BreakdownBox
icon="payment-transfer"
text="$250"
description="grants instantly transferred through Hack Club Bank"
delay="200"
bg="blue"
/>
<Zoom delay="300">
<Card
variant="translucent"
sx={{
borderRadius: 'default',
px: 3,
pb: 4,
pt: 2,
display: 'flex',
flexDirection: 'column'
}}
>
<Text
variant="subtitle"
sx={{
fontWeight: 'bold',
color: 'blue',
textShadow: '0px 0px 21px #ffffff'
}}
>
Open a Demo Account
</Text>
<Text variant="caption">
While you wait for your hardware, explore and get familiar with
Hack Club Bank with limited access to features until you get
fully activated.
</Text>
<Signup />
</Card>
</Zoom>
{/* <Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'left'
}}
>
<Button
variant="outlineLg"
sx={{
color: 'white',
mt: 3
}}
as="a"
href="https://github.com/hackclub/wom"
target="_blank"
rel="noreferrer"
>
Apply
</Button>
</Box> */}
</Grid>
<Button variant="ctaLg" as="a" href="#apply" style={{ zIndex: '100', textAlign: 'center' }}>
RSVP
</Button>
</Container>
</>
)
}
export default Recap

137
components/winter/rsvp.js Normal file
View file

@ -0,0 +1,137 @@
import Icon from '@hackclub/icons'
import { useRef, useState } from 'react'
import {
Box,
Label,
Input,
Button,
Text,
Alert,
Card,
Heading,
Grid
} from 'theme-ui'
const Loading = () => (
<Box
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
width: '100%',
border: '2px solid #f3f3f3',
borderTop: '2px solid #ec3750',
borderRadius: '50%',
width: '10px',
height: '10px',
animation: 'spin 2s linear infinite',
'@keyframes spin': {
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' }
}
}}
></Box>
)
const Rsvp = () => {
const [submitting, setSubmitting] = useState(false)
const [submitted, setSubmitted] = useState(false)
const formRef = useRef(null)
const handleSubmit = async e => {
e.preventDefault()
setSubmitting(true)
await fetch('/api/winter-rsvp', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
Name: e.target.name.value,
Email: e.target.email.value
})
})
formRef.current.reset()
setSubmitting(false)
setSubmitted(true)
}
return (
<Card
sx={{
maxWidth: 'narrowPlus',
mx: 'auto',
mt: [3, 4],
background: 'rgb(255,255,255, 0.45)',
backdropFilter: 'blur(8px)'
}}
>
<Heading as="h2" variant="subheadline" sx={{ mb: 1 }}>
Get up to $250 to build a hardware project this winter.
</Heading>
<Text sx={{ color: 'muted' }}>
RSVP to get notified when applications open.
</Text>
<Grid
as="form"
ref={formRef}
onSubmit={handleSubmit}
gap={[2, 3]}
sx={{
mt: [null, 3],
gridTemplateColumns: [null, '1fr 1fr auto'],
textAlign: 'left',
alignItems: 'end',
input: { bg: 'sunken' }
}}
>
<div>
<Label htmlFor="location">Name</Label>
<Input
autofillBackgroundColor="highlight"
type="text"
name="name"
id="name"
placeholder="Fiona Hackworth"
required
/>
</div>
<div>
<Label htmlFor="email">Email</Label>
<Input
autofillBackgroundColor="highlight"
type="email"
name="email"
id="email"
placeholder="fiona@hackclub.com"
required
/>
</div>
<Button type="submit" sx={{ mt: [2, 0] }}>
{submitting ? (
<>
<Loading />
&nbsp;RSVP
</>
) : (
'RSVP'
)}
</Button>
</Grid>
{submitted && (
<Alert variant="primary" sx={{ bg: 'green', mt: [2, 3] }}>
<Icon glyph="send" />
<Text sx={{ ml: 2 }}>Signed up!</Text>
</Alert>
)}
</Card>
)
}
export default Rsvp

View file

@ -0,0 +1,128 @@
import { Box, Flex, Container, Text, Badge, Link } from 'theme-ui'
import { Slide } from 'react-reveal'
import Icon from '../icon'
function TimelineStep({ children }) {
return (
<Flex
sx={{
marginX: 4,
paddingY: 4,
flexDirection: 'row',
alignItems: 'center',
'&:before': {
content: '""',
background: 'snow',
height: ['420px', '320px', '320px'],
width: '4px',
marginLeft: 36,
position: 'absolute',
zIndex: 0
},
'&:first-of-type:before': {
top: [0, null, 'auto'],
width: [0, null, 0],
left: [0, null, 0]
},
'&:last-of-type:before': {
bottom: [0, null, 'auto'],
left: [0, null, 0],
width: [0, null, 0]
}
}}
>
{children}
</Flex>
)
}
function Circle({ children }) {
return (
<Box
sx={{
p: 14,
background: 'red',
color: 'white',
backgroundImage:
'radial-gradient(ellipse farthest-corner at top left, #5bc0de, #338eda)',
borderRadius: '100%',
display: 'inline-block',
lineHeight: 0,
position: 'relative',
zIndex: 999
}}
>
{children}
</Box>
)
}
function Step({ icon, name, duration, href }) {
return (
<TimelineStep sx={{ pb: 1 }}>
<Slide left>
<Circle>
{href ? (
<Link href={href} sx={{ cursor: 'pointer', zIndex: 999 }}>
<Icon glyph={icon} size={48} color="white" />
</Link>
) : (
<Icon glyph={icon} size={48} />
)}
</Circle>
<Container
sx={{
mt: 0,
display: 'flex',
justifyContent: 'left',
flexDirection: 'column',
textAlign: 'left'
}}
>
<Badge
variant="pill"
sx={{
bg: 'smoke',
color: 'darker',
fontWeight: 'normal',
textTransform: 'uppercase',
width: 'fit-content',
fontSize: 18,
px: 3
}}
>
{duration}
</Badge>
<Text
sx={{ color: 'white', fontSize: 24, maxWidth: [300, null, 550] }}
>
{name}
</Text>
</Container>
</Slide>
</TimelineStep>
)
}
export default function RealTimeline() {
return (
<Flex sx={{ flexDirection: 'column', justifyContent: 'center', pb: 4 }}>
<Step
icon="post"
name="Instructions sent out on how to submit your hardware plan to qualify for the grant."
duration="When we reach 500 RSVPs"
/>
<Step
icon="send"
name="Deadline for sharing your hardware plan. Make sure to order your hardware by this time!"
duration="January 15"
/>
<Step
icon="slack"
name="Start of a 10 days building in public challenge where you share daily updates on your hardware project"
duration="February 15"
/>
</Flex>
)
}

View file

@ -74,6 +74,11 @@ const nextConfig = {
destination: '/bank/first/',
permanent: false
},
{
source: '/wom/',
destination: '/winter/',
permanent: false
},
{ source: '/workshops/slack/', destination: '/slack/', permanent: true },
{ source: '/community/', destination: '/slack/', permanent: true },
{ source: '/hack_camp/', destination: '/camp/', permanent: true },

View file

@ -27,6 +27,7 @@
"axios": "^1.2.0",
"country-list-js": "^3.1.7",
"globby": "^11.0.4",
"gsap": "^3.11.3",
"jquery": "^3.6.1",
"lodash": "^4.17.21",
"next": "^12.3.1",

35
pages/api/winter-rsvp.js Normal file
View file

@ -0,0 +1,35 @@
import AirtablePlus from 'airtable-plus'
const airtable = new AirtablePlus({
baseID: 'app1o9tRo6XulLnsr',
apiKey: process.env.AIRTABLE_API_KEY,
tableName: 'rsvp'
})
export default async function handler(req, res) {
if (req.method === 'POST') {
const rsvp = await airtable.create({
Name: req.body.Name,
Email: req.body.Email,
IP: req.headers['x-forwarded-for'] || req.socket.remoteAddress
})
const url = process.env.WOM_SLACK_WEBHOOK_URL
const body = JSON.stringify({
rsvp
})
fetch(url, {
body,
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(res => res.status(200).json({ success: true }))
.catch(error => {
console.log(error)
res.json({ status: 'Something went wrong', error })
})
} else {
res.status(405).json({ status: 'error', error: 'Must send POST request' })
}
}

View file

@ -126,10 +126,10 @@ const Page = () => (
priority
/>
<Announcement
copy="Epoch: celebrate the New Year with Hack Club."
caption="Join 150+ hackers in Delhi for a magical high-school hackathon!"
href="https://epoch.hackclub.com"
iconLeft="explore"
copy="Join the community for a winter of hardware hacking."
caption="Get a grant of up to $250 to build an electronics project."
href="/winter"
iconLeft="idea"
color="primary"
/>
@ -464,10 +464,10 @@ const Page = () => (
name="Tools to hack on"
desc={
<>
We build tools, such as{" "}
<a href="https://sprig.hackclub.com">Sprig</a>, that your members can
use to make projects with in meetings! Built more of them with us in our
{" "}<Link href="/slack">Slack community</Link>.
We build tools, such as{' '}
<a href="https://sprig.hackclub.com">Sprig</a>, that your
members can use to make projects with in meetings! Built more of
them with us in our <Link href="/slack">Slack community</Link>.
</>
}
></Feature>

115
pages/winter.js Normal file
View file

@ -0,0 +1,115 @@
import Head from 'next/head'
import Meta from '@hackclub/meta'
import Nav from '../components/nav'
import {
Box,
Container,
Heading,
Button,
Text,
Image,
Input,
Label,
Link,
Flex
} from 'theme-ui'
import Snowfall from 'react-snowfall'
import Footer from '../components/footer'
import FadeIn from '../components/fade-in'
import { useState } from 'react'
import ForceTheme from '../components/force-theme'
import RealTimeline from '../components/winter/timeline'
import InfoGrid from '../components/winter/info'
import Breakdown from '../components/winter/breakdown'
import Projects from '../components/winter/projects'
import Landing from '../components/winter/landing'
import Recap from '../components/winter/recap'
import { Zoom } from 'react-reveal'
import useSWR from 'swr'
import fetcher from '../lib/fetcher'
export function Winter() {
const { data: rsvps } = useSWR(
'http://airbridge.hackclub.com/v0.1/Winter%20Hardware%20Wonderland/rsvp',
fetcher,
{ refreshInterval: 1000 }
)
return (
<>
<Box as="main" sx={{ bg: 'blue' }}>
<Meta
as={Head}
title="Winter Hardware Wonderland"
description="Join the Hack Club community for a winter of hardware hacking, and get a $250 grant to build your project."
image="/winter/og-image.png" // TODO: add og image
/>
<Nav light />
<Snowfall />
<ForceTheme theme="light" />
<Landing rsvpCount={500 - rsvps?.length} />
<Breakdown />
<Projects />
<InfoGrid />
<Container>
<Zoom>
<Heading
variant="headline"
sx={{
textShadow: '0px 0px 21px #E1F1FF',
color: 'white',
fontSize: [3, 4, 5],
pb: 4,
maxWidth: '90%'
}}
>
You've RSVPed, what's next?
</Heading>
</Zoom>
<Flex
sx={{
flexDirection: ['column', null, 'row'],
justifyContent: 'center',
alignItems: 'center'
}}
>
<Box
sx={{ display: 'flex', flexDirection: ['row', null, 'column'] }}
>
<Zoom>
<Heading
variant="title"
sx={{
color: 'white',
textTransform: 'uppercase',
transform: ['none', null, 'rotate(-90deg)'],
textShadow: '0px 0px 21px #E1F1FF'
}}
>
Timeline
</Heading>
<Image
src="https://cloud-lbajgdi3a-hack-club-bot.vercel.app/0fox_1.png"
alt="Illustrated orange fox sleeping in a curled position"
sx={{
width: ['100px', null, '80%'],
pt: [null, null, 6],
ml: [2, null, null]
}}
/>
</Zoom>
</Box>
<RealTimeline />
</Flex>
</Container>
{/* Timeline */}
<Recap />
{/* <Signup /> */}
<Footer />
</Box>
</>
)
}
export default Winter

BIN
public/winter/1.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
public/winter/10.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

BIN
public/winter/11.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 MiB

BIN
public/winter/12.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 KiB

BIN
public/winter/13.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
public/winter/14.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

BIN
public/winter/15.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
public/winter/16.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 KiB

BIN
public/winter/17.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

BIN
public/winter/18.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 KiB

BIN
public/winter/19.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 KiB

BIN
public/winter/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB

BIN
public/winter/20.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

BIN
public/winter/21.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
public/winter/22.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

BIN
public/winter/23.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
public/winter/24.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 KiB

BIN
public/winter/25.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 KiB

BIN
public/winter/26.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
public/winter/27.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

BIN
public/winter/28.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 KiB

BIN
public/winter/29.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
public/winter/3.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 KiB

BIN
public/winter/30.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
public/winter/31.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

BIN
public/winter/4.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
public/winter/5.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
public/winter/6.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 KiB

BIN
public/winter/7.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

BIN
public/winter/8.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 KiB

BIN
public/winter/9.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 KiB

View file

@ -2537,6 +2537,11 @@ grapheme-splitter@^1.0.4:
resolved "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz"
integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
gsap@^3.11.3:
version "3.11.3"
resolved "https://registry.yarnpkg.com/gsap/-/gsap-3.11.3.tgz#ca5be827f56fe8d5720e08343390f74fb89a05f3"
integrity sha512-xc/iIJy+LWiMbRa4IdMtdnnKa/7PXEK6NNzV71gdOYUVeTZN7UWnLU0fB7Hi1iwiz4ZZoYkBZPPYGg+2+zzFHA==
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz"