├── .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 | ![Gastby](https://snipcart.com/media/204100/grav-headless.png) 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 | 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 | --------------------------------------------------------------------------------