├── .gitignore ├── FONT_LICENSES ├── Diplomata │ └── OFL.txt ├── Gravitas_One │ └── OFL.txt └── Smythe │ └── OFL.txt ├── LICENSE ├── README.md ├── airbridge.js ├── components ├── color-switcher.js ├── cta.js ├── cursor.js ├── faq.js ├── flag.js ├── footer.js ├── gallery.js ├── header.js ├── meta.js ├── multiplayer-mouse.js ├── route.js └── telegram.js ├── lib ├── questions.js ├── theme.js ├── use-has-mounted.js └── use-prefers-motion.js ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages ├── _app.js ├── _document.js ├── api │ ├── _lib │ │ ├── chromium.ts │ │ ├── options.ts │ │ ├── parser.ts │ │ ├── sanitizer.ts │ │ ├── template.ts │ │ └── types.ts │ ├── hello.js │ ├── sign-in-slack.js │ ├── slack-verification-code.js │ └── ticket.js ├── hub.js ├── launch.js └── meetups.mdx ├── prettier.config.js ├── public ├── PLEASE WORK.mp4 ├── Untitled Project 300000.mp4 ├── Untitled%20Project%203fw.mp4 ├── android-icon-144x144.png ├── android-icon-192x192.png ├── android-icon-36x36.png ├── android-icon-48x48.png ├── android-icon-72x72.png ├── android-icon-96x96.png ├── apple-icon-114x114.png ├── apple-icon-120x120.png ├── apple-icon-144x144.png ├── apple-icon-152x152.png ├── apple-icon-180x180.png ├── apple-icon-57x57.png ├── apple-icon-60x60.png ├── apple-icon-72x72.png ├── apple-icon-76x76.png ├── apple-icon-precomposed.png ├── apple-icon.png ├── browserconfig.xml ├── coat-of-arms.png ├── cursors │ ├── cursor.png │ ├── cursor.svg │ ├── dino-down.png │ └── dino-up.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-96x96.png ├── favicon.ico ├── header.png ├── lander.mp4 ├── manifest.json ├── meta.png ├── ms-icon-144x144.png ├── ms-icon-150x150.png ├── ms-icon-310x310.png ├── ms-icon-70x70.png ├── route.svg ├── signatures │ ├── all.svg │ ├── christina_asquith.png │ ├── claire_wang.png │ ├── matthew_stanciu.png │ ├── max_wofford.png │ ├── sam_poder.png │ ├── tina_soriano.png │ ├── tom_preston-werner.png │ ├── zach_fogg.png │ └── zach_latta.png ├── timeline.png ├── title.gif ├── title.png └── zephyr-poster │ ├── 0.png │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ └── combined.png ├── slack-manifest.yml ├── styles ├── Home.module.css └── globals.css ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .now 2 | .next 3 | node_modules 4 | .DS_Store 5 | *.pem 6 | 7 | # debug 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # local env files 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | .env 18 | 19 | # vercel 20 | .vercel 21 | -------------------------------------------------------------------------------- /FONT_LICENSES/Diplomata/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Eduardo Tunni (http://www.tipo.net.ar), 2 | with Reserved Font Name "Diplomata" 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /FONT_LICENSES/Gravitas_One/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 by Sorkin Type Co (www.sorkintype.com), 2 | with Reserved Font Name "Gravitas". 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /FONT_LICENSES/Smythe/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 by vernon adams (vern@newtypography.co.uk), 2 | with Reserved Font Name Smythe. 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Hack Club 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](./public/meta.png) 2 | 3 | # Hacker Zephyr Site 4 | 5 | _Created using Theme UI & Next.js. Based on [Hack Club Theme Starter](https://github.com/hackclub/theme-starter)._ 6 | 7 | ## Usage 8 | 9 | 1. Import this repo to your coding environment of choice. Download it, `git clone`, or use the GitHub import on Glitch/Repl.it. 10 | 2. `yarn` to install dependencies. 11 | 3. `yarn dev` to start your server. 12 | 4. Start adding your own pages & components in their respective directories. 13 | -------------------------------------------------------------------------------- /airbridge.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/all-aboard/04c289fd20f66db18902d139a3b03b9a7ef96eb8/airbridge.js -------------------------------------------------------------------------------- /components/color-switcher.js: -------------------------------------------------------------------------------- 1 | import { IconButton, useColorMode } from 'theme-ui' 2 | 3 | const ColorSwitcher = props => { 4 | const [mode, setMode] = useColorMode() 5 | return ( 6 | setMode(mode === 'dark' ? 'light' : 'dark')} 8 | title={`Switch to ${mode === 'dark' ? 'light' : 'dark'} mode`} 9 | sx={{ 10 | position: 'absolute', 11 | top: [2, 3], 12 | right: [2, 3], 13 | color: 'primary', 14 | cursor: 'pointer', 15 | borderRadius: 'circle', 16 | transition: 'box-shadow .125s ease-in-out', 17 | ':hover,:focus': { 18 | boxShadow: '0 0 0 3px', 19 | outline: 'none' 20 | } 21 | }} 22 | {...props} 23 | > 24 | 25 | 33 | 34 | 35 | 36 | ) 37 | } 38 | 39 | export default ColorSwitcher 40 | -------------------------------------------------------------------------------- /components/cta.js: -------------------------------------------------------------------------------- 1 | import { Box, Heading, Text, Button, Grid, Container, Flex } from 'theme-ui' 2 | 3 | const buttonSX = { 4 | background: '#759d3e', 5 | color: 'white', 6 | minWidth: [5, 8], 7 | maxWidth: '75wh', 8 | border: 'none', 9 | m: 0, 10 | mt: 0, 11 | p: 4, 12 | width: '100%', 13 | boxShadow: 'card', 14 | border: '4px dashed #eaeedc', 15 | ':hover,:focus': { 16 | boxShadow: 'elevated', 17 | color: 'black', 18 | } 19 | } 20 | 21 | const CTA = () => ( 22 | 31 | 32 | 42 | 43 | The interest form has now closed. 44 | 45 | Thank you to all who registered. 46 | 47 | 48 | 49 | 50 | 62 | 63 | 64 | 65 | ) 66 | 67 | export default CTA 68 | -------------------------------------------------------------------------------- /components/cursor.js: -------------------------------------------------------------------------------- 1 | import { Image } from 'theme-ui' 2 | const GhostCursor = ({ 3 | x, 4 | y 5 | }) => ( 6 | 10 | ) 11 | export default GhostCursor -------------------------------------------------------------------------------- /components/faq.js: -------------------------------------------------------------------------------- 1 | import questions from '../lib/questions' 2 | import { 3 | Container, 4 | Grid, 5 | Heading, 6 | Box, 7 | Text 8 | } from 'theme-ui' 9 | 10 | const FAQ = () => ( 11 | 15 | 16 | Glossary 17 | 18 | 19 | {questions.map(({ question, answer }, index) => ( 20 | 32 | {question} 38 | {answer} 42 | 43 | ))} 44 | 45 | 46 | ) 47 | 48 | export default FAQ -------------------------------------------------------------------------------- /components/flag.js: -------------------------------------------------------------------------------- 1 | const Flag = props => ( 2 | 8 | 49 | 50 | ) 51 | 52 | export default Flag -------------------------------------------------------------------------------- /components/footer.js: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | Container, 4 | Heading, 5 | Text, 6 | Grid, 7 | Flex, 8 | Link 9 | } from 'theme-ui' 10 | 11 | export default function Footer() { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | A Gift from Hack Club HQ 19 | 20 | 21 | 22 | Home 23 | {' '} 24 | |{' '} 25 | 26 | Slack 27 | {' '} 28 | |{' '} 29 | 33 | Workshops 34 | {' '} 35 | |{' '} 36 | 40 | Scrapbook 41 | 42 | 43 | 44 | 45 | 46 | 52 | 62 | h 63 | 64 | 65 | 66 | 67 | 68 | ) 69 | } 70 | -------------------------------------------------------------------------------- /components/gallery.js: -------------------------------------------------------------------------------- 1 | import { Grid, Container, Heading, Box, Text } from 'theme-ui' 2 | import Route from './route' 3 | 4 | const Photo = ({ alt, src, height, display, width, backgroundPosition }) => { 5 | return ( 6 | 7 | 24 | 25 | {alt} 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | const Gallery = () => ( 33 | 34 | 35 | 36 | Oh, the Places You'll Go 37 | 38 | 39 | You'll be spending time both on and off the train. Here's the view. 40 | 41 | 42 | 43 | 44 | 49 | 54 | 59 | 64 | 70 | 76 | 77 | 84 | 85 | 86 | 87 | ) 88 | 89 | export default Gallery 90 | -------------------------------------------------------------------------------- /components/header.js: -------------------------------------------------------------------------------- 1 | import { Box, Image } from 'theme-ui' 2 | import usePrefersMotion from '../lib/use-prefers-motion' 3 | 4 | const Static = () => ( 5 | 16 | This summer, we're closing Zoom. All aboard the Hacker Zephyr. Trans America, Burlington Vermont - Los Angeles California 26 | 27 | ) 28 | 29 | const Header = () => { 30 | const prefersMotion = usePrefersMotion() 31 | if (prefersMotion) { 32 | return ( 33 | 43 | 61 | 62 |

