├── .babelrc ├── src ├── components │ ├── modal │ │ ├── index.ts │ │ ├── style.ts │ │ └── Modal.tsx │ ├── seo │ │ ├── index.ts │ │ ├── useSiteMetadata.ts │ │ └── Seo.tsx │ ├── avatar │ │ ├── index.ts │ │ ├── style.ts │ │ └── Avatar.tsx │ ├── footer │ │ ├── index.ts │ │ ├── style.ts │ │ └── Footer.tsx │ ├── header │ │ ├── index.ts │ │ ├── style.ts │ │ └── Header.tsx │ ├── layout │ │ ├── index.ts │ │ ├── style.ts │ │ └── Layout.tsx │ ├── animated │ │ ├── index.ts │ │ ├── components │ │ │ ├── animated-word │ │ │ │ ├── index.ts │ │ │ │ ├── components │ │ │ │ │ ├── animated-letter │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── style.ts │ │ │ │ │ │ └── AnimatedLetter.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── style.ts │ │ │ │ └── AnimatedWord.tsx │ │ │ └── index.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ └── useActiveAnimation.ts │ │ ├── Animated.tsx │ │ └── style.ts │ ├── blog-post │ │ ├── index.ts │ │ ├── style.ts │ │ └── BlogPost.tsx │ ├── link-list │ │ ├── index.ts │ │ ├── style.ts │ │ └── LinkList.tsx │ ├── navigation │ │ ├── index.ts │ │ ├── main │ │ │ ├── index.ts │ │ │ ├── style.ts │ │ │ └── MainNavigation.tsx │ │ ├── mobile │ │ │ ├── index.ts │ │ │ ├── style.ts │ │ │ └── MobileNavigation.tsx │ │ ├── content.ts │ │ ├── style.ts │ │ ├── Navigation.tsx │ │ └── Navigation_ReduxExample.tsx │ └── index.ts ├── containers │ ├── about │ │ ├── index.ts │ │ ├── content.ts │ │ └── About.tsx │ ├── welcome │ │ ├── index.ts │ │ ├── content.ts │ │ └── Welcome.tsx │ ├── blog-post-list │ │ ├── index.ts │ │ ├── style.ts │ │ ├── BlogPostList.tsx │ │ └── useBlogPosts.ts │ └── index.ts ├── images │ ├── favicon.png │ └── website-icon.png ├── utils │ ├── isMobileView.ts │ ├── isTabletView.ts │ ├── isDesktopView.ts │ ├── compose.ts │ └── index.ts ├── state │ ├── utils │ │ ├── index.ts │ │ └── componentProps.ts │ ├── actions.ts │ ├── index.ts │ ├── reducer.ts │ ├── props.ts │ └── store.ts ├── styles │ ├── theme.ts │ ├── color.ts │ ├── palette.ts │ ├── typography.ts │ └── muiTheme.ts └── pages │ ├── 404.tsx │ ├── about.tsx │ ├── index.tsx │ └── blog.tsx ├── .env.production ├── .env.development ├── .prettierrc ├── gatsby-node.js ├── gatsby-ssr.js ├── gatsby-browser.js ├── LICENSE ├── .gitignore ├── package.json ├── gatsby-config.js ├── bin └── setup.js └── README.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-gatsby"] 3 | } -------------------------------------------------------------------------------- /src/components/modal/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Modal'; 2 | -------------------------------------------------------------------------------- /src/components/seo/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Seo'; 2 | -------------------------------------------------------------------------------- /src/containers/about/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './About'; 2 | -------------------------------------------------------------------------------- /src/components/avatar/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Avatar'; 2 | -------------------------------------------------------------------------------- /src/components/footer/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Footer'; 2 | -------------------------------------------------------------------------------- /src/components/header/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Header'; 2 | -------------------------------------------------------------------------------- /src/components/layout/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Layout'; 2 | -------------------------------------------------------------------------------- /src/containers/welcome/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Welcome'; 2 | -------------------------------------------------------------------------------- /src/components/animated/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Animated'; 2 | -------------------------------------------------------------------------------- /src/components/blog-post/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BlogPost'; 2 | -------------------------------------------------------------------------------- /src/components/link-list/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './LinkList'; 2 | -------------------------------------------------------------------------------- /src/components/navigation/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Navigation'; 2 | -------------------------------------------------------------------------------- /src/components/navigation/main/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './MainNavigation'; 2 | -------------------------------------------------------------------------------- /src/containers/blog-post-list/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BlogPostList'; 2 | -------------------------------------------------------------------------------- /src/components/navigation/mobile/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './MobileNavigation'; 2 | -------------------------------------------------------------------------------- /src/components/animated/components/animated-word/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './AnimatedWord'; 2 | -------------------------------------------------------------------------------- /src/components/animated/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AnimatedWord } from './animated-word'; -------------------------------------------------------------------------------- /src/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chronisp/gatsby-starter/HEAD/src/images/favicon.png -------------------------------------------------------------------------------- /src/images/website-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chronisp/gatsby-starter/HEAD/src/images/website-icon.png -------------------------------------------------------------------------------- /src/utils/isMobileView.ts: -------------------------------------------------------------------------------- 1 | const isMobileView = width => width === 'xs'; 2 | 3 | export default isMobileView; 4 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | CONTENTFUL_SPACE_ID=XXXXXXXXXX 2 | CONTENTFUL_ACCESS_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXX 3 | CONTENTFUL_ENV=master 4 | -------------------------------------------------------------------------------- /src/components/animated/components/animated-word/components/animated-letter/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './AnimatedLetter'; 2 | -------------------------------------------------------------------------------- /src/utils/isTabletView.ts: -------------------------------------------------------------------------------- 1 | const isTabletView = width => width === 'md' || width === 'sm'; 2 | 3 | export default isTabletView; 4 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | CONTENTFUL_SPACE_ID=XXXXXXXXXX 2 | CONTENTFUL_ACCESS_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXX 3 | CONTENTFUL_ENV=development 4 | -------------------------------------------------------------------------------- /src/components/animated/components/animated-word/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AnimatedLetter } from './animated-letter'; 2 | -------------------------------------------------------------------------------- /src/components/animated/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import useActiveAnimation from './useActiveAnimation'; 2 | 3 | export { useActiveAnimation }; 4 | -------------------------------------------------------------------------------- /src/utils/isDesktopView.ts: -------------------------------------------------------------------------------- 1 | const isDesktopView = width => width === 'lg' || width === 'xl'; 2 | 3 | export default isDesktopView; 4 | -------------------------------------------------------------------------------- /src/state/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { stateProp, actionProp, connectProps } from './componentProps'; 2 | 3 | export { stateProp, actionProp, connectProps }; 4 | -------------------------------------------------------------------------------- /src/utils/compose.ts: -------------------------------------------------------------------------------- 1 | const compose = (...funcs) => a => 2 | funcs.reverse().reduce((result, func) => func(result), a); 3 | 4 | export default compose; 5 | -------------------------------------------------------------------------------- /src/state/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'redux-actions'; 2 | 3 | const toggleMenu = createAction('TOGGLE_MENU'); 4 | 5 | export { toggleMenu }; 6 | -------------------------------------------------------------------------------- /src/state/index.ts: -------------------------------------------------------------------------------- 1 | import { connectProps } from './utils'; 2 | import { open, toggleMenu } from './props'; 3 | 4 | export { connectProps, open, toggleMenu }; 5 | -------------------------------------------------------------------------------- /src/containers/index.ts: -------------------------------------------------------------------------------- 1 | export { default as About } from './about'; 2 | export { default as BlogPostList } from './blog-post-list'; 3 | export { default as Welcome } from './welcome'; 4 | -------------------------------------------------------------------------------- /src/components/blog-post/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ typography }) => ({ 2 | container: { 3 | padding: `${typography.pxToRem(16)} 0`, 4 | }, 5 | }); 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /src/containers/blog-post-list/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ typography }) => ({ 2 | container: { 3 | padding: `${typography.pxToRem(32)} 0`, 4 | }, 5 | }); 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /src/components/navigation/content.ts: -------------------------------------------------------------------------------- 1 | const options = [ 2 | { to: '/', text: 'Home' }, 3 | { to: '/blog/', text: 'Blog' }, 4 | { to: '/about/', text: 'About' }, 5 | ]; 6 | 7 | export default options; 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "crlf", 3 | "semi": true, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "es5", 7 | "jsxSingleQuote": true, 8 | "jsxBracketSameLine": true 9 | } 10 | -------------------------------------------------------------------------------- /src/components/animated/components/animated-word/components/animated-letter/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ palette }) => ({ 2 | letter: { 3 | display: 'inline-block', 4 | }, 5 | }); 6 | 7 | export default styles; 8 | -------------------------------------------------------------------------------- /src/components/animated/components/animated-word/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ palette }) => ({ 2 | word: { 3 | whiteSpace: 'nowrap', 4 | display: 'inline-block', 5 | }, 6 | }); 7 | 8 | export default styles; 9 | -------------------------------------------------------------------------------- /src/styles/theme.ts: -------------------------------------------------------------------------------- 1 | import muiTheme from './muiTheme'; 2 | 3 | const theme = { 4 | ...muiTheme, 5 | app: { 6 | maxWidth: muiTheme.typography.pxToRem(960), 7 | }, 8 | }; 9 | 10 | export default theme; 11 | -------------------------------------------------------------------------------- /src/containers/welcome/content.ts: -------------------------------------------------------------------------------- 1 | const content = { 2 | tagline: 'Kick off your project using', 3 | title: 'Gatsby Starter.', 4 | subtitle: 'Featuring Redux, Material UI, Contentful & other stuff!', 5 | }; 6 | 7 | export default content; 8 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import compose from './compose'; 2 | import isDesktopView from './isDesktopView'; 3 | import isTabletView from './isTabletView'; 4 | import isMobileView from './isMobileView'; 5 | 6 | export { compose, isDesktopView, isTabletView, isMobileView }; 7 | -------------------------------------------------------------------------------- /src/components/footer/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ app, typography }) => ({ 2 | container: { 3 | display: 'flex', 4 | margin: `${typography.pxToRem(8)} auto 0`, 5 | maxWidth: app.maxWidth, 6 | width: '100%', 7 | }, 8 | }); 9 | 10 | export default styles; 11 | -------------------------------------------------------------------------------- /src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Layout, SEO } from 'components'; 4 | 5 | const NotFoundPage = () => ( 6 | 7 | 8 |

