├── .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 |
12 | © {new Date().getFullYear()}, Built with
13 | {` `}
14 | Gatsby
15 |
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 |
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 |
14 | {options.map((link, i) => (
15 |
16 |
20 | {link.text}
21 |
22 |
23 | ))}
24 |
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 |
14 | {links.map(({ Icon, to, newTab, text }, index) => (
15 |
16 |
21 |
22 | {text}
23 |
24 |
25 | ))}
26 |
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 |
3 |
4 |
5 | Gatsby Starter
6 |
7 |
8 |
9 | Gatsby Starter for creating portfolio & blog.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
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 |
--------------------------------------------------------------------------------