Merge pull request #7 from Jinksi/vanilla-css

Vanilla css
This commit is contained in:
Eric Jinks 2018-01-23 13:38:47 +10:00 committed by GitHub
commit 28bc0062b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 994 additions and 689 deletions

View file

@ -4,12 +4,11 @@
A starter project for creating lightning-fast, offline-first websites with [Netlify CMS](https://netlifycms.org) and React.
- **[Create React App](https://github.com/facebookincubator/create-react-app)**
- **[Styled Components](https://github.com/styled-components/styled-components)** for component-based styling
- **[React Router](https://github.com/ReactTraining/react-router)** for routing
- **[React Helmet](https://github.com/nfl/react-helmet)** for document titles, descriptions, meta
- **[React Snapshot](https://github.com/geelen/react-snapshot)** for pre-rendering to static html so it works without Javascript ⭐️
- **[Netlify CMS](https://github.com/netlify/netlify-cms)** for content management
* **[Create React App](https://github.com/facebookincubator/create-react-app)**
* **[React Router](https://github.com/ReactTraining/react-router)** for routing
* **[React Helmet](https://github.com/nfl/react-helmet)** for document titles, descriptions, meta
* **[React Snapshot](https://github.com/geelen/react-snapshot)** for pre-rendering to static html so it works without Javascript ⭐️
* **[Netlify CMS](https://github.com/netlify/netlify-cms)** for content management
I aim to include commonly used components and best-practices e.g. forms, settings, <head> tags, lazy-loading images, etc.
@ -19,41 +18,40 @@ I aim to include commonly used components and best-practices e.g. forms, setting
1. Hit the **Deploy to Netlify** button. This will:
- Clone the repo into your Github account
- Create you a new project on Netlify, build & deploy
* Clone the repo into your Github account
* Create you a new project on Netlify, build & deploy
1. Once your Netlify project has been created, change a couple of settings:
- Enable **Identity**
- Change **Registration Preferences** to **Invite Only**
- Enable **Git Gateway**
* Enable **Identity**
* Change **Registration Preferences** to **Invite Only**
* Enable **Git Gateway**
1. Invite users (probably yourself) to enable admin access
- Open the **Identity** tab and hit **Invite Users**
* Open the **Identity** tab and hit **Invite Users**
## Show me the CMS!
The CMS lives at [\__YOUR_SITE_NAME__.netlify.com/admin](https://__YOUR_SITE_NAME__.netlify.com/admin).
The CMS lives at [\_\_YOUR_SITE_NAME\_\_.netlify.com/admin](https://__YOUR_SITE_NAME__.netlify.com/admin).
## Developing
1. Clone your repo to your local machine
1. Install dependencies
1. Install dependencies
`yarn` or `npm install`
`yarn` or `npm install`
1. Run the development server
`yarn start` or `npm run start`
`yarn start` or `npm run start`
If you are adding or editing content locally in the CMS, a couple of things to note:
1. Changes will be pushed to the remote repo.
1. Changes will be pushed to the remote repo.
1. You will be prompted to enter your site's url, this is necessary for Netlify Identity to manage user login. This is stored in `localStorage`, so you might have to empty your browser cache if you are switching projects but remaining on `localhost:3000`.
1. You will be prompted to enter your site's url, this is necessary for Netlify Identity to manage user login. This is stored in `localStorage`, so you might have to empty your browser cache if you are switching projects but remaining on `localhost:3000`.
## Editing CMS fields

View file

@ -2,18 +2,18 @@
title: Hello World!
subtitle: <Home />
---
# 🌶 Netlify CMS + React Starter
[![Standard - JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](http://standardjs.com/)
A starter project for creating lightning-fast, offline-first websites with [Netlify CMS](https://netlifycms.org) and React.
- **[Create React App](https://github.com/facebookincubator/create-react-app)**
- **[Styled Components](https://github.com/styled-components/styled-components)** for component-based styling
- **[React Router](https://github.com/ReactTraining/react-router)** for routing
- **[React Helmet](https://github.com/nfl/react-helmet)** for document titles, descriptions, meta
- **[React Snapshot](https://github.com/geelen/react-snapshot)** for pre-rendering to static html so it works without Javascript ⭐️
- **[Netlify CMS](https://github.com/netlify/netlify-cms)** for content management
* **[Create React App](https://github.com/facebookincubator/create-react-app)**
* **[React Router](https://github.com/ReactTraining/react-router)** for routing
* **[React Helmet](https://github.com/nfl/react-helmet)** for document titles, descriptions, meta
* **[React Snapshot](https://github.com/geelen/react-snapshot)** for pre-rendering to static html so it works without Javascript ⭐️
* **[Netlify CMS](https://github.com/netlify/netlify-cms)** for content management
I aim to include commonly used components and best-practices e.g. forms, settings, <head> tags, lazy-loading images, etc.
@ -22,34 +22,36 @@ I aim to include commonly used components and best-practices e.g. forms, setting
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/Jinksi/netlify-cms-react-starter)
1. Hit the **Deploy to Netlify** button. This will:
- Clone the repo into your Github account
- Create you a new project on Netlify, build & deploy
* Clone the repo into your Github account
* Create you a new project on Netlify, build & deploy
1. Once your Netlify project has been created, change a couple of settings:
- Enable **Identity**
- Change **Registration Preferences** to **Invite Only**
- Enable **Git Gateway**
* Enable **Identity**
* Change **Registration Preferences** to **Invite Only**
* Enable **Git Gateway**
1. Invite users (probably yourself) to enable admin access
- Open the **Identity** tab and hit **Invite Users**
* Open the **Identity** tab and hit **Invite Users**
## Developing
1. Clone your repo to your local machine
1. Install dependencies
1. Install dependencies
`yarn` or `npm install`
`yarn` or `npm install`
1. Run the development server
`yarn start` or `npm run start`
`yarn start` or `npm run start`
If you are adding or editing content locally in the CMS, a couple of things to note:
- Changes will be pushed to the remote repo.
* Changes will be pushed to the remote repo.
- You will be prompted to enter your site's url, this is necessary for Netlify Identity to manage user login. This is stored in `localStorage`, so you might have to empty your browser cache if you are switching projects but remaining on `localhost:3000`.
* You will be prompted to enter your site's url, this is necessary for Netlify Identity to manage user login. This is stored in `localStorage`, so you might have to empty your browser cache if you are switching projects but remaining on `localhost:3000`.
## Editing CMS fields

View file

@ -16,6 +16,7 @@
"glob": "^7.1.2",
"gray-matter": "^3.1.1",
"js-yaml": "^3.10.0",
"prop-types": "^15.6.0",
"react-scripts": "^1.0.10",
"sharp": "^0.18.4",
"snazzy": "^7.0.0",
@ -23,33 +24,33 @@
"sw-precache": "^5.2.0"
},
"dependencies": {
"@researchgate/react-intersection-observer": "^0.5.0",
"@researchgate/react-intersection-observer": "^0.6.0",
"core-js": "^2.5.3",
"intersection-observer": "^0.5.0",
"lodash": "^4.17.4",
"netlify-identity-widget": "^1.2.0",
"polished": "^1.7.0",
"normalize.css": "^7.0.0",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-helmet": "^5.1.3",
"react-markdown": "^2.5.0",
"react-router-dom": "^4.1.1",
"react-snapshot": "^1.1.0",
"styled-components": "^2.1.0"
"react-snapshot": "^1.1.0"
},
"scripts": {
"start": "npm run watch:content & react-scripts start",
"build": "npm run prepare && react-scripts build && react-snapshot && npm run sw",
"start": "npm run watch:content && react-scripts start",
"build":
"npm run prepare-content && react-scripts build && react-snapshot && npm run sw",
"parse-content": "node ./functions/parse-content.js",
"resize-images": "node ./functions/resize-images.js",
"prepare": "npm run parse-content & npm run resize-images",
"watch:content": "chokidar 'content/**/**' -c 'npm run prepare' --initial",
"prepare-content": "npm run parse-content && npm run resize-images",
"watch:content":
"chokidar 'content/**/**' -c 'npm run prepare-content' --initial",
"sw": "sw-precache --config='sw-precache-config.js'",
"test": "standard | snazzy && react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"reactSnapshot": {
"include": [
"/404"
]
"include": ["/404"]
}
}

View file

@ -1,4 +1,4 @@
/* Admin tweaks 1.0.2 */
/* Admin tweaks 1.0.3 */
.nc-mediaLibrary-cardImage {
object-fit: contain;
height: auto;
@ -33,6 +33,11 @@
width: auto;
}
.nc-entryListing-cardImage {
background-size: contain;
background-color: whitesmoke;
}
.nc-mediaLibrary-cardText {
margin: 0;
}
@ -41,3 +46,33 @@
display: flex;
flex-direction: column;
}
/* Custom logo */
/* .nc-githubAuthenticationPage-logo {
background-image: url(/images/teeth-on-ferry-logo@2x.png);
background-position: center;
background-repeat: no-repeat;
background-size: 200px;
}
.nc-githubAuthenticationPage-logo svg {
display: none;
}
.nc-githubAuthenticationPage-button {
text-indent: -1000px;
overflow: hidden;
width: 8rem;
}
.nc-githubAuthenticationPage-button:after {
content: 'Login';
display: block;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: auto;
text-indent: 0;
} */

View file

@ -6,14 +6,14 @@
<title>Content Manager</title>
<!-- Include the styles for the Netlify CMS UI, after your own styles -->
<link rel="stylesheet" href="https://unpkg.com/netlify-cms@1.0.2/dist/cms.css" />
<link rel="stylesheet" href="https://unpkg.com/netlify-cms@1.0.3/dist/cms.css" />
<link rel="stylesheet" href="admin.css" />
</head>
<body>
<!-- Include the script that builds the page and powers Netlify CMS -->
<script src="https://identity-js.netlify.com/v1/netlify-identity-widget.js"></script>
<script src="https://unpkg.com/netlify-cms@1.0.2/dist/cms.js"></script>
<script src="https://unpkg.com/netlify-cms@1.0.3/dist/cms.js"></script>
<script>
// redirect when logging out

BIN
public/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -14,7 +14,7 @@
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Netlify CMS + React Starter</title>
<!-- Place Google Fonts css here -->
</head>
<body>
<div id="root"></div>

View file

@ -9,9 +9,9 @@ import About from './views/About'
import Contact from './views/Contact'
import NoMatch from './views/NoMatch'
import Nav from './components/Nav'
import Footer from './components/Footer'
import GithubCorner from './components/GithubCorner'
import ServiceWorkerNotifications from './components/ServiceWorkerNotifications'
import globalStyles from './globalStyles'
import data from './data.json'
class App extends Component {
@ -19,10 +19,6 @@ class App extends Component {
data
}
componentWillMount () {
globalStyles()
}
getDocument = (collection, name) =>
this.state.data[collection] &&
this.state.data[collection].filter(page => page.name === name)[0]
@ -38,7 +34,7 @@ class App extends Component {
} = this.getDocument('settings', 'global')
return (
<Router>
<div>
<div className='React-Wrap'>
<ScrollToTop />
<ServiceWorkerNotifications reloadOnUpdate />
<GithubCorner url='https://github.com/Jinksi/netlify-cms-react-starter' />
@ -85,8 +81,9 @@ class App extends Component {
/>
)}
/>
<Route component={NoMatch} />
<Route render={() => <NoMatch siteUrl={siteUrl} />} />
</Switch>
<Footer />
</div>
</Router>
)

View file

@ -0,0 +1,18 @@
.BackgroundImage {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-position: center;
transition: opacity 0.5s ease;
overflow: hidden;
}
.BackgroundImage-lazy {
filter: blur(5px);
}
.BackgroundImage-lazy-loaded {
filter: blur(0);
}

View file

@ -0,0 +1,74 @@
import React from 'react'
import PropTypes from 'prop-types'
import 'intersection-observer'
import Observer from '@researchgate/react-intersection-observer'
import './BackgroundImage.css'
import { getImageSrc } from '../util/getImageUrl'
export default class BackgroundImage extends React.Component {
static defaultProps = {
lazy: false,
src: ''
}
state = {
src:
this.props.src.indexOf('http') === 0
? ''
: getImageSrc(this.props.src, this.props.lazy ? 10 : 1800),
dataSrc: getImageSrc(this.props.src, 1800),
loaded: false
}
handleIntersection = e => {
if (e.isIntersecting) {
const img = new Image()
img.src = this.state.dataSrc
img.onload = () => {
this.setState({
src: this.state.dataSrc,
loaded: true
})
}
}
}
componentWillReceiveProps (nextProps) {
if (this.props.src === nextProps.src) return
this.setState({
src: getImageSrc(nextProps.src, nextProps.lazy ? 10 : 1800),
dataSrc: getImageSrc(nextProps.src, 1800)
})
}
render () {
let className = this.props.className || ''
if (this.state.loaded) className += ' BackgroundImage-lazy-loaded'
if (this.props.lazy) className += ' BackgroundImage-lazy'
const options = {
onChange: this.handleIntersection,
onlyOnce: true,
rootMargin: '0% 0% 100%'
}
return (
<Observer {...options}>
<div
className={`BackgroundImage absolute ${className || ''}`}
src={this.state.src}
style={{
backgroundImage: `url(${this.state.src})`,
backgroundSize: this.props.contain ? 'contain' : 'cover',
opacity: this.props.opacity || 1
}}
/>
</Observer>
)
}
}
BackgroundImage.propTypes = {
src: PropTypes.string.isRequired
}

View file

@ -0,0 +1,8 @@
.Content {
white-space: pre-line;
}
.Content--Image {
max-width: 100%;
height: auto;
}

View file

@ -1,10 +1,12 @@
import React from 'react'
import styled from 'styled-components'
import Marked from 'react-markdown'
import { getImageSrc, getImageSrcset } from '../util/getImageUrl'
import './Content.css'
export default ({ source }) => (
<Content
<Marked
className='Content'
source={source}
renderers={{
Image: ImageWithSrcset
@ -12,18 +14,12 @@ export default ({ source }) => (
/>
)
const Image = styled.img`
max-width: 100%;
height: auto;
`
const Content = styled(Marked)`
white-space: pre-line;
`
const ImageWithSrcset = props => (
<Image
const ImageWithSrcset = ({ nodeKey, src, alt, ...props }) => (
<img
className='Content--Image'
{...props}
src={getImageSrc(props.src)}
srcSet={getImageSrcset(props.src)}
src={getImageSrc(src)}
srcSet={getImageSrcset(src)}
alt={alt}
/>
)

View file

@ -0,0 +1,60 @@
.EnquiryForm {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
max-width: 500px;
margin: 2.5rem 0;
}
.EnquiryForm > * + * {
margin-top: 1.5rem;
}
.EnquiryForm--Label {
width: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.EnquiryForm--Input,
.EnquiryForm--Textarea {
font-family: inherit;
font-weight: 400;
flex-grow: 1;
box-sizing: border-box;
display: block;
margin: 0;
border: none;
padding: 1em;
line-height: 1;
transition: border-color 0.2s;
resize: none;
background: var(--lightGrey);
border-radius: 0;
}
.EnquiryForm--Input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px var(--lightGrey) inset !important;
}
.EnquiryForm--Input:focus {
outline: none;
border-color: black;
}
.EnquiryForm--Input[disabled] {
opacity: 0.4;
pointer-events: none;
cursor: progress;
}
.EnquiryForm--Input::placeholder {
text-transform: uppercase;
}
.EnquiryForm--SubmitButton {
width: 90%;
margin: 2rem auto 0;
}

View file

@ -0,0 +1,62 @@
import React from 'react'
import './EnquiryFormSimple.css'
export default ({
name = 'Simple Form',
subject = '', // optional subject of the notification email
action = ''
}) => (
<form
className='EnquiryForm'
name={name}
action={action}
data-netlify=''
data-netlify-honeypot='_gotcha'
>
<label className='EnquiryForm--Label'>
<input
className='EnquiryForm--Input'
type='text'
placeholder='Name'
name='name'
required
/>
</label>
<label className='EnquiryForm--Label'>
<input
className='EnquiryForm--Input'
type='phone'
placeholder='Phone'
name='phone'
required
/>
</label>
<label className='EnquiryForm--Label'>
<input
className='EnquiryForm--Input'
type='email'
placeholder='Email'
name='email'
required
/>
</label>
<label className='EnquiryForm--Label'>
<textarea
className='EnquiryForm--Textarea'
placeholder='Message'
name='message'
rows='10'
required
/>
</label>
<input type='text' name='_gotcha' style={{ display: 'none' }} />
{!!subject && <input type='hidden' name='subject' value={subject} />}
<input type='hidden' name='form-name' value={name} />
<input
className='Button Button--tertiary EnquiryForm--SubmitButton'
type='submit'
value='Enquire'
/>
</form>
)

14
src/components/Footer.css Normal file
View file

@ -0,0 +1,14 @@
.Footer {
background: var(--primary);
color: white;
}
.Footer--Lower {
background: var(--primaryDark);
padding: 1rem 0;
font-weight: 100;
}
.Footer--Lower a {
color: inherit;
}

13
src/components/Footer.js Normal file
View file

@ -0,0 +1,13 @@
import React from 'react'
import './Footer.css'
export default ({ globalSettings, socialSettings, navLinks }) => (
<footer className='Footer'>
<div className='Footer--Lower'>
<div className='Container taCenter'>
<span>© 2017 All rights reserved.</span>
</div>
</div>
</footer>
)

View file

@ -0,0 +1,48 @@
.GithubCorner {
position: fixed;
top: 0;
right: 0;
z-index: 2;
}
.GithubCorner svg {
fill: #151513;
color: #fff;
border: 0;
max-width: 12.5vmin;
max-height: 12.5vmin;
}
.GithubCorner .octo-arm {
transform-origin: 130px 106px;
}
.GithubCorner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
@media (max-width: 500px) {
.GithubCorner:hover .octo-arm {
animation: none;
}
.GithubCorner .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
}
@keyframes octocat-wave {
0%,
100% {
transform: rotate(0);
}
20%,
60% {
transform: rotate(-25deg);
}
40%,
80% {
transform: rotate(10deg);
}
}

View file

@ -1,59 +1,20 @@
import React from 'react'
import styled from 'styled-components'
const GithubCorner = styled.a`
position: fixed;
top: 0;
right: 0;
z-index: 2;
svg {
fill: #151513;
color: #fff;
border: 0;
max-width: 12.5vmin;
max-height: 12.5vmin;
}
.octo-arm {
transform-origin: 130px 106px;
}
&:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave {
0%, 100% {
transform: rotate(0);
}
20%, 60% {
transform: rotate(-25deg);
}
40%, 80% {
transform: rotate(10deg)
}
}
@media (max-width:500px) {
&:hover .octo-arm {
animation: none;
}
.octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
}
`
import './GithubCorner.css'
export default ({ url }) => (
<GithubCorner href={url} aria-label='View source on Github'>
<a className='GithubCorner' href={url} aria-label='View source on Github'>
<svg width='80' height='80' viewBox='0 0 250 250' aria-hidden='true'>
<path d='M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z' />
<path className='octo-arm' d='M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2' fill='currentColor' />
<path className='octo-body' d='M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z' fill='currentColor' />
<path
className='octo-arm'
d='M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2'
fill='currentColor'
/>
<path
className='octo-body'
d='M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z'
fill='currentColor'
/>
</svg>
</GithubCorner>
</a>
)

View file

@ -0,0 +1,18 @@
.LazyEmbed {
position: relative;
padding-bottom: 56.25%;
height: 0;
overflow: hidden;
max-width: 100%;
height: auto;
}
.LazyEmbed iframe,
.LazyEmbed object,
.LazyEmbed embed {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

View file

@ -0,0 +1,55 @@
import React from 'react'
import 'intersection-observer'
import Observer from '@researchgate/react-intersection-observer'
import './LazyEmbed.css'
class LazyEmbed extends React.Component {
static defaultProps = {
lazy: true
}
state = {
src: null,
dataSrc: this.props.src,
loaded: !this.props.lazy
}
handleIntersection = e => {
if (e.isIntersecting) {
this.setState({
src: this.state.dataSrc,
loaded: true
})
}
}
componentWillReceiveProps (nextProps) {
if (this.props.src === nextProps.src) return
this.setState({
src: nextProps.src,
dataSrc: nextProps.src
})
}
render () {
let className = this.props.className || ''
if (this.state.loaded) className += ' loaded'
const options = {
onChange: this.handleIntersection,
onlyOnce: true,
rootMargin: '0% 0% 100%'
}
return (
<Observer {...options}>
<div
className={`LazyEmbed ${className}`}
dangerouslySetInnerHTML={{ __html: this.state.src }}
/>
</Observer>
)
}
}
export default LazyEmbed

View file

@ -0,0 +1,14 @@
.LazyImage {
display: block;
width: 100%;
margin-bottom: 2.5rem;
filter: blur(5px);
}
.LazyImage.loaded {
filter: blur(0);
}
.LazyImage:last-child {
margin-bottom: 0;
}

View file

@ -1,53 +1,85 @@
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { getImageSrc, getImageSrcset } from '../util/getImageUrl'
import 'intersection-observer'
import Observer from '@researchgate/react-intersection-observer'
const Image = styled.img`
display: block;
width: 100%;
margin-bottom: 2.5rem;
&:last-child {
margin-bottom: 0;
}
`
import { getImageSrc, getImageSrcset } from '../util/getImageUrl'
import './LazyImage.css'
class LazyImage extends React.Component {
static defaultProps = {
lazy: false,
enableSrcset: true,
imageSize: '300'
}
state = {
src: getImageSrc(this.props.src, '10'),
srcSet: '',
dataSrc: getImageSrc(this.props.src, '300'),
dataSrcSet: getImageSrcset(this.props.src)
src: getImageSrc(
this.props.src,
this.props.lazy ? '10' : this.props.imageSize
),
srcSet: this.props.lazy ? '' : getImageSrcset(this.props.src),
dataSrc: getImageSrc(this.props.src, this.props.imageSize),
dataSrcSet: getImageSrcset(this.props.src),
loaded: !this.props.lazy
}
handleIntersection = e => {
if (e.isIntersecting) {
this.setState({
src: this.state.dataSrc,
srcSet: this.state.dataSrcSet
})
const img = new Image()
img.src = this.state.dataSrc
img.onload = () => {
this.setState({
src: this.state.dataSrc,
srcSet: this.state.dataSrcSet,
loaded: true
})
}
}
}
componentWillReceiveProps (nextProps) {
if (this.props.src === nextProps.src) return
this.setState({
src: getImageSrc(nextProps.src, '10'),
srcSet: '',
dataSrc: getImageSrc(nextProps.src, '300'),
dataSrcSet: getImageSrcset(nextProps.src)
})
}
render () {
const { sizes, alt, className } = this.props
const { sizes, lazy, onClick, alt } = this.props
let className = this.props.className || ''
if (this.state.loaded) className += ' loaded'
const options = {
onChange: this.handleIntersection,
onlyOnce: true,
rootMargin: '0% 0% 50%'
rootMargin: '0% 0% 100%'
}
if (!lazy) {
return (
<img
className={`LazyImage ${className}`}
src={this.state.src}
srcSet={this.props.enableSrcset ? this.state.srcSet : undefined}
sizes={sizes || '100vw'}
onClick={onClick}
alt={alt}
/>
)
}
return (
<Observer {...options}>
<Image
<img
className={`LazyImage ${className}`}
src={this.state.src}
srcSet={this.state.srcSet}
srcSet={this.props.enableSrcset ? this.state.srcSet : undefined}
sizes={sizes || '100vw'}
onClick={onClick}
alt={alt}
className={className}
/>
</Observer>
)

9
src/components/Logo.css Normal file
View file

@ -0,0 +1,9 @@
.Logo {
display: block;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
height: 2rem;
width: 2.5rem;
margin-right: 1rem;
}

View file

@ -1,7 +1,12 @@
import styled from 'styled-components'
import React from 'react'
export default styled.span`
display: block;
font-size: 2rem;
padding: 0 1rem;
`
import './Logo.css'
export default ({ inverted }) => (
<div
className='Logo'
style={{
backgroundImage: 'url(/images/logo.png)'
}}
/>
)

6
src/components/Nav.css Normal file
View file

@ -0,0 +1,6 @@
.Nav {
background: white;
position: sticky;
top: 0;
z-index: 1;
}

View file

@ -1,27 +1,27 @@
import React from 'react'
import styled from 'styled-components'
import { Container, Flex } from './common'
import NavLink from './NavLink'
import { Link } from 'react-router-dom'
import Logo from './Logo'
import NavLink from './NavLink'
import './Nav.css'
const Nav = styled.div`
background: white;
position: sticky;
top: 0;
z-index: 1;
`
export default (props) => (
<Nav>
<Container>
<Flex alignCenter>
<Logo>
<span role='img' aria-label='Chili'>🌶</span>
</Logo>
<NavLink to='/' exact>Home</NavLink>
<NavLink to='/about/' exact>About</NavLink>
<NavLink to='/contact/' exact>Contact</NavLink>
</Flex>
</Container>
</Nav>
export default ({ handlePopupOpen }) => (
<nav className='Nav'>
<div className='Container'>
<div className='Flex alignCenter justifyStart'>
<Link to='/'>
<Logo />
</Link>
<NavLink to='/' exact>
Home
</NavLink>
<NavLink to='/about/' exact>
About
</NavLink>
<NavLink to='/contact/' exact>
Contact
</NavLink>
</div>
</div>
</nav>
)

View file

@ -0,0 +1,17 @@
.NavLink {
padding: 0.5rem 1rem;
display: block;
font-weight: 400;
transition: color 0.2s, border-bottom-color 0.2s;
color: inherit;
text-decoration: none;
border-bottom: 1px solid;
border-bottom-color: transparent;
}
.NavLink:hover,
.NavLink:active,
.NavLink:focus {
color: var(--primary);
border-bottom-color: currentColor;
}

View file

@ -1,29 +1,10 @@
import React from 'react'
import { Route, Link } from 'react-router-dom'
import styled from 'styled-components'
import { NavLink } from 'react-router-dom'
import { color } from '../globalStyles'
import './NavLink.css'
const NavLink = styled.span`
a {
padding: .5rem 1rem;
display: block;
font-weight: 400;
transition: color 0.2s, border-bottom-color 0.2s;
color: ${props => props.active ? color.primary : 'inherit'};
text-decoration: none;
border-bottom: 1px solid;
border-bottom-color: ${props => props.active ? color.primary : 'transparent'};
&:hover, &:active, &:focus {
color: ${props => props.active ? color.primary : 'inherit'};
}
}
`
export default ({ to, exact, match, children }) => (
<Route path={to} exact={exact} children={({match}) => (
<NavLink active={match}>
<Link to={to}>{children}</Link>
</NavLink>
)} />
export default ({ className, children, ...props }) => (
<NavLink {...props} className={`NavLink ${className || ''}`}>
{children}
</NavLink>
)

View file

@ -1,119 +0,0 @@
import React from 'react'
import styled from 'styled-components'
import { color } from '../globalStyles'
export default ({
name = 'Simple Form',
subject = '', // optional subject of the notification email
action = ''
}) => (
<StyledForm
name={name}
action={action}
data-netlify=''
data-netlify-honeypot='_gotcha'
>
<Label className='Label'>
<Input
className='Input'
type='text'
placeholder='Your Name'
name='name'
required
/>
</Label>
<Label className='Label'>
<Input
className='Input'
type='email'
placeholder='Your Email'
name='email'
required
/>
</Label>
<Label className='Label'>
<Textarea
className='Input'
placeholder='Message'
name='message'
rows='10'
required
/>
</Label>
<Input type='text' name='_gotcha' style={{ display: 'none' }} />
{!!subject && <Input type='hidden' name='subject' value={subject} />}
<Input type='hidden' name='form-name' value={name} />
<Button className='button' type='submit' value='Send' />
</StyledForm>
)
const StyledForm = styled.form`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
max-width: 500px;
margin: 2.5rem 0;
& > * + * {
margin-top: 1.5rem;
}
`
const Label = styled.label`
width: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
`
const Input = styled.input`
font-family: inherit;
font-weight: 400;
flex-grow: 1;
box-sizing: border-box;
display: block;
margin: 0;
border: none;
border-bottom: 1px solid #bababa;
padding: 0.5em 0;
line-height: 1;
transition: border-color 0.2s;
resize: none;
&:focus {
outline: none;
border-color: black;
}
&[disabled] {
opacity: 0.4;
pointer-events: none;
cursor: progress;
}
`
const Textarea = Input.withComponent('textarea')
const Button = styled.input`
background: ${color.primary};
color: white;
text-transform: uppercase;
text-decoration: none;
font-weight: 400;
letter-spacing: 0.15em;
font-size: 1.4rem;
display: inline-block;
padding: 1rem 2rem;
border: none;
transition: all 0.2s ease;
cursor: pointer;
&:hover,
&:focus {
color: white;
}
&[disabled] {
opacity: 0.4;
pointer-events: none;
cursor: progress;
}
`

View file

@ -1,7 +0,0 @@
import styled from 'styled-components'
export default styled.div`
p {
margin-bottom: 1rem;
}
`

View file

@ -0,0 +1,13 @@
.PageHeader {
line-height: 1em;
color: var(--primary);
background: var(--primary);
color: white;
}
.PageHeader--Title {
}
.PageHeader--Subtitle {
font-weight: 200;
}

View file

@ -1,35 +1,17 @@
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { Section, Container, BackgroundImage } from './common'
import { getImageSrc } from '../util/getImageUrl'
import { color } from '../globalStyles'
const Header = styled(Section)`
line-height: 1em;
color: ${color.primary};
background: ${color.primary};
color: white;
h2 {
font-weight: 200;
}
`
import BackgroundImage from './BackgroundImage'
import './PageHeader.css'
const PageHeader = ({ title, subtitle, backgroundImage }) => (
<Header relative>
{backgroundImage && (
<BackgroundImage
image={getImageSrc(backgroundImage, '1200')}
opacity={0.5}
/>
)}
<Container relative>
<h1>{title}</h1>
{subtitle ? <h2>{subtitle}</h2> : ''}
</Container>
</Header>
<div className='Section PageHeader relative'>
{backgroundImage && <BackgroundImage src={backgroundImage} opacity={0.5} />}
<div className='Container relative'>
<h1 className='PageHeader--Title'>{title}</h1>
{subtitle ? <h2 className='PageHeader--Subtitle'>{subtitle}</h2> : ''}
</div>
</div>
)
PageHeader.propTypes = {

View file

@ -1,40 +1,4 @@
import React, { Component } from 'react'
import styled from 'styled-components'
import { color } from '../globalStyles'
const Notification = styled.div`
position: fixed;
bottom: 0;
left: 0;
right: 0;
text-align: center;
background: ${color.primary};
color: white;
padding: 1rem;
min-height: 5rem;
transition: transform 0.1s ease-in-out;
transform: translateY(${props => (props.message ? '0' : '100%')});
`
const CloseButton = styled.button.attrs({
children: 'x'
})`
border: none;
background: white;
color: ${color.primary};
border-radius: 100%;
width: 2.5rem;
height: 2.5rem;
position: absolute;
right: 1rem;
top: 1rem;
cursor: pointer;
&:hover,
&:focus {
opacity: 0.7;
outline: none;
}
`
import { Component } from 'react'
export default class ServiceWorkerNotifications extends Component {
static defaultProps = {
@ -93,11 +57,6 @@ export default class ServiceWorkerNotifications extends Component {
render () {
this.props.reloadOnUpdate && this.reloadIfUpdated()
return this.state.message ? (
<Notification message={this.state.message}>
{this.state.message}
<CloseButton onClick={this.handleDismiss} />
</Notification>
) : null
return null
}
}

View file

@ -1,82 +0,0 @@
import styled, { css } from 'styled-components'
const mixin = {
position: css`
${props => {
if (props.relative) return 'position: relative;'
if (props.absolute) return 'position: absolute;'
}};
`,
textAlign: css`
${props => {
if (props.taCenter) return 'text-align: center;'
if (props.taLeft) return 'text-align: left;'
if (props.taRight) return 'text-align: right;'
}};
`
}
export const Absolute = styled.div`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
`
export const Relative = styled.div`
position: relative;
z-index: 0;
`
export const Section = styled.section`
${mixin.position}
width: 100%;
padding: ${props => {
if (props.thick) return '10rem 0'
if (props.thin) return '2.5rem 0'
return '5rem 0'
}};
`
export const Container = styled.div`
${mixin.position}
${mixin.textAlign}
margin: 0 auto;
width: 90vw;
max-width: ${props => {
if (props.skinny) return '888px'
if (props.skinnier) return '555px'
return '1111px'
}};
`
export const Flex = styled.div`
display: flex;
flex-direction: ${props => props.column ? 'column' : 'row'};
justify-content: ${props => {
if (props.justifyCenter) return 'center'
if (props.justifyEnd) return 'flex-end'
if (props.justifyBetween) return 'space-between'
if (props.justifyAround) return 'space-around'
return 'flex-start'
}};
align-items: ${props => {
if (props.alignStart) return 'flex-start'
if (props.alignEnd) return 'flex-end'
if (props.alignStretch) return 'stretch'
return 'center'
}};
height: ${props => props.fill ? '100%' : 'auto'};
width: ${props => props.fill ? '100%' : 'auto'};
`
export const BackgroundImage = styled(Absolute)`
background-size: cover;
background-position: center;
background-image: url(${props => props.image});
opacity: ${props => props.opacity || 1};
transition: opacity .5s ease;
`

254
src/globalStyles.css Normal file
View file

@ -0,0 +1,254 @@
:root {
--primary: #ff3d3d;
--secondary: #212121;
--lightGrey: whitesmoke;
--midGrey: #cacaca;
--font-primary: 'Open Sans', sans-serif;
--font-system: '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto',
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
}
html {
box-sizing: border-box;
font-size: 62.5%;
min-height: 100%;
background: var(--lightGrey);
}
body {
font-family: var(--font-system);
min-height: 100%;
position: relative;
background: white;
color: var(--secondary);
font-size: 1.6em;
font-weight: 300;
letter-spacing: 0.01em;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
a {
color: var(--primary);
}
strong {
font-weight: 600;
}
p {
margin-top: 0;
margin-bottom: 1em;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
margin-bottom: 0.5em;
}
pre {
background: var(--lightGrey);
line-height: 1.45;
font-size: 85%;
border-radius: 3px;
padding: 16px;
}
code {
font-size: 85%;
padding: 0.15em 0;
background: var(--lightGrey);
border-radius: 3px;
}
code:before,
code:after {
letter-spacing: -0.2em;
content: '\00a0';
}
pre code {
font-size: inherit;
}
pre code:before,
pre code:after {
display: none;
}
/* Utility Classes */
.light {
background-color: var(--lightGrey);
}
.white {
background-color: white;
}
.dark {
background-color: var(--primary);
color: white;
}
.dark h1,
.dark h2,
.dark h3,
.dark h4,
.dark h5,
.dark h6 {
color: white;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.taCenter {
text-align: center;
}
.taLeft {
text-align: left;
}
.taRight {
text-align: right;
}
/* Layout */
.Section {
width: 100%;
padding: 5rem 0;
}
.Section.thick {
padding: 10rem 0;
}
.Section.thin {
padding: 2.5rem 0;
}
.Section.noPadding {
padding: 0;
}
.Container {
max-width: 1111px;
width: 90%;
margin: 0 auto;
}
.Container.skinny {
max-width: 888px;
}
.Container.skinnier {
max-width: 555px;
}
/* Flex */
.Flex {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.Flex.column {
flex-direction: column;
}
.Flex.justifyCenter {
justify-content: center;
}
.Flex.justifyEnd {
justify-content: flex-end;
}
.Flex.justifyBetween {
justify-content: space-between;
}
.Flex.justifyAround {
justify-content: space-around;
}
.Flex.alignStart {
align-items: flex-start;
}
.Flex.alignEnd {
align-items: flex-end;
}
.Flex.alignStretch {
align-items: stretch;
}
.Flex.fill {
height: 100%;
width: 100%;
}
/* Buttons */
.Button {
border-radius: none;
display: inline-block;
background: var(--primary);
color: white;
text-transform: uppercase;
padding: 1.5rem;
border: 1px solid;
border-radius: 0.1rem;
border-color: var(--primary);
text-decoration: none;
font-size: 1.8rem;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
font-family: inherit;
cursor: pointer;
max-width: 35rem;
}
.Button:hover,
.Button:focus {
background: white;
border-color: currentColor;
color: var(--tertiary);
}
.Button[disabled] {
opacity: 0.4;
pointer-events: none;
cursor: progress;
}
.Button.Button--tertiary {
background: var(--tertiary);
border-color: var(--tertiary);
}
.Button.Button--tertiary:hover,
.Button.Button--tertiary:focus {
background: transparent;
border-color: currentColor;
color: var(--tertiary);
}
.Button.Button--inverted {
background: white;
border-color: white;
color: var(--tertiary);
}
.Button.Button--inverted:hover,
.Button.Button--inverted:focus {
background: transparent;
border-color: currentColor;
color: white;
background-image: linear-gradient(rgba(255, 255, 255, 0.1), rgba(0, 0, 0, 0)),
linear-gradient(
-32deg,
rgba(255, 255, 255, 0),
rgba(255, 255, 255, 0.05) 50%,
rgba(255, 255, 255, 0) 50%,
rgba(255, 255, 255, 0)
);
}

View file

@ -1,93 +0,0 @@
import { injectGlobal } from 'styled-components'
import { normalize } from 'polished'
export const font = {
primary: `'Open Sans', sans-serif`,
system: `-apple-system, BlinkMacSystemFont,
"Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans",
"Droid Sans", "Helvetica Neue", sans-serif`
}
export const color = {
primary: '#ff3d3d',
secondary: '#212121',
lightGrey: 'whitesmoke'
}
export default () => injectGlobal`
${normalize()}
html {
box-sizing: border-box;
font-size: 62.5%;
background: ${color.lightGrey};
}
body {
font-family: ${font.system};
min-height: 100vh;
position: relative;
background: white;
color: ${color.secondary};
font-size: 1.6em;
font-weight: 300;
letter-spacing: .01em;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
a {
color: ${color.primary};
}
strong {
font-weight: 600;
}
p {
margin-top: 0;
margin-bottom: 1em;
}
h1, h2, h3, h4, h5 ,h6{
margin: 0;
margin-bottom: 0.5em;
}
pre {
background: ${color.lightGrey};
line-height: 1.45;
font-size: 85%;
border-radius: 3px;
padding: 16px;
}
code {
font-size: 85%;
padding: 0.15em 0;
background: ${color.lightGrey};
border-radius: 3px;
&:before,
&:after {
letter-spacing: -0.2em;
content: "\00a0";
}
pre & {
font-size: inherit;
&:before,
&:after {
display: none;
}
}
}
`

View file

@ -1,8 +1,12 @@
import 'core-js/es6'
import React from 'react'
import { render } from 'react-snapshot'
import App from './App'
import registerServiceWorker from './registerServiceWorker'
import './netlifyIdentity'
import 'normalize.css'
import './globalStyles.css'
const rootEl = document.getElementById('root')
render(<App />, rootEl)

View file

@ -11,7 +11,7 @@ const parseFilename = filename => {
}
const getImageSrcset = path => {
if (path.match(/^http/) || path.match(/svg$/)) return null
if (!path || path.match(/^http/) || path.match(/svg$/) || window.CMS) { return null }
const { filename, extname } = parseFilename(path)
const pathname = encodeURI(filename.replace(outputDir, resizedDir))
@ -22,7 +22,7 @@ const getImageSrcset = path => {
}
const getImageSrc = (path, sizeRequested) => {
if (path.match(/^http/) || path.match(/svg$/)) return path
if (!path || path.match(/^http/) || path.match(/svg$/) || window.CMS) { return path }
sizeRequested = parseInt(sizeRequested, 10)
let size
if (sizeRequested) {

2
src/views/About.css Normal file
View file

@ -0,0 +1,2 @@
.About {
}

View file

@ -1,14 +1,13 @@
import React from 'react'
import Helmet from 'react-helmet'
import Page from '../components/Page'
import PageHeader from '../components/PageHeader'
import LazyImage from '../components/LazyImage'
import { Container, Section } from '../components/common'
import Content from '../components/Content.js'
import './About.css'
export default ({ page }) => (
<Page>
<div className='About'>
<Helmet>
<title>{page.title}</title>
</Helmet>
@ -17,17 +16,17 @@ export default ({ page }) => (
subtitle={page.subtitle}
backgroundImage={page.featuredImage}
/>
<Section thin>
<Container>
<div className='Section thin'>
<div className='Container'>
<Content source={page.section1} />
</Container>
</Section>
<Section thin>
<Container>
</div>
</div>
<div className='Seciton thin'>
<div className='Container'>
<Content source={page.section2} />
<p>The image below is a {'<LazyImage />'}</p>
<LazyImage src={page.featuredImage} alt='LazyImage' />
</Container>
</Section>
</Page>
</div>
</div>
</div>
)

2
src/views/Contact.css Normal file
View file

@ -0,0 +1,2 @@
.Contact {
}

View file

@ -1,31 +1,31 @@
import React from 'react'
import Helmet from 'react-helmet'
import Page from '../components/Page'
import PageHeader from '../components/PageHeader'
import NetlifyControlledForm from '../components/NetlifyControlledForm'
import NetlifySimpleForm from '../components/NetlifySimpleForm'
import { Container, Section } from '../components/common'
import Marked from 'react-markdown'
// import NetlifyControlledForm from '../components/NetlifyControlledForm'
import EnquiryFormSimple from '../components/EnquiryFormSimple'
import Content from '../components/Content'
import './Contact.css'
export default ({ page }) => (
<Page>
<div className='Contact'>
<PageHeader title={page.title} subtitle='<Contact />' />
<Section thin>
<Container>
<Marked source={page.content} />
<div className='Section thin'>
<div className='Container'>
<Content source={page.content} />
<br />
<h3>{'<NetlifyControlledForm />'}</h3>
<NetlifyControlledForm name={'Controlled Form'} />
{/* <NetlifyControlledForm name={'Controlled Form'} /> */}
<br />
<h3>{'<NetlifySimpleForm />'}</h3>
<NetlifySimpleForm name='Simple Form' />
<EnquiryFormSimple name='Simple Form' />
<em>Note: these will only work when deployed on Netlify</em>
<br />
<em>Also, they are active and I will receive submissions</em> 😉
</Container>
</Section>
</div>
</div>
<Helmet>
<title>{page.title}</title>
</Helmet>
</Page>
</div>
)

2
src/views/Home.css Normal file
View file

@ -0,0 +1,2 @@
.Home {
}

View file

@ -1,23 +1,23 @@
import React from 'react'
import Helmet from 'react-helmet'
import Page from '../components/Page'
import { Container, Section } from '../components/common'
import Content from '../components/Content'
import PageHeader from '../components/PageHeader'
import './Home.css'
export default ({ page }) => {
const { title, subtitle } = page
return (
<Page>
<main className='Home'>
<PageHeader title={title} subtitle={subtitle} />
<Section thin>
<Container>
<div className='Section thin'>
<div className='Container'>
<Content source={page.content} />
</Container>
</Section>
</div>
</div>
<Helmet>
<title>{title}</title>
</Helmet>
</Page>
</main>
)
}

6
src/views/NoMatch.css Normal file
View file

@ -0,0 +1,6 @@
.body--NoMatch .React-Wrap {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 100vh;
}

View file

@ -1,19 +1,31 @@
import React from 'react'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'
import Page from '../components/Page'
import PageHeader from '../components/PageHeader'
import { Section, Container } from '../components/common'
export default () => (
<Page>
<PageHeader title='404' subtitle='<NoMatch />' />
<Section>
<Container taCenter>
<h3>404 Page Not Found</h3>
</Container>
</Section>
import './NoMatch.css'
const NoMatch = ({ siteUrl }) => (
<div className='NoMatch'>
<section className='Section thick'>
<div className='Container taCenter'>
<h1>404 - Page Not Found</h1>
<p>
We can't find the page you are looking for!<br />Head back to{' '}
<a href={siteUrl}>
{siteUrl.replace(/(^https?:\/\/)/, '').replace(/\/$/, '')}
</a>
</p>
</div>
</section>
<Helmet>
<title>404 Page Not Found</title>
<body className='body--NoMatch' />
</Helmet>
</Page>
</div>
)
NoMatch.propTypes = {
siteUrl: PropTypes.string.isRequired
}
export default NoMatch

View file

@ -3,11 +3,9 @@ module.exports = {
stripPrefix: 'build/',
staticFileGlobs: [
'build/**/*.html',
'build/**/*.svg',
'build/images/*.jpg',
'build/images/uploads/resized/*.jpg',
'build/**/*.png',
'build/**/*.gif',
'build/**/*.svg', // all svgs
'build/images/*.+(jpg|png|gif)',
'build/images/uploads/**/*10.+(jpg|png|gif)', // 10px resized images
'build/**/*.ico',
'build/**/*.json',
'build/static/**/!(*map*)',

View file

@ -54,9 +54,9 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"
"@researchgate/react-intersection-observer@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@researchgate/react-intersection-observer/-/react-intersection-observer-0.5.0.tgz#c83341fe2d86467816373e9141c37b71b90798d9"
"@researchgate/react-intersection-observer@^0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@researchgate/react-intersection-observer/-/react-intersection-observer-0.6.0.tgz#505f20b62c8941a035442b61ac448870afb5d8a2"
dependencies:
invariant "^2.2.2"
prop-types "^15.6.0"
@ -1260,13 +1260,6 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"
buffer@^5.0.3:
version "5.0.8"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.8.tgz#84daa52e7cf2fa8ce4195bc5cf0f7809e0930b24"
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
builtin-modules@^1.0.0, builtin-modules@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
@ -1687,6 +1680,10 @@ core-js@^2.4.0, core-js@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
core-js@^2.5.3:
version "2.5.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -1772,10 +1769,6 @@ crypto-browserify@^3.11.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
css-color-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
css-color-names@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
@ -1816,14 +1809,6 @@ css-selector-tokenizer@^0.7.0:
fastparse "^1.1.1"
regexpu-core "^1.0.0"
css-to-react-native@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.0.4.tgz#cf4cc407558b3474d4ba8be1a2cd3b6ce713101b"
dependencies:
css-color-keywords "^1.0.0"
fbjs "^0.8.5"
postcss-value-parser "^3.3.0"
css-what@2.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
@ -2840,7 +2825,7 @@ fb-watchman@^2.0.0:
dependencies:
bser "^2.0.0"
fbjs@^0.8.16, fbjs@^0.8.5, fbjs@^0.8.9:
fbjs@^0.8.16:
version "0.8.16"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
dependencies:
@ -3376,10 +3361,6 @@ hoek@4.x.x:
version "4.2.0"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
hoist-non-react-statics@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
hoist-non-react-statics@^2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0"
@ -3729,10 +3710,6 @@ is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
is-function@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
is-glob@^2.0.0, is-glob@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
@ -3798,12 +3775,6 @@ is-plain-obj@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
is-plain-object@^2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
dependencies:
isobject "^3.0.1"
is-posix-bracket@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
@ -3892,10 +3863,6 @@ isobject@^2.0.0:
dependencies:
isarray "1.0.0"
isobject@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
isomorphic-fetch@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
@ -4857,6 +4824,10 @@ normalize-url@^1.4.0:
query-string "^4.1.0"
sort-keys "^1.0.0"
normalize.css@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-7.0.0.tgz#abfb1dd82470674e0322b53ceb1aaf412938e4bf"
npm-conf@^1.1.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9"
@ -5230,10 +5201,6 @@ pluralize@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
polished@^1.7.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/polished/-/polished-1.9.0.tgz#76d277d6610dbcb85477dbba1bd1aa8e642741b6"
portfinder@^1.0.9:
version "1.0.13"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9"
@ -6722,24 +6689,6 @@ style-loader@0.19.0:
loader-utils "^1.0.2"
schema-utils "^0.3.0"
styled-components@^2.1.0:
version "2.2.3"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.2.3.tgz#154575c269880c840f903f580287dab155cf684c"
dependencies:
buffer "^5.0.3"
css-to-react-native "^2.0.3"
fbjs "^0.8.9"
hoist-non-react-statics "^1.2.0"
is-function "^1.0.1"
is-plain-object "^2.0.1"
prop-types "^15.5.4"
stylis "3.x"
supports-color "^3.2.3"
stylis@3.x:
version "3.4.3"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.4.3.tgz#875bd0db3db37bb6de08f89275fc38ee2e32ee75"
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"