├── config └── test │ ├── __mocks__ │ ├── fileMock.js │ └── gatsby.js │ ├── loadershim.js │ ├── jest-preprocess.js │ └── test-setup.js ├── src ├── components │ ├── Footer │ │ ├── index.js │ │ ├── Footer.module.scss │ │ └── Footer.js │ ├── Header │ │ ├── index.js │ │ ├── Header.module.scss │ │ └── Header.js │ ├── Layout │ │ ├── index.js │ │ ├── Layout.module.scss │ │ └── Layout.js │ ├── HelloWorld │ │ ├── index.js │ │ ├── HelloWorld.module.scss │ │ ├── tests │ │ │ └── HelloWorld.test.js │ │ └── HelloWorld.js │ ├── image.js │ └── seo.js ├── images │ └── moustache.png ├── styles │ ├── base.scss │ ├── _theme.scss │ ├── _mixins.scss │ ├── _global.scss │ ├── _breakpoints.scss │ ├── _font.scss │ └── _reset.scss ├── data │ └── static-data.js ├── pages │ ├── tests │ │ └── index.test.js │ ├── 404.js │ ├── index.js │ └── about.js ├── blog │ └── example-blog-post.md └── templates │ ├── blog-post.module.scss │ └── blog-post.js ├── .prettierrc ├── gatsby-ssr.js ├── gatsby-browser.js ├── README.md ├── jest.config.js ├── LICENSE ├── .gitignore ├── gatsby-node.js ├── gatsby-config.js └── package.json /config/test/__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub' 2 | -------------------------------------------------------------------------------- /config/test/loadershim.js: -------------------------------------------------------------------------------- 1 | global.___loader = { 2 | enqueue: jest.fn(), 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | import Footer from './Footer' 2 | export default Footer 3 | -------------------------------------------------------------------------------- /src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import Header from './Header' 2 | export default Header 3 | -------------------------------------------------------------------------------- /src/components/Layout/index.js: -------------------------------------------------------------------------------- 1 | import Layout from './Layout' 2 | export default Layout; 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /src/components/HelloWorld/index.js: -------------------------------------------------------------------------------- 1 | import HelloWorld from './HelloWorld' 2 | export default HelloWorld 3 | -------------------------------------------------------------------------------- /src/images/moustache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexdinesh/gatsby-boilerplate/HEAD/src/images/moustache.png -------------------------------------------------------------------------------- /src/styles/base.scss: -------------------------------------------------------------------------------- 1 | @import 'reset'; 2 | // uncomment if you're using custom font 3 | // @import 'font'; 4 | @import 'global'; -------------------------------------------------------------------------------- /src/data/static-data.js: -------------------------------------------------------------------------------- 1 | export const frontmatter = { 2 | staticData: { 3 | greet: 'Whoa! This is naaaice!', 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /config/test/jest-preprocess.js: -------------------------------------------------------------------------------- 1 | const babelOptions = { 2 | presets: ['babel-preset-gatsby'], 3 | } 4 | 5 | module.exports = require('babel-jest').createTransformer(babelOptions) 6 | -------------------------------------------------------------------------------- /config/test/test-setup.js: -------------------------------------------------------------------------------- 1 | // Enzyme adapter for React 16 2 | import Enzyme from 'enzyme' 3 | import Adapter from 'enzyme-adapter-react-16' 4 | 5 | Enzyme.configure({ adapter: new Adapter() }) 6 | -------------------------------------------------------------------------------- /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 8 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import '@styles/base.scss' 2 | 3 | /** 4 | * Implement Gatsby's Browser APIs in this file. 5 | * 6 | * See: https://www.gatsbyjs.org/docs/browser-apis/ 7 | */ 8 | 9 | // You can delete this file if you're not using it 10 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.module.scss: -------------------------------------------------------------------------------- 1 | @import '@styles/_theme.scss'; 2 | 3 | .footer { 4 | height: $footerHeight; 5 | font-size: 0.75rem; 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | color: $textAlternate; 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/tests/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import Index from '../index' 4 | 5 | describe('', () => { 6 | it('should render', () => { 7 | const renderedComponent = shallow() 8 | expect(renderedComponent.length).toEqual(1) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /src/components/HelloWorld/HelloWorld.module.scss: -------------------------------------------------------------------------------- 1 | @import '@styles/_theme.scss'; 2 | @import '@styles/_breakpoints.scss'; 3 | 4 | .container { 5 | margin: 1rem 0; 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: center; 9 | align-items: center; 10 | 11 | > h1 { 12 | color: $colorPrimary; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/HelloWorld/tests/HelloWorld.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | import HelloWorld from '../HelloWorld' 4 | 5 | describe('', () => { 6 | it('should render', () => { 7 | const renderedComponent = shallow() 8 | expect(renderedComponent.length).toEqual(1) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /src/blog/example-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Example Blog Post 3 | published: true 4 | description: Example post for starter template 5 | tags: webdev, react, javascript, tutorial 6 | date: '2018-03-12' 7 | --- 8 | 9 | Showing of an example blog post. You write it markdown and this page will automatically be converted to a web page. 10 | 11 | Have a nice day! ✨ 12 | -------------------------------------------------------------------------------- /config/test/__mocks__/gatsby.js: -------------------------------------------------------------------------------- 1 | const React = require('react') 2 | const gatsby = jest.requireActual('gatsby') 3 | 4 | module.exports = { 5 | ...gatsby, 6 | graphql: jest.fn(), 7 | Link: jest.fn().mockImplementation(({ to, ...rest }) => 8 | React.createElement('a', { 9 | ...rest, 10 | href: to, 11 | }) 12 | ), 13 | StaticQuery: jest.fn(), 14 | } 15 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Layout from '@components/Layout' 4 | import SEO from '@components/seo' 5 | 6 | const NotFoundPage = () => ( 7 | 8 | 9 |

NOT FOUND

10 |

You just hit a route that doesn't exist... the sadness.