NOT FOUND

9 |
10 | ); 11 | 12 | export default NotFoundPage; 13 | -------------------------------------------------------------------------------- /src/pages/about.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Layout, SEO } from 'components'; 4 | import { About } from 'containers'; 5 | 6 | const AboutPage = () => ( 7 | 8 | 9 | 10 | 11 | ); 12 | 13 | export default AboutPage; 14 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Layout, SEO } from 'components'; 4 | import { Welcome } from 'containers'; 5 | 6 | const IndexPage = () => ( 7 | 8 | 9 | 10 | 11 | ); 12 | 13 | export default IndexPage; 14 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | exports.onCreateWebpackConfig = ({ getConfig, stage }) => { 2 | const config = getConfig() 3 | if (stage.startsWith('develop') && config.resolve) { 4 | config.resolve.alias = { 5 | ...config.resolve.alias, 6 | 'react-dom': '@hot-loader/react-dom' 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/blog.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Layout, SEO } from 'components'; 4 | import { BlogPostList } from 'containers'; 5 | 6 | const BlogPage = () => ( 7 | 8 | 9 | 10 | 11 | ); 12 | 13 | export default BlogPage; 14 | -------------------------------------------------------------------------------- /src/state/reducer.ts: -------------------------------------------------------------------------------- 1 | import { handleActions } from 'redux-actions'; 2 | 3 | import { toggleMenu } from './actions'; 4 | 5 | const reducer = handleActions( 6 | { 7 | [toggleMenu]: (state, { payload }) => ({ 8 | open: !state.open, 9 | }), 10 | }, 11 | { 12 | open: false, 13 | } // initial state 14 | ); 15 | 16 | export default reducer; 17 | -------------------------------------------------------------------------------- /src/components/navigation/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ palette, typography }) => ({ 2 | container: { 3 | display: 'flex', 4 | flexDirection: 'row', 5 | justifyContent: 'space-between', 6 | listStyle: 'none', 7 | margin: 0, 8 | padding: 0, 9 | position: 'relative', 10 | zIndex: 10, 11 | }, 12 | }); 13 | 14 | export default styles; 15 | -------------------------------------------------------------------------------- /src/state/props.ts: -------------------------------------------------------------------------------- 1 | import { stateProp, actionProp } from './utils'; 2 | 3 | import { toggleMenu as toggleMenuAction } from './actions'; 4 | 5 | const open = stateProp(state => ({ 6 | open: state.open, 7 | })); 8 | 9 | const toggleMenu = actionProp(dispatch => ({ 10 | toggleMenu: () => dispatch(toggleMenuAction()), 11 | })); 12 | 13 | export { open, toggleMenu }; 14 | -------------------------------------------------------------------------------- /gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CssBaseline from '@material-ui/core/CssBaseline'; 3 | import { ThemeProvider } from '@material-ui/styles'; 4 | import theme from 'styles/theme'; 5 | 6 | export const wrapRootElement = ({ element }) => { 7 | return ( 8 | 9 | 10 | {element} 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CssBaseline from '@material-ui/core/CssBaseline'; 3 | import { ThemeProvider } from '@material-ui/styles'; 4 | import theme from 'styles/theme'; 5 | 6 | export const wrapRootElement = ({ element }) => { 7 | return ( 8 | 9 | 10 | {element} 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/styles/color.ts: -------------------------------------------------------------------------------- 1 | const fade = (hex, opacity) => { 2 | hex = hex.replace('#', ''); 3 | const r = parseInt(hex.substring(0, 2), 16); 4 | const g = parseInt(hex.substring(2, 4), 16); 5 | const b = parseInt(hex.substring(4, 6), 16); 6 | 7 | const result = `rgba(${r},${g},${b},${opacity ? opacity : 1})`; 8 | return result; 9 | }; 10 | 11 | const color = { 12 | fade, 13 | }; 14 | 15 | export default color; 16 | -------------------------------------------------------------------------------- /src/components/avatar/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ typography }) => ({ 2 | container: { 3 | borderRadius: '50%', 4 | overflow: 'hidden', 5 | border: `${typography.pxToRem(2)} solid white`, 6 | boxShadow: `0 0 0 ${typography.pxToRem(2)} rgba(0,0,0,.1)`, 7 | height: typography.pxToRem(36), 8 | width: typography.pxToRem(36), 9 | }, 10 | avatar: { 11 | width: '100%', 12 | }, 13 | }); 14 | 15 | export default styles; 16 | -------------------------------------------------------------------------------- /src/components/seo/useSiteMetadata.ts: -------------------------------------------------------------------------------- 1 | import { useStaticQuery, graphql } from 'gatsby'; 2 | 3 | export const useSiteMetadata = () => { 4 | const { site } = useStaticQuery( 5 | graphql` 6 | query { 7 | site { 8 | siteMetadata { 9 | title 10 | description 11 | author 12 | } 13 | } 14 | } 15 | ` 16 | ); 17 | return site.siteMetadata; 18 | }; 19 | 20 | export default useSiteMetadata; 21 | -------------------------------------------------------------------------------- /src/state/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly'; 3 | 4 | import reducer from './reducer'; 5 | 6 | const composeEnhancers = composeWithDevTools({ name: 'Gatsby Starter' }); 7 | 8 | const store = preloadedState => { 9 | return createStore( 10 | reducer, 11 | preloadedState, 12 | composeEnhancers(applyMiddleware()) 13 | ); 14 | }; 15 | 16 | export default store; 17 | -------------------------------------------------------------------------------- /src/styles/palette.ts: -------------------------------------------------------------------------------- 1 | const palette = { 2 | background: { 3 | default: '#fff', 4 | }, 5 | text: { 6 | primary: '#17252A', 7 | secondary: '#17252A', 8 | }, 9 | primary: { 10 | light: '#17252A', 11 | main: '#17252A', 12 | dark: '#17252A', 13 | contrastText: '#fff', 14 | }, 15 | secondary: { 16 | light: '#6affff', 17 | main: '#3AAFA9', 18 | dark: '#00a7bc', 19 | contrastText: '#fff', 20 | }, 21 | }; 22 | 23 | export default palette; 24 | -------------------------------------------------------------------------------- /src/components/footer/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | 4 | import styles from './style'; 5 | 6 | interface Props { 7 | classes: any; 8 | }; 9 | 10 | const Footer = ({ classes }: Props) => ( 11 | 16 | ); 17 | 18 | export default withStyles(styles)(Footer); 19 | -------------------------------------------------------------------------------- /src/components/avatar/Avatar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import avatarIcon from 'images/website-icon.png'; 3 | import { withStyles } from '@material-ui/styles'; 4 | 5 | import styles from './style'; 6 | 7 | interface Props { 8 | classes: any; 9 | }; 10 | 11 | const Avatar = ({ classes }: Props) => ( 12 |
13 | avatar 14 |
15 | ); 16 | 17 | export default withStyles(styles)(Avatar); 18 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Animated } from './animated'; 2 | export { default as Avatar } from './avatar'; 3 | export { default as BlogPost } from './blog-post'; 4 | export { default as Footer } from './footer'; 5 | export { default as Header } from './header'; 6 | export { default as Layout } from './layout'; 7 | export { default as LinkList } from './link-list'; 8 | export { default as Modal } from './modal'; 9 | export { default as Navigation } from './navigation'; 10 | export { default as SEO } from './seo'; 11 | -------------------------------------------------------------------------------- /src/components/animated/hooks/useActiveAnimation.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | const useActiveAnimation = timeout => { 4 | const [activeAnimation, setActiveAnimation] = useState(false); 5 | 6 | useEffect(() => { 7 | let timer = setTimeout(() => { 8 | setActiveAnimation(false); 9 | }, timeout); 10 | 11 | return () => clearTimeout(timer); 12 | }, [activeAnimation, timeout]); 13 | 14 | return [activeAnimation, setActiveAnimation]; 15 | }; 16 | 17 | export default useActiveAnimation; 18 | -------------------------------------------------------------------------------- /src/containers/about/content.ts: -------------------------------------------------------------------------------- 1 | import { FaGithub } from 'react-icons/fa'; 2 | 3 | const content = { 4 | title: 'About.', 5 | subtitle: 'Find out more on Github', 6 | links: [ 7 | { 8 | to: 'https://github.com/chronisp', 9 | text: 'chronisp', 10 | Icon: FaGithub, 11 | newTab: true, 12 | }, 13 | { 14 | to: 'https://github.com/chronisp/gatsby-starter', 15 | text: 'Gatsby Starter', 16 | Icon: FaGithub, 17 | newTab: true, 18 | }, 19 | ], 20 | }; 21 | 22 | export default content; 23 | -------------------------------------------------------------------------------- /src/components/header/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ app, typography }) => ({ 2 | container: { 3 | display: 'flex', 4 | justifyContent: 'space-between', 5 | alignItems: 'center', 6 | margin: `${typography.pxToRem(8)} auto 0`, 7 | maxWidth: app.maxWidth, 8 | width: '100%', 9 | }, 10 | logo: { 11 | border: 'none', 12 | float: 'left', 13 | transition: 'all .3s', 14 | textDecoration: 'none', 15 | }, 16 | navigation: { 17 | float: 'right', 18 | position: 'relative', 19 | }, 20 | }); 21 | 22 | export default styles; 23 | -------------------------------------------------------------------------------- /src/components/link-list/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ palette, typography }) => ({ 2 | container: { 3 | display: 'flex', 4 | listStyle: 'none', 5 | padding: 0, 6 | position: 'relative', 7 | flexWrap: 'wrap', 8 | }, 9 | linkText: { 10 | paddingLeft: typography.pxToRem(8), 11 | }, 12 | link: { 13 | border: 'none', 14 | display: 'flex', 15 | padding: `${typography.pxToRem(8)} ${typography.pxToRem(8)}`, 16 | textDecoration: 'none', 17 | color: palette.text.secondary, 18 | }, 19 | }); 20 | 21 | export default styles; 22 | -------------------------------------------------------------------------------- /src/components/navigation/main/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ palette, typography }) => ({ 2 | menuContainer: { 3 | borderRadius: typography.pxToRem(4), 4 | display: 'inline-block', 5 | }, 6 | navLink: { 7 | border: 'none', 8 | display: 'inline-block', 9 | padding: `${typography.pxToRem(8)} ${typography.pxToRem(8)}`, 10 | textDecoration: 'none', 11 | color: palette.text.secondary, 12 | }, 13 | navLinkActive: { 14 | borderBottom: `${typography.pxToRem(2)} solid ${palette.primary.main}`, 15 | }, 16 | }); 17 | 18 | export default styles; 19 | -------------------------------------------------------------------------------- /src/components/modal/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ typography }) => ({ 2 | scrollContainer: { 3 | minHeight: '100%', 4 | }, 5 | fullScreenContainer: { 6 | width: '100%', 7 | height: '100%', 8 | position: 'absolute', 9 | top: 0, 10 | left: 0, 11 | }, 12 | centeredContainer: { 13 | width: '80%', 14 | height: '90%', 15 | position: 'absolute', 16 | top: '5%', 17 | left: '10%', 18 | }, 19 | content: { 20 | backgroundColor: '#fafafa', 21 | width: '100%', 22 | height: '100%', 23 | }, 24 | }); 25 | 26 | export default styles; 27 | -------------------------------------------------------------------------------- /src/components/navigation/mobile/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ palette, typography }) => ({ 2 | menuIcon: { 3 | cursor: 'pointer', 4 | }, 5 | menuContainer: { 6 | position: 'absolute', 7 | top: typography.pxToRem(16), 8 | right: typography.pxToRem(-8), 9 | textAlign: 'right', 10 | padding: `${typography.pxToRem(16)} ${typography.pxToRem(8)}`, 11 | }, 12 | navLink: { 13 | border: 'none', 14 | display: 'inline-block', 15 | padding: `${typography.pxToRem(8)} 0`, 16 | textDecoration: 'none', 17 | color: palette.text.secondary, 18 | }, 19 | navLinkActive: { 20 | fontWeight: 500, 21 | }, 22 | }); 23 | 24 | export default styles; 25 | -------------------------------------------------------------------------------- /src/components/header/Header.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'gatsby'; 2 | import React from 'react'; 3 | import { withStyles } from '@material-ui/styles'; 4 | 5 | import { Avatar, Navigation, Animated } from 'components'; 6 | 7 | import styles from './style'; 8 | interface Props { 9 | classes: any; 10 | }; 11 | 12 | const Header = ({ classes }: Props) => ( 13 |
14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 |
23 | ); 24 | 25 | export default withStyles(styles)(Header); 26 | -------------------------------------------------------------------------------- /src/containers/about/About.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import Fade from '@material-ui/core/Fade'; 4 | 5 | import { Animated, LinkList } from 'components'; 6 | 7 | import content from './content'; 8 | 9 | const { title, subtitle, links } = content; 10 | 11 | const About = () => ( 12 | <> 13 | 14 | 15 | {title} 16 | 17 | 18 | 19 | {subtitle} 20 | 21 | 22 | 23 | ); 24 | 25 | export default About; 26 | -------------------------------------------------------------------------------- /src/containers/blog-post-list/BlogPostList.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | 4 | import { BlogPost } from 'components'; 5 | 6 | import useBlogPosts from './useBlogPosts'; 7 | import styles from './style'; 8 | 9 | interface Props { 10 | classes: any; 11 | }; 12 | 13 | const BlogPostList = ({ classes }: Props) => { 14 | const { allContentfulBlogPost } = useBlogPosts(); 15 | 16 | return ( 17 |
18 | {allContentfulBlogPost.edges.map(({ node }, index) => ( 19 | 20 | ))} 21 |
22 | ); 23 | }; 24 | 25 | export default withStyles(styles)(BlogPostList); 26 | -------------------------------------------------------------------------------- /src/styles/typography.ts: -------------------------------------------------------------------------------- 1 | const typography = typography => ({ 2 | fontFamily: 'Roboto', 3 | fontSize: 16, 4 | h1: { 5 | fontSize: typography.pxToRem(70), 6 | lineHeight: 1.1, 7 | fontWeight: 800, 8 | }, 9 | h2: { 10 | fontSize: typography.pxToRem(56), 11 | lineHeight: 1.1, 12 | fontWeight: 500, 13 | }, 14 | h3: { 15 | fontSize: typography.pxToRem(28), 16 | lineHeight: 1.1, 17 | fontWeight: 300, 18 | }, 19 | body1: { 20 | fontSize: typography.pxToRem(16), 21 | lineHeight: 1.1, 22 | fontWeight: 300, 23 | }, 24 | body2: { 25 | fontSize: typography.pxToRem(16), 26 | lineHeight: 1.1, 27 | fontWeight: 300, 28 | }, 29 | }); 30 | 31 | export default typography; 32 | -------------------------------------------------------------------------------- /src/components/navigation/main/MainNavigation.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'gatsby-link'; 3 | import { withStyles } from '@material-ui/styles'; 4 | 5 | import styles from './style'; 6 | 7 | interface Props { 8 | classes: any; 9 | options: Array; 10 | }; 11 | 12 | const MainNavigation = ({ classes, options }: Props) => ( 13 | 25 | ); 26 | 27 | export default withStyles(styles)(MainNavigation); 28 | -------------------------------------------------------------------------------- /src/containers/welcome/Welcome.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import Fade from '@material-ui/core/Fade'; 4 | 5 | import { Animated } from 'components'; 6 | 7 | import content from './content'; 8 | 9 | const { tagline, title, subtitle } = content; 10 | 11 | const Welcome = () => ( 12 | <> 13 | 14 | {tagline} 15 | 16 | 17 | 18 | {title} 19 | 20 | 21 | 22 | {subtitle} 23 | 24 | 25 | ); 26 | 27 | export default Welcome; 28 | -------------------------------------------------------------------------------- /src/components/blog-post/BlogPost.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | import Typography from '@material-ui/core/Typography'; 4 | import Img from 'gatsby-image'; 5 | 6 | import styles from './style'; 7 | 8 | interface Props { 9 | classes: any; 10 | data: any; 11 | }; 12 | 13 | const BlogPost = ({ classes, data }: Props) => ( 14 |
15 | {data.heroImage ? : null} 16 | 17 | {data.title} 18 | 19 | 25 |
26 | ); 27 | 28 | export default withStyles(styles)(BlogPost); 29 | -------------------------------------------------------------------------------- /src/components/link-list/LinkList.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | 4 | import styles from './style'; 5 | 6 | interface Props { 7 | classes: any; 8 | links: Array; 9 | }; 10 | 11 | const LinkList = ({ classes, links }: Props) => ( 12 |
13 | 27 |
28 | ); 29 | 30 | export default withStyles(styles)(LinkList); 31 | -------------------------------------------------------------------------------- /src/state/utils/componentProps.ts: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | 3 | const STATE_PROP = 'STATE_PROP'; 4 | const ACTION_PROP = 'ACTION_PROP'; 5 | 6 | const stateProp = (mapStateToProp: Object => Object) => { 7 | mapStateToProp.type = STATE_PROP; 8 | return mapStateToProp; 9 | }; 10 | 11 | const actionProp = mapActionToProp => { 12 | mapActionToProp.type = ACTION_PROP; 13 | return mapActionToProp; 14 | }; 15 | 16 | const combineMaps = (propsType, propsMaps) => stateOrDispatch => 17 | propsMaps 18 | .filter(map => map.type === propsType) 19 | .reduce((props, map) => Object.assign(props, map(stateOrDispatch)), {}); 20 | 21 | const connectProps = (...propsMaps) => 22 | connect( 23 | combineMaps(STATE_PROP, propsMaps), 24 | combineMaps(ACTION_PROP, propsMaps) 25 | ); 26 | 27 | export { stateProp, actionProp, connectProps }; 28 | -------------------------------------------------------------------------------- /src/containers/blog-post-list/useBlogPosts.ts: -------------------------------------------------------------------------------- 1 | import { useStaticQuery, graphql } from 'gatsby'; 2 | 3 | const useBlogPosts = () => { 4 | const data = useStaticQuery( 5 | graphql` 6 | query { 7 | allContentfulBlogPost(sort: { order: DESC, fields: publishDate }) { 8 | edges { 9 | node { 10 | title 11 | author { 12 | name 13 | } 14 | publishDate 15 | body { 16 | childMarkdownRemark { 17 | html 18 | } 19 | } 20 | heroImage { 21 | fluid(maxWidth: 960) { 22 | ...GatsbyContentfulFluid_withWebp 23 | } 24 | } 25 | } 26 | } 27 | } 28 | } 29 | ` 30 | ); 31 | return data; 32 | }; 33 | 34 | export default useBlogPosts; 35 | -------------------------------------------------------------------------------- /src/components/layout/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ app, breakpoints, typography }) => ({ 2 | '@global': { 3 | html: { 4 | [breakpoints.up('xl')]: { 5 | fontSize: '0.833333vw !important', 6 | }, 7 | }, 8 | }, 9 | container: { 10 | minHeight: '100vh', 11 | }, 12 | scrollFix: { 13 | marginRight: '-17px !important', 14 | marginBottom: '-17px !important', 15 | }, 16 | pageContainer: { 17 | display: 'flex', 18 | flexDirection: 'column', 19 | justifyContent: 'space-between', 20 | margin: '0 auto', 21 | minHeight: '100%', 22 | padding: `${typography.pxToRem(16)} ${typography.pxToRem(48)}`, 23 | transition: 'filter .5s, opacity .5s', 24 | boxSizing: 'border-box', 25 | }, 26 | content: { 27 | margin: 'auto', 28 | maxWidth: app.maxWidth, 29 | width: '100%', 30 | }, 31 | background: { 32 | backgroundImage: ``, 33 | backgroundSize: 'cover', 34 | }, 35 | }); 36 | 37 | export default styles; 38 | -------------------------------------------------------------------------------- /src/components/animated/components/animated-word/components/animated-letter/AnimatedLetter.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | import classNames from 'classnames'; 4 | 5 | import { useActiveAnimation } from 'components/animated/hooks'; 6 | 7 | import styles from './style'; 8 | 9 | interface Props { 10 | classes: any; 11 | letter: string; 12 | animation: string; 13 | animateLetter: boolean; 14 | timeout: number; 15 | }; 16 | 17 | const AnimatedLetter = ({ 18 | classes, 19 | letter, 20 | animation, 21 | animateLetter, 22 | timeout, 23 | }: Props) => { 24 | const [activeAnimation, setActiveAnimation] = useActiveAnimation(timeout); 25 | 26 | return ( 27 | animateLetter && setActiveAnimation(true)} 30 | className={classNames(activeAnimation && animation, classes.letter)}> 31 | {letter} 32 | 33 | ); 34 | }; 35 | 36 | export default withStyles(styles)(AnimatedLetter); 37 | -------------------------------------------------------------------------------- /src/components/navigation/Navigation.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | import withWidth from '@material-ui/core/withWidth'; 4 | 5 | import { compose, isMobileView } from 'utils'; 6 | 7 | import MainNavigation from './main'; 8 | import MobileNavigation from './mobile'; 9 | import options from './content'; 10 | import styles from './style'; 11 | 12 | interface Props { 13 | classes: any; 14 | width: string; 15 | }; 16 | 17 | const Navigation = ({ classes, width }: Props) => { 18 | const [open, setOpen] = useState(false); 19 | 20 | return ( 21 |
22 | {isMobileView(width) ? ( 23 | setOpen(!open)} 27 | /> 28 | ) : ( 29 | 30 | )} 31 |
32 | ); 33 | }; 34 | 35 | export default compose( 36 | withWidth(), 37 | withStyles(styles) 38 | )(Navigation); 39 | -------------------------------------------------------------------------------- /src/components/navigation/mobile/MobileNavigation.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'gatsby-link'; 3 | import { withStyles } from '@material-ui/styles'; 4 | import { FaBars } from 'react-icons/fa'; 5 | 6 | import styles from './style'; 7 | 8 | interface Props { 9 | classes: any; 10 | options: Array; 11 | open: boolean; 12 | onClick: () => void; 13 | }; 14 | 15 | const MobileNavigation = ({ classes, options, onClick, open }: Props) => ( 16 | <> 17 | 18 | {open && ( 19 |
20 | {options.map((link, i) => ( 21 |
22 | 26 | {link.text} 27 | 28 |
29 | ))} 30 |
31 | )} 32 | 33 | ); 34 | 35 | export default withStyles(styles)(MobileNavigation); 36 | -------------------------------------------------------------------------------- /src/components/navigation/Navigation_ReduxExample.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | import withWidth from '@material-ui/core/withWidth'; 4 | 5 | import { compose, isMobileView } from 'utils'; 6 | import { connectProps, open, toggleMenu } from 'state'; 7 | 8 | import MainNavigation from './main'; 9 | import MobileNavigation from './mobile'; 10 | import options from './content'; 11 | import styles from './style'; 12 | 13 | interface Props { 14 | classes: any; 15 | width: string; 16 | open: boolean; 17 | toggleMenu: () => void; 18 | }; 19 | 20 | const Navigation = ({ classes, width, open, toggleMenu }: Props) => ( 21 |
22 | {isMobileView(width) ? ( 23 | toggleMenu()} 27 | /> 28 | ) : ( 29 | 30 | )} 31 |
32 | ); 33 | 34 | export default compose( 35 | connectProps(open, toggleMenu), 36 | withWidth(), 37 | withStyles(styles) 38 | )(Navigation); 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 gatsbyjs 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 | 23 | -------------------------------------------------------------------------------- /src/components/modal/Modal.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | import MUIlModal from '@material-ui/core/Modal'; 4 | import Fade from '@material-ui/core/Fade'; 5 | import { Scrollbars } from 'react-custom-scrollbars'; 6 | 7 | import styles from './style'; 8 | 9 | interface Props { 10 | classes: any; 11 | children: ReactElement; 12 | fullScreen: boolean; 13 | open: boolean; 14 | }; 15 | 16 | const Modal = ({ classes, children, fullScreen, ...rest }: Props) => ( 17 | 18 | 19 |
23 |
24 | 29 | {children} 30 | 31 |
32 |
33 |
34 |
35 | ); 36 | 37 | export default withStyles(styles)(Modal); 38 | -------------------------------------------------------------------------------- /src/components/layout/Layout.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | import { Scrollbars } from 'react-custom-scrollbars'; 4 | import Fade from '@material-ui/core/Fade'; 5 | import classNames from 'classnames'; 6 | 7 | import { Header } from 'components'; 8 | 9 | import styles from './style'; 10 | 11 | require('typeface-roboto'); 12 | 13 | interface Props { 14 | classes: any; 15 | children: ReactElement; 16 | noBackground: boolean; 17 | }; 18 | 19 | const Layout = ({ classes, children, noBackground }: Props) => ( 20 |
} 25 | className={classes.container}> 26 |
31 |
32 | 33 |
{children}
34 |
35 |
36 | 37 | ); 38 | 39 | export default withStyles(styles)(Layout); 40 | -------------------------------------------------------------------------------- /src/components/seo/Seo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Helmet from 'react-helmet'; 3 | import useSiteMetadata from './useSiteMetadata'; 4 | 5 | interface Props { 6 | description?: string; 7 | lang?: string; 8 | meta: Array; 9 | title: string; 10 | }; 11 | 12 | function SEO({ description, lang, meta, title }: Props) { 13 | const siteMetadata = useSiteMetadata(); 14 | 15 | const metaDescription = description || siteMetadata.description; 16 | 17 | return ( 18 | 43 | ); 44 | } 45 | 46 | SEO.defaultProps = { 47 | lang: `en`, 48 | meta: [], 49 | description: ``, 50 | }; 51 | 52 | export default SEO; 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /src/components/animated/components/animated-word/AnimatedWord.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | import classNames from 'classnames'; 4 | 5 | import { useActiveAnimation } from 'components/animated/hooks'; 6 | 7 | import { AnimatedLetter } from './components'; 8 | import styles from './style'; 9 | 10 | interface Props { 11 | classes: any; 12 | word: string; 13 | animateWord: boolean; 14 | animation: string; 15 | timeout: number; 16 | }; 17 | 18 | const AnimatedWord = ({ 19 | classes, 20 | word, 21 | animateWord, 22 | animation, 23 | timeout, 24 | }: Props) => { 25 | const [activeAnimation, setActiveAnimation] = useActiveAnimation(timeout); 26 | 27 | return ( 28 | animateWord && setActiveAnimation(true)} 31 | className={classNames(activeAnimation && animation, classes.word)}> 32 | {animateWord 33 | ? word 34 | : word 35 | .split('') 36 | .map((letter, index) => ( 37 | 44 | ))} 45 |   46 | 47 | ); 48 | }; 49 | 50 | export default withStyles(styles)(AnimatedWord); 51 | -------------------------------------------------------------------------------- /src/components/animated/Animated.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from 'react'; 2 | import { withStyles } from '@material-ui/styles'; 3 | import classNames from 'classnames'; 4 | import isString from 'lodash/isString'; 5 | 6 | import { AnimatedWord } from './components'; 7 | import styles from './style'; 8 | import { useActiveAnimation } from './hooks'; 9 | 10 | interface Props { 11 | children: ReactElement; 12 | classes: any; 13 | animateWord: boolean; 14 | animation: string; 15 | timeout: number; 16 | }; 17 | 18 | const Animated = ({ 19 | children, 20 | classes, 21 | animateWord, 22 | animation, 23 | timeout, 24 | }: Props) => { 25 | const [activeAnimation, setActiveAnimation] = useActiveAnimation(timeout); 26 | 27 | return isString(children) ? ( 28 | children 29 | .split(' ') 30 | .map((word, index) => ( 31 | 38 | )) 39 | ) : ( 40 | setActiveAnimation(true)} 43 | className={classNames( 44 | activeAnimation && classes[animation], 45 | classes.item 46 | )}> 47 | {children} 48 | 49 | ); 50 | }; 51 | 52 | Animated.defaultProps = { 53 | animateWord: false, 54 | animation: 'rubberBand', 55 | timeout: 600, 56 | }; 57 | 58 | export default withStyles(styles)(Animated); 59 | -------------------------------------------------------------------------------- /src/components/animated/style.ts: -------------------------------------------------------------------------------- 1 | const styles = ({ palette }) => ({ 2 | item: { 3 | display: 'inline-block', 4 | }, 5 | rubberBand: { 6 | animation: '$rubberBandEffect 0.6s ease infinite', 7 | }, 8 | heartBeat: { 9 | animation: '$heartBeatEffect 0.6s ease-in-out infinite', 10 | }, 11 | '@keyframes rubberBandEffect': { 12 | '0%': { 13 | '-webkit-transform': 'scale(1.0)', 14 | '-ms-transform': 'scale(1.0)', 15 | transform: 'scale(1.0)', 16 | }, 17 | '30%': { 18 | '-webkit-transform': 'scaleX(1.25) scaleY(0.75)', 19 | '-ms-transform': 'scaleX(1.25) scaleY(0.75)', 20 | transform: 'scaleX(1.25) scaleY(0.75)', 21 | }, 22 | '40%': { 23 | '-webkit-transform': 'scaleX(0.75) scaleY(1.25)', 24 | '-ms-transform': 'scaleX(0.75) scaleY(1.25)', 25 | transform: 'scaleX(0.75) scaleY(1.25)', 26 | }, 27 | '60%': { 28 | '-webkit-transform': 'scaleX(1.15) scaleY(0.85)', 29 | '-ms-transform': 'scaleX(1.15) scaleY(0.85)', 30 | transform: 'scaleX(1.15) scaleY(0.85)', 31 | }, 32 | '100%': { 33 | '-webkit-transform': 'scale(1.0)', 34 | '-ms-transform': 'scale(1.0)', 35 | transform: 'scale(1.0)', 36 | }, 37 | }, 38 | '@keyframes heartBeatEffect': { 39 | '0%': { 40 | transform: 'scale(1)', 41 | }, 42 | '14%': { 43 | transform: 'scale(1.1)', 44 | }, 45 | '28%': { 46 | transform: 'scale(1)', 47 | }, 48 | '42%': { 49 | transform: 'scale(1.1)', 50 | }, 51 | '70%': { 52 | transform: 'scale(1)', 53 | }, 54 | }, 55 | }); 56 | 57 | export default styles; 58 | -------------------------------------------------------------------------------- /src/styles/muiTheme.ts: -------------------------------------------------------------------------------- 1 | import { createMuiTheme } from '@material-ui/core/styles'; 2 | import merge from 'lodash/merge'; 3 | 4 | import palette from './palette'; 5 | import typography from './typography'; 6 | import color from './color'; 7 | 8 | const muiTheme = createMuiTheme({ 9 | typography: { 10 | useNextVariants: true, 11 | fontFamily: typography.fontFamily, 12 | }, 13 | palette, 14 | color, 15 | }); 16 | 17 | muiTheme.typography = merge( 18 | muiTheme.typography, 19 | typography(muiTheme.typography) 20 | ); 21 | 22 | // Overrides 23 | muiTheme.overrides = { 24 | MuiTypography: { 25 | colorInherit: { 26 | color: 'inherit', 27 | }, 28 | colorSecondary: { 29 | color: muiTheme.palette.secondary.main, 30 | }, 31 | colorPrimary: { 32 | color: muiTheme.palette.primary.main, 33 | }, 34 | h1: { 35 | [muiTheme.breakpoints.down('sm')]: { 36 | fontSize: muiTheme.typography.pxToRem(56), 37 | }, 38 | [muiTheme.breakpoints.down('xs')]: { 39 | fontSize: muiTheme.typography.pxToRem(40), 40 | }, 41 | }, 42 | h2: { 43 | [muiTheme.breakpoints.down('sm')]: { 44 | fontSize: muiTheme.typography.pxToRem(48), 45 | }, 46 | [muiTheme.breakpoints.down('xs')]: { 47 | fontSize: muiTheme.typography.pxToRem(32), 48 | }, 49 | }, 50 | h3: { 51 | [muiTheme.breakpoints.down('sm')]: { 52 | fontSize: muiTheme.typography.pxToRem(21), 53 | }, 54 | [muiTheme.breakpoints.down('xs')]: { 55 | fontSize: muiTheme.typography.pxToRem(18), 56 | }, 57 | }, 58 | subtitle1: { 59 | [muiTheme.breakpoints.down('sm')]: { 60 | fontSize: muiTheme.typography.pxToRem(14), 61 | }, 62 | }, 63 | body1: { 64 | [muiTheme.breakpoints.down('sm')]: { 65 | fontSize: muiTheme.typography.pxToRem(14), 66 | }, 67 | }, 68 | body2: { 69 | [muiTheme.breakpoints.down('sm')]: { 70 | fontSize: muiTheme.typography.pxToRem(12), 71 | }, 72 | }, 73 | }, 74 | }; 75 | 76 | export default muiTheme; 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-starter", 3 | "private": true, 4 | "description": "Gatsby Starter", 5 | "version": "1.2.0", 6 | "author": "Polychronis Papadakis ", 7 | "dependencies": { 8 | "@material-ui/core": "^4.11.3", 9 | "@material-ui/styles": "^4.11.3", 10 | "classnames": "^2.2.6", 11 | "gatsby": "^3.0.4", 12 | "gatsby-image": "^2.2.37", 13 | "gatsby-plugin-manifest": "^3.0.0", 14 | "gatsby-plugin-material-ui": "^2.1.10", 15 | "gatsby-plugin-module-resolver": "^1.0.3", 16 | "gatsby-plugin-offline": "^4.0.0", 17 | "gatsby-plugin-react-helmet": "^4.0.0", 18 | "gatsby-plugin-react-redux": "^1.1.0", 19 | "gatsby-plugin-sharp": "^3.0.1", 20 | "gatsby-source-contentful": "^5.0.3", 21 | "gatsby-source-filesystem": "^3.0.0", 22 | "gatsby-transformer-remark": "^3.0.0", 23 | "gatsby-transformer-sharp": "^3.0.0", 24 | "lodash": "^4.17.21", 25 | "react": "^16.14.0", 26 | "react-custom-scrollbars": "^4.2.1", 27 | "react-dom": "^16.14.0", 28 | "react-helmet": "^6.1.0", 29 | "react-icons": "^4.2.0", 30 | "react-redux": "^7.2.2", 31 | "redux": "^4.0.5", 32 | "redux-actions": "^2.6.5", 33 | "typeface-roboto": "^1.1.13" 34 | }, 35 | "devDependencies": { 36 | "@babel/core": "^7.13.10", 37 | "@hot-loader/react-dom": "^16.14.0", 38 | "babel-preset-gatsby": "^1.0.0", 39 | "chalk": "^4.1.0", 40 | "inquirer": "^8.0.0", 41 | "prettier": "^1.19.1", 42 | "redux-devtools-extension": "^2.13.9", 43 | "yargs-parser": "^20.2.7" 44 | }, 45 | "keywords": [ 46 | "gatsby" 47 | ], 48 | "license": "MIT", 49 | "scripts": { 50 | "clean": "rm -rf node_modules", 51 | "build": "gatsby build", 52 | "develop": "gatsby develop", 53 | "format": "prettier --write src/**/*.{js,jsx}", 54 | "serve": "gatsby serve", 55 | "setup": "node ./bin/setup.js" 56 | }, 57 | "repository": { 58 | "type": "git", 59 | "url": "https://github.com/chronisp/gatsby-starter" 60 | }, 61 | "bugs": { 62 | "url": "https://github.com/chronisp/gatsby-starter/issues" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({ 2 | path: `.env.${process.env.NODE_ENV}`, 3 | }) 4 | 5 | module.exports = { 6 | siteMetadata: { 7 | title: `Gatsby Starter`, 8 | description: `Gatsby Starter`, 9 | author: `Polychronis Papadakis`, 10 | }, 11 | plugins: [ 12 | `gatsby-transformer-sharp`, 13 | `gatsby-transformer-remark`, 14 | `gatsby-plugin-sharp`, 15 | `gatsby-plugin-react-helmet`, 16 | { 17 | resolve: `gatsby-plugin-typescript`, 18 | }, 19 | { 20 | resolve: `gatsby-plugin-material-ui`, 21 | }, 22 | { 23 | resolve: 'gatsby-plugin-module-resolver', 24 | options: { 25 | root: './src', 26 | aliases: { 27 | 'components': './components', 28 | 'containers': './containers', 29 | 'images': './images', 30 | 'state': './state', 31 | 'styles': './styles', 32 | 'utils': './utils', 33 | static: { 34 | root: './public', 35 | alias: './static' 36 | } 37 | } 38 | } 39 | }, 40 | { 41 | resolve: `gatsby-source-filesystem`, 42 | options: { 43 | name: `images`, 44 | path: `${__dirname}/src/images`, 45 | }, 46 | }, 47 | { 48 | resolve: `gatsby-plugin-manifest`, 49 | options: { 50 | name: `Gatsby Starter`, 51 | short_name: `Gatsby Starter`, 52 | start_url: `/`, 53 | background_color: `black`, 54 | theme_color: `black`, 55 | display: `minimal-ui`, 56 | icon: `src/images/favicon.png`, 57 | }, 58 | }, 59 | { 60 | resolve: `gatsby-plugin-react-redux`, 61 | options: { 62 | pathToCreateStoreModule: "./src/state/store", 63 | serialize: { 64 | space: 0, 65 | isJSON: true, 66 | unsafe: false, 67 | }, 68 | }, 69 | }, 70 | { 71 | resolve: `gatsby-source-contentful`, 72 | options: { 73 | spaceId: process.env.CONTENTFUL_SPACE_ID, 74 | accessToken:process.env.CONTENTFUL_ACCESS_TOKEN, 75 | environment: process.env.CONTENTFUL_ENV, 76 | } 77 | }, 78 | ], 79 | } 80 | -------------------------------------------------------------------------------- /bin/setup.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | const chalk = require('chalk') 3 | const path = require('path') 4 | const { writeFileSync } = require('fs') 5 | 6 | const argv = require('yargs-parser')(process.argv.slice(2)) 7 | 8 | console.log(` 9 | To set up this project you need to provide your Contentful ${chalk.yellow('Space ID')} 10 | and ${chalk.yellow('Access Token')}. 11 | `) 12 | 13 | const questions = [ 14 | { 15 | name: 'spaceId', 16 | message: 'Your Space ID', 17 | when: !argv.spaceId && !process.env.CONTENTFUL_SPACE_ID, 18 | validate: input => 19 | /^[a-z0-9]{12}$/.test(input) || 20 | 'Space ID must be 12 lowercase characters', 21 | }, 22 | { 23 | name: 'accessToken', 24 | when: !argv.accessToken && !process.env.CONTENTFUL_ACCESS_TOKEN_TOKEN, 25 | message: 'Your Access Token', 26 | }, 27 | ] 28 | 29 | inquirer 30 | .prompt(questions) 31 | .then(({ spaceId, accessToken }) => { 32 | const { CONTENTFUL_SPACE_ID, CONTENTFUL_ACCESS_TOKEN } = process.env 33 | 34 | spaceId = CONTENTFUL_SPACE_ID || argv.spaceId || spaceId 35 | accessToken = 36 | CONTENTFUL_ACCESS_TOKEN || argv.accessToken || accessToken 37 | 38 | console.log('Writing config file...') 39 | const configFiles = [`.env.development`, `.env.production`] 40 | .map(file => path.join(__dirname, '..', file)) 41 | 42 | const devContents = [ 43 | `CONTENTFUL_SPACE_ID=${spaceId}`, 44 | `CONTENTFUL_ACCESS_TOKEN=${accessToken}`, 45 | `CONTENTFUL_ENV=development` 46 | ].join('\n') + '\n' 47 | 48 | const prodContents = [ 49 | `CONTENTFUL_SPACE_ID=${spaceId}`, 50 | `CONTENTFUL_ACCESS_TOKEN=${accessToken}`, 51 | `CONTENTFUL_ENV=master` 52 | ].join('\n') + '\n' 53 | 54 | configFiles.forEach(file => { 55 | if (file.includes('.env.development')) { 56 | writeFileSync(file, devContents, 'utf8') 57 | } else if (file.includes('.env.production')) { 58 | writeFileSync(file, prodContents, 'utf8') 59 | } 60 | console.log(`Config file ${chalk.yellow(file)} written`) 61 | }) 62 | return { spaceId } 63 | }) 64 | .then((_, error) => { 65 | console.log( 66 | `All set! You can now run ${chalk.yellow( 67 | 'yarn develop' 68 | )} to see it in action.` 69 | ) 70 | }) 71 | .catch(error => console.error(error)) 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Gatsby 3 |

