diff --git a/src/App.js b/src/App.js
index bfe936b..9d26173 100644
--- a/src/App.js
+++ b/src/App.js
@@ -5,6 +5,7 @@ import Helmet from 'react-helmet'
import ScrollToTop from './components/ScrollToTop'
import Home from './views/Home'
import About from './views/About'
+import Contact from './views/Contact'
import NoMatch from './views/NoMatch'
import Nav from './components/Nav'
import NavLink from './components/NavLink'
@@ -13,7 +14,7 @@ import GithubCorner from './components/GithubCorner'
import ServiceWorkerNotifications from './components/ServiceWorkerNotifications'
import globalStyles from './globalStyles'
-const siteTitle = 'HyperStatic'
+export const siteTitle = 'HyperStatic'
const routes = [
{
title: 'Home',
@@ -25,6 +26,11 @@ const routes = [
path: '/about/',
comp: About,
exact: true
+ }, {
+ title: 'Contact',
+ path: '/contact/',
+ comp: Contact,
+ exact: true
}
]
diff --git a/src/components/NetlifyForm.js b/src/components/NetlifyForm.js
new file mode 100644
index 0000000..d8d050d
--- /dev/null
+++ b/src/components/NetlifyForm.js
@@ -0,0 +1,230 @@
+import React, { Component } from 'react'
+import styled from 'styled-components'
+import { stringify } from 'qs'
+import { color } from '../globalStyles'
+import { siteTitle } from '../App'
+const fetch = window.fetch
+
+class Form extends Component {
+ state = {
+ name: '',
+ email: '',
+ message: '',
+ subject: `New Submission from ${siteTitle}!`,
+ _gotcha: '',
+ disabled: false,
+ alert: '',
+ action: '/contact-us/',
+ 'form-name': 'Contact'
+ }
+
+ form = null
+
+ componentDidMount () {
+ Array.from(document.querySelectorAll('.Form .Input')).forEach(input => {
+ input.addEventListener('invalid', () => {
+ console.log(input)
+ input.dataset.touched = true
+ })
+ input.addEventListener('blur', () => {
+ if (input.value !== '') input.dataset.touched = true
+ })
+ })
+ }
+
+ handleSubmit = (e) => {
+ e.preventDefault()
+ const data = {
+ name: this.state.name,
+ email: this.state.email,
+ message: this.state.message,
+ subject: this.state.subject,
+ _gotcha: this.state._gotcha,
+ 'form-name': this.state['form-name']
+ }
+ this.setState({ disabled: true })
+ fetch(this.state.action + '?' + stringify(data), {
+ method: 'POST'
+ })
+ .then(res => {
+ if (res.ok) {
+ return res
+ } else {
+ throw new Error('Network error')
+ }
+ })
+ .then(res => {
+ this.setState({
+ disabled: false,
+ alert: 'Thanks for your enquiry, we will get back to you soon.',
+ name: '',
+ email: '',
+ message: '',
+ subject: `New Submission from ${window.location.host}!`,
+ _gotcha: ''
+ })
+ })
+ .catch(err => {
+ console.log(err)
+ this.setState({
+ disabled: false,
+ alert: '❗️ There is a problem, your message has not been sent, please try contacting us via email'
+ })
+ })
+ }
+
+ handleChange = (e) => {
+ this.setState({
+ [e.target.name]: e.target.value
+ })
+ }
+
+ render () {
+ return (
+ { this.form = form }}
+ className='Form'
+ action={this.state.action}
+ onSubmit={this.handleSubmit}
+ data-netlify=''
+ data-netlify-honeypot='_gotcha'
+ >
+ { this.state.alert && {this.state.alert} }
+
+
+
+
+
+
+
+
+ )
+ }
+}
+
+export default Form
+
+const LineGroup = () => (
+
+
+
+
+
+
+)
+
+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 Line = styled.svg`
+ width: 100%;
+ height: 1px;
+ stroke: #bababa;
+ stroke-width: 2px;
+
+ & .focus,
+ & .valid,
+ & .invalid {
+ transition: all 0.2s;
+ stroke-dasharray: 0, 20;
+ stroke-dashoffset: -20;
+ }
+
+ & .focus {
+ stroke: black;
+ }
+
+ & .valid {
+ stroke: hsl(166, 72%, 40%);
+ }
+
+ & .invalid {
+ stroke: var(--col1);
+ }
+`
+const Input = styled.input`
+ font-family: inherit;
+ font-weight: 400;
+ flex-grow: 1;
+ box-sizing: border-box;
+ display: block;
+ margin: 0;
+ border: none;
+ padding: 0.5em 0;
+ line-height: 1;
+ transition: border-color 0.2s;
+ resize: none;
+
+ &:focus {
+ outline: none;
+ }
+
+ &:focus ~ ${Line} .focus,
+ &:valid ~ ${Line} .valid,
+ &[data-touched]:invalid ~ ${Line} .invalid {
+ stroke-dasharray: 40, 0;
+ stroke-dashoffset: 0;
+ }
+
+ &[disabled]{
+ opacity: .4;
+ pointer-events: none;
+ cursor: progress;
+ }
+`
+const Textarea = Input.withComponent('textarea')
+const Alert = styled.p`
+ background: whitesmoke;
+ width: 100%;
+ padding: 2rem;
+`
+const Button = styled.input`
+ background: ${color.primary};
+ color: white;
+ text-transform: uppercase;
+ text-decoration: none;
+ font-weight: 400;
+ letter-spacing: .15em;
+ font-size: 1.4rem;
+ display: inline-block;
+ padding: 1rem 2rem;
+ border: none;
+ transition: all .2s ease;
+ cursor: pointer;
+
+ &:hover,
+ &:focus {
+ color: white;
+ }
+
+ &[disabled] {
+ opacity: .4;
+ pointer-events: none;
+ cursor: progress;
+ }
+`
diff --git a/src/globalStyles.js b/src/globalStyles.js
index 13567f6..bf089ae 100644
--- a/src/globalStyles.js
+++ b/src/globalStyles.js
@@ -47,9 +47,10 @@ export default () => injectGlobal`
color: ${color.primary};
}
- strong{
+ strong {
font-weight: 600;
}
+
p {
margin-top: 0;
margin-bottom: 1em;
@@ -59,4 +60,34 @@ export default () => injectGlobal`
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;
+ }
+ }
+ }
`
diff --git a/src/views/Contact.js b/src/views/Contact.js
new file mode 100644
index 0000000..60061da
--- /dev/null
+++ b/src/views/Contact.js
@@ -0,0 +1,37 @@
+import React from 'react'
+import Helmet from 'react-helmet'
+import Page from '../components/Page'
+import PageHeader from '../components/PageHeader'
+import NetlifyForm from '../components/NetlifyForm'
+import { Container, Section } from '../components/common'
+import Marked from 'react-markdown'
+
+const content = `
+ # Example contact form
+
+ This form is setup to use Netlify's form handling:
+
+ - the form action is set to the current absolute url: \`action: '/contact/'\`
+ - a name attribute is sent with the form's data \`'form-name': 'Contact'\`
+ - netlify data attributes are added to the form \`data-netlify data-netlify-honeypot\`
+
+ Find out more in the [Netlify Docs](https://www.netlify.com/docs/form-handling/).
+`
+
+export default ({ title }) => (
+
+
+
+
+
+
+ {''}
+
+ Note: this will only work when deployed on Netlify
+
+
+
+ {title}
+
+
+)