11 |
12 | ) 13 | 14 | export default NotFoundPage 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gatsby-Starter | Dinesh Pandiyan 2 | 3 | A gatsby starter template setup with best practices and provides great development experience. 4 | 5 | - SASS 6 | - CSS Modules 7 | - Blog 8 | - Static File-Based Data Loading 9 | - Optimized Font Load with Google Fonts 10 | - Custom Webpack Config 11 | - Responsive Base Layout 12 | - Unit Tests with Jest and Enzyme 13 | 14 | ## License 15 | 16 | MIT © Dinesh Pandiyan 17 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Layout from '@components/Layout' 3 | import SEO from '@components/seo' 4 | import HelloWorld from '@components/HelloWorld' 5 | 6 | const IndexPage = ({ data }) => ( 7 | 8 | 12 | 13 | 14 | ) 15 | 16 | export default IndexPage 17 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.module.scss: -------------------------------------------------------------------------------- 1 | @import '@styles/_theme.scss'; 2 | 3 | .page-wrapper { 4 | min-height: 100vh; 5 | margin: 0 auto; 6 | max-width: 90%; 7 | 8 | @media (min-width: 845.9px) { 9 | max-width: 769px; 10 | } 11 | } 12 | 13 | .content-container { 14 | margin: 0 auto; 15 | max-width: 960px; 16 | min-height: calc(100vh - #{$headerHeight} - #{$footerHeight}); 17 | display: flex; 18 | flex-direction: column; 19 | justify-content: center; 20 | } 21 | -------------------------------------------------------------------------------- /src/styles/_theme.scss: -------------------------------------------------------------------------------- 1 | $primary-blue: #5badf0; 2 | $alternate-blue: #3f86ed; 3 | $primary-cyan: #79c6c0; 4 | $primary-pink: #d23669; 5 | $primary-grey: #607d8b; 6 | $border-grey: #eee; 7 | $bg-white: #fffeff; 8 | 9 | $text-black: #333; 10 | $text-grey: #aaa; 11 | 12 | $colorPrimary: $primary-pink; 13 | $colorAlternate: $primary-cyan; 14 | $colorBG: $bg-white; 15 | $textPrimary: $text-black; 16 | $textAlternate: $text-grey; 17 | $borderGrey: $border-grey; 18 | 19 | // variables 20 | $headerHeight: 4rem; 21 | $footerHeight: 3rem; 22 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import React from 'react' 3 | import styles from './Footer.module.scss' 4 | 5 | const Footer = ({ siteMetadata }) => ( 6 | 18 | ) 19 | 20 | Footer.propTypes = { 21 | siteMetadata: PropTypes.object, 22 | } 23 | 24 | export default Footer 25 | -------------------------------------------------------------------------------- /src/pages/about.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | import Layout from '@components/Layout' 4 | import SEO from '@components/seo' 5 | 6 | /* eslint-disable jsx-a11y/accessible-emoji */ 7 | const AboutPage = () => ( 8 | 9 | 10 |

About me

11 |

Content for about section goes here 🥳

12 |

13 | You can go back  14 | 15 | home 16 | 17 | . 18 |

19 |
20 | ) 21 | /* eslint-enable jsx-a11y/accessible-emoji */ 22 | 23 | export default AboutPage 24 | -------------------------------------------------------------------------------- /src/components/Header/Header.module.scss: -------------------------------------------------------------------------------- 1 | @import '@styles/_theme.scss'; 2 | 3 | .header { 4 | height: $headerHeight; 5 | display: flex; 6 | align-items: center; 7 | justify-content: center; 8 | } 9 | 10 | .menu { 11 | height: $headerHeight; 12 | font-size: 0.875rem; 13 | display: flex; 14 | flex: 1; 15 | align-items: center; 16 | justify-content: flex-end; 17 | 18 | .menu-item { 19 | color: $colorPrimary; 20 | margin: 0 0.5rem; 21 | justify-content: flex-end; 22 | padding-bottom: 0.25rem; 23 | 24 | &:hover { 25 | box-shadow: 0 1.5px 0 0 currentColor; 26 | } 27 | } 28 | } 29 | 30 | .title { 31 | margin: 0; 32 | } 33 | -------------------------------------------------------------------------------- /src/styles/_mixins.scss: -------------------------------------------------------------------------------- 1 | @import '@styles/_theme.scss'; 2 | @import '@styles/_breakpoints.scss'; 3 | 4 | @mixin anchor-style { 5 | color: $textPrimary; 6 | 7 | &:hover { 8 | color: $colorPrimary; 9 | } 10 | } 11 | 12 | @mixin smooth-font { 13 | $smoothTextShadowColor: rgba(0, 0, 0, 0.01) 0 0 1px; 14 | font-size: 100%; 15 | -webkit-text-size-adjust: 100%; 16 | font-variant-ligatures: none; 17 | text-rendering: optimizeLegibility; 18 | -moz-osx-font-smoothing: grayscale; 19 | -webkit-font-smoothing: antialiased; 20 | text-shadow: $smoothTextShadowColor; 21 | } 22 | 23 | @mixin font-size-reset { 24 | font-size: 15px; 25 | 26 | @include breakpoint-large-up { 27 | font-size: 17px; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import React from 'react' 3 | import { Link } from 'gatsby' 4 | import styles from './Header.module.scss' 5 | 6 | const Header = ({ showHome }) => ( 7 |
8 |
9 | {showHome ? ( 10 |
11 | Home 12 |
13 | ) : null} 14 |
15 | About 16 |
17 |
18 |
19 | ) 20 | 21 | Header.propTypes = { 22 | siteMetadata: PropTypes.object, 23 | showHome: PropTypes.bool, 24 | } 25 | 26 | export default Header 27 | -------------------------------------------------------------------------------- /src/styles/_global.scss: -------------------------------------------------------------------------------- 1 | @import '@styles/_theme.scss'; 2 | @import '@styles/_mixins.scss'; 3 | 4 | html { 5 | // add site level custom font here, if applicable 6 | font-family: 'Helvetica Neue', 'sans-serif'; 7 | @include smooth-font; 8 | @include font-size-reset; 9 | color: $textPrimary; 10 | background-color: $colorBG; 11 | } 12 | 13 | body { 14 | // add site level custom font here, if applicable 15 | font-family: 'Helvetica Neue', 'sans-serif'; 16 | @include smooth-font; 17 | @include font-size-reset; 18 | color: $textPrimary; 19 | line-height: 1.6; 20 | background-color: $colorBG; 21 | } 22 | 23 | a { 24 | color: $colorPrimary; 25 | text-decoration: none; 26 | } 27 | 28 | // markdown override 29 | div p img { 30 | display: block; 31 | margin: auto; 32 | max-width: 75%; 33 | } 34 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.jsx?$': '/config/test/jest-preprocess.js', 4 | }, 5 | moduleNameMapper: { 6 | '.+\\.(css|styl|less|sass|scss)$': 'identity-obj-proxy', 7 | '.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 8 | '/config/test/__mocks__/fileMock.js', 9 | '^@components(.*)$': '/src/components$1', 10 | '^@styles(.*)$': '/src/styles$1', 11 | }, 12 | testPathIgnorePatterns: ['node_modules', '.cache'], 13 | transformIgnorePatterns: ['node_modules/(?!(gatsby)/)'], 14 | globals: { 15 | __PATH_PREFIX__: '', 16 | }, 17 | testURL: 'http://localhost', 18 | setupFiles: ['/config/test/loadershim.js'], 19 | setupTestFrameworkScriptFile: '/config/test/test-setup.js', 20 | } 21 | -------------------------------------------------------------------------------- /src/templates/blog-post.module.scss: -------------------------------------------------------------------------------- 1 | @import '@styles/_theme.scss'; 2 | 3 | .title { 4 | margin: 0; 5 | } 6 | 7 | .date { 8 | color: $textAlternate; 9 | font-style: italic; 10 | font-size: 0.875rem; 11 | margin: 0.25rem 0; 12 | } 13 | 14 | .content { 15 | margin: 1.5rem 0; 16 | line-height: 1.8; 17 | 18 | pre { 19 | padding: 1rem; 20 | 21 | code { 22 | &:after { 23 | display: none; 24 | } 25 | } 26 | } 27 | 28 | blockquote { 29 | font-size: 0.875rem; 30 | font-style: italic; 31 | background: #f9f9f9; 32 | border-left: 6px solid #ccc; 33 | margin: 1.5rem; 34 | padding: 1rem; 35 | quotes: '\201C' '\201D' '\2018' '\2019'; 36 | 37 | &:before { 38 | color: #ccc; 39 | content: open-quote; 40 | font-size: 4em; 41 | line-height: 0.1em; 42 | margin-right: 0.25rem; 43 | vertical-align: -0.4em; 44 | } 45 | 46 | p { 47 | display: inline; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/templates/blog-post.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { graphql } from 'gatsby' 3 | import Layout from '@components/Layout' 4 | import SEO from '@components/seo' 5 | import styles from './blog-post.module.scss' 6 | 7 | const BlogPost = ({ data }) => { 8 | const post = data.markdownRemark 9 | return ( 10 | 11 | 12 |

{post.frontmatter.title}

13 |
{post.frontmatter.date}
14 |
18 | 19 | ) 20 | } 21 | 22 | export default BlogPost 23 | 24 | export const query = graphql` 25 | query BlogPostQuery($slug: String!) { 26 | markdownRemark(fields: { slug: { eq: $slug } }) { 27 | html 28 | frontmatter { 29 | title 30 | date(formatString: "MMMM D, YYYY") 31 | } 32 | } 33 | } 34 | ` 35 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { StaticQuery, graphql } from 'gatsby' 4 | import Header from '@components/Header' 5 | import Footer from '@components/Footer' 6 | import styles from './Layout.module.scss' 7 | 8 | const Layout = ({ children, showHome = true }) => ( 9 | ( 22 |
23 |
24 |
{children}
25 |
26 |
27 | )} 28 | /> 29 | ) 30 | 31 | Layout.propTypes = { 32 | children: PropTypes.node.isRequired, 33 | showHome: PropTypes.bool, 34 | } 35 | 36 | export default Layout 37 | -------------------------------------------------------------------------------- /src/components/image.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { StaticQuery, graphql } from 'gatsby' 3 | import Img from 'gatsby-image' 4 | 5 | /* 6 | * This component is built using `gatsby-image` to automatically serve optimized 7 | * images with lazy loading and reduced file sizes. The image is loaded using a 8 | * `StaticQuery`, which allows us to load the image from directly within this 9 | * component, rather than having to pass the image data down from pages. 10 | * 11 | * For more information, see the docs: 12 | * - `gatsby-image`: https://gatsby.app/gatsby-image 13 | * - `StaticQuery`: https://gatsby.app/staticquery 14 | */ 15 | 16 | const Image = () => ( 17 | } 30 | /> 31 | ) 32 | export default Image 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Dineshkumar Pandiyan 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /src/components/HelloWorld/HelloWorld.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import React from 'react' 3 | import styles from './HelloWorld.module.scss' 4 | import { StaticQuery, graphql } from 'gatsby' 5 | 6 | const HelloWorld = ({ siteMetadata, greetings }) => ( 7 |
8 |

9 | {siteMetadata.title} | {siteMetadata.fullName} 10 |

11 |
{greetings}
12 |
13 | ) 14 | 15 | HelloWorld.propTypes = { 16 | siteMetadata: PropTypes.object, 17 | greetings: PropTypes.string, 18 | } 19 | 20 | export default () => ( 21 | ( 44 | 51 | )} 52 | /> 53 | ) 54 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { createFilePath } = require(`gatsby-source-filesystem`) 3 | 4 | exports.onCreateWebpackConfig = ({ 5 | stage, 6 | rules, 7 | loaders, 8 | plugins, 9 | actions, 10 | }) => { 11 | actions.setWebpackConfig({ 12 | resolve: { 13 | alias: { 14 | '@components': path.resolve(__dirname, 'src', 'components'), 15 | '@styles': path.resolve(__dirname, 'src', 'styles'), 16 | }, 17 | }, 18 | }) 19 | } 20 | 21 | exports.onCreateNode = ({ node, getNode, boundActionCreators }) => { 22 | const { createNodeField } = boundActionCreators 23 | if (node.internal.type === `MarkdownRemark`) { 24 | const slug = createFilePath({ node, getNode, basePath: `pages` }) 25 | createNodeField({ 26 | node, 27 | name: `slug`, 28 | value: slug, 29 | }) 30 | } 31 | } 32 | 33 | exports.createPages = ({ graphql, boundActionCreators }) => { 34 | const { createPage } = boundActionCreators 35 | return new Promise((resolve, reject) => { 36 | graphql(` 37 | { 38 | allMarkdownRemark { 39 | edges { 40 | node { 41 | fields { 42 | slug 43 | } 44 | } 45 | } 46 | } 47 | } 48 | `).then(result => { 49 | result.data.allMarkdownRemark.edges.forEach(({ node }) => { 50 | createPage({ 51 | path: node.fields.slug, 52 | component: path.resolve(`./src/templates/blog-post.js`), 53 | context: { 54 | // Data passed to context is available in page queries as GraphQL variables. 55 | slug: node.fields.slug, 56 | }, 57 | }) 58 | }) 59 | resolve() 60 | }) 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | title: `Gatsby-Starter`, 4 | description: `A good description that will show up in Google Search`, 5 | author: `@flexdinesh`, 6 | fullName: `Dinesh Pandiyan`, 7 | twitterHandle: `https://twitter.com/flexdinesh`, 8 | }, 9 | plugins: [ 10 | `gatsby-plugin-react-helmet`, 11 | // markdown to pages for blog 12 | `gatsby-transformer-remark`, 13 | { 14 | resolve: `gatsby-source-filesystem`, 15 | options: { 16 | name: `images`, 17 | path: `${__dirname}/src/images`, 18 | }, 19 | }, 20 | { 21 | resolve: `gatsby-source-filesystem`, 22 | options: { 23 | name: `data`, 24 | path: `${__dirname}/src/data`, 25 | }, 26 | }, 27 | { 28 | resolve: `gatsby-source-filesystem`, 29 | options: { 30 | name: `blog`, 31 | path: `${__dirname}/src/blog/`, 32 | }, 33 | }, 34 | // loading static data through GraphQL query 35 | `gatsby-transformer-javascript-frontmatter`, 36 | `gatsby-transformer-sharp`, 37 | `gatsby-plugin-sharp`, 38 | // favicon generator 39 | { 40 | resolve: `gatsby-plugin-manifest`, 41 | options: { 42 | name: `favicon`, 43 | short_name: `favicon`, 44 | start_url: `/`, 45 | background_color: `#5badf0`, 46 | theme_color: `#5badf0`, 47 | display: `minimal-ui`, 48 | icon: `src/images/moustache.png`, // This path is relative to the root of the site. 49 | }, 50 | }, 51 | `gatsby-plugin-sass`, 52 | /* 53 | // google-analytics 54 | { 55 | resolve: `gatsby-plugin-google-analytics`, 56 | options: { 57 | trackingId: 'your-tracking-code', 58 | // Puts tracking script in the head instead of the body 59 | head: false, 60 | // Setting this parameter is optional 61 | anonymize: true, 62 | // Setting this parameter is also optional 63 | respectDNT: true, 64 | }, 65 | },*/ 66 | ], 67 | } 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dinesh-pandiyan-gatsby-starter", 3 | "version": "0.1.0", 4 | "private": true, 5 | "description": "Gatsby Starter | Dinesh Pandiyan", 6 | "keywords": [ 7 | "developer", 8 | "javascript", 9 | "portfolio", 10 | "react" 11 | ], 12 | "license": "MIT", 13 | "author": "Dinesh Pandiyan ", 14 | "scripts": { 15 | "build": "gatsby build", 16 | "clean": "rm -rf public .cache", 17 | "develop": "gatsby develop", 18 | "format": "prettier --write \"src/**/*.js\"", 19 | "serve:build": "npm run clean && npm run build && http-server ./public", 20 | "start": "npm run develop", 21 | "start:prod": "npm run build && npm run serve:build", 22 | "test": "jest" 23 | }, 24 | "dependencies": { 25 | "@fortawesome/fontawesome-svg-core": "^1.2.12", 26 | "@fortawesome/free-brands-svg-icons": "^5.6.3", 27 | "@fortawesome/free-regular-svg-icons": "^5.6.3", 28 | "@fortawesome/free-solid-svg-icons": "^5.6.3", 29 | "@fortawesome/react-fontawesome": "^0.1.3", 30 | "gatsby": "^2.0.76", 31 | "gatsby-image": "^2.0.20", 32 | "gatsby-plugin-google-analytics": "^2.0.9", 33 | "gatsby-plugin-manifest": "^2.0.9", 34 | "gatsby-plugin-offline": "^2.0.16", 35 | "gatsby-plugin-react-helmet": "^3.0.2", 36 | "gatsby-plugin-sharp": "^2.0.14", 37 | "gatsby-source-filesystem": "^2.0.8", 38 | "gatsby-transformer-javascript-frontmatter": "^2.0.5", 39 | "gatsby-transformer-remark": "^2.1.19", 40 | "gatsby-transformer-sharp": "^2.1.8", 41 | "prop-types": "^15.6.2", 42 | "react": "^16.6.3", 43 | "react-dom": "^16.6.3", 44 | "react-helmet": "^5.2.0", 45 | "react-typography": "^0.16.18", 46 | "typography": "^0.16.18" 47 | }, 48 | "devDependencies": { 49 | "@babel/core": "^7.2.2", 50 | "babel-core": "^7.0.0-bridge.0", 51 | "babel-jest": "^23.6.0", 52 | "babel-preset-gatsby": "^0.1.6", 53 | "enzyme": "^3.8.0", 54 | "enzyme-adapter-react-16": "^1.7.1", 55 | "gatsby-plugin-sass": "^2.0.7", 56 | "gh-pages": "^2.0.1", 57 | "http-server": "^0.11.1", 58 | "identity-obj-proxy": "^3.0.0", 59 | "jest": "^23.6.0", 60 | "node-sass": "^4.11.0", 61 | "prettier": "^1.15.2" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/components/seo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Helmet from 'react-helmet' 4 | import { StaticQuery, graphql } from 'gatsby' 5 | 6 | function SEO({ description, lang, meta, keywords, title }) { 7 | return ( 8 | { 11 | const metaDescription = 12 | description || data.site.siteMetadata.description 13 | return ( 14 | 0 60 | ? { 61 | name: `keywords`, 62 | content: keywords.join(`, `), 63 | } 64 | : [] 65 | ) 66 | .concat(meta)} 67 | /> 68 | ) 69 | }} 70 | /> 71 | ) 72 | } 73 | 74 | SEO.defaultProps = { 75 | lang: `en`, 76 | meta: [], 77 | keywords: [], 78 | } 79 | 80 | SEO.propTypes = { 81 | description: PropTypes.string, 82 | lang: PropTypes.string, 83 | meta: PropTypes.array, 84 | keywords: PropTypes.arrayOf(PropTypes.string), 85 | title: PropTypes.string.isRequired, 86 | } 87 | 88 | export default SEO 89 | 90 | // moustache.png will be set as link preview image 91 | const detailsQuery = graphql` 92 | query DefaultSEOQuery { 93 | site { 94 | siteMetadata { 95 | title 96 | description 97 | author 98 | fullName 99 | } 100 | } 101 | allFile( 102 | filter: { sourceInstanceName: { eq: "images" }, name: { eq: "moustache" } } 103 | ) { 104 | edges { 105 | node { 106 | name 107 | extension 108 | relativePath 109 | publicURL 110 | childImageSharp { 111 | resize { 112 | width 113 | height 114 | aspectRatio 115 | originalName 116 | src 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | ` 124 | -------------------------------------------------------------------------------- /src/styles/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | $breakpoints: ( 2 | 'xsmall' : 0px, // mobile-m max=375px 3 | 'small' : 376px, // mobile-l max=425px 4 | 'medium' : 426px, // tablet max=768px 5 | 'large' : 769px, // laptop-m max=1024px => ipad pro falls under this 6 | 'xlarge' : 1025px, // laptop-l max=1440px 7 | 'xxlarge' : 1440px, // 4k max=2560px 8 | 'xxxlarge': 2560px 9 | ); 10 | 11 | @mixin breakpoint-all { 12 | $value: map-get($breakpoints, xsmall); 13 | 14 | @media (min-width: $value) { 15 | @content; 16 | } 17 | } 18 | 19 | @mixin breakpoint-small-only { 20 | $min-value: map-get($breakpoints, small); 21 | $max-value: map-get($breakpoints, medium) - 1; 22 | 23 | @media (min-width: $min-value) and (max-width: $max-value){ 24 | @content; 25 | } 26 | } 27 | 28 | @mixin breakpoint-small-down { 29 | $max-value: map-get($breakpoints, medium) - 1; 30 | 31 | @media (max-width: $max-value){ 32 | @content; 33 | } 34 | } 35 | 36 | @mixin breakpoint-small-up { 37 | $min-value: map-get($breakpoints, small); 38 | 39 | @media (min-width: $min-value){ 40 | @content; 41 | } 42 | } 43 | 44 | @mixin breakpoint-medium-only { 45 | $min-value: map-get($breakpoints, medium); 46 | $max-value: map-get($breakpoints, large) - 1; 47 | 48 | @media (min-width: $min-value) and (max-width: $max-value){ 49 | @content; 50 | } 51 | } 52 | 53 | @mixin breakpoint-medium-down { 54 | $max-value: map-get($breakpoints, large) - 1; 55 | 56 | @media (max-width: $max-value){ 57 | @content; 58 | } 59 | } 60 | 61 | @mixin breakpoint-medium-up { 62 | $min-value: map-get($breakpoints, medium); 63 | 64 | @media (min-width: $min-value){ 65 | @content; 66 | } 67 | } 68 | 69 | @mixin breakpoint-large-only { 70 | $min-value: map-get($breakpoints, large); 71 | $max-value: map-get($breakpoints, xlarge) - 1; 72 | 73 | @media (min-width: $min-value) and (max-width: $max-value){ 74 | @content; 75 | } 76 | } 77 | 78 | @mixin breakpoint-large-down { 79 | $max-value: map-get($breakpoints, xlarge) - 1; 80 | 81 | @media (max-width: $max-value){ 82 | @content; 83 | } 84 | } 85 | 86 | @mixin breakpoint-large-up { 87 | $min-value: map-get($breakpoints, large); 88 | 89 | @media (min-width: $min-value){ 90 | @content; 91 | } 92 | } 93 | 94 | @mixin breakpoint-xlarge-only { 95 | $min-value: map-get($breakpoints, xlarge); 96 | $max-value: map-get($breakpoints, xxlarge) - 1; 97 | 98 | @media (min-width: $min-value) and (max-width: $max-value){ 99 | @content; 100 | } 101 | } 102 | 103 | @mixin breakpoint-xlarge-down { 104 | $max-value: map-get($breakpoints, xxlarge) - 1; 105 | 106 | @media (max-width: $max-value){ 107 | @content; 108 | } 109 | } 110 | 111 | @mixin breakpoint-xlarge-up { 112 | $min-value: map-get($breakpoints, xlarge); 113 | 114 | @media (min-width: $min-value){ 115 | @content; 116 | } 117 | } 118 | 119 | @mixin breakpoint-xxlarge-only { 120 | $min-value: map-get($breakpoints, xxlarge); 121 | $max-value: map-get($breakpoints, xxxlarge) - 1; 122 | 123 | @media (min-width: $min-value) and (max-width: $max-value){ 124 | @content; 125 | } 126 | } 127 | 128 | @mixin breakpoint-xxlarge-down { 129 | $max-value: map-get($breakpoints, xxxlarge) - 1; 130 | 131 | @media (max-width: $max-value){ 132 | @content; 133 | } 134 | } 135 | 136 | @mixin breakpoint-xxlarge-up { 137 | $min-value: map-get($breakpoints, xxlarge); 138 | 139 | @media (min-width: $min-value){ 140 | @content; 141 | } 142 | } -------------------------------------------------------------------------------- /src/styles/_font.scss: -------------------------------------------------------------------------------- 1 | /* If you have to load google fonts, load it here. It will be cached on first load and will prevent FOUT to an extent */ 2 | 3 | // /* cyrillic-ext */ 4 | // @font-face { 5 | // font-family: 'Open Sans'; 6 | // font-style: normal; 7 | // font-weight: 400; 8 | // src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFWJ0bf8pkAp6a.woff2) format('woff2'); 9 | // unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; 10 | // } 11 | // /* cyrillic */ 12 | // @font-face { 13 | // font-family: 'Open Sans'; 14 | // font-style: normal; 15 | // font-weight: 400; 16 | // src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFUZ0bf8pkAp6a.woff2) format('woff2'); 17 | // unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; 18 | // } 19 | // /* greek-ext */ 20 | // @font-face { 21 | // font-family: 'Open Sans'; 22 | // font-style: normal; 23 | // font-weight: 400; 24 | // src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFWZ0bf8pkAp6a.woff2) format('woff2'); 25 | // unicode-range: U+1F00-1FFF; 26 | // } 27 | // /* greek */ 28 | // @font-face { 29 | // font-family: 'Open Sans'; 30 | // font-style: normal; 31 | // font-weight: 400; 32 | // src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFVp0bf8pkAp6a.woff2) format('woff2'); 33 | // unicode-range: U+0370-03FF; 34 | // } 35 | // /* vietnamese */ 36 | // @font-face { 37 | // font-family: 'Open Sans'; 38 | // font-style: normal; 39 | // font-weight: 400; 40 | // src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFWp0bf8pkAp6a.woff2) format('woff2'); 41 | // unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB; 42 | // } 43 | // /* latin-ext */ 44 | // @font-face { 45 | // font-family: 'Open Sans'; 46 | // font-style: normal; 47 | // font-weight: 400; 48 | // src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFW50bf8pkAp6a.woff2) format('woff2'); 49 | // unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; 50 | // } 51 | // /* latin */ 52 | // @font-face { 53 | // font-family: 'Open Sans'; 54 | // font-style: normal; 55 | // font-weight: 400; 56 | // src: local('Open Sans Regular'), local('OpenSans-Regular'), url(https://fonts.gstatic.com/s/opensans/v15/mem8YaGs126MiZpBA-UFVZ0bf8pkAg.woff2) format('woff2'); 57 | // unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 58 | // } 59 | // /* cyrillic-ext */ 60 | // @font-face { 61 | // font-family: 'Open Sans'; 62 | // font-style: normal; 63 | // font-weight: 700; 64 | // src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOX-hpKKSTj5PW.woff2) format('woff2'); 65 | // unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; 66 | // } 67 | // /* cyrillic */ 68 | // @font-face { 69 | // font-family: 'Open Sans'; 70 | // font-style: normal; 71 | // font-weight: 700; 72 | // src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOVuhpKKSTj5PW.woff2) format('woff2'); 73 | // unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; 74 | // } 75 | // /* greek-ext */ 76 | // @font-face { 77 | // font-family: 'Open Sans'; 78 | // font-style: normal; 79 | // font-weight: 700; 80 | // src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOXuhpKKSTj5PW.woff2) format('woff2'); 81 | // unicode-range: U+1F00-1FFF; 82 | // } 83 | // /* greek */ 84 | // @font-face { 85 | // font-family: 'Open Sans'; 86 | // font-style: normal; 87 | // font-weight: 700; 88 | // src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOUehpKKSTj5PW.woff2) format('woff2'); 89 | // unicode-range: U+0370-03FF; 90 | // } 91 | // /* vietnamese */ 92 | // @font-face { 93 | // font-family: 'Open Sans'; 94 | // font-style: normal; 95 | // font-weight: 700; 96 | // src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOXehpKKSTj5PW.woff2) format('woff2'); 97 | // unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB; 98 | // } 99 | // /* latin-ext */ 100 | // @font-face { 101 | // font-family: 'Open Sans'; 102 | // font-style: normal; 103 | // font-weight: 700; 104 | // src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOXOhpKKSTj5PW.woff2) format('woff2'); 105 | // unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; 106 | // } 107 | // /* latin */ 108 | // @font-face { 109 | // font-family: 'Open Sans'; 110 | // font-style: normal; 111 | // font-weight: 700; 112 | // src: local('Open Sans Bold'), local('OpenSans-Bold'), url(https://fonts.gstatic.com/s/opensans/v15/mem5YaGs126MiZpBA-UN7rgOUuhpKKSTjw.woff2) format('woff2'); 113 | // unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 114 | // } 115 | -------------------------------------------------------------------------------- /src/styles/_reset.scss: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | article, 5 | aside, 6 | details, 7 | figcaption, 8 | figure, 9 | footer, 10 | header, 11 | main, 12 | menu, 13 | nav, 14 | section, 15 | summary { 16 | display: block; 17 | } 18 | audio, 19 | canvas, 20 | progress, 21 | video { 22 | display: inline-block; 23 | } 24 | audio:not([controls]) { 25 | display: none; 26 | height: 0; 27 | } 28 | progress { 29 | vertical-align: baseline; 30 | } 31 | [hidden], 32 | template { 33 | display: none; 34 | } 35 | a { 36 | background-color: transparent; 37 | -webkit-text-decoration-skip: objects; 38 | } 39 | a:active, 40 | a:hover { 41 | outline-width: 0; 42 | } 43 | abbr[title] { 44 | border-bottom: none; 45 | text-decoration: underline; 46 | text-decoration: underline dotted; 47 | } 48 | b, 49 | strong { 50 | font-weight: inherit; 51 | font-weight: bolder; 52 | } 53 | dfn { 54 | font-style: italic; 55 | } 56 | h1 { 57 | font-size: 2em; 58 | margin: 0.67em 0; 59 | } 60 | mark { 61 | background-color: #ff0; 62 | color: #000; 63 | } 64 | small { 65 | font-size: 80%; 66 | } 67 | sub, 68 | sup { 69 | font-size: 75%; 70 | line-height: 0; 71 | position: relative; 72 | vertical-align: baseline; 73 | } 74 | sub { 75 | bottom: -0.25em; 76 | } 77 | sup { 78 | top: -0.5em; 79 | } 80 | img { 81 | border-style: none; 82 | } 83 | svg:not(:root) { 84 | overflow: hidden; 85 | } 86 | code, 87 | kbd, 88 | pre, 89 | samp { 90 | font-family: monospace, monospace; 91 | font-size: 1em; 92 | } 93 | figure { 94 | margin: 1em 40px; 95 | } 96 | hr { 97 | box-sizing: content-box; 98 | height: 0; 99 | overflow: visible; 100 | } 101 | button, 102 | input, 103 | optgroup, 104 | select, 105 | textarea { 106 | font: inherit; 107 | margin: 0; 108 | } 109 | optgroup { 110 | font-weight: 700; 111 | } 112 | button, 113 | input { 114 | overflow: visible; 115 | } 116 | button, 117 | select { 118 | text-transform: none; 119 | } 120 | [type='reset'], 121 | [type='submit'], 122 | button, 123 | html [type='button'] { 124 | -webkit-appearance: button; 125 | } 126 | [type='button']::-moz-focus-inner, 127 | [type='reset']::-moz-focus-inner, 128 | [type='submit']::-moz-focus-inner, 129 | button::-moz-focus-inner { 130 | border-style: none; 131 | padding: 0; 132 | } 133 | [type='button']:-moz-focusring, 134 | [type='reset']:-moz-focusring, 135 | [type='submit']:-moz-focusring, 136 | button:-moz-focusring { 137 | outline: 1px dotted ButtonText; 138 | } 139 | fieldset { 140 | border: 1px solid silver; 141 | margin: 0 2px; 142 | padding: 0.35em 0.625em 0.75em; 143 | } 144 | legend { 145 | box-sizing: border-box; 146 | color: inherit; 147 | display: table; 148 | max-width: 100%; 149 | padding: 0; 150 | white-space: normal; 151 | } 152 | textarea { 153 | overflow: auto; 154 | } 155 | [type='checkbox'], 156 | [type='radio'] { 157 | box-sizing: border-box; 158 | padding: 0; 159 | } 160 | [type='number']::-webkit-inner-spin-button, 161 | [type='number']::-webkit-outer-spin-button { 162 | height: auto; 163 | } 164 | [type='search'] { 165 | -webkit-appearance: textfield; 166 | outline-offset: -2px; 167 | } 168 | [type='search']::-webkit-search-cancel-button, 169 | [type='search']::-webkit-search-decoration { 170 | -webkit-appearance: none; 171 | } 172 | ::-webkit-input-placeholder { 173 | color: inherit; 174 | opacity: 0.54; 175 | } 176 | ::-webkit-file-upload-button { 177 | -webkit-appearance: button; 178 | font: inherit; 179 | } 180 | html { 181 | font: 112.5%/1.45em georgia, serif; 182 | box-sizing: border-box; 183 | overflow-y: scroll; 184 | } 185 | * { 186 | box-sizing: inherit; 187 | } 188 | *:before { 189 | box-sizing: inherit; 190 | } 191 | *:after { 192 | box-sizing: inherit; 193 | } 194 | body { 195 | // color: hsla(0, 0%, 0%, 0.8); 196 | // font-family: georgia, serif; 197 | font-weight: normal; 198 | word-wrap: break-word; 199 | font-kerning: normal; 200 | -moz-font-feature-settings: 'kern', 'liga', 'clig', 'calt'; 201 | -ms-font-feature-settings: 'kern', 'liga', 'clig', 'calt'; 202 | -webkit-font-feature-settings: 'kern', 'liga', 'clig', 'calt'; 203 | font-feature-settings: 'kern', 'liga', 'clig', 'calt'; 204 | } 205 | img { 206 | max-width: 100%; 207 | margin-left: 0; 208 | margin-right: 0; 209 | margin-top: 0; 210 | padding-bottom: 0; 211 | padding-left: 0; 212 | padding-right: 0; 213 | padding-top: 0; 214 | margin-bottom: 1.45rem; 215 | } 216 | h1 { 217 | margin-left: 0; 218 | margin-right: 0; 219 | margin-top: 0; 220 | padding-bottom: 0; 221 | padding-left: 0; 222 | padding-right: 0; 223 | padding-top: 0; 224 | margin-bottom: 1.45rem; 225 | color: inherit; 226 | // font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 227 | // Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 228 | font-weight: bold; 229 | text-rendering: optimizeLegibility; 230 | font-size: 2.25rem; 231 | line-height: 1.1; 232 | } 233 | h2 { 234 | margin-left: 0; 235 | margin-right: 0; 236 | margin-top: 0; 237 | padding-bottom: 0; 238 | padding-left: 0; 239 | padding-right: 0; 240 | padding-top: 0; 241 | margin-bottom: 1.45rem; 242 | color: inherit; 243 | // font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 244 | // Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 245 | font-weight: bold; 246 | text-rendering: optimizeLegibility; 247 | font-size: 1.62671rem; 248 | line-height: 1.1; 249 | } 250 | h3 { 251 | margin-left: 0; 252 | margin-right: 0; 253 | margin-top: 0; 254 | padding-bottom: 0; 255 | padding-left: 0; 256 | padding-right: 0; 257 | padding-top: 0; 258 | margin-bottom: 1.45rem; 259 | color: inherit; 260 | // font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 261 | // Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 262 | font-weight: bold; 263 | text-rendering: optimizeLegibility; 264 | font-size: 1.38316rem; 265 | line-height: 1.1; 266 | } 267 | h4 { 268 | margin-left: 0; 269 | margin-right: 0; 270 | margin-top: 0; 271 | padding-bottom: 0; 272 | padding-left: 0; 273 | padding-right: 0; 274 | padding-top: 0; 275 | margin-bottom: 1.45rem; 276 | color: inherit; 277 | // font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 278 | // Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 279 | font-weight: bold; 280 | text-rendering: optimizeLegibility; 281 | font-size: 1rem; 282 | line-height: 1.1; 283 | } 284 | h5 { 285 | margin-left: 0; 286 | margin-right: 0; 287 | margin-top: 0; 288 | padding-bottom: 0; 289 | padding-left: 0; 290 | padding-right: 0; 291 | padding-top: 0; 292 | margin-bottom: 1.45rem; 293 | color: inherit; 294 | // font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 295 | // Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 296 | font-weight: bold; 297 | text-rendering: optimizeLegibility; 298 | font-size: 0.85028rem; 299 | line-height: 1.1; 300 | } 301 | h6 { 302 | margin-left: 0; 303 | margin-right: 0; 304 | margin-top: 0; 305 | padding-bottom: 0; 306 | padding-left: 0; 307 | padding-right: 0; 308 | padding-top: 0; 309 | margin-bottom: 1.45rem; 310 | color: inherit; 311 | // font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 312 | // Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 313 | font-weight: bold; 314 | text-rendering: optimizeLegibility; 315 | font-size: 0.78405rem; 316 | line-height: 1.1; 317 | } 318 | hgroup { 319 | margin-left: 0; 320 | margin-right: 0; 321 | margin-top: 0; 322 | padding-bottom: 0; 323 | padding-left: 0; 324 | padding-right: 0; 325 | padding-top: 0; 326 | margin-bottom: 1.45rem; 327 | } 328 | ul { 329 | margin-left: 1.45rem; 330 | margin-right: 0; 331 | margin-top: 0; 332 | padding-bottom: 0; 333 | padding-left: 0; 334 | padding-right: 0; 335 | padding-top: 0; 336 | margin-bottom: 1.45rem; 337 | list-style-position: outside; 338 | list-style-image: none; 339 | } 340 | ol { 341 | margin-left: 1.45rem; 342 | margin-right: 0; 343 | margin-top: 0; 344 | padding-bottom: 0; 345 | padding-left: 0; 346 | padding-right: 0; 347 | padding-top: 0; 348 | margin-bottom: 1.45rem; 349 | list-style-position: outside; 350 | list-style-image: none; 351 | } 352 | dl { 353 | margin-left: 0; 354 | margin-right: 0; 355 | margin-top: 0; 356 | padding-bottom: 0; 357 | padding-left: 0; 358 | padding-right: 0; 359 | padding-top: 0; 360 | margin-bottom: 1.45rem; 361 | } 362 | dd { 363 | margin-left: 0; 364 | margin-right: 0; 365 | margin-top: 0; 366 | padding-bottom: 0; 367 | padding-left: 0; 368 | padding-right: 0; 369 | padding-top: 0; 370 | margin-bottom: 1.45rem; 371 | } 372 | p { 373 | margin-left: 0; 374 | margin-right: 0; 375 | margin-top: 0; 376 | padding-bottom: 0; 377 | padding-left: 0; 378 | padding-right: 0; 379 | padding-top: 0; 380 | margin-bottom: 1.45rem; 381 | } 382 | figure { 383 | margin-left: 0; 384 | margin-right: 0; 385 | margin-top: 0; 386 | padding-bottom: 0; 387 | padding-left: 0; 388 | padding-right: 0; 389 | padding-top: 0; 390 | margin-bottom: 1.45rem; 391 | } 392 | pre { 393 | margin-left: 0; 394 | margin-right: 0; 395 | margin-top: 0; 396 | padding-bottom: 0; 397 | padding-left: 0; 398 | padding-right: 0; 399 | padding-top: 0; 400 | margin-bottom: 1.45rem; 401 | font-size: 0.85rem; 402 | line-height: 1.42; 403 | background: hsla(0, 0%, 0%, 0.04); 404 | border-radius: 3px; 405 | overflow: auto; 406 | word-wrap: normal; 407 | padding: 1.45rem; 408 | } 409 | table { 410 | margin-left: 0; 411 | margin-right: 0; 412 | margin-top: 0; 413 | padding-bottom: 0; 414 | padding-left: 0; 415 | padding-right: 0; 416 | padding-top: 0; 417 | margin-bottom: 1.45rem; 418 | font-size: 1rem; 419 | line-height: 1.45rem; 420 | border-collapse: collapse; 421 | width: 100%; 422 | } 423 | fieldset { 424 | margin-left: 0; 425 | margin-right: 0; 426 | margin-top: 0; 427 | padding-bottom: 0; 428 | padding-left: 0; 429 | padding-right: 0; 430 | padding-top: 0; 431 | margin-bottom: 1.45rem; 432 | } 433 | blockquote { 434 | margin-left: 1.45rem; 435 | margin-right: 1.45rem; 436 | margin-top: 0; 437 | padding-bottom: 0; 438 | padding-left: 0; 439 | padding-right: 0; 440 | padding-top: 0; 441 | margin-bottom: 1.45rem; 442 | } 443 | form { 444 | margin-left: 0; 445 | margin-right: 0; 446 | margin-top: 0; 447 | padding-bottom: 0; 448 | padding-left: 0; 449 | padding-right: 0; 450 | padding-top: 0; 451 | margin-bottom: 1.45rem; 452 | } 453 | noscript { 454 | margin-left: 0; 455 | margin-right: 0; 456 | margin-top: 0; 457 | padding-bottom: 0; 458 | padding-left: 0; 459 | padding-right: 0; 460 | padding-top: 0; 461 | margin-bottom: 1.45rem; 462 | } 463 | iframe { 464 | margin-left: 0; 465 | margin-right: 0; 466 | margin-top: 0; 467 | padding-bottom: 0; 468 | padding-left: 0; 469 | padding-right: 0; 470 | padding-top: 0; 471 | margin-bottom: 1.45rem; 472 | } 473 | hr { 474 | margin-left: 0; 475 | margin-right: 0; 476 | margin-top: 0; 477 | padding-bottom: 0; 478 | padding-left: 0; 479 | padding-right: 0; 480 | padding-top: 0; 481 | margin-bottom: calc(1.45rem - 1px); 482 | background: hsla(0, 0%, 0%, 0.2); 483 | border: none; 484 | height: 1px; 485 | } 486 | address { 487 | margin-left: 0; 488 | margin-right: 0; 489 | margin-top: 0; 490 | padding-bottom: 0; 491 | padding-left: 0; 492 | padding-right: 0; 493 | padding-top: 0; 494 | margin-bottom: 1.45rem; 495 | } 496 | b { 497 | font-weight: bold; 498 | } 499 | strong { 500 | font-weight: bold; 501 | } 502 | dt { 503 | font-weight: bold; 504 | } 505 | th { 506 | font-weight: bold; 507 | } 508 | li { 509 | margin-bottom: calc(1.45rem / 2); 510 | } 511 | ol li { 512 | padding-left: 0; 513 | } 514 | ul li { 515 | padding-left: 0; 516 | } 517 | li > ol { 518 | margin-left: 1.45rem; 519 | margin-bottom: calc(1.45rem / 2); 520 | margin-top: calc(1.45rem / 2); 521 | } 522 | li > ul { 523 | margin-left: 1.45rem; 524 | margin-bottom: calc(1.45rem / 2); 525 | margin-top: calc(1.45rem / 2); 526 | } 527 | blockquote *:last-child { 528 | margin-bottom: 0; 529 | } 530 | li *:last-child { 531 | margin-bottom: 0; 532 | } 533 | p *:last-child { 534 | margin-bottom: 0; 535 | } 536 | li > p { 537 | margin-bottom: calc(1.45rem / 2); 538 | } 539 | code { 540 | font-size: 0.85rem; 541 | line-height: 1.45rem; 542 | } 543 | kbd { 544 | font-size: 0.85rem; 545 | line-height: 1.45rem; 546 | } 547 | samp { 548 | font-size: 0.85rem; 549 | line-height: 1.45rem; 550 | } 551 | abbr { 552 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 553 | cursor: help; 554 | } 555 | acronym { 556 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 557 | cursor: help; 558 | } 559 | abbr[title] { 560 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 561 | cursor: help; 562 | text-decoration: none; 563 | } 564 | thead { 565 | text-align: left; 566 | } 567 | td, 568 | th { 569 | text-align: left; 570 | border-bottom: 1px solid hsla(0, 0%, 0%, 0.12); 571 | font-feature-settings: 'tnum'; 572 | -moz-font-feature-settings: 'tnum'; 573 | -ms-font-feature-settings: 'tnum'; 574 | -webkit-font-feature-settings: 'tnum'; 575 | padding-left: 0.96667rem; 576 | padding-right: 0.96667rem; 577 | padding-top: 0.725rem; 578 | padding-bottom: calc(0.725rem - 1px); 579 | } 580 | th:first-child, 581 | td:first-child { 582 | padding-left: 0; 583 | } 584 | th:last-child, 585 | td:last-child { 586 | padding-right: 0; 587 | } 588 | tt, 589 | code { 590 | background-color: hsla(0, 0%, 0%, 0.04); 591 | border-radius: 3px; 592 | font-family: 'SFMono-Regular', Consolas, 'Roboto Mono', 'Droid Sans Mono', 593 | 'Liberation Mono', Menlo, Courier, monospace; 594 | padding: 0; 595 | padding-top: 0.2em; 596 | padding-bottom: 0.2em; 597 | } 598 | pre code { 599 | background: none; 600 | line-height: 1.42; 601 | } 602 | code:before, 603 | code:after, 604 | tt:before, 605 | tt:after { 606 | letter-spacing: -0.2em; 607 | content: ' '; 608 | } 609 | pre code:before, 610 | pre code:after, 611 | pre tt:before, 612 | pre tt:after { 613 | content: ''; 614 | } 615 | --------------------------------------------------------------------------------