To Vanilla!

This commit is contained in:
Eric Jinks 2018-01-23 12:21:56 +10:00
parent 9987db1956
commit 05e7c2babd
37 changed files with 610 additions and 545 deletions

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,35 +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",
"normalize.css": "^7.0.0",
"polished": "^1.7.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-content && 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-content": "npm run parse-content & npm run resize-images",
"watch:content": "chokidar 'content/**/**' -c 'npm run prepare-content' --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"]
}
}

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

@ -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>
)

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;
`

View file

@ -142,8 +142,8 @@ pre code:after {
}
.Container {
max-width: 1467px;
width: 90vw;
max-width: 1111px;
width: 90%;
margin: 0 auto;
}
.Container.skinny {

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>
)
}

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

@ -0,0 +1,5 @@
.body--NoMatch .React-Wrap {
display: flex;
flex-direction: column;
justify-content: space-between;
}

View file

@ -1,19 +1,24 @@
import React from 'react'
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'
export default ({ siteUrl }) => (
<div className='Page'>
<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>
)

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"
@ -1776,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"
@ -1820,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"
@ -2844,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:
@ -3380,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"
@ -3733,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"
@ -3802,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"
@ -3896,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"
@ -5238,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"
@ -6730,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"