diff --git a/.gitignore b/.gitignore index 8c3f20f9..c79f3933 100755 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ .next node_modules .DS_Store -public +public/home diff --git a/components/fade-in.js b/components/fade-in.js new file mode 100644 index 00000000..b44aa8c5 --- /dev/null +++ b/components/fade-in.js @@ -0,0 +1,26 @@ +import React from 'react' +import { Box } from 'theme-ui' +import styled from '@emotion/styled' +import { keyframes } from '@emotion/core' + +const fadeIn = keyframes({ from: { opacity: 0 }, to: { opacity: 1 } }) + +const Wrapper = styled(Box)` + @media (prefers-reduced-motion: no-preference) { + animation-name: ${fadeIn}; + animation-fill-mode: backwards; + } +` + +const FadeIn = ({ duration = 300, delay = 0, ...props }) => ( + +) + +export default FadeIn diff --git a/components/ship/why.mdx b/components/ship/why.mdx new file mode 100644 index 00000000..ff924ecb --- /dev/null +++ b/components/ship/why.mdx @@ -0,0 +1,17 @@ +import { Card } from 'theme-ui' + + + +## Your first ship your first day. + +Students in many traditional computer science classes are lucky to make a single project. At Hack Clubs, every member makes & ships their first website their very first meeting. + + + + + +## Keeping your eyes on the prize. + +Instead of learning programming concepts in isolation, learning by shipping means you focus on what you need to build real projects. It’s more fun & leads to better learning. + + diff --git a/components/slide-up.js b/components/slide-up.js new file mode 100644 index 00000000..7cd51c0d --- /dev/null +++ b/components/slide-up.js @@ -0,0 +1,29 @@ +import React from 'react' +import { Box } from 'theme-ui' +import styled from '@emotion/styled' +import { keyframes } from '@emotion/core' + +const slideUp = keyframes({ + from: { transform: 'translateY(100%)', opacity: 0 }, + to: { transform: 'translateY(0)', opacity: 1 } +}) + +const Wrapper = styled(Box)` + @media (prefers-reduced-motion: no-preference) { + animation-name: ${slideUp}; + animation-fill-mode: backwards; + } +` + +const SlideUp = ({ duration = 300, delay = 0, ...props }) => ( + +) + +export default SlideUp diff --git a/components/stat.js b/components/stat.js new file mode 100644 index 00000000..60f59289 --- /dev/null +++ b/components/stat.js @@ -0,0 +1,92 @@ +import { Flex, Text } from 'theme-ui' +import { isEmpty } from 'lodash' + +const Stat = ({ + value, + label, + unit = '', + color = 'text', + of, + center = false, + reversed = false, + half = false, + lg = false, + ...props +}) => ( + + + + {!isEmpty(unit) && ( + + )} + {!isEmpty(of) && ( + + )} + + {!isEmpty(label) && ( + + )} + +) + +export default Stat diff --git a/lib/dates.js b/lib/dates.js index a7efd66c..0aed21e1 100644 --- a/lib/dates.js +++ b/lib/dates.js @@ -10,8 +10,8 @@ export const tinyDt = d => export const timeSince = ( previous, absoluteDuration = false, - current = new Date().toISOString(), - longForm = false + longForm = false, + current = new Date().toISOString() ) => { const msPerMinute = 60 * 1000 const msPerHour = msPerMinute * 60 @@ -22,7 +22,7 @@ export const timeSince = ( const future = new Date(previous) - new Date(current) const past = new Date(current) - new Date(previous) - const elapsed = [future, past].sort()[0] + const elapsed = [future, past].sort()[1] let humanizedTime if (elapsed < msPerMinute) { diff --git a/lib/theme.js b/lib/theme.js index d894174f..8f1658b2 100644 --- a/lib/theme.js +++ b/lib/theme.js @@ -104,5 +104,23 @@ theme.forms.labelCheckbox = { theme.layout.copy.maxWidth = [null, null, 'copyPlus'] theme.text.lead = {} +theme.text.eyebrow = { + color: 'muted', + fontSize: [3, 4], + fontWeight: 'heading', + letterSpacing: 'headline', + lineHeight: 'subheading', + textTransform: 'uppercase', + mt: 0, + mb: 2 +} + +theme.text.titleUltra = { + ...theme.text.title, + fontSize: [5, 6, 7], + lineHeight: 0.875 +} + +theme.text.subtitle.mt = 3 export default theme diff --git a/pages/ship.js b/pages/ship.js new file mode 100644 index 00000000..fccd5a81 --- /dev/null +++ b/pages/ship.js @@ -0,0 +1,296 @@ +import { + Avatar, + Badge, + Box, + Button, + Container, + Card, + Grid, + Heading, + Image, + Text, + Flex +} from 'theme-ui' +import NextLink from 'next/link' +import Head from 'next/head' +import Meta from '@hackclub/meta' +import Nav from '../components/nav' +import SlideUp from '../components/slide-up' +import Why from '../components/ship/why.mdx' +import Icon from '../components/icon' +import Stat from '../components/stat' +import Footer from '../components/footer' +import { timeSince } from '../lib/dates' +import { orderBy, filter, take, map, uniq, reverse } from 'lodash' +import { keyframes } from '@emotion/core' + +const ShipBadge = props => ( + +) + +const Ship = ({ timestamp, message, url, img, username, avatar }) => ( + + {img && ( + + )} + + + {message} + + + + {avatar && ( + + )} + + + {username} + + + {timeSince(new Date(timestamp), false, true)} + + + + {url && !url?.includes('hackclub.slack.com') && ( + + )} + + + +) + +const waves = keyframes({ + '0%': { backgroundPositionX: '0' }, + '100%': { backgroundPositionX: '-100%' } +}) + +export default ({ ships, stats }) => ( + <> + +