63 | This summer, we're closing Zoom. All aboard the Hacker Zephyr. Trans 64 | America, Burlington Vermont - Los Angeles California 65 |

66 |
67 |
68 | ) 69 | } else { 70 | return 71 | } 72 | } 73 | 74 | export default Header 75 | -------------------------------------------------------------------------------- /components/meta.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | 3 | const Meta = ({ 4 | name = 'Hack Club', 5 | title = 'The Hacker Zephyr', 6 | description = `Every summer Hack Club does something special. This year, we're chartering a train across America, and hosting the world's longest hackathon on land (in miles).`, 7 | image = 'https://slack-oauth-starter-test-xy120.hackclub.dev/meta.png' 8 | }) => ( 9 | 10 | {title} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 32 | 37 | 42 | 47 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | ) 64 | 65 | export default Meta -------------------------------------------------------------------------------- /components/multiplayer-mouse.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import useWebSocket, { ReadyState } from 'react-use-websocket' 3 | import GhostCursor from "./cursor" 4 | 5 | const ENDPOINT = 'https://cursor-chat-multiplayer.maxwofford.repl.co' 6 | 7 | const MultiplayerMouse = () => { 8 | const socket = io(ENDPOINT) 9 | // User session 10 | const [userID, setUserID] = useState(null) 11 | socket.on('assign id', ({ id }) => { 12 | setUserID(id) 13 | }) 14 | 15 | // Other users/cursors 16 | const [cursors, setCursors] = useState({}) 17 | socket.on('user moved', ({ id, x, y, ts}) => { 18 | cursors[id] = {x, y, ts} 19 | setCursors(cursors) 20 | }) 21 | socket.on('user left', ({ id }) => { 22 | console.log(Object.keys(cursors), id) 23 | delete cursors[id] 24 | setCursors(cursors) 25 | console.log(Object.keys(cursors), id) 26 | }) 27 | 28 | // Track own mouse position 29 | let mousePos = { x: 0, y: 0 } 30 | const emitMousePos = () => { 31 | if (userID) { 32 | socket.emit('move user', { 33 | x: (mousePos.x / window.innerWidth) * 100, 34 | y: (mousePos.y / window.innerHeight) * 100, 35 | ts: Date.now() 36 | }) 37 | } 38 | } 39 | // Track mouse movement 40 | useEffect(() => { 41 | const setFromEvent = e => { 42 | mousePos = { x: e.pageX, y: e.pageY } 43 | emitMousePos() 44 | } 45 | window.addEventListener("mousemove", setFromEvent) 46 | 47 | // cleanup on unmount 48 | return () => { 49 | window.removeEventListener("mousemove", setFromEvent) 50 | } 51 | }, []) 52 | 53 | useEffect(() => { 54 | // document.onmousemove = event => { 55 | // setXMousePos(event.pageX) 56 | // setYMousePos(event.pageY) 57 | // emitMousePos() 58 | // } 59 | // document.onscroll = event => { 60 | // if (xLastScrolled != window.scrollX) { 61 | // setXMousePos(xLastScrolled - window.scrollX) 62 | // setXLastScrolled(window.scrollX) 63 | // } 64 | // if (yLastScrolled != window.scrollY) { 65 | // setYMousePos(yLastScrolled - window.scrollY) 66 | // setYLastScrolled(window.scrollY) 67 | // } 68 | // emitMousePos() 69 | // } 70 | 71 | // socket.on('assign id', ({ id }) => { 72 | // setUserID(id) 73 | // }) 74 | 75 | // cleanup useEffect when component unmounts 76 | return () => { 77 | 78 | } 79 | }, []) 80 | 81 | return ( 82 | <> 83 | {Object.keys(cursors).map((key) => ( 84 | 85 | ))} 86 | 87 | ) 88 | } 89 | 90 | export default MultiplayerMouse -------------------------------------------------------------------------------- /components/route.js: -------------------------------------------------------------------------------- 1 | import { Box, Flex, Image } from 'theme-ui' 2 | 3 | const Route = () => ( 4 | 5 | 11 | 20 | 21 | ) 22 | 23 | export default Route -------------------------------------------------------------------------------- /components/telegram.js: -------------------------------------------------------------------------------- 1 | import { Container, Box, Image, Heading } from 'theme-ui' 2 | 3 | const Telegram = () => ( 4 | 5 | 19 | 28 | 39 | Postal Telegram – Immediate 40 | 41 |

Every summer Hack Clubbers do something special.

42 |

43 | In 2019, 75 Hack Club leaders gathered in San Francisco for Flagship. 44 |

45 |

46 | In 2020, 300 Hack Clubbers received $50,000 for hardware projects. 47 | We launched Scrapbook, and 48 | hosted the Summer of Making 49 | . 50 |

51 |

52 | This year, we're chartering a train across America. 53 |

54 |

55 | We're starting at{' '} 56 | 57 | Hack Club HQ 58 | in Burlington, Vermont, heading south to New York City, west through Chicago, and crossing the Rockies on our way 59 | to San Francisco. From there, we'll follow the Pacific Ocean and end in Los Angeles, where we'll finish at SpaceX. 60 |

61 |

62 | Onboard, we're hosting the world's longest hackathon on land: 3,502 miles long. 63 |

64 |

65 | This won't be a regular hackathon. Imagine a travelling hacker festival, where the goal is to make beautiful and interesting things with code. 66 |

67 |

68 | The hackathon begins when we see the Atlantic Ocean and ends at the Pacific Ocean in Los Angeles. On The Hacker Zephyr, you'll meet eclectic guests, including vaudevillians, political exiles, startup founders, and a contortionist. 69 |

70 |

71 | This trip is a gift from Hack Club to you and asks nothing in return. We 72 | want this to be a special, technical, and soul-expanding adventure that you'll remember for the rest of your life. 73 |

74 |

75 | Please register your interest below. We are trying to fit as many Hack Clubbers as we can on The Hacker Zephyr. 76 |

77 |
78 | 82 |
83 |
84 | ) 85 | 86 | export default Telegram 87 | -------------------------------------------------------------------------------- /lib/questions.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | question: 'Who is eligible to board the train?', 4 | answer: 5 | 'All members of the Hack Club Slack who are fully vaccinated by the boarding date are eligible to board the train. Limited tickets are available, though.' 6 | }, 7 | { 8 | question: 'My parents have concerns, what should I do?', 9 | answer: ( 10 | <> 11 | We have prepared a guide for parents. You can access it here. If 12 | needed, your parents can also contact us at zephyr@hackclub.com 13 | 14 | ) 15 | }, 16 | { 17 | question: 'Will housing, food and drink be provided?', 18 | answer: 19 | 'Yes. All essentials will be covered by us. Housing will take place in both sleeper trains and reputable hotels.' 20 | }, 21 | { 22 | question: 'What will I need to participate?', 23 | answer: 24 | 'We recommend bringing clothing, toiletries and electronics. A more extensive list will be provided at a later date.' 25 | }, 26 | { 27 | question: 'Will travel to Burlington and from LA be covered?', 28 | answer: 29 | 'Yes, we will reimburse the costs of flights and / or trains required to get to Burlington and home from LA. ' 30 | }, 31 | { 32 | question: 'How can I get in touch?', 33 | answer: ( 34 | <> 35 | If you've got more questions, feel free to contact the team at 36 | {' '}zephyr@hackclub.com and we will do our best to help. 37 | 38 | ) 39 | } 40 | ] 41 | -------------------------------------------------------------------------------- /lib/theme.js: -------------------------------------------------------------------------------- 1 | import { withModalTheme } from '@mattjennings/react-modal' 2 | 3 | const theme = { 4 | useCustomProperties: true, 5 | initialColorMode: 'light', 6 | colors: { 7 | text: '#fff', 8 | background: '#fff', 9 | primary: '#000', 10 | secondary: '#45373c', 11 | muted: '#e0e0e0', 12 | highlight: '#3c3c3c', 13 | gray: '#6c6c6c', 14 | red: '#be100e', 15 | accent: '#3f3f3f', 16 | modes: { 17 | dark: { 18 | text: '#fff', 19 | background: '#fff', 20 | primary: '#000', 21 | secondary: '#45373c', 22 | muted: '#e0e0e0', 23 | highlight: '#3c3c3c', 24 | gray: '#6c6c6c', 25 | red: '#be100e', 26 | accent: '#3f3f3f' 27 | } 28 | } 29 | }, 30 | fonts: { 31 | body: "'Fraunces', monospace", 32 | heading: "'Fraunces', monospace", 33 | monospace: 'monospace' 34 | }, 35 | lineHeights: { 36 | limit: 0.875, 37 | title: 1, 38 | heading: 1.125, 39 | subheading: 1.25, 40 | caption: 1.375, 41 | body: 1.5 42 | }, 43 | fontWeights: { 44 | body: 400, 45 | bold: 700, 46 | heading: 700 47 | }, 48 | letterSpacings: { 49 | title: '-0.009em', 50 | headline: '0.009em' 51 | }, 52 | sizes: { 53 | widePlus: 2048, 54 | wide: 1536, 55 | layoutPlus: 1200, 56 | layout: 1024, 57 | copyUltra: 980, 58 | copyPlus: 700, 59 | copy: 680, 60 | narrowPlus: 600, 61 | narrow: 512 62 | }, 63 | radii: { 64 | small: 4, 65 | default: 8, 66 | extra: 12, 67 | ultra: 16, 68 | circle: 99999 69 | }, 70 | shadows: { 71 | text: '0 1px 2px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.125)', 72 | small: '0 1px 2px rgba(0, 0, 0, 0.0625), 0 2px 4px rgba(0, 0, 0, 0.0625)', 73 | card: '0 4px 8px rgba(0, 0, 0, 0.125)', 74 | elevated: '0 1px 2px rgba(0, 0, 0, 0.0625), 0 8px 12px rgba(0, 0, 0, 0.125)' 75 | }, 76 | text: { 77 | heading: { 78 | fontWeight: 'bold', 79 | lineHeight: 'heading', 80 | mt: 0, 81 | mb: 0 82 | }, 83 | ultratitle: { 84 | fontSize: [5, 6, 7], 85 | lineHeight: 'limit', 86 | fontWeight: 'bold', 87 | letterSpacing: 'title' 88 | }, 89 | title: { 90 | fontSize: [5, 5, 6], 91 | fontWeight: 'bold', 92 | letterSpacing: 'title', 93 | lineHeight: 'title' 94 | }, 95 | subtitle: { 96 | mt: 3, 97 | fontSize: [2, 3], 98 | fontWeight: 'body', 99 | letterSpacing: 'headline', 100 | lineHeight: 'subheading' 101 | }, 102 | headline: { 103 | variant: 'text.heading', 104 | letterSpacing: 'headline', 105 | lineHeight: 'heading', 106 | fontSize: 4, 107 | mt: 3, 108 | mb: 3 109 | }, 110 | subheadline: { 111 | variant: 'text.heading', 112 | letterSpacing: 'headline', 113 | fontSize: 2, 114 | mt: 0, 115 | mb: 3 116 | }, 117 | eyebrow: { 118 | color: 'muted', 119 | fontSize: [3, 4], 120 | fontWeight: 'heading', 121 | letterSpacing: 'headline', 122 | lineHeight: 'subheading', 123 | textTransform: 'uppercase', 124 | mt: 0, 125 | mb: 2 126 | }, 127 | lead: { 128 | fontSize: [2, 3], 129 | my: [2, 3] 130 | }, 131 | caption: { 132 | color: 'muted', 133 | fontWeight: 'medium', 134 | letterSpacing: 'headline', 135 | lineHeight: 'caption' 136 | } 137 | }, 138 | alerts: { 139 | primary: { 140 | borderRadius: 'default', 141 | bg: 'orange', 142 | color: 'background', 143 | fontWeight: 'body' 144 | } 145 | }, 146 | badges: { 147 | pill: { 148 | borderRadius: 'circle', 149 | px: 3, 150 | py: 1, 151 | fontSize: 1 152 | }, 153 | outline: { 154 | variant: 'badges.pill', 155 | bg: 'transparent', 156 | border: '1px solid', 157 | borderColor: 'currentColor', 158 | fontWeight: 'body' 159 | } 160 | }, 161 | buttons: { 162 | primary: { 163 | cursor: 'pointer', 164 | bg: 'background', 165 | color: 'text', 166 | fontFamily: 'inherit', 167 | borderRadius: 'base', 168 | border: '3px solid black', 169 | ':hover,:focus': { 170 | boxShadow: 'card', 171 | bg: '#e8e0cc' 172 | } 173 | }, 174 | lg: { 175 | variant: 'buttons.primary', 176 | fontSize: 3, 177 | lineHeight: 'title', 178 | px: 4, 179 | py: 3 180 | }, 181 | outline: { 182 | variant: 'buttons.primary', 183 | bg: 'transparent', 184 | color: 'primary', 185 | border: '2px solid currentColor' 186 | }, 187 | outlineLg: { 188 | variant: 'buttons.primary', 189 | bg: 'transparent', 190 | color: 'primary', 191 | border: '2px solid currentColor', 192 | lineHeight: 'title', 193 | fontSize: 3, 194 | px: 4, 195 | py: 3 196 | }, 197 | cta: { 198 | variant: 'buttons.primary', 199 | fontSize: 2, 200 | backgroundImage: t => t.util.gx('orange', 'red') 201 | }, 202 | ctaLg: { 203 | variant: 'buttons.primary', 204 | lineHeight: 'title', 205 | fontSize: 3, 206 | px: 4, 207 | py: 3, 208 | backgroundImage: t => t.util.gx('orange', 'red') 209 | } 210 | }, 211 | cards: { 212 | primary: { 213 | bg: 'elevated', 214 | color: 'text', 215 | p: [3, 4], 216 | borderRadius: 'extra', 217 | boxShadow: 'card', 218 | overflow: 'hidden' 219 | }, 220 | sunken: { 221 | bg: 'sunken', 222 | p: [3, 4], 223 | borderRadius: 'extra' 224 | }, 225 | interactive: { 226 | variant: 'cards.primary', 227 | textDecoration: 'none', 228 | WebkitTapHighlightColor: 'transparent', 229 | transition: 'transform .125s ease-in-out, box-shadow .125s ease-in-out', 230 | ':hover,:focus': { 231 | transform: 'scale(1.0625)', 232 | boxShadow: 'elevated' 233 | } 234 | }, 235 | center: { 236 | variant: 'cards.primary', 237 | margin: '0', 238 | position: 'absolute', 239 | top: '50%', 240 | left: '50%', 241 | bg: 'background', 242 | transform: 'translate(-50%, -50%)' 243 | } 244 | }, 245 | forms: { 246 | input: { 247 | bg: 'elevated', 248 | color: 'text', 249 | fontFamily: 'inherit', 250 | borderRadius: 'base', 251 | border: '1px solid black', 252 | '::-webkit-input-placeholder': { color: 'placeholder' }, 253 | '::-moz-placeholder': { color: 'placeholder' }, 254 | ':-ms-input-placeholder': { color: 'placeholder' }, 255 | '&[type="search"]::-webkit-search-decoration': { display: 'none' } 256 | }, 257 | textarea: { variant: 'forms.input' }, 258 | select: { variant: 'forms.input' }, 259 | label: { 260 | color: 'text', 261 | display: 'flex', 262 | flexDirection: 'column', 263 | textAlign: 'left', 264 | lineHeight: 'caption', 265 | fontSize: 2 266 | }, 267 | labelHoriz: { 268 | color: 'text', 269 | display: 'flex', 270 | alignItems: 'center', 271 | textAlign: 'left', 272 | lineHeight: 'caption', 273 | fontSize: 2, 274 | svg: { color: 'muted' } 275 | }, 276 | slider: { 277 | color: 'primary' 278 | }, 279 | hidden: { 280 | position: 'absolute', 281 | height: '1px', 282 | width: '1px', 283 | overflow: 'hidden', 284 | clip: 'rect(1px, 1px, 1px, 1px)', 285 | whiteSpace: 'nowrap' 286 | } 287 | }, 288 | layout: { 289 | container: { 290 | maxWidth: ['layout', null, 'layout'], 291 | width: '100%', 292 | mx: 'auto', 293 | px: 3 294 | }, 295 | wide: { 296 | variant: 'layout.container', 297 | maxWidth: [null, null, null] 298 | }, 299 | copy: { 300 | variant: 'layout.container', 301 | maxWidth: ['copy', null, 'copyPlus'] 302 | }, 303 | narrow: { 304 | variant: 'layout.container', 305 | maxWidth: ['narrow', null, 'narrowPlus'] 306 | } 307 | }, 308 | styles: { 309 | root: { 310 | fontFamily: 'body', 311 | lineHeight: 'body', 312 | fontWeight: 'body', 313 | color: 'text', 314 | margin: 0, 315 | minHeight: '100vh', 316 | textRendering: 'optimizeLegibility', 317 | WebkitFontSmoothing: 'antialiased', 318 | MozOsxFontSmoothing: 'grayscale' 319 | }, 320 | h1: { 321 | variant: 'text.heading', 322 | fontSize: 5, 323 | fontWeight: '800' 324 | }, 325 | h2: { 326 | variant: 'text.heading', 327 | fontSize: 4 328 | }, 329 | h3: { 330 | variant: 'text.heading', 331 | fontSize: 3 332 | }, 333 | h4: { 334 | variant: 'text.heading', 335 | fontSize: 2 336 | }, 337 | h5: { 338 | variant: 'text.heading', 339 | fontSize: 1 340 | }, 341 | h6: { 342 | variant: 'text.heading', 343 | fontSize: 0 344 | }, 345 | p: { 346 | color: 'text', 347 | fontWeight: 'body', 348 | lineHeight: 'body', 349 | my: 3 350 | }, 351 | img: { 352 | maxWidth: '100%' 353 | }, 354 | hr: { 355 | border: 0, 356 | borderBottom: '1px solid', 357 | borderColor: 'border' 358 | }, 359 | a: { 360 | color: 'white', 361 | textDecoration: 'underline' 362 | }, 363 | pre: { 364 | fontFamily: 'monospace', 365 | fontSize: 1, 366 | p: 3, 367 | color: 'text', 368 | bg: 'sunken', 369 | overflow: 'auto', 370 | borderRadius: 'default', 371 | code: { 372 | color: 'inherit', 373 | mx: 0, 374 | px: 0 375 | } 376 | }, 377 | code: { 378 | fontFamily: 'monospace', 379 | fontSize: 'inherit', 380 | color: 'purple', 381 | bg: 'sunken', 382 | borderRadius: 'small', 383 | mx: 1, 384 | px: 1 385 | }, 386 | 'p > code, li > code': { 387 | color: 'purple', 388 | fontSize: '0.875em' 389 | }, 390 | 'p > a > code, li > a > code': { 391 | color: 'red', 392 | fontSize: '0.875em' 393 | }, 394 | li: { 395 | my: 2 396 | }, 397 | table: { 398 | width: '100%', 399 | my: 4, 400 | borderCollapse: 'separate', 401 | borderSpacing: 0, 402 | 'th,td': { 403 | textAlign: 'left', 404 | py: '4px', 405 | pr: '4px', 406 | pl: 0, 407 | borderColor: 'border', 408 | borderBottomStyle: 'solid' 409 | } 410 | }, 411 | th: { 412 | verticalAlign: 'bottom', 413 | borderBottomWidth: '2px' 414 | }, 415 | td: { 416 | verticalAlign: 'top', 417 | borderBottomWidth: '1px' 418 | } 419 | } 420 | } 421 | 422 | export default withModalTheme(theme) 423 | -------------------------------------------------------------------------------- /lib/use-has-mounted.js: -------------------------------------------------------------------------------- 1 | // Full credit to https://joshwcomeau.com/snippets/react-hooks/use-has-mounted 2 | import React from 'react' 3 | 4 | function useHasMounted() { 5 | const [hasMounted, setHasMounted] = React.useState(false) 6 | React.useEffect(() => { 7 | setHasMounted(true) 8 | }, []) 9 | return hasMounted 10 | } 11 | 12 | export default useHasMounted -------------------------------------------------------------------------------- /lib/use-prefers-motion.js: -------------------------------------------------------------------------------- 1 | // Inspired by https://joshwcomeau.com/snippets/react-hooks/use-prefers-reduced-motion 2 | import React from 'react' 3 | 4 | const QUERY = '(prefers-reduced-motion: no-preference)' 5 | const isRenderingOnServer = typeof window === 'undefined' 6 | 7 | const getInitialState = () => { 8 | // For our initial server render, we won't know if the user 9 | // prefers reduced motion, but it doesn't matter. This value 10 | // will be overwritten on the client, before any animations 11 | // occur. 12 | return isRenderingOnServer ? false : window.matchMedia(QUERY).matches 13 | } 14 | 15 | function usePrefersMotion() { 16 | const [prefersMotion, setPrefersMotion] = React.useState(getInitialState) 17 | React.useEffect(() => { 18 | const mediaQueryList = window.matchMedia(QUERY) 19 | const listener = event => { 20 | setPrefersMotion(!event.matches) 21 | } 22 | mediaQueryList.addListener(listener) 23 | return () => { 24 | mediaQueryList.removeListener(listener) 25 | } 26 | }, []) 27 | return prefersMotion 28 | } 29 | 30 | export default usePrefersMotion -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const withMDX = require('@next/mdx')({ extension: /\.mdx?$/ }) 2 | module.exports = withMDX({ 3 | pageExtensions: ['js', 'jsx', 'mdx'], 4 | images: { 5 | domains: [ 6 | 'cloud-16uu33t6r-hack-club-bot.vercel.app', 7 | 'upload.wikimedia.org', 8 | '261.com', 9 | 'cloud-3q64c1iul-hack-club-bot.vercel.app' 10 | ] 11 | }, 12 | async redirects() { 13 | return [ 14 | { 15 | source: '/', 16 | destination: 'https://www.youtube.com/watch?v=2BID8_pGuqA', 17 | permanent: true, 18 | }, 19 | ] 20 | }, 21 | }) 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "all-aboard", 3 | "version": "0.0.1", 4 | "authors": [ 5 | "Sam Poder ", 6 | "Max Wofford " 7 | ], 8 | "license": "MIT", 9 | "private": true, 10 | "scripts": { 11 | "dev": "next", 12 | "build": "next build", 13 | "start": "next start" 14 | }, 15 | "dependencies": { 16 | "@hackclub/meta": "^1.1.32", 17 | "@hackclub/theme": "^0.3.3", 18 | "@mattjennings/react-modal": "^1.0.2", 19 | "@mattjennings/react-modal-stack": "^1.0.2", 20 | "@mdx-js/loader": "^1.6.22", 21 | "@next/mdx": "^10.0.4", 22 | "@octokit/core": "^3.5.1", 23 | "airtable-plus": "^1.0.4", 24 | "chrome-aws-lambda": "^10.1.0", 25 | "framer-motion": "3.10.6", 26 | "hls.js": "^1.0.7", 27 | "marked": "^2.1.3", 28 | "next": "^12.1.4", 29 | "palx": "^1.0.3", 30 | "puppeteer-core": "^10.4.0", 31 | "react": "^17.0.2", 32 | "react-dom": "^17.0.2", 33 | "react-tilt": "^0.1.4", 34 | "theme-ui": "^0.10.0", 35 | "twemoji": "^13.1.0", 36 | "uuid": "^8.3.2" 37 | }, 38 | "devDependencies": { 39 | "typescript": "^4.3.5", 40 | "@types/react": "^17.0.39" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import NextApp from 'next/app' 3 | import theme from '../lib/theme' 4 | import { ThemeProvider } from 'theme-ui' 5 | import '../styles/globals.css' 6 | import Script from 'next/script' 7 | 8 | export default class App extends NextApp { 9 | render() { 10 | const { Component, pageProps } = this.props 11 | return ( 12 | 13 |