4 |

5 | Gatsby Starter 6 |

7 | 8 |

9 | Gatsby Starter for creating portfolio & blog. 10 |

11 | 12 |

13 | 14 | Netlify 15 | 16 | 17 | Codacy grade 18 | 19 | GitHub license 20 |

21 | 22 | ## Key Features 23 | These are the key features of the project: 24 | * [Gatsby](https://www.gatsbyjs.org/) (GraphQL for queries) 25 | * [React](https://reactjs.org/) (everything in React Hooks) 26 | * [TypeScript](https://www.typescriptlang.org/) (in progress...) 27 | * [Redux](https://redux.js.org/) (custom HOF to connect actions & props) 28 | * [Material UI](https://material-ui.com/) (palette, typography & breakpoints configuration) 29 | * [Contentful CMS](https://www.contentful.com/) (blog integration) 30 | * [Netlify Deployment Support](https://www.netlify.com) 31 | * [React Helmet](https://github.com/nfl/react-helmet) 32 | * [Prettier](https://prettier.io/) 33 | * [Gatsby-image](https://www.gatsbyjs.org/packages/gatsby-image/) 34 | * Config files for each environment 35 | 36 | ## Get Started 37 | Make sure that you have Node.js (>=12.13.0) and yarn v.1 or above installed. 38 | 39 | Install Gatsby CLI 40 | ```sh 41 | npm install -g gatsby-cli 42 | ``` 43 | Clone repository 44 | ```sh 45 | git clone https://github.com/chronisp/gatsby-starter.git 46 | ``` 47 | Move to project directory 48 | ```sh 49 | cd 50 | ``` 51 | Install all dependencies 52 | ```sh 53 | yarn install 54 | ``` 55 | 56 | ### Contentful blog 57 | Setup Contentful settings 58 | ```sh 59 | yarn setup 60 | ``` 61 | Follow the instructions in order to setup ```CONTENTFUL_SPACE_ID``` and ```CONTENTFUL_ACCESS_TOKEN```. Required settings will be defined in both ```.env.development``` and ```.env.production``` files. 62 | 63 | For more info on how to setup Contentful follow this [link](https://www.contentful.com/r/knowledgebase/gatsbyjs-and-contentful-in-five-minutes/). 64 | 65 | ### Development mode 66 | Start development server 67 | ```sh 68 | yarn develop 69 | ``` 70 | Your site is now running at ```http://localhost:8000```. 71 | 72 | ### Production mode 73 | Create a production build 74 | ```sh 75 | yarn build 76 | ``` 77 | Serve the production build locally 78 | ```sh 79 | yarn serve 80 | ``` 81 | Your site is running at ```http://localhost:9000```. 82 | 83 | ### Linting 84 | Format all JS files 85 | ```sh 86 | yarn format 87 | ``` 88 | 89 | For any building or deployment issues, ensure you have setup your environment according to [Gatsby guide](https://www.gatsbyjs.org/docs/preparing-your-environment/) for [Windows](https://www.gatsbyjs.org/docs/gatsby-on-windows/) or [Linux](https://www.gatsbyjs.org/docs/gatsby-on-linux/). 90 | --------------------------------------------------------------------------------