diff --git a/.gitignore b/.gitignore
index 4083bcd3..8c3f20f9 100755
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
.next
node_modules
.DS_Store
+public
diff --git a/components/elon.mdx b/components/elon.mdx
new file mode 100644
index 00000000..8917c254
--- /dev/null
+++ b/components/elon.mdx
@@ -0,0 +1,50 @@
+Elon Musk holds a special place amongst hackers. After growing up in a difficult
+family situation in South Africa, working his way in small jobs until reaching
+Los Angeles, teaching himself to code, and making hundreds of millions
+co-founding PayPal, he kept on building.
+
+It was a huge honor last month to have Elon [spend an hour in a Hack Club
+AMA](https://youtu.be/riru9OzScwk)—at one point he remarked we were “asking
+better questions than all the mainstream media.”
+
+Afterwards, Elon wanted to support Hack Club further.
+
+# Today, I’m proud to share: Elon Musk is donating $500,000 to Hack Club.
+
+In so many ways, this is a milestone for every Hack Clubber. 6 years ago, I
+started Hack Club as a 16-year-old programmer living on my own, scraping by,
+barely able to make rent. Now Elon Musk is one of our largest supporters.
+
+Elon is supporting us because we are a community of builders. When hackers see
+problems in the world, we don’t blame someone else: we try to take them on
+ourselves to solve. Elon is very selective about the nonprofits he supports and
+I’m proud Hack Club is one of them.
+
+So…how is Hack Club going to invest $500k? We want to use this to help 1000 more
+students start and join Hack Clubs in their communities. For those already in
+Hack Clubs, we look to you to help us make a more high-quality experience. We’re
+a lot of what we’ve already been doing (and [what I wrote about at the beginning
+of the year](https://zachinto2020.wordpress.com/2019/12/31/as-midnight-approaches/)):
+we’ll spend as little money as possible at all times, and we’ll hire a small
+number of diverse staff from video game engineers to media producers to make
+Hack Club better. We are pushing hard now to expand users of [Hack Club
+Bank](https://hackclub.com/bank/), and continuing to try and make the Hack Club
+Slack the best place to be a teenager on the intenet.
+
+We’ll have a proper announcement in a few weeks, but one thing we’re doing after
+winning the [Frank Grant](https://grant.frank.ly/) and now receiving Elon’s
+gift, is open sourcing our finances. Hack Club HQ has been running on Hack Club
+Bank since February and starting today, you can see our account publicly at
+https://bank.hackclub.com/hq. You can track how we spend every single dollar of
+Elon’s gift. Soon, we will also launch https://frank.ly/ on Hack Club’s
+website.
+
+Hack Club’s mission is to build a new generation of hackers. This starts in high
+school, where Hack Club students learn to be technically proficient, build their
+friend network, learn to raise and spend money, and develop into kind, curious,
+thoughtful, optimistic, and honest leaders.
+
+Elon Musk is now supporting you and your work, so go out and do amazing things.
+Elon can’t wait to see what you make.
+
+—Zach
diff --git a/components/elon/amount.js b/components/elon/amount.js
new file mode 100644
index 00000000..61284d46
--- /dev/null
+++ b/components/elon/amount.js
@@ -0,0 +1,11 @@
+import Sparkles from '../sparkles'
+
+const Amount = () => (
+ $500,000
+)
+
+export default Amount
diff --git a/components/elon/copy.mdx b/components/elon/copy.mdx
new file mode 100644
index 00000000..73f79dc8
--- /dev/null
+++ b/components/elon/copy.mdx
@@ -0,0 +1,24 @@
+import Amount from './amount'
+import Signature from '../signature'
+
+# Today, I’m proud to share: Elon Musk is donating to [Hack Club](https://hackclub.com).
+
+Elon Musk is one of the most prolific and ambitious hackers of the last decade.
+
+It was a huge honor last month to have Elon [spend an hour in an ask-me-anything call with our community of high schoolers](https://youtu.be/riru9OzScwk)—at one point he remarked we were “asking better questions than all the mainstream media” and called our community “very wholesome.”
+
+**Afterwards, Elon wanted to support Hack Club further.**
+
+When hackers see problems in the world, we don’t blame someone else: we try to take them on to solve. Elon is very selective about the nonprofits he supports and I’m proud Hack Club is one of them.
+
+So…how will Hack Club invest $500,000? We want to use this to help 1,000 more students start and join Hack Clubs in their towns ([see the worldwide map](https://hackclub.com/map/)). For those already in Hack Club, we look to you to help us make a higher-quality experience. We plan to continue much of what we’re already doing (and [what I wrote about in January](https://zachinto2020.wordpress.com/2019/12/31/as-midnight-approaches/)): spending as little money as possible at all times, growing slowly, adding diverse staff to make Hack Club better (video game designers, software engineers, media producers, and more). We are pushing hard to try and make the [Hack Club Slack](https://hackclub.com/) the best place to be a teenager on the internet and expanding [Hack Club Bank](https://hackclub.com/bank/).
+
+We’ll be fully transparent in how we spend this money. One thing we’ve been working toward after winning the [Frank Grant](https://grant.frank.ly/) is open sourcing our finances. Hack Club HQ has been running on Hack Club Bank since February, and starting today, [**you can see our finances publicly**](https://bank.hackclub.com/hq). Through Hack Club Bank, you can track how we spend every dollar of Elon’s gift. Soon, we’ll also launch [Frank’s](https://frank.ly/) transparency tools on [hackclub.com](https://hackclub.com/).
+
+Hack Club’s mission is to build a new generation of hackers. This starts in high school, where Hack Club students learn to be technically proficient, build their friend network, learn to raise and spend money, and develop into kind, curious, thoughtful, optimistic, and honest leaders. And now Elon Musk is one of our largest supporters.
+
+To every Hack Clubber: Elon is now supporting you and your work, so go forth and do amazing things. We can’t wait to show Elon what you make.
+
+
+
+Zach Latta, Founder
diff --git a/components/home/announcement.js b/components/home/announcement.js
index af0f885c..4449dc36 100644
--- a/components/home/announcement.js
+++ b/components/home/announcement.js
@@ -10,7 +10,7 @@ const unfold = keyframes({
const Announcement = ({
caption,
copy,
- iconLeft,
+ iconLeft = 'announcement',
iconRight = 'info',
color = 'accent',
...props
@@ -49,12 +49,13 @@ const Announcement = ({
as="p"
sx={{ flex: '1 1 auto', strong: { display: ['inline', 'block'] } }}
>
+ {copy}
{caption && (
- {caption}{' '}
+ {' '}
+ {caption}
)}
- {copy}
diff --git a/components/signature.js b/components/signature.js
new file mode 100644
index 00000000..6e8813f8
--- /dev/null
+++ b/components/signature.js
@@ -0,0 +1,17 @@
+import { Image, useColorMode } from 'theme-ui'
+
+const Signature = () => {
+ const [colorMode] = useColorMode()
+ return (
+
+ )
+}
+
+export default Signature
diff --git a/components/sparkles.js b/components/sparkles.js
new file mode 100644
index 00000000..19ec9500
--- /dev/null
+++ b/components/sparkles.js
@@ -0,0 +1,116 @@
+// Full credit to https://joshwcomeau.com/react/animated-sparkles-in-react/
+import styled from '@emotion/styled'
+import { keyframes } from '@emotion/core'
+import { range, sample, random } from 'lodash'
+import { Text } from 'theme-ui'
+import theme from '@hackclub/theme'
+
+import useRandomInterval from '../lib/use-random-interval'
+import usePrefersReducedMotion from '../lib/use-prefers-reduced-motion'
+
+const generateSparkle = color => {
+ const sparkle = {
+ id: String(random(10000, 99999)),
+ createdAt: Date.now(),
+ color,
+ size: random(10, 20),
+ style: {
+ top: random(0, 100) + '%',
+ left: random(0, 100) + '%'
+ }
+ }
+ return sparkle
+}
+
+const Sparkles = ({ colors = ['orange', 'yellow', 'green'], children, sx, props, ...delegated }) => {
+ const allColors = colors.map(n => theme.colors[n])
+ const getColor = () => sample(allColors)
+ const [sparkles, setSparkles] = React.useState(() => {
+ return range(3).map(() => generateSparkle(getColor()))
+ })
+ const prefersReducedMotion = usePrefersReducedMotion()
+ useRandomInterval(
+ () => {
+ const sparkle = generateSparkle(getColor())
+ const now = Date.now()
+ const nextSparkles = sparkles.filter(sp => {
+ const delta = now - sp.createdAt
+ return delta < 750
+ })
+ nextSparkles.push(sparkle)
+ setSparkles(nextSparkles)
+ },
+ prefersReducedMotion ? null : 50,
+ prefersReducedMotion ? null : 450
+ )
+
+ return (
+
+ {sparkles.map(sparkle => (
+
+ ))}
+ {children}
+
+ )
+}
+
+const Sparkle = ({ size, color, style }) => {
+ const path =
+ 'M26.5 25.5C19.0043 33.3697 0 34 0 34C0 34 19.1013 35.3684 26.5 43.5C33.234 50.901 34 68 34 68C34 68 36.9884 50.7065 44.5 43.5C51.6431 36.647 68 34 68 34C68 34 51.6947 32.0939 44.5 25.5C36.5605 18.2235 34 0 34 0C34 0 33.6591 17.9837 26.5 25.5Z'
+ return (
+
+
+
+
+
+ )
+}
+
+const comeInOut = keyframes`
+ 0% {
+ transform: scale(0);
+ }
+ 50% {
+ transform: scale(1);
+ }
+ 100% {
+ transform: scale(0);
+ }
+`
+const spin = keyframes`
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(180deg);
+ }
+`
+const Wrapper = styled.span`
+ display: inline-block;
+ position: relative;
+`
+const SparkleWrapper = styled.span`
+ position: absolute;
+ display: block;
+ @media (prefers-reduced-motion: no-preference) {
+ animation: ${comeInOut} 1000ms forwards;
+ }
+`
+const SparkleSvg = styled.svg`
+ display: block;
+ @media (prefers-reduced-motion: no-preference) {
+ animation: ${spin} 1250ms linear;
+ }
+`
+const ChildWrapper = styled(Text)`
+ position: relative;
+ z-index: 1;
+ font-weight: bold;
+`
+
+export default Sparkles
diff --git a/lib/theme.js b/lib/theme.js
index a398caa5..d894174f 100644
--- a/lib/theme.js
+++ b/lib/theme.js
@@ -7,6 +7,9 @@ theme.useColorSchemeMediaQuery = false
theme.lineHeights.limit = 0.875
+// Temporary hack
+theme.breakpoints = ['32em', '48em', '64em', '256em', '512em']
+
theme.util = {
reduceMotion: '@media (prefers-reduced-motion: reduce)',
reduceTransparency: '@media (prefers-reduced-transparency: reduce)',
@@ -98,6 +101,8 @@ theme.forms.labelCheckbox = {
svg: { color: 'muted' }
}
+theme.layout.copy.maxWidth = [null, null, 'copyPlus']
+
theme.text.lead = {}
export default theme
diff --git a/lib/use-prefers-reduced-motion.js b/lib/use-prefers-reduced-motion.js
new file mode 100644
index 00000000..6860a962
--- /dev/null
+++ b/lib/use-prefers-reduced-motion.js
@@ -0,0 +1,32 @@
+// Full credit to https://joshwcomeau.com/snippets/react-hooks/use-prefers-reduced-motion
+import React from 'react'
+
+const QUERY = '(prefers-reduced-motion: no-preference)'
+const isRenderingOnServer = typeof window === 'undefined'
+
+const getInitialState = () => {
+ // For our initial server render, we won't know if the user
+ // prefers reduced motion, but it doesn't matter. This value
+ // will be overwritten on the client, before any animations
+ // occur.
+ return isRenderingOnServer ? true : !window.matchMedia(QUERY).matches
+}
+
+function usePrefersReducedMotion() {
+ const [prefersReducedMotion, setPrefersReducedMotion] = React.useState(
+ getInitialState
+ )
+ React.useEffect(() => {
+ const mediaQueryList = window.matchMedia(QUERY)
+ const listener = event => {
+ setPrefersReducedMotion(!event.matches)
+ }
+ mediaQueryList.addListener(listener)
+ return () => {
+ mediaQueryList.removeListener(listener)
+ }
+ }, [])
+ return prefersReducedMotion
+}
+
+export default usePrefersReducedMotion
diff --git a/lib/use-random-interval.js b/lib/use-random-interval.js
new file mode 100644
index 00000000..8da57106
--- /dev/null
+++ b/lib/use-random-interval.js
@@ -0,0 +1,33 @@
+// Full credit to https://joshwcomeau.com/snippets/react-hooks/use-random-interval
+import React from 'react'
+
+// Utility helper for random number generation
+const random = (min, max) => Math.floor(Math.random() * (max - min)) + min
+
+const useRandomInterval = (callback, minDelay, maxDelay) => {
+ const timeoutId = React.useRef(null)
+ const savedCallback = React.useRef(callback)
+ React.useEffect(() => {
+ savedCallback.current = callback
+ })
+ React.useEffect(() => {
+ let isEnabled = typeof minDelay === 'number' && typeof maxDelay === 'number'
+ if (isEnabled) {
+ const handleTick = () => {
+ const nextTickAt = random(minDelay, maxDelay)
+ timeoutId.current = window.setTimeout(() => {
+ savedCallback.current()
+ handleTick()
+ }, nextTickAt)
+ }
+ handleTick()
+ }
+ return () => window.clearTimeout(timeoutId.current)
+ }, [minDelay, maxDelay])
+ const cancel = React.useCallback(function () {
+ window.clearTimeout(timeoutId.current)
+ }, [])
+ return cancel
+}
+
+export default useRandomInterval
diff --git a/package.json b/package.json
index c7c31c27..eaadcaed 100755
--- a/package.json
+++ b/package.json
@@ -11,15 +11,15 @@
},
"dependencies": {
"@hackclub/icons": "^0.0.3",
- "@hackclub/meta": "^0.1.1",
+ "@hackclub/meta": "1.0.0",
"@hackclub/theme": "^0.1.1",
"@mdx-js/loader": "^1.6.1",
- "@next/mdx": "^9.4.0",
+ "@next/mdx": "^9.4.1",
"@segment/snippet": "^4.11.0",
"airtable-plus": "^1.0.4",
"isomorphic-unfetch": "^3.0.0",
"lodash": "^4.17.15",
- "next": "^9.4.0",
+ "next": "^9.4.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-player": "^2.0.1",
diff --git a/pages/_app.js b/pages/_app.js
index 21ad189f..48998003 100755
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -12,10 +12,7 @@ export default class App extends NextApp {
const { Component, pageProps } = this.props
return (
-
- Hack Club
-
-
+
)
diff --git a/pages/elon.js b/pages/elon.js
new file mode 100644
index 00000000..f444f122
--- /dev/null
+++ b/pages/elon.js
@@ -0,0 +1,147 @@
+import {
+ Avatar,
+ Badge,
+ BaseStyles,
+ Box,
+ Button,
+ Container,
+ Flex,
+ Grid,
+ Heading,
+ Text
+} from 'theme-ui'
+import Head from 'next/head'
+import NextLink from 'next/link'
+import Meta from '@hackclub/meta'
+import Nav from '../components/nav'
+import Icon from '../components/icon'
+import ForceTheme from '../components/force-theme'
+import Footer from '../components/footer'
+import ElonCopy from '../components/elon/copy.mdx'
+
+export default () => (
+ <>
+
+
+
+ theme.util.gradient('yellow', 'green')
+ }}
+ >
+
+
+ Hack Club “makes me feel much more optimistic{' '}
+ about the future.”
+
+ —Elon Musk
+
+
+
+
+
+
+ Zach Latta, founder
+
+
+ May 15, 2020
+
+
+
+
+ t.util.gradient('yellow', 'orange'),
+ color: 'white',
+ py: [4, 5]
+ }}
+ >
+
+
+
+
+ Teenager? New here? Welcome!
+
+
+ Hack Club is a global community of high school makers & student-led
+ coding clubs. We’ve got a 24/7 Slack chatroom of 9k teenagers
+ learning to code & building amazing projects, & you’ll fit right in.
+
+
+
+
+
+
+
+
+ >
+)
diff --git a/pages/index.js b/pages/index.js
index 4087451b..f98749de 100644
--- a/pages/index.js
+++ b/pages/index.js
@@ -10,6 +10,7 @@ import {
} from 'theme-ui'
import { keyframes } from '@emotion/core'
import { Slide } from 'react-reveal'
+import NextLink from 'next/link'
import Head from 'next/head'
import Meta from '@hackclub/meta'
import Nav from '../components/nav'
@@ -18,7 +19,7 @@ import Footer from '../components/footer'
import Announcement from '../components/home/announcement'
import SlackEvents from '../components/home/slack-events'
import JoinForm from '../components/home/join-form'
-import { timeSince } from '../lib/dates'
+// import { timeSince } from '../lib/dates'
const fade = keyframes({ from: { opacity: 0 }, to: { opacity: 1 } })
const slide = keyframes({
@@ -127,16 +128,14 @@ export default () => (
}}
>
- {/*
-
- */}
+
+
+