Skip to content

Commit 6737288

Browse files
authored
feat: add hero graphic, fix scrolled DOM warning, fix hover button, a… (#59)
* feat: add hero graphic, fix scrolled DOM warning, fix hover button, add viewport meta, update social links, fix 404 alt text * fix: members.js return shape, aria-hidden on decorations, remove Facebook link * fix: use Emotion keyframes instead of style tag, add aria-hidden to decorations * fix: add AbortController, remove unused imports * fix: remove unused Static component, pass AbortController signal to getLiveCount
1 parent 0d8e8f4 commit 6737288

6 files changed

Lines changed: 167 additions & 74 deletions

File tree

components/flag.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const waveFlagScaled = keyframes`
2121
`
2222

2323
const scrolled = (props) =>
24-
props.scrolled &&
24+
props.$scrolled &&
2525
css`
2626
transform: scale(0.875);
2727
height: 56px;
@@ -31,7 +31,9 @@ const scrolled = (props) =>
3131
}
3232
`
3333

34-
const Base = styled('a')`
34+
const Base = styled('a', {
35+
shouldForwardProp: (prop) => !['scrolled', '$scrolled'].includes(prop)
36+
})`
3537
background-image: url(https://assets.hackclub.com/flag-orpheus-top.svg);
3638
background-repeat: no-repeat;
3739
background-position: top left;
@@ -56,8 +58,8 @@ const Base = styled('a')`
5658
${scrolled};
5759
`
5860

59-
const Flag = (props) => (
60-
<Base href="https://hackclub.com" title="Hack Club Homepage" {...props} />
61+
const Flag = ({ scrolled, ...props }) => (
62+
<Base href="https://hackclub.com" title="Hack Club Homepage" $scrolled={scrolled} {...props} />
6163
)
6264

6365
export default Flag

components/nav.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const fixed = (props) =>
3030

3131
const Root = styled(Box, {
3232
shouldForwardProp: (prop) =>
33-
!['bgColor', 'scrolled', 'toggled'].includes(prop)
33+
!['bgColor', 'scrolled', 'toggled', 'color', 'dark'].includes(prop)
3434
})`
3535
position: fixed;
3636
top: 0;
@@ -104,7 +104,7 @@ const layout = (props) =>
104104
}
105105
`
106106
const NavBar = styled(Box, {
107-
shouldForwardProp: (prop) => !['isMobile', 'toggled'].includes(prop)
107+
shouldForwardProp: (prop) => !['isMobile', 'toggled','scrolled','color','dark'].includes(prop)
108108
})`
109109
display: none;
110110
${layout};
@@ -200,7 +200,7 @@ function Header({ unfixed, color, bgColor, dark, fixed, ...props }) {
200200
as="header"
201201
>
202202
<Content>
203-
<Flag scrolled={scrolled || fixed} />
203+
<Flag scrolled={scrolled || fixed || undefined} />
204204
<Navigation
205205
as="nav"
206206
aria-hidden={!!mobile}

components/slack/header.js

Lines changed: 140 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,122 @@
1+
/** @jsxImportSource theme-ui */
2+
import { useState, useEffect } from 'react'
13
import { Box, Card, Grid, Heading, Text } from 'theme-ui'
2-
import usePrefersMotion from '../../lib/use-prefers-motion'
3-
import useHasMounted from '../../lib/use-has-mounted'
4+
import { keyframes } from '@emotion/react'
5+
import { getLiveCount, formatted as defaultFormatted } from '../../lib/members'
6+
7+
const float1 = keyframes`
8+
0%, 100% { transform: translateY(0px); }
9+
50% { transform: translateY(-18px); }
10+
`
11+
const float2 = keyframes`
12+
0%, 100% { transform: translateY(0px); }
13+
50% { transform: translateY(-12px); }
14+
`
15+
const float3 = keyframes`
16+
0%, 100% { transform: translateY(0px); }
17+
50% { transform: translateY(-20px); }
18+
`
19+
20+
const HeroGraphic = () => (
21+
<Box
22+
sx={{
23+
position: 'absolute',
24+
top: 0, left: 0, right: 0, bottom: 0,
25+
overflow: 'hidden',
26+
zIndex: 0,
27+
pointerEvents: 'none'
28+
}}
29+
>
30+
<Box sx={{
31+
position: 'absolute', top: '10%', left: '5%',
32+
width: ['60px', '90px'], height: ['60px', '90px'],
33+
borderRadius: '50%', bg: 'rgba(255,255,255,0.15)',
34+
animation: `${float1} 6s ease-in-out infinite`
35+
}} />
36+
<Box sx={{
37+
position: 'absolute', top: '60%', left: '2%',
38+
width: ['30px', '50px'], height: ['30px', '50px'],
39+
borderRadius: '50%', bg: 'rgba(255,255,255,0.1)',
40+
animation: `${float2} 8s ease-in-out infinite`
41+
}} />
42+
<Box sx={{
43+
position: 'absolute', top: '20%', right: ['80px', '200px'],
44+
width: ['40px', '70px'], height: ['40px', '70px'],
45+
borderRadius: '50%', bg: 'rgba(255,255,255,0.12)',
46+
animation: `${float3} 7s ease-in-out infinite`
47+
}} />
48+
<Box aria-hidden="true" sx={{
49+
position: 'absolute',
50+
bottom: '-10px',
51+
left: ['10px', '40px'],
52+
fontSize: ['80px', '140px'],
53+
fontWeight: 800,
54+
color: 'rgba(255,255,255,0.08)',
55+
lineHeight: 1,
56+
fontFamily: 'inherit',
57+
userSelect: 'none'
58+
}}>
59+
#
60+
</Box>
61+
<Box aria-hidden="true" sx={{
62+
position: 'absolute',
63+
top: '5px',
64+
right: ['80px', '220px'],
65+
fontSize: ['60px', '100px'],
66+
fontWeight: 800,
67+
color: 'rgba(255,255,255,0.06)',
68+
lineHeight: 1,
69+
fontFamily: 'inherit',
70+
userSelect: 'none'
71+
}}>
72+
#
73+
</Box>
74+
</Box>
75+
)
76+
77+
const MemberBadge = () => {
78+
const [count, setCount] = useState(defaultFormatted)
79+
80+
useEffect(() => {
81+
const controller = new AbortController()
82+
const timeout = setTimeout(() => controller.abort(), 3000)
83+
getLiveCount(controller.signal)
84+
.then(data => setCount(data.formatted))
85+
.catch(() => {})
86+
.finally(() => clearTimeout(timeout))
87+
return () => {
88+
controller.abort()
89+
clearTimeout(timeout)
90+
}
91+
}, [])
92+
93+
return (
94+
<Box sx={{
95+
display: 'inline-flex',
96+
alignItems: 'center',
97+
gap: 2,
98+
bg: 'rgba(255,255,255,0.15)',
99+
border: '1px solid rgba(255,255,255,0.3)',
100+
borderRadius: '999px',
101+
px: 3, py: 1, mb: 3,
102+
backdropFilter: 'blur(8px)'
103+
}}>
104+
<Box sx={{
105+
width: '8px', height: '8px',
106+
borderRadius: '50%', bg: '#2eb67d',
107+
boxShadow: '0 0 6px #2eb67d',
108+
animation: 'pulse 2s ease-in-out infinite',
109+
'@keyframes pulse': {
110+
'0%, 100%': { opacity: 1 },
111+
'50%': { opacity: 0.4 }
112+
}
113+
}} />
114+
<Text sx={{ color: 'white', fontSize: 1, fontWeight: 600, letterSpacing: '0.03em' }}>
115+
{count} hackers online
116+
</Text>
117+
</Box>
118+
)
119+
}
4120

5121
const Content = ({ onJoinClick }) => (
6122
<Grid
@@ -9,31 +125,22 @@ const Content = ({ onJoinClick }) => (
9125
pb={[3, 4]}
10126
sx={{
11127
backgroundImage:
12-
'radial-gradient( ellipse farthest-corner at top left, #ff8c37, #ec3750)'
128+
'radial-gradient(ellipse farthest-corner at top left, #ff8c37, #ec3750)',
129+
position: 'relative'
13130
}}
14131
>
15-
<Box
16-
sx={{
17-
position: 'relative',
18-
zIndex: 1,
19-
textShadow: 'text',
20-
textAlign: ['center', 'center']
21-
}}
22-
>
132+
<HeroGraphic />
133+
<Box sx={{ position: 'relative', zIndex: 1, textShadow: 'text', textAlign: ['center', 'center'] }}>
134+
<MemberBadge />
23135
<Heading
24136
as="h1"
25137
variant="title"
26-
sx={{
27-
color: 'white',
28-
fontSize: [5, 6, 7],
29-
lineHeight: 'limit',
30-
mb: [2, 3]
31-
}}
138+
sx={{ color: 'white', fontSize: [5, 6, 7], lineHeight: 'limit', mb: [2, 3] }}
32139
>
33140
Hack Club Slack
34141
</Heading>
35142
</Box>
36-
<Box sx={{ zIndex: 5, display: 'flex', alignItems: 'center' }}>
143+
<Box sx={{ zIndex: 5, display: 'flex', alignItems: 'center', position: 'relative' }}>
37144
<Card
38145
sx={{
39146
variant: 'cards.translucent',
@@ -47,14 +154,7 @@ const Content = ({ onJoinClick }) => (
47154
<br />
48155
Join up to make friends, find projects, and have fun.
49156
</Text>
50-
<Box
51-
sx={{
52-
display: 'flex',
53-
gap: 3,
54-
justifyContent: 'center',
55-
flexWrap: 'wrap'
56-
}}
57-
>
157+
<Box sx={{ display: 'flex', gap: 3, justifyContent: 'center', flexWrap: 'wrap' }}>
58158
<Text
59159
as="button"
60160
onClick={onJoinClick}
@@ -64,8 +164,7 @@ const Content = ({ onJoinClick }) => (
64164
'radial-gradient(ellipse farthest-corner at top left, #ff8c37, #ec3750)',
65165
color: 'white',
66166
fontSize: [2, 3],
67-
px: 5,
68-
py: 3,
167+
px: 5, py: 3,
69168
borderRadius: 'extra',
70169
fontWeight: 'bold',
71170
textDecoration: 'none',
@@ -80,7 +179,7 @@ const Content = ({ onJoinClick }) => (
80179
transform: 'scale(1.05)',
81180
boxShadow: '0 0 0 2px white',
82181
backgroundImage:
83-
'radial-gradient(ellipse farthest-corner at bottom right, #ff8c373f, #ec37503f)'
182+
'radial-gradient(ellipse farthest-corner at bottom right, #ff8c37, #ec3750)'
84183
}
85184
}}
86185
>
@@ -110,42 +209,18 @@ const Cover = () => (
110209
/>
111210
)
112211

113-
const Static = ({
114-
img = 'https://cloud-r4rrjh2z8-hack-club-bot.vercel.app/02020-07-25_a1tcva4ch6mmr6j2cfmcb4e9ync3yhar.png',
115-
onJoinClick
116-
}) => (
117-
<Box
118-
as="section"
119-
id="slack"
120-
sx={{
121-
position: 'relative',
122-
overflow: 'hidden',
123-
backgroundImage: `url(${img})`,
124-
backgroundSize: 'cover'
125-
}}
212+
213+
const Slack=({onJoinClick})=>(
214+
<Box
215+
as="section"
216+
id="slack"
217+
sx={{
218+
position:'relative',
219+
overflow:'hidden'
220+
}}
126221
>
127-
<Cover />
128-
<Content onJoinClick={onJoinClick} />
222+
<Cover/>
223+
<Content onJoinClick={onJoinClick}/>
129224
</Box>
130225
)
131-
132-
const Slack = ({ onJoinClick }) => {
133-
const hasMounted = useHasMounted()
134-
const prefersMotion = usePrefersMotion()
135-
if (hasMounted && prefersMotion) {
136-
return (
137-
<Box
138-
as="section"
139-
id="slack"
140-
sx={{ overflow: 'hidden', position: 'relative' }}
141-
>
142-
<Cover />
143-
<Content onJoinClick={onJoinClick} />
144-
</Box>
145-
)
146-
} else {
147-
return <Static onJoinClick={onJoinClick} />
148-
}
149-
}
150-
151-
export default Slack
226+
export default Slack

lib/members.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
11
export const count = 27253
22
export const formatted = count.toLocaleString('en-US')
33
export const thousands = Math.round(count / 1000)
4+
5+
export async function getLiveCount(signal) {
6+
try {
7+
const res = await fetch('https://slack-data.hackclub.dev/api/stats', { signal })
8+
if (!res.ok) throw new Error('Failed to fetch')
9+
const data = await res.json()
10+
const live = data.member_count ?? count
11+
return {
12+
count: live,
13+
formatted: live.toLocaleString('en-US'),
14+
thousands: Math.round(live / 1000)
15+
}
16+
} catch {
17+
return { count, formatted, thousands }
18+
}
19+
}

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pages/_document.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ const org = {
1010
'https://twitter.com/hackclub',
1111
'https://github.com/hackclub',
1212
'https://www.instagram.com/starthackclub',
13-
'https://www.facebook.com/Hack-Club-741805665870458',
14-
'https://www.youtube.com/channel/UCQzO0jpcRkP-9eWKMpJyB0w'
13+
'https://www.youtube.com/c/HackClubHQ'
1514
],
1615
contactPoint: [
1716
{

0 commit comments

Comments
 (0)