├── .gitignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── gatsby-config.js ├── gatsby-node.js ├── gatsby-ssr.js ├── package-lock.json ├── package.json └── src ├── components ├── ads │ ├── ads-sidebar.js │ └── style.js ├── blog-post-card │ ├── blog-post-card.js │ └── style.js ├── common │ └── style.js ├── footer │ ├── footer.js │ └── style.js ├── header │ ├── header.js │ ├── search.js │ ├── style.js │ └── themeToggle.js ├── hero │ ├── featured-post.js │ ├── hero.js │ └── style.js ├── latest-posts │ ├── latest-posts.js │ └── style.js ├── layout │ ├── layout.css │ └── layout.js ├── mobile-header │ ├── hamburger.js │ ├── mobile-header.js │ ├── mobile-nav.js │ ├── mobile-search.js │ └── style.js ├── pagination │ ├── pagination.js │ └── style.js └── seo │ └── seo.js ├── images ├── demo-ad.png ├── favicon.png ├── icons │ ├── chevron.svg │ ├── icons.svg │ ├── info.svg │ └── logo.svg ├── logo.png ├── moon.svg └── ogbanner.jpg ├── pages ├── 404.js └── index.js ├── templates ├── BlogSingle.js ├── blog-page │ ├── blog-page.js │ ├── blog-posts.js │ └── style.js └── blog-post │ ├── blog-post-image.js │ ├── blog-post.js │ └── style.js └── utils ├── breakpoints.js └── themeHelper.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-error.log* 6 | yarn-debug.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 variable files 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 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .cache 2 | package.json 3 | package-lock.json 4 | public -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "none", 7 | "arrowParens": "avoid", 8 | "bracketSpacing": false 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Rahul Raj 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Flat Magazine

3 | Flat Magazine Blog 4 |

5 | 6 |

7 | A flat design Gatsby Starter powered by Sanity. 8 |

9 | 10 | ## ⚡ Features 11 | 12 | - [Sanity.io](https://www.sanity.io/) integration 13 | - Styled with [styled-components](https://styled-components.com/) 14 | - Search Support for Posts with [elasticlunr-search](https://www.gatsbyjs.org/packages/@gatsby-contrib/gatsby-plugin-elasticlunr-search) 15 | - Dark Mode 16 | - Featured Posts Section 17 | - Pagination 18 | - Home Page, Blog Archive Page, Blog Post Page 19 | - Automatic [XML Sitemap](https://www.gatsbyjs.org/packages/gatsby-plugin-sitemap) and [Robots.txt ](https://www.gatsbyjs.org/packages/gatsby-plugin-robots-txt) genration 20 | - Responsive design 21 | 22 | ## 🚀 Quick Start 23 | 24 | #### Create a Gatsby site 25 | 26 | Use the Gatsby CLI to create a new site, specifying the Flat Magazine starter. 27 | 28 | ```sh 29 | # Create a new Gatsby site using the Flat Magazine starter 30 | gatsby new blog https://github.com/damnitrahul/gatsby-starter-flat-magazine 31 | ``` 32 | 33 | Supply your sanity.io tokens to `gatsby-source-sanity` options in `gatsby-config.js` 34 | 35 | ``` 36 | { 37 | resolve: 'gatsby-source-sanity', 38 | options: { 39 | projectId: [YOUR_SANITY_PROJECT_ID], 40 | dataset: [YOUR_SANITY_PROJECT_DATASET], 41 | token: [YOUR_SANITY_TOKEN] 42 | } 43 | ``` 44 | 45 | ### Clone the customised Sanity Studio for Flat Magazine [HERE](https://github.com/damnitrahul/sanity-studio-flat-magazine) 46 | 47 | Setup the Studio with your credential, Deploy GraphQL API and Start publishing your content 48 | 49 | #### Start Developing 50 | 51 | Navigate into your new site’s directory and start it up. 52 | 53 | ```sh 54 | cd blog 55 | gatsby develop 56 | ``` 57 | 58 | #### Open the source code and start editing! 59 | 60 | Your site is now running at `http://localhost:8000`! 61 | 62 | Note: You'll also see a second link: `http://localhost:8000/___graphql`. This is a tool you can use to experiment with querying your data. Learn more about using this tool in the [Gatsby tutorial](https://www.gatsbyjs.org/tutorial/part-five/#introducing-graphiql). 63 | 64 | Open the `blog` directory in your code editor of choice and edit `src/templates/index-template.js`. Save your changes and the browser will update in real time! 65 | 66 | ## 💫 Deploy with Netlify 67 | 68 | ### Current deployment status 69 | 70 | [![Netlify Status](https://api.netlify.com/api/v1/badges/2959a2f5-d392-4b58-88c5-b341f57eed00/deploy-status)](https://app.netlify.com/sites/flat-magazine/deploys) 71 | 72 | [Netlify](https://netlify.com) CMS can run in any frontend web environment, but the quickest way to try it out is by running it on a pre-configured starter site with Netlify. Use the button below to build and deploy your own copy of the repository: 73 | 74 | Deploy to Netlify 75 | 76 | After clicking that button, you’ll authenticate with GitHub and choose a repository name. Netlify will then automatically create a repository in your GitHub account with a copy of the files from the template. Next, it will build and deploy the new site on Netlify, bringing you to the site dashboard when the build is complete. 77 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | title: `Flat Magazine`, 4 | description: `A Blog Theme for Gatsby. Using Sanity.io as backend.`, 5 | author: `@damnitrahul`, 6 | siteURL: 'https://blog.damnitrahul.com', 7 | siteUrl: 'https://blog.damnitrahul.com' 8 | }, 9 | plugins: [ 10 | `gatsby-plugin-react-helmet`, 11 | { 12 | resolve: `gatsby-source-filesystem`, 13 | options: { 14 | name: `images`, 15 | path: `${__dirname}/src/images` 16 | } 17 | }, 18 | `gatsby-plugin-sharp`, 19 | `gatsby-transformer-sharp`, 20 | { 21 | resolve: `gatsby-plugin-manifest`, 22 | options: { 23 | name: `gatsby-starter-default`, 24 | short_name: `starter`, 25 | start_url: `/`, 26 | background_color: `#663399`, 27 | theme_color: `#663399`, 28 | display: `minimal-ui`, 29 | icon: `src/images/favicon.png` // This path is relative to the root of the site. 30 | } 31 | }, 32 | { 33 | resolve: 'gatsby-source-sanity', 34 | options: { 35 | projectId: process.env.GATSBY_PROJECT_ID, // Put your credentials 36 | dataset: process.env.GATSBY_PROJECT_DATASET, 37 | token: process.env.GATSBY_MY_SANITY_TOKEN 38 | } 39 | }, 40 | { 41 | resolve: `@gatsby-contrib/gatsby-plugin-elasticlunr-search`, 42 | options: { 43 | // Fields to index 44 | fields: [`title`, `slug`], 45 | // How to resolve each field`s value for a supported node type 46 | resolvers: { 47 | // For any node of type MarkdownRemark, list how to resolve the fields` values 48 | SanityPost: { 49 | title: node => node.title, 50 | slug: node => node.slug.current 51 | } 52 | } 53 | } 54 | }, 55 | { 56 | resolve: 'gatsby-plugin-page-progress', 57 | options: { 58 | includePaths: [{regex: '^/blog/'}], 59 | height: 3, 60 | prependToBody: false, 61 | color: `#fd413c`, 62 | footerHeight: 500 63 | } 64 | }, 65 | 'gatsby-plugin-styled-components', 66 | 'gatsby-plugin-sitemap', 67 | 'gatsby-plugin-robots-txt' 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's Node APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/node-apis/ 5 | */ 6 | 7 | // You can delete this file if you're not using it 8 | const path = require('path') 9 | module.exports.createPages = async ({graphql, actions}) => { 10 | const {createPage} = actions 11 | const blogTemplate = path.resolve('./src/templates/blog-post/blog-post.js') 12 | const blogPageTemplate = path.resolve( 13 | './src/templates/blog-page/blog-page.js' 14 | ) 15 | 16 | const res = await graphql(` 17 | query MyQuery { 18 | allSanityPost { 19 | totalCount 20 | edges { 21 | node { 22 | title 23 | slug { 24 | current 25 | } 26 | } 27 | } 28 | } 29 | } 30 | `) 31 | 32 | res.data.allSanityPost.edges.forEach((edge, i, arr) => { 33 | createPage({ 34 | path: `/blog/${edge.node.slug.current}`, 35 | component: blogTemplate, 36 | context: { 37 | prev: 38 | i === 0 ? arr[i + 2].node.slug.current : arr[i - 1].node.slug.current, 39 | prevTitle: i === 0 ? arr[i + 2].node.title : arr[i - 1].node.title, 40 | slug: edge.node.slug.current, 41 | next: 42 | i === arr.length - 1 43 | ? arr[i - 2].node.slug.current 44 | : arr[i + 1].node.slug.current, 45 | nextTitle: 46 | i === arr.length - 1 ? arr[i - 2].node.title : arr[i + 1].node.title 47 | } 48 | }) 49 | }) 50 | 51 | const pages = Math.ceil(res.data.allSanityPost.totalCount / 10) 52 | Array.from({length: pages}).forEach((_, i) => { 53 | createPage({ 54 | path: i === 0 ? `/blog` : `/blog/${i + 1}`, 55 | component: blogPageTemplate, 56 | context: { 57 | skip: i * 10, 58 | current: i + 1, 59 | pages: pages 60 | } 61 | }) 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const InitialTheme = () => { 4 | let codeToRunOnClient = ` 5 | (function() { 6 | function getInitialColorMode() { 7 | const persistedColorPreference = window.localStorage.getItem('color-mode'); 8 | const hasPersistedPreference = typeof persistedColorPreference === 'string'; 9 | 10 | if (hasPersistedPreference) { 11 | return persistedColorPreference; 12 | } 13 | 14 | const mql = window.matchMedia('(prefers-color-scheme: dark)'); 15 | const hasMediaQueryPreference = typeof mql.matches === 'boolean'; 16 | if (hasMediaQueryPreference) { 17 | return mql.matches ? 'dark' : 'light'; 18 | } 19 | return 'light'; 20 | } 21 | const colorMode = getInitialColorMode(); 22 | const root = document.documentElement; 23 | root.setAttribute('data-theme', colorMode) 24 | })()` 25 | return