├── .nvmrc
├── gatsby-config.js
├── gatsby-browser.js
├── gatsby-ssr.js
├── .gitignore
├── src
├── pages
│ ├── 404.js
│ ├── page-2.js
│ ├── index.js
│ ├── products.module.css
│ └── products.js
├── components
│ ├── Header
│ │ └── index.js
│ ├── product.module.css
│ └── product.js
└── layouts
│ ├── index.js
│ └── index.css
├── plugins
└── grav-headless-plugin
│ ├── package.json
│ ├── gatsby-node.js
│ └── package-lock.json
├── README.md
├── package.json
├── gatsby-node.js
└── LICENSE
/.nvmrc:
--------------------------------------------------------------------------------
1 | 8.9.4
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteMetadata: {
3 | title: 'Gatsby Default Starter',
4 | },
5 | plugins: ['gatsby-plugin-react-helmet', 'grav-headless-plugin']
6 | };
7 |
--------------------------------------------------------------------------------
/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implement Gatsby's Browser APIs in this file.
3 | *
4 | * See: https://www.gatsbyjs.org/docs/browser-apis/
5 | */
6 |
7 | // You can delete this file if you're not using it
--------------------------------------------------------------------------------
/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
3 | *
4 | * See: https://www.gatsbyjs.org/docs/ssr-apis/
5 | */
6 |
7 | // You can delete this file if you're not using it
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project dependencies
2 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
3 | node_modules
4 | .cache/
5 | # Build directory
6 | public/
7 | .DS_Store
8 | yarn-error.log
9 |
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const NotFoundPage = () => (
4 |
5 |
NOT FOUND
6 |
You just hit a route that doesn't exist... the sadness.
7 |
8 | )
9 |
10 | export default NotFoundPage
11 |
--------------------------------------------------------------------------------
/src/pages/page-2.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'gatsby-link'
3 |
4 | const SecondPage = () => (
5 |
6 |
Hi from the second page
7 |
Welcome to page 2
8 |
Go back to the homepage
9 |
10 | )
11 |
12 | export default SecondPage
13 |
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'gatsby-link'
3 |
4 | const IndexPage = () => (
5 |
6 |
Hi folks!
7 |
8 |
Welcome to this Gatsby + Grav CMS + Snipcart demo store.
9 |
10 |
See our products!
11 |
12 | )
13 |
14 | export default IndexPage
15 |
--------------------------------------------------------------------------------
/src/pages/products.module.css:
--------------------------------------------------------------------------------
1 | .item {
2 | float: left;
3 | display: block;
4 | padding: 2em;
5 | width: 33%;
6 | text-align: center;
7 | }
8 |
9 | .itemsList {
10 | list-style-type: none;
11 | }
12 |
13 | .figCaption {
14 | text-align: center;
15 | }
16 |
17 | .image {
18 | border: solid 1px rebeccapurple;
19 | border-radius: 50%;
20 | padding: 1em;
21 | }
--------------------------------------------------------------------------------
/plugins/grav-headless-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "grav-headless-plugin",
3 | "version": "1.0.0",
4 | "description": "Sample plugin that generates Gatsby nodes from an API provided by Grav",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "Snipcart",
10 | "license": "ISC",
11 | "dependencies": {
12 | "axios": "^0.17.1",
13 | "gatsby-node-helpers": "^0.1.3",
14 | "slug": "^0.9.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Grav as Headless CMS Tied to Gatsby with GraphQL Schema
2 |
3 | 
4 |
5 | This repo contains the code for a demo Gatsby website, it uses Snipcart for e-commerce and gets the product information from a Grav headless setup.
6 |
7 | Steps:
8 |
9 | - Set up Grav CMS as a headless CMS.
10 | - Install React-powered static site generator Gatsby.
11 | - Create a source plugin to query the API with GraphQL.
12 | - The result? A small demo shop powered by Snipcart.
13 |
14 | > [Read full tutorial](https://snipcart.com/blog/react-graphql-grav-cms-headless-tutorial)
15 |
16 | > [See live demo](https://snipcart-gatsby-grav.netlify.com/)
17 |
18 | Enjoy folks!
19 |
--------------------------------------------------------------------------------
/src/components/Header/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'gatsby-link'
3 |
4 | const Header = () => (
5 |
11 |
18 |
19 |
26 | Gatsby
27 |
28 |
29 |
30 |
31 | )
32 |
33 | export default Header
34 |
--------------------------------------------------------------------------------
/src/components/product.module.css:
--------------------------------------------------------------------------------
1 | .productFigure img {
2 | border: solid 1px rebeccapurple;
3 | border-radius: 50%;
4 | padding: 1em;
5 | width: 250px;
6 | }
7 |
8 | section {
9 | clear: both;
10 | }
11 |
12 | .productFigure {
13 | float: left;
14 | }
15 |
16 | .actions {
17 | margin-top: 1em;
18 | text-align: center;
19 | }
20 |
21 | .buyButton {
22 | border: none;
23 | background-color: rebeccapurple;
24 | color: white;
25 | padding: 0.5em;
26 | border-radius: 5px;
27 | cursor: pointer;
28 | }
29 |
30 | .breadcrumb {
31 | margin-top: -1em;
32 | margin-bottom: 1em;
33 | }
34 |
35 | .breadcrumb a {
36 | text-decoration: none;
37 | font-size: smaller;
38 | border-bottom: solid 1px rebeccapurple;
39 | padding-bottom: 2px;
40 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-starter-default",
3 | "description": "Gatsby default starter",
4 | "version": "1.0.0",
5 | "author": "Kyle Mathews ",
6 | "dependencies": {
7 | "gatsby": "^1.9.158",
8 | "gatsby-link": "^1.6.34",
9 | "gatsby-plugin-react-helmet": "^2.0.3",
10 | "react-helmet": "^5.2.0"
11 | },
12 | "keywords": [
13 | "gatsby"
14 | ],
15 | "license": "MIT",
16 | "main": "n/a",
17 | "scripts": {
18 | "build": "gatsby build",
19 | "develop": "gatsby develop",
20 | "format": "prettier --trailing-comma es5 --no-semi --single-quote --write \"src/**/*.js\"",
21 | "test": "echo \"Error: no test specified\" && exit 1"
22 | },
23 | "devDependencies": {
24 | "axios": "^0.17.1",
25 | "gatsby-node-helpers": "^0.1.3",
26 | "prettier": "^1.10.2",
27 | "slash": "^1.0.0",
28 | "slug": "^0.9.1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const slash = require('slash')
2 | const path = require('path')
3 |
4 | exports.createPages = async ({graphql, boundActionCreators}) => {
5 | let result = await graphql(`
6 | {
7 | allSnipcartProduct {
8 | edges {
9 | node {
10 | path,
11 | userDefinedId,
12 | name
13 | }
14 | }
15 | }
16 | }
17 | `)
18 |
19 | const { createPage } = boundActionCreators
20 | const productTemplate = path.resolve('src/components/product.js')
21 |
22 | result.data.allSnipcartProduct
23 | .edges
24 | .map(x => x.node)
25 | .forEach(node => {
26 | createPage({
27 | path: node.path,
28 | component: slash(productTemplate),
29 | context: {
30 | id: node.userDefinedId
31 | }
32 | })
33 | })
34 | }
--------------------------------------------------------------------------------
/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/layouts/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Helmet from 'react-helmet'
4 |
5 | import Header from '../components/Header'
6 | import './index.css'
7 |
8 | const TemplateWrapper = ({ children }) => (
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
29 | {children()}
30 |
31 |
32 | )
33 |
34 | TemplateWrapper.propTypes = {
35 | children: PropTypes.func,
36 | }
37 |
38 | export default TemplateWrapper
39 |
--------------------------------------------------------------------------------
/src/pages/products.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'gatsby-link'
3 | import styles from './products.module.css'
4 |
5 | export default ({data}) => (
6 |
7 |
Products
8 |
9 |
10 | {data.allSnipcartProduct.edges.map((o, index) =>
11 | -
12 |
13 |
14 |
15 | Buy the {o.node.name} now
16 |
17 |
18 |
19 | )}
20 |
21 |
22 | )
23 |
24 | export const query = graphql`
25 | query snipcartProducts {
26 | allSnipcartProduct {
27 | edges {
28 | node {
29 | path,
30 | userDefinedId,
31 | name,
32 | image,
33 | price,
34 | description
35 | }
36 | }
37 | }
38 | }
39 | `
--------------------------------------------------------------------------------
/src/components/product.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'gatsby-link'
3 | import styles from './product.module.css'
4 | const NETLIFY_URL = 'https://snipcart-gatsby-grav.netlify.com'
5 | export default ({data, location}) => (
6 |
7 |
{data.snipcartProduct.name}
8 |
9 | Back to the products
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | {data.snipcartProduct.description}
19 |
20 |
21 |
29 |
30 |
31 |
32 | )
33 |
34 | export const query = graphql`
35 | query ProductById($id: String!) {
36 | snipcartProduct(userDefinedId: {eq:$id}) {
37 | userDefinedId,
38 | description,
39 | image,
40 | name,
41 | price
42 | }
43 | }
44 | `
--------------------------------------------------------------------------------
/plugins/grav-headless-plugin/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios').default
2 | const createNodeHelpers = require('gatsby-node-helpers').default
3 | const slug = require('slug')
4 | // This should be changed for production or
5 | const API_URL = 'https://snipcart-grav-headless.azurewebsites.net'
6 |
7 | exports.sourceNodes = async ({boundActionCreators}) => {
8 |
9 | const {createNode} = boundActionCreators
10 |
11 | const data = await fetchProducts()
12 |
13 | data.forEach(x => {
14 | createNode(x)
15 | })
16 |
17 | return
18 | }
19 |
20 | fetchProducts = async () => {
21 | const {
22 | createNodeFactory,
23 | generateNodeId,
24 | generateTypeName
25 | } = createNodeHelpers({
26 | typePrefix: `Snipcart`
27 | })
28 |
29 | const ProductNode = createNodeFactory('Product', node => {
30 | return node
31 | })
32 |
33 | try
34 | {
35 | // This is where we call Grav API.
36 | const response = await axios.get(`${API_URL}/products`, {
37 | params: {
38 | "return-as": "json"
39 | }
40 | })
41 |
42 | return response.data.children
43 | .map(x => x.header)
44 | .map(x => Object.assign(x, {
45 | userDefinedId: x.id,
46 | image: `${API_URL}/${getImagePath(x)}`,
47 | path: `/products/${slug(x.name)}-${slug(x.id)}`.toLowerCase()
48 | }))
49 | .map(ProductNode)
50 | }
51 | catch (error) {
52 | console.log(error)
53 | throw e
54 | }
55 | }
56 |
57 | function getImagePath(node) {
58 | if (node.image) {
59 | let image = Object.keys(node.image)
60 | .map(x => node.image[x])[0]
61 |
62 | return image.path;
63 | }
64 |
65 | return null;
66 | }
--------------------------------------------------------------------------------
/plugins/grav-headless-plugin/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "grav-headless-plugin",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "axios": {
8 | "version": "0.17.1",
9 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz",
10 | "integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=",
11 | "requires": {
12 | "follow-redirects": "1.4.0",
13 | "is-buffer": "1.1.6"
14 | }
15 | },
16 | "debug": {
17 | "version": "3.1.0",
18 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
19 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
20 | "requires": {
21 | "ms": "2.0.0"
22 | }
23 | },
24 | "follow-redirects": {
25 | "version": "1.4.0",
26 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.0.tgz",
27 | "integrity": "sha512-SLUmsiaGeQa2qgJJzJgHpQ6lARP3uyVr0SkMryJmoE86XvUeM7RkYD5FT0rNyjCV5zHlNUpcp3l/6oUkqMEOqg==",
28 | "requires": {
29 | "debug": "3.1.0"
30 | }
31 | },
32 | "gatsby-node-helpers": {
33 | "version": "0.1.3",
34 | "resolved": "https://registry.npmjs.org/gatsby-node-helpers/-/gatsby-node-helpers-0.1.3.tgz",
35 | "integrity": "sha1-VqqnjEc3m78HbuReh13GzIMVBq0=",
36 | "requires": {
37 | "json-stringify-safe": "5.0.1",
38 | "lodash": "4.17.4"
39 | }
40 | },
41 | "is-buffer": {
42 | "version": "1.1.6",
43 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
44 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
45 | },
46 | "json-stringify-safe": {
47 | "version": "5.0.1",
48 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
49 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
50 | },
51 | "lodash": {
52 | "version": "4.17.4",
53 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
54 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
55 | },
56 | "ms": {
57 | "version": "2.0.0",
58 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
59 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
60 | },
61 | "slug": {
62 | "version": "0.9.1",
63 | "resolved": "https://registry.npmjs.org/slug/-/slug-0.9.1.tgz",
64 | "integrity": "sha1-rwj2CKfBFRa2F3iqgA3OhMUYz9o=",
65 | "requires": {
66 | "unicode": "10.0.0"
67 | }
68 | },
69 | "unicode": {
70 | "version": "10.0.0",
71 | "resolved": "https://registry.npmjs.org/unicode/-/unicode-10.0.0.tgz",
72 | "integrity": "sha1-5dUcHbk7bHGguHngsMSvfm/faI4="
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/layouts/index.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-family: sans-serif;
3 | -ms-text-size-adjust: 100%;
4 | -webkit-text-size-adjust: 100%;
5 | }
6 | body {
7 | margin: 0;
8 | }
9 | article,
10 | aside,
11 | details,
12 | figcaption,
13 | figure,
14 | footer,
15 | header,
16 | main,
17 | menu,
18 | nav,
19 | section,
20 | summary {
21 | display: block;
22 | }
23 | audio,
24 | canvas,
25 | progress,
26 | video {
27 | display: inline-block;
28 | }
29 | audio:not([controls]) {
30 | display: none;
31 | height: 0;
32 | }
33 | progress {
34 | vertical-align: baseline;
35 | }
36 | [hidden],
37 | template {
38 | display: none;
39 | }
40 | a {
41 | background-color: transparent;
42 | -webkit-text-decoration-skip: objects;
43 | }
44 | a:active,
45 | a:hover {
46 | outline-width: 0;
47 | }
48 | abbr[title] {
49 | border-bottom: none;
50 | text-decoration: underline;
51 | text-decoration: underline dotted;
52 | }
53 | b,
54 | strong {
55 | font-weight: inherit;
56 | font-weight: bolder;
57 | }
58 | dfn {
59 | font-style: italic;
60 | }
61 | h1 {
62 | font-size: 2em;
63 | margin: .67em 0;
64 | }
65 | mark {
66 | background-color: #ff0;
67 | color: #000;
68 | }
69 | small {
70 | font-size: 80%;
71 | }
72 | sub,
73 | sup {
74 | font-size: 75%;
75 | line-height: 0;
76 | position: relative;
77 | vertical-align: baseline;
78 | }
79 | sub {
80 | bottom: -.25em;
81 | }
82 | sup {
83 | top: -.5em;
84 | }
85 | img {
86 | border-style: none;
87 | }
88 | svg:not(:root) {
89 | overflow: hidden;
90 | }
91 | code,
92 | kbd,
93 | pre,
94 | samp {
95 | font-family: monospace, monospace;
96 | font-size: 1em;
97 | }
98 | figure {
99 | margin: 1em 40px;
100 | }
101 | hr {
102 | box-sizing: content-box;
103 | height: 0;
104 | overflow: visible;
105 | }
106 | button,
107 | input,
108 | optgroup,
109 | select,
110 | textarea {
111 | font: inherit;
112 | margin: 0;
113 | }
114 | optgroup {
115 | font-weight: 700;
116 | }
117 | button,
118 | input {
119 | overflow: visible;
120 | }
121 | button,
122 | select {
123 | text-transform: none;
124 | }
125 | [type=reset],
126 | [type=submit],
127 | button,
128 | html [type=button] {
129 | -webkit-appearance: button;
130 | }
131 | [type=button]::-moz-focus-inner,
132 | [type=reset]::-moz-focus-inner,
133 | [type=submit]::-moz-focus-inner,
134 | button::-moz-focus-inner {
135 | border-style: none;
136 | padding: 0;
137 | }
138 | [type=button]:-moz-focusring,
139 | [type=reset]:-moz-focusring,
140 | [type=submit]:-moz-focusring,
141 | button:-moz-focusring {
142 | outline: 1px dotted ButtonText;
143 | }
144 | fieldset {
145 | border: 1px solid silver;
146 | margin: 0 2px;
147 | padding: .35em .625em .75em;
148 | }
149 | legend {
150 | box-sizing: border-box;
151 | color: inherit;
152 | display: table;
153 | max-width: 100%;
154 | padding: 0;
155 | white-space: normal;
156 | }
157 | textarea {
158 | overflow: auto;
159 | }
160 | [type=checkbox],
161 | [type=radio] {
162 | box-sizing: border-box;
163 | padding: 0;
164 | }
165 | [type=number]::-webkit-inner-spin-button,
166 | [type=number]::-webkit-outer-spin-button {
167 | height: auto;
168 | }
169 | [type=search] {
170 | -webkit-appearance: textfield;
171 | outline-offset: -2px;
172 | }
173 | [type=search]::-webkit-search-cancel-button,
174 | [type=search]::-webkit-search-decoration {
175 | -webkit-appearance: none;
176 | }
177 | ::-webkit-input-placeholder {
178 | color: inherit;
179 | opacity: .54;
180 | }
181 | ::-webkit-file-upload-button {
182 | -webkit-appearance: button;
183 | font: inherit;
184 | }
185 | html {
186 | font: 112.5%/1.45em georgia, serif;
187 | box-sizing: border-box;
188 | overflow-y: scroll;
189 | }
190 | * {
191 | box-sizing: inherit;
192 | }
193 | *:before {
194 | box-sizing: inherit;
195 | }
196 | *:after {
197 | box-sizing: inherit;
198 | }
199 | body {
200 | color: hsla(0, 0%, 0%, 0.8);
201 | font-family: georgia, serif;
202 | font-weight: normal;
203 | word-wrap: break-word;
204 | font-kerning: normal;
205 | -moz-font-feature-settings: "kern", "liga", "clig", "calt";
206 | -ms-font-feature-settings: "kern", "liga", "clig", "calt";
207 | -webkit-font-feature-settings: "kern", "liga", "clig", "calt";
208 | font-feature-settings: "kern", "liga", "clig", "calt";
209 | }
210 | img {
211 | max-width: 100%;
212 | margin-left: 0;
213 | margin-right: 0;
214 | margin-top: 0;
215 | padding-bottom: 0;
216 | padding-left: 0;
217 | padding-right: 0;
218 | padding-top: 0;
219 | margin-bottom: 1.45rem;
220 | }
221 | h1 {
222 | margin-left: 0;
223 | margin-right: 0;
224 | margin-top: 0;
225 | padding-bottom: 0;
226 | padding-left: 0;
227 | padding-right: 0;
228 | padding-top: 0;
229 | margin-bottom: 1.45rem;
230 | color: inherit;
231 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
232 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
233 | font-weight: bold;
234 | text-rendering: optimizeLegibility;
235 | font-size: 2.25rem;
236 | line-height: 1.1;
237 | }
238 | h2 {
239 | margin-left: 0;
240 | margin-right: 0;
241 | margin-top: 0;
242 | padding-bottom: 0;
243 | padding-left: 0;
244 | padding-right: 0;
245 | padding-top: 0;
246 | margin-bottom: 1.45rem;
247 | color: inherit;
248 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
249 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
250 | font-weight: bold;
251 | text-rendering: optimizeLegibility;
252 | font-size: 1.62671rem;
253 | line-height: 1.1;
254 | }
255 | h3 {
256 | margin-left: 0;
257 | margin-right: 0;
258 | margin-top: 0;
259 | padding-bottom: 0;
260 | padding-left: 0;
261 | padding-right: 0;
262 | padding-top: 0;
263 | margin-bottom: 1.45rem;
264 | color: inherit;
265 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
266 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
267 | font-weight: bold;
268 | text-rendering: optimizeLegibility;
269 | font-size: 1.38316rem;
270 | line-height: 1.1;
271 | }
272 | h4 {
273 | margin-left: 0;
274 | margin-right: 0;
275 | margin-top: 0;
276 | padding-bottom: 0;
277 | padding-left: 0;
278 | padding-right: 0;
279 | padding-top: 0;
280 | margin-bottom: 1.45rem;
281 | color: inherit;
282 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
283 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
284 | font-weight: bold;
285 | text-rendering: optimizeLegibility;
286 | font-size: 1rem;
287 | line-height: 1.1;
288 | }
289 | h5 {
290 | margin-left: 0;
291 | margin-right: 0;
292 | margin-top: 0;
293 | padding-bottom: 0;
294 | padding-left: 0;
295 | padding-right: 0;
296 | padding-top: 0;
297 | margin-bottom: 1.45rem;
298 | color: inherit;
299 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
300 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
301 | font-weight: bold;
302 | text-rendering: optimizeLegibility;
303 | font-size: 0.85028rem;
304 | line-height: 1.1;
305 | }
306 | h6 {
307 | margin-left: 0;
308 | margin-right: 0;
309 | margin-top: 0;
310 | padding-bottom: 0;
311 | padding-left: 0;
312 | padding-right: 0;
313 | padding-top: 0;
314 | margin-bottom: 1.45rem;
315 | color: inherit;
316 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
317 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
318 | font-weight: bold;
319 | text-rendering: optimizeLegibility;
320 | font-size: 0.78405rem;
321 | line-height: 1.1;
322 | }
323 | hgroup {
324 | margin-left: 0;
325 | margin-right: 0;
326 | margin-top: 0;
327 | padding-bottom: 0;
328 | padding-left: 0;
329 | padding-right: 0;
330 | padding-top: 0;
331 | margin-bottom: 1.45rem;
332 | }
333 | ul {
334 | margin-left: 1.45rem;
335 | margin-right: 0;
336 | margin-top: 0;
337 | padding-bottom: 0;
338 | padding-left: 0;
339 | padding-right: 0;
340 | padding-top: 0;
341 | margin-bottom: 1.45rem;
342 | list-style-position: outside;
343 | list-style-image: none;
344 | }
345 | ol {
346 | margin-left: 1.45rem;
347 | margin-right: 0;
348 | margin-top: 0;
349 | padding-bottom: 0;
350 | padding-left: 0;
351 | padding-right: 0;
352 | padding-top: 0;
353 | margin-bottom: 1.45rem;
354 | list-style-position: outside;
355 | list-style-image: none;
356 | }
357 | dl {
358 | margin-left: 0;
359 | margin-right: 0;
360 | margin-top: 0;
361 | padding-bottom: 0;
362 | padding-left: 0;
363 | padding-right: 0;
364 | padding-top: 0;
365 | margin-bottom: 1.45rem;
366 | }
367 | dd {
368 | margin-left: 0;
369 | margin-right: 0;
370 | margin-top: 0;
371 | padding-bottom: 0;
372 | padding-left: 0;
373 | padding-right: 0;
374 | padding-top: 0;
375 | margin-bottom: 1.45rem;
376 | }
377 | p {
378 | margin-left: 0;
379 | margin-right: 0;
380 | margin-top: 0;
381 | padding-bottom: 0;
382 | padding-left: 0;
383 | padding-right: 0;
384 | padding-top: 0;
385 | margin-bottom: 1.45rem;
386 | }
387 | figure {
388 | margin-left: 0;
389 | margin-right: 0;
390 | margin-top: 0;
391 | padding-bottom: 0;
392 | padding-left: 0;
393 | padding-right: 0;
394 | padding-top: 0;
395 | margin-bottom: 1.45rem;
396 | }
397 | pre {
398 | margin-left: 0;
399 | margin-right: 0;
400 | margin-top: 0;
401 | padding-bottom: 0;
402 | padding-left: 0;
403 | padding-right: 0;
404 | padding-top: 0;
405 | margin-bottom: 1.45rem;
406 | font-size: 0.85rem;
407 | line-height: 1.42;
408 | background: hsla(0, 0%, 0%, 0.04);
409 | border-radius: 3px;
410 | overflow: auto;
411 | word-wrap: normal;
412 | padding: 1.45rem;
413 | }
414 | table {
415 | margin-left: 0;
416 | margin-right: 0;
417 | margin-top: 0;
418 | padding-bottom: 0;
419 | padding-left: 0;
420 | padding-right: 0;
421 | padding-top: 0;
422 | margin-bottom: 1.45rem;
423 | font-size: 1rem;
424 | line-height: 1.45rem;
425 | border-collapse: collapse;
426 | width: 100%;
427 | }
428 | fieldset {
429 | margin-left: 0;
430 | margin-right: 0;
431 | margin-top: 0;
432 | padding-bottom: 0;
433 | padding-left: 0;
434 | padding-right: 0;
435 | padding-top: 0;
436 | margin-bottom: 1.45rem;
437 | }
438 | blockquote {
439 | margin-left: 1.45rem;
440 | margin-right: 1.45rem;
441 | margin-top: 0;
442 | padding-bottom: 0;
443 | padding-left: 0;
444 | padding-right: 0;
445 | padding-top: 0;
446 | margin-bottom: 1.45rem;
447 | }
448 | form {
449 | margin-left: 0;
450 | margin-right: 0;
451 | margin-top: 0;
452 | padding-bottom: 0;
453 | padding-left: 0;
454 | padding-right: 0;
455 | padding-top: 0;
456 | margin-bottom: 1.45rem;
457 | }
458 | noscript {
459 | margin-left: 0;
460 | margin-right: 0;
461 | margin-top: 0;
462 | padding-bottom: 0;
463 | padding-left: 0;
464 | padding-right: 0;
465 | padding-top: 0;
466 | margin-bottom: 1.45rem;
467 | }
468 | iframe {
469 | margin-left: 0;
470 | margin-right: 0;
471 | margin-top: 0;
472 | padding-bottom: 0;
473 | padding-left: 0;
474 | padding-right: 0;
475 | padding-top: 0;
476 | margin-bottom: 1.45rem;
477 | }
478 | hr {
479 | margin-left: 0;
480 | margin-right: 0;
481 | margin-top: 0;
482 | padding-bottom: 0;
483 | padding-left: 0;
484 | padding-right: 0;
485 | padding-top: 0;
486 | margin-bottom: calc(1.45rem - 1px);
487 | background: hsla(0, 0%, 0%, 0.2);
488 | border: none;
489 | height: 1px;
490 | }
491 | address {
492 | margin-left: 0;
493 | margin-right: 0;
494 | margin-top: 0;
495 | padding-bottom: 0;
496 | padding-left: 0;
497 | padding-right: 0;
498 | padding-top: 0;
499 | margin-bottom: 1.45rem;
500 | }
501 | b {
502 | font-weight: bold;
503 | }
504 | strong {
505 | font-weight: bold;
506 | }
507 | dt {
508 | font-weight: bold;
509 | }
510 | th {
511 | font-weight: bold;
512 | }
513 | li {
514 | margin-bottom: calc(1.45rem / 2);
515 | }
516 | ol li {
517 | padding-left: 0;
518 | }
519 | ul li {
520 | padding-left: 0;
521 | }
522 | li > ol {
523 | margin-left: 1.45rem;
524 | margin-bottom: calc(1.45rem / 2);
525 | margin-top: calc(1.45rem / 2);
526 | }
527 | li > ul {
528 | margin-left: 1.45rem;
529 | margin-bottom: calc(1.45rem / 2);
530 | margin-top: calc(1.45rem / 2);
531 | }
532 | blockquote *:last-child {
533 | margin-bottom: 0;
534 | }
535 | li *:last-child {
536 | margin-bottom: 0;
537 | }
538 | p *:last-child {
539 | margin-bottom: 0;
540 | }
541 | li > p {
542 | margin-bottom: calc(1.45rem / 2);
543 | }
544 | code {
545 | font-size: 0.85rem;
546 | line-height: 1.45rem;
547 | }
548 | kbd {
549 | font-size: 0.85rem;
550 | line-height: 1.45rem;
551 | }
552 | samp {
553 | font-size: 0.85rem;
554 | line-height: 1.45rem;
555 | }
556 | abbr {
557 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
558 | cursor: help;
559 | }
560 | acronym {
561 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
562 | cursor: help;
563 | }
564 | abbr[title] {
565 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
566 | cursor: help;
567 | text-decoration: none;
568 | }
569 | thead {
570 | text-align: left;
571 | }
572 | td,
573 | th {
574 | text-align: left;
575 | border-bottom: 1px solid hsla(0, 0%, 0%, 0.12);
576 | font-feature-settings: "tnum";
577 | -moz-font-feature-settings: "tnum";
578 | -ms-font-feature-settings: "tnum";
579 | -webkit-font-feature-settings: "tnum";
580 | padding-left: 0.96667rem;
581 | padding-right: 0.96667rem;
582 | padding-top: 0.725rem;
583 | padding-bottom: calc(0.725rem - 1px);
584 | }
585 | th:first-child,
586 | td:first-child {
587 | padding-left: 0;
588 | }
589 | th:last-child,
590 | td:last-child {
591 | padding-right: 0;
592 | }
593 | tt,
594 | code {
595 | background-color: hsla(0, 0%, 0%, 0.04);
596 | border-radius: 3px;
597 | font-family: "SFMono-Regular", Consolas, "Roboto Mono", "Droid Sans Mono",
598 | "Liberation Mono", Menlo, Courier, monospace;
599 | padding: 0;
600 | padding-top: 0.2em;
601 | padding-bottom: 0.2em;
602 | }
603 | pre code {
604 | background: none;
605 | line-height: 1.42;
606 | }
607 | code:before,
608 | code:after,
609 | tt:before,
610 | tt:after {
611 | letter-spacing: -0.2em;
612 | content: " ";
613 | }
614 | pre code:before,
615 | pre code:after,
616 | pre tt:before,
617 | pre tt:after {
618 | content: "";
619 | }
620 | @media only screen and (max-width: 480px) {
621 | html {
622 | font-size: 100%;
623 | }
624 | }
625 |
--------------------------------------------------------------------------------