├── .gitignore ├── LICENSE ├── README.md ├── gatsby-config.js ├── gatsby-node.js ├── netlify.toml ├── package-lock.json ├── package.json ├── src ├── cms │ ├── cms.js │ └── preview-templates │ │ └── BlogPostPreview.js ├── components │ ├── Navbar.js │ ├── RelatedPostsSection.js │ ├── colors.js │ ├── footer.js │ ├── head.js │ ├── icons.js │ ├── images │ │ ├── logo-filled.png │ │ └── touch │ │ │ ├── apple-launch-img.png │ │ │ ├── apple-touch-icon-180x180.png │ │ │ ├── touch-icon-144x144.png │ │ │ └── touch-icon-192x192.png │ ├── rounded-line.js │ ├── socialMediaButtons.js │ └── util.js ├── layouts │ ├── index.js │ └── style.css ├── pages │ ├── 404.js │ ├── index.js │ ├── its-night-and-theyre-sleeping.md │ ├── my-succulents-are-doing-just-fine.md │ ├── so-many-shades-of-green.md │ ├── some-are-spiky-some-are-smooth.md │ └── wait-one-of-them-looks-wrinkly.md └── templates │ └── blog-post.js ├── static ├── admin │ └── config.yml ├── favicon.ico ├── img │ ├── averie-woodard-111823-unsplash.jpg │ ├── averie-woodard-111829-unsplash.jpg │ ├── averie-woodard-111831-unsplash.jpg │ ├── averie-woodard-181273-unsplash.jpg │ ├── fletcher-clay-217243-unsplash.jpg │ └── kace-rodriguez-82280-unsplash.jpg └── preview.png └── yarn.lock /.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 | 6 | # Build directory 7 | public/ 8 | static/admin/*.bundle.* 9 | .DS_Store 10 | yarn-error.log 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gatsby + Netlify (CMS) blog template 2 | 3 | ### Preview the template 4 | Here! [https://gatsby-netlify-cms-blog.netlify.com/](https://gatsby-netlify-cms-blog.netlify.com/) 5 | 6 | ![preview](https://gatsby-netlify-cms-blog.netlify.com/preview.png) 7 | 8 | ## Quick start 9 | 10 | #### Download it and run it on your local machine 11 | 12 | If you have yet to use Gatsby, install the CLI tool [gatsby-cli](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-cli) 13 | 14 | ``` 15 | npm install --global gatsby-cli 16 | ``` 17 | (and if you've never used npm, click [here](https://www.npmjs.com/get-npm)) 18 | 19 | In terminal: 20 | ``` 21 | git clone https://github.com/cjimmy/gatsby-netlify-cms-blog 22 | cd gatsby-netlify-cms-blog 23 | yarn install 24 | gatsby develop 25 | ``` 26 | 27 | ### Deploy it 28 | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/cjimmy/gatsby-netlify-cms-blog) 29 | 30 | ☝️ _What happens when you click on the button?_ It takes you to Netlify, where you have to click a few more things before anything real happens. (It clones this repo, and then deploys it using Netlify to the real internet.) But go ahead and deploy this site (assuming you have a Github account). This start is made for Netlify, so this button isn't just here as any hosting/deployment solution. 31 | 32 | ### Login and write a post 33 | 1. In Netlify web interface, go to the `Identity` section and click `Enable Identity`, allowing you to sign in to the CMS. 34 | 2. In Netlify, **Settings > Identity > Services** click on `Enable Git Gateway`. This is necessary to log in to the CMS. (otherwise you'll get an error `Unexpected token < in JSON at position 0`) 35 | 3. The CMS editor is located at `[YOUR_NETLIFY_SITE].netlify.com/admin/` where you can now sign up for a login. 36 | 37 | ## About 38 | This is a simple blog template based on [Gatsby](https://www.gatsbyjs.org/), and [Netlify CMS](https://www.netlifycms.org) 39 | 40 | It follows the [JAMstack architecture](https://jamstack.org) by using Git as a single source of truth, and [Netlify](https://www.netlify.com) for continuous deployment, and CDN distribution. 41 | 42 | It is largely based on @AustinGreen's [gatsby-start-netlify-cms](https://github.com/AustinGreen/gatsby-starter-netlify-cms) but a little more opinionated and different visual design. 43 | 44 | Some notable structural changes: 45 | * **_Styled Components_ instead of inline styles:** Gatsby likes to inline css styles. I much prefer [styled components](https://www.styled-components.com/) which Gatsby neatly has a module for. 46 | * **Removes Sass** – because we already have _one_ CSS module. 47 | * **Adds react-flexbox-grid** – for a grid system. Not necessary for the example, just makes things pretty. 48 | * **Adds fields to frontmatter** – to have an image for the blog post, add authors, and to fill out the tag to make it social media friendly. (The frontmatter of a post is kinda like the meta info + head). 49 | * **Removes tag pages** – and instead queries for the top 3 most recent posts that share a common tag. 50 | 51 | ### Other resources and info 52 | * **Gatsby documentation** (v1, not v2): 53 | * **Netlify documentation** (sans CMS): 54 | * **[Netlify CMS Quick Start Guide]** (https://www.netlifycms.org/docs/quick-start/#authentication) 55 | 56 | ## Advanced 57 | 58 | ### Github authentication 59 | Netlify allows [authentication via Github](https://www.netlifycms.org/docs/authentication-backends/) rather than the Netlify identity system. To do this, you have to go into the _organization's_ settings. (Github has a lot of different Settings pages.) 60 | 61 | In there, on the side menu, you'll see a section for OAuth apps. 62 | * `Application name` is what will be shown to users authenticating 63 | * `Homepage URL` is the homepage of the app 64 | * `Application Description` is a description 65 | * `Authorization callback URL` for Netlify should be `https://api.netlify.com/auth/done` per their docs 66 | 67 | With this, you'll get `Client ID` and `Client Secret` that you'll use in the Netlify interface 68 | 69 | ### Add comments 70 | Even though it's a static site, you can take advantage of the continuous deployment: [https://jamstack-comments.netlify.com/](https://jamstack-comments.netlify.com/) 71 | 72 | ### Paginate front page 73 | TODO 74 | 75 | ### Add a subscribe form 76 | Using lambdas hosted on Netlify 77 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | title: 'Gatsby-netlify-cms-starter', 4 | }, 5 | plugins: [ 6 | 'gatsby-plugin-react-helmet', 7 | `gatsby-plugin-styled-components`, 8 | { 9 | resolve: 'gatsby-source-filesystem', 10 | options: { 11 | path: `${__dirname}/src/pages`, 12 | name: 'pages', 13 | }, 14 | }, 15 | 'gatsby-plugin-sharp', 16 | 'gatsby-transformer-sharp', 17 | { 18 | resolve: 'gatsby-transformer-remark', 19 | options: { 20 | plugins: [], 21 | }, 22 | }, 23 | { 24 | resolve: 'gatsby-plugin-netlify-cms', 25 | options: { 26 | modulePath: `${__dirname}/src/cms/cms.js`, 27 | }, 28 | }, 29 | 'gatsby-plugin-netlify', // make sure to keep it last in the array 30 | ], 31 | } 32 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { createFilePath } = require('gatsby-source-filesystem') 3 | 4 | exports.createPages = ({ boundActionCreators, graphql }) => { 5 | const { createPage } = boundActionCreators 6 | 7 | return graphql(` 8 | { 9 | allMarkdownRemark(limit: 1000) { 10 | edges { 11 | node { 12 | id 13 | fields { 14 | slug 15 | } 16 | frontmatter { 17 | tags 18 | templateKey 19 | } 20 | } 21 | } 22 | } 23 | } 24 | `).then(result => { 25 | if (result.errors) { 26 | result.errors.forEach(e => console.error(e.toString())) 27 | return Promise.reject(result.errors) 28 | } 29 | 30 | const posts = result.data.allMarkdownRemark.edges 31 | 32 | posts.forEach(edge => { 33 | const id = edge.node.id 34 | const slug = edge.node.fields.slug 35 | const tags = edge.node.frontmatter.tags 36 | createPage({ 37 | path: slug, 38 | tags: tags, 39 | component: path.resolve( 40 | `src/templates/${String(edge.node.frontmatter.templateKey)}.js` 41 | ), 42 | // additional data can be passed via context 43 | // id and tags gets passed to the graphql query as parameters 44 | context: { 45 | id, 46 | tags 47 | }, 48 | }) 49 | }) 50 | }) 51 | } 52 | 53 | exports.onCreateNode = ({ node, boundActionCreators, getNode }) => { 54 | const { createNodeField } = boundActionCreators 55 | 56 | if (node.internal.type === `MarkdownRemark`) { 57 | const value = createFilePath({ node, getNode }) 58 | createNodeField({ 59 | name: `slug`, 60 | node, 61 | value, 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | # config file for netlify to build the project 2 | 3 | #build folder to publish + build command 4 | [build] 5 | publish = "public" 6 | command = "yarn build" 7 | 8 | #build tool versions 9 | [build.environment] 10 | YARN_VERSION = "1.5.1" 11 | YARN_FLAGS = "--no-ignore-optional" 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-netlify-cms", 3 | "description": "Just a starter", 4 | "version": "1.1.3", 5 | "author": "By The Bay", 6 | "dependencies": { 7 | "gatsby": "^1.9.273", 8 | "gatsby-link": "^1.6.37", 9 | "gatsby-plugin-netlify": "^1.0.19", 10 | "gatsby-plugin-netlify-cms": "^1.0.9", 11 | "gatsby-plugin-react-helmet": "^2.0.5", 12 | "gatsby-plugin-sass": "^1.0.17", 13 | "gatsby-plugin-sharp": "^1.6.48", 14 | "gatsby-plugin-styled-components": "^2.0.11", 15 | "gatsby-remark-images": "^1.5.50", 16 | "gatsby-source-filesystem": "^1.5.23", 17 | "gatsby-transformer-remark": "^1.7.44", 18 | "gatsby-transformer-sharp": "^1.6.27", 19 | "immutable": "^3.8.2", 20 | "lodash": "^4.17.5", 21 | "lodash-webpack-plugin": "^0.11.4", 22 | "netlify-cms": "^1.7.0", 23 | "prop-types": "^15.6.0", 24 | "react": "^16.4.1", 25 | "react-flexbox-grid": "^2.1.2", 26 | "react-helmet": "^5.2.0", 27 | "slate": "^0.34.2", 28 | "styled-components": "^3.3.3", 29 | "uuid": "^3.2.1" 30 | }, 31 | "keywords": [ 32 | "gatsby" 33 | ], 34 | "license": "MIT", 35 | "main": "n/a", 36 | "scripts": { 37 | "start": "npm run develop", 38 | "build": "gatsby build", 39 | "develop": "gatsby develop", 40 | "serve": "gatsby serve", 41 | "format": "prettier --trailing-comma es5 --no-semi --single-quote --write \"{gatsby-*.js,src/**/*.js}\"", 42 | "test": "echo \"Error: no test specified\" && exit 1" 43 | }, 44 | "devDependencies": { 45 | "prettier": "^1.7.4" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/cms/cms.js: -------------------------------------------------------------------------------- 1 | import CMS from 'netlify-cms' 2 | import BlogPostPreview from './preview-templates/BlogPostPreview' 3 | 4 | 5 | //-- path is absolute from built project 6 | //-- Gatsby compiles .css files to `styles.css` despite the file in `layouts/` 7 | //-- being named 'style.css' 8 | CMS.registerPreviewStyle('/styles.css') 9 | //-- Tells Netlify CMS what component is rendered for template key 'blog' 10 | CMS.registerPreviewTemplate('blog', BlogPostPreview) 11 | -------------------------------------------------------------------------------- /src/cms/preview-templates/BlogPostPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { BlogPostTemplate } from '../../templates/blog-post' 4 | 5 | //-- What Netlify CMS uses to render the preview pane 6 | const BlogPostPreview = ({ entry, widgetFor }) => { 7 | console.log(entry) 8 | return( 9 | 20 | ) 21 | } 22 | 23 | //-- getIn is from immutable.js 24 | BlogPostPreview.propTypes = { 25 | entry: PropTypes.shape({ 26 | getIn: PropTypes.func, 27 | }), 28 | widgetFor: PropTypes.func, 29 | } 30 | 31 | export default BlogPostPreview 32 | -------------------------------------------------------------------------------- /src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Link from 'gatsby-link' 3 | import Color from './colors' 4 | import styled from 'styled-components' 5 | import logo from './images/logo-filled.png' 6 | 7 | const Nav = styled.div` 8 | position: sticky; 9 | top: 0; 10 | background-color: ${Color('pink')}; 11 | height: 50px; 12 | z-index: 10; 13 | width: 100%; 14 | display: flex; 15 | flex-direction: row; 16 | align-items: center; 17 | box-shadow: 0 3px 10px rgba(0,0,0,0.05); 18 | ` 19 | const InnerContainer = styled.div` 20 | width: 100%; 21 | display: flex; 22 | justify-content: space-between; 23 | align-items: center; 24 | ` 25 | const Flex = styled.div` 26 | display: flex; 27 | ` 28 | 29 | const Padding = styled.div` 30 | width: 40px; 31 | ` 32 | 33 | const Logo = styled.img` 34 | height: 30px; 35 | width: auto; 36 | ` 37 | 38 | const NavLink = styled(Link)` 39 | text-decoration: none; 40 | color: black; 41 | padding: 3px 20px; 42 | ` 43 | 44 | const Navbar = (props) => { 45 | const links = navLinks.map( (link,i) => { 46 | return( 47 |

{link.label}

48 | ) 49 | }) 50 | return( 51 | 65 | ) 66 | } 67 | 68 | 69 | const navLinks = [ 70 | { label:"Posts", to: "/"}, 71 | // { label:"About", to: "/about"} 72 | ] 73 | 74 | export default Navbar 75 | -------------------------------------------------------------------------------- /src/components/RelatedPostsSection.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styled from 'styled-components' 4 | import {Spacer} from './util' 5 | import Link from 'gatsby-link' 6 | import Color from './colors' 7 | 8 | const Container = styled.div` 9 | display: flex; 10 | flex-direction: row; 11 | flex-wrap: wrap; 12 | align-items: flex-start; 13 | justify-content: center; 14 | ` 15 | 16 | const PostContainer = styled.div` 17 | width: 200px; 18 | padding: 20px 40px; 19 | border-radius: 2px; 20 | ` 21 | 22 | const DropShadow = styled.div` 23 | padding: 0; 24 | margin: 15px 30px; 25 | border-top: 4px solid #666; 26 | box-shadow: 3px 4px 10px rgba(0,0,0,0.3); 27 | transition-property: transform, box-shadow; 28 | transition-duration: 100ms; 29 | transition-timing-function: ease-out; 30 | @media not all and (hover: none) { 31 | &:hover { 32 | transform: translateY(-4px); 33 | border-top: 4px solid ${props=>props.color?Color(props.color):Color('teal')}; 34 | box-shadow: 6px 5px 18px rgba(0,0,0,0.2); 35 | } 36 | } 37 | ` 38 | 39 | const StyledLink = styled(Link)` 40 | text-decoration: none; 41 | color: #333; 42 | ` 43 | const PostDescription = styled.div` 44 | font-size: 14px; 45 | ` 46 | const SectionTitle = styled.h2` 47 | text-align: center; 48 | ` 49 | 50 | /* 51 | Shows the related posts based on tags. Takes the posts and creates a flex container 52 | where it displays the title and description, linking to the article 53 | */ 54 | 55 | const RelatedPostsSection = (props) => { 56 | const numPosts = props.posts ? props.posts.length : 0; 57 | const HeadingText = numPosts > 1?"Read some related posts":"Here's one more"; 58 | const RelatedPosts = 59 | props.posts?props.posts.map((post, i)=>{ 60 | const {title, description, color} = post.node.frontmatter; 61 | return( 62 | 63 | 64 | 65 |

{title}

66 | {description} 67 |
68 |
69 |
70 | ) 71 | }):null; 72 | return( 73 | 74 | { 75 | props.posts && props.posts.length ? 76 |
77 | {HeadingText} 78 | 79 | 80 | {RelatedPosts} 81 | 82 | 83 |
84 | : null 85 | } 86 |
87 | ) 88 | } 89 | 90 | RelatedPostsSection.propTypes = { 91 | posts: PropTypes.array 92 | } 93 | 94 | export default RelatedPostsSection 95 | -------------------------------------------------------------------------------- /src/components/colors.js: -------------------------------------------------------------------------------- 1 | /* 2 | How I like to do colors. 3 | Allows me to specific Color('pink') and get a predefined 4 | pink that I can then change throughout the app posthoc. 5 | It also allows me to change the saturation, tint, or opacity within the function call 6 | for example, Color('pink',0,10) is 10/255 brighter than Color('pink') 7 | useful for gradients or optically compensating. 8 | Feel free to delete all this. 9 | */ 10 | const ColorMap = new Map([ 11 | ['orange', 'hsla(29, 100%, 62%,1)'], //'rgba(255,155,60,1)', //#FF9B3C 12 | ['teal', 'hsla(177,54%,67%,1)'], //'rgba(111,179,190,1)', // #6FB3BE 13 | ['red', 'hsla(360,100%,69%,1)'], //'rgba(255,97,98,1)',// #FF6162 14 | ['tan', 'hsla(59,100%,89%,1)'],//'rgb(255, 254, 201)', // #FFFEC9 15 | ['mint', 'hsla(167,100%,89%,1)'],//'rgb(201, 255, 243)', // #C9FFF3 16 | ['pink', 'hsla(0,100%,93%,1)'],//'rgb(255, 218, 218)', // #FFDADA 17 | ['yellow', 'hsla(44,100%,67%,1)'],//'rgb(255, 209, 87)',// #FFD157 18 | ['lightblue', 'hsla(197,100%,87%,1)'], 19 | ['blue', 'hsla(219,87%,66%,1)'],//'rgba(92,144,244,1)', // #5C90F4 20 | ['lightpurple', 'hsla(253, 100%, 74%,1)'],//'rgb(150, 122, 255)', // #967aff 21 | ['purple', 'hsla(258, 100%, 69%,1)'],//'rgb(145, 97, 255)', // #9161FF 22 | ['darkpurple', 'hsla(272, 99%, 54%,1)'],//'rgb(144, 19, 254)', // #9013FE 23 | ]) 24 | 25 | const Color = (name, deltaSaturation=0, deltaTint=0, opacity=1) => { 26 | if(!name) return '#000'; 27 | const base = ColorMap.get(name); 28 | if(opacity === 1 && deltaTint === 0) {return base;} 29 | let hsl = base.split(','); 30 | let newSaturation = parseInt(hsl[1],10) + deltaSaturation; 31 | newSaturation = (newSaturation > 100) ? 100 : newSaturation; 32 | let newLightness = parseInt(hsl[2],10) + deltaTint; 33 | newLightness = (newLightness > 100) ? 100 : newLightness; 34 | hsl[1] = (newSaturation + '%'); 35 | hsl[2] = (newLightness + '%'); 36 | hsl[3] = (opacity+')'); 37 | return hsl.join(); 38 | } 39 | 40 | export default Color 41 | -------------------------------------------------------------------------------- /src/components/footer.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Row, Col } from 'react-flexbox-grid'; 3 | import styled from 'styled-components' 4 | import Link from 'gatsby-link' 5 | import Color from './colors' 6 | 7 | const FooterWrapper = styled(Row)` 8 | display: flex; 9 | background-color: navy; 10 | background:linear-gradient(45deg,${Color('darkpurple')},${Color('blue')}); 11 | padding-top: 70px; 12 | padding-bottom: 45px; 13 | box-shadow: 0 -2px 10px rgba(0,0,0,0.1); 14 | @media print { 15 | display: none; 16 | } 17 | `; 18 | 19 | const FinePrint = styled.div` 20 | font-weight: normal; 21 | margin-bottom: 35px; 22 | font-size: 14px; 23 | line-height: 18px; 24 | color: white; 25 | `; 26 | 27 | const StyledLink = styled.a` 28 | color: white; 29 | ` 30 | 31 | 32 | 33 | const Footer = () => { 34 | return ( 35 | 36 | 42 | 43 | This site was built with a Gatsby + Netlify CMS starter by @cjimmy. Please star the repo on Github if you found it useful. 44 |
45 | All rights reserved © 46 |
47 | 48 | 49 |
50 | ); 51 | } 52 | 53 | 54 | export default Footer; 55 | -------------------------------------------------------------------------------- /src/components/head.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Helmet from "react-helmet"; 4 | import Color from './colors' 5 | 6 | import appleTouchIcon from './images/touch/apple-touch-icon-180x180.png' 7 | import appleLaunchImg from './images/touch/apple-launch-img.png' 8 | import touchIcon192 from './images/touch/touch-icon-192x192.png' 9 | import touchIcon144 from './images/touch/touch-icon-144x144.png' 10 | 11 | 12 | /* This will go at the top of every page in the tag unless overridden. 13 | 14 | all these props will be used for Meta, Facebook og, and Twitter cards 15 | 16 | props 17 | --------- 18 | url will be the canonical url that this page links to 19 | title will be the title at the top of every page. It should end with "– By The Bay" 20 | headline should be short and descriptive (70 chars) 21 | description should be 160 characters max and explain what the purpose of the page is 22 | image should be a url to an image, could be static. 23 | URL of image to use in the card. 24 | Images must be less than 5MB in size. JPG, PNG, WEBP and GIF formats are supported. 25 | Only the first frame of an animated GIF will be used. SVG is not supported. 26 | USAGE: 27 | import Head from 'layouts/head' 28 | 29 | 36 | 37 | */ 38 | 39 | const Head = (props) => ( 40 | 41 | {props.title} 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | ); 79 | 80 | 81 | Head.defaultProps = { 82 | url: "https://examples.com", 83 | title: "Default title for pages", 84 | headline: "Default headline for social media", 85 | description: "Default description for social media", 86 | image: "",//-- replace with image link, must be direct link, not relative, since apps like FB will be accessing it 87 | } 88 | 89 | 90 | Head.propTypes = { 91 | url: PropTypes.string.isRequired, 92 | title: PropTypes.string.isRequired, 93 | headline: PropTypes.string.isRequired, 94 | description: PropTypes.string.isRequired, 95 | image: PropTypes.string.isRequired, 96 | } 97 | 98 | export default Head; 99 | -------------------------------------------------------------------------------- /src/components/icons.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | //-- a set of svg icons 4 | 5 | export const NavArrow = (props) => ( 6 | 7 | ) 8 | export const EmailIcon = (props) => ( 9 | 10 | 11 | 12 | ); 13 | export const FbIcon = (props) => ( 14 | 15 | 16 | 17 | ); 18 | export const TwitterIcon = (props) => ( 19 | 20 | ); 21 | -------------------------------------------------------------------------------- /src/components/images/logo-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/src/components/images/logo-filled.png -------------------------------------------------------------------------------- /src/components/images/touch/apple-launch-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/src/components/images/touch/apple-launch-img.png -------------------------------------------------------------------------------- /src/components/images/touch/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/src/components/images/touch/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /src/components/images/touch/touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/src/components/images/touch/touch-icon-144x144.png -------------------------------------------------------------------------------- /src/components/images/touch/touch-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/src/components/images/touch/touch-icon-192x192.png -------------------------------------------------------------------------------- /src/components/rounded-line.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import PropTypes from 'prop-types' 4 | 5 | //-- creates a rounded line (nicer than a border-top, etc) 6 | 7 | const StyledLine = styled.div` 8 | display: inline-block; 9 | box-sizing: border-box; 10 | height: 0; 11 | width: 100%; 12 | border: 2px solid ${props=>props.color?props.color:"#333"}; 13 | border-radius: 2px; 14 | margin-bottom: 5px; 15 | `; 16 | 17 | const Line = (props) => ( 18 | 19 | ) 20 | 21 | export default Line; 22 | 23 | Line.propTypes = { 24 | color: PropTypes.string 25 | } 26 | -------------------------------------------------------------------------------- /src/components/socialMediaButtons.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styled from 'styled-components' 4 | import {EmailIcon, FbIcon, TwitterIcon} from './icons' 5 | import Color from './colors' 6 | 7 | //-- takes a title and description and 8 | //-- creates a row of social media buttons 9 | 10 | const Center = styled.div` 11 | display: flex; 12 | flex-direction: row; 13 | justify-content: center; 14 | align-items: center; 15 | height: 70px; 16 | text-align: center; 17 | margin-top: 30px; 18 | `; 19 | 20 | const IconContainer = styled.a` 21 | display: block; 22 | height: 40px; 23 | width: 40px; 24 | padding: 5px; 25 | box-sizing: border-box; 26 | border-radius: 4px; 27 | margin: 0 5px; 28 | background-color: ${Color('pink')}; 29 | box-shadow: 3px 3px 10px rgba(0,0,0,0.05); 30 | @media not all and (hover: none) { 31 | &:hover{ 32 | background-color: ${Color('pink')}; 33 | } 34 | } 35 | `; 36 | 37 | const Contain = styled.div` 38 | height: 100%; 39 | align-items: center; 40 | display: flex; 41 | justify-content: center; 42 | object-fit: contain; 43 | `; 44 | 45 | const Label = styled.h4` 46 | margin-right: 10px; 47 | ` 48 | 49 | const fbAppId = 'YOUR_FACBEOOK_APP_ID' //-- replace with your app id https://developers.facebook.com/docs/apps/register/ 50 | 51 | const SocialMediaButtons = (props) => { 52 | const {title, description, url} = props; 53 | const twitterUrl = `https://twitter.com/intent/tweet?url=${url}&text=${description}`; 54 | const facebookUrl = `https://www.facebook.com/dialog/share?app_id=${fbAppId}&display=popup&href=${url}`; 55 | const emailUrl = `mailto:?subject=${title}&body=${description} ${url}`; 56 | 57 | return( 58 |
59 | 60 | }/> 61 | }/> 62 | }/> 63 |
64 | ); 65 | } 66 | 67 | const Icon = (props) => { 68 | return( 69 | 74 | {props.img} 75 | 76 | ); 77 | } 78 | 79 | SocialMediaButtons.propTypes = { 80 | title: PropTypes.string.isRequired, 81 | description: PropTypes.string.isRequired, 82 | url: PropTypes.string.isRequired, 83 | } 84 | 85 | export default SocialMediaButtons; 86 | -------------------------------------------------------------------------------- /src/components/util.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | /*Util functions*/ 4 | 5 | //-- vertical spacer, easier to read than a buried css rule (i.e. margin) 6 | export const Spacer = styled.div` 7 | height: ${props=>props.height}px; 8 | @media screen and (max-width: 767px) { 9 | height: ${props=>props.xsHeight}px; 10 | } 11 | `; 12 | -------------------------------------------------------------------------------- /src/layouts/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Navbar from '../components/Navbar' 4 | import Footer from '../components/footer' 5 | import {Spacer} from '../components/util' 6 | import './style.css' 7 | 8 | 9 | /* 10 | In Gatsby, this "wraps" every page, where the 11 | contents of the page are under {props.children()} 12 | 13 | in Gatsby v2, this is deprecated and you have to manually 14 | wrap all the pages that you want wrapped. This makes 15 | more sense IMO. 16 | */ 17 | 18 | const TemplateWrapper = (props) => ( 19 |
20 | 21 | {props.children()} 22 | 23 |
24 |
25 | ) 26 | 27 | TemplateWrapper.propTypes = { 28 | children: PropTypes.func 29 | } 30 | 31 | 32 | export default TemplateWrapper 33 | -------------------------------------------------------------------------------- /src/layouts/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | position: relative; 3 | font-family: "Georgia", "Times New Roman", Serif; 4 | font-size: 16px; 5 | line-height: 25px; 6 | color: #323232; 7 | margin: 0 auto; 8 | padding: 0; 9 | overflow-x: hidden; 10 | box-sizing: border-box; 11 | max-width: 1440px; 12 | background-color: white; 13 | box-shadow: 0 0 40px rgba(0,0,0,0.1); 14 | } 15 | 16 | 17 | @media screen and (min-width: 768px) { 18 | html { 19 | text-rendering: optimizeLegibility; 20 | } 21 | } 22 | 23 | a { 24 | color: #9161FF; 25 | text-decoration: underline; 26 | } 27 | 28 | a:hover, a:active { 29 | text-decoration: none; 30 | } 31 | em { 32 | font-style: italic; 33 | } 34 | b { 35 | font-style: bold; 36 | } 37 | 38 | ::selection { 39 | background: #FFE87C; 40 | } 41 | 42 | ul { 43 | margin-top: 0; 44 | padding-left: 20px; 45 | } 46 | 47 | h1 { 48 | font-family: Abril Fatface, Georgia, Times New Roman, slab-serif; 49 | /*font-size: 40px;*/ 50 | font-weight: 700; 51 | line-height: 70px; 52 | font-size: 70px; 53 | letter-spacing: -2px; 54 | } 55 | 56 | @media screen and (max-width: 767px) { 57 | h1 { 58 | font-size: 36px; 59 | line-height: 39px; 60 | letter-spacing: -1px; 61 | } 62 | } 63 | 64 | h2 { 65 | font-family: 'Roboto Condensed', 'Helvetica Condensed', 'Helvetica Neue', Helvetica, sans-serif; 66 | font-weight: 400; 67 | font-size: 28px; 68 | line-height: 32px; 69 | letter-spacing: 0.3px; 70 | text-transform: uppercase; 71 | margin-top: 45px; 72 | margin-bottom: 0; 73 | } 74 | h3 { 75 | font-family: 'Roboto Condensed', 'Helvetica Condensed', 'Helvetica Neue', Helvetica, sans-serif; 76 | font-weight: 600; 77 | font-size: 14px; 78 | line-height: 24px; 79 | text-transform: uppercase; 80 | } 81 | 82 | h4 { 83 | font-family: Roboto, 'Helvetica Neue', Helvetica, sans-serif; 84 | font-weight: 400; 85 | font-size: 13px; 86 | line-height: 1.5em; 87 | letter-spacing: 1.4px; 88 | text-transform: uppercase; 89 | margin: 0; 90 | } 91 | h5 { 92 | font-family: Helvetica Neue, Helvetica, Roboto, sans-serif; 93 | font-weight: bold; 94 | letter-spacing: -0.1px; 95 | } 96 | 97 | /*remove red outline in moz*/ 98 | input:invalid { 99 | box-shadow: none; 100 | } 101 | input:-moz-submit-invalid { 102 | box-shadow: none; 103 | } 104 | input:-moz-ui-invalid { 105 | box-shadow:none; 106 | } 107 | 108 | blockquote { 109 | margin-left: 0; 110 | margin-top: 5px; 111 | margin-bottom: 5px; 112 | font-style: italic; 113 | padding-left: 40px; 114 | border-left: 3px solid #eee; 115 | } 116 | 117 | /* Styles for blog post */ 118 | 119 | .blog-post-body > div { 120 | display: flex; 121 | flex-direction: column; 122 | align-items: flex-start; 123 | justify-content: center; 124 | width: 50%; 125 | box-sizing: border-box; 126 | margin-left: auto; 127 | margin-right: auto; 128 | background-color: white; 129 | } 130 | 131 | .blog-post-body div p > img { 132 | max-height: 350px; 133 | margin-left: -10%; 134 | width: 120%; 135 | object-fit: cover; 136 | align-self: center; 137 | border-radius: 4px; 138 | } 139 | 140 | .blog-post-body h2, .blog-post-body h3{ 141 | align-self: flex-start; 142 | } 143 | 144 | .blog-post-body h2 { 145 | margin-left: -10% 146 | } 147 | 148 | .author-head-img { 149 | width: auto; 150 | height: 40px; 151 | margin-right: 20px; 152 | user-select: none; 153 | filter: drop-shadow(1px 3px 2px #eee); 154 | } 155 | 156 | /*aimed at social media icons on netlify CMS*/ 157 | .social-media-icons svg { 158 | width: 40px; 159 | } 160 | 161 | @media screen and (max-width: 767px) { 162 | .blog-post-body div { 163 | margin-left: 5%; 164 | margin-right: 5%; 165 | width: 90%; 166 | box-sizing: border-box; 167 | } 168 | 169 | .blog-post-body p > img { 170 | max-height: 100px; 171 | width: 110%; 172 | margin-left: -5%; 173 | object-fit: cover; 174 | } 175 | 176 | .blog-post-body h2 { 177 | margin-left: 0; 178 | } 179 | } 180 | 181 | 182 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default () => ( 4 |
5 |

Sorry! Page not found.

6 | You just hit a route that doesn't exist. Head back to the home page to continue exploring. 7 |
8 | ) 9 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Row, Col } from 'react-flexbox-grid'; 3 | import PropTypes from 'prop-types' 4 | import styled from 'styled-components' 5 | import Link from 'gatsby-link' 6 | import Color from '../components/colors' 7 | import {Spacer} from '../components/util' 8 | import Head from '../components/head' 9 | 10 | const PostMetaTextContainer = styled.div` 11 | margin-right: 20px; 12 | width: 60%; 13 | @media screen and (max-width: 767px) { 14 | width: 100%; 15 | } 16 | 17 | ` 18 | const PostContainer = styled.div` 19 | position: relative; 20 | margin: 40px 0; 21 | background: white; 22 | ` 23 | 24 | const Line = styled.div` 25 | margin-top: 12px; 26 | height: 4px; 27 | border-radius: 2px; 28 | width: 100%; 29 | background-color: ${props=>props.color?Color(props.color):Color('pink')}; 30 | ` 31 | 32 | const PostTitle = styled.h2` 33 | text-decoration: none; 34 | color: #323232; 35 | text-align: right; 36 | margin: 0; 37 | 38 | @media not all and (hover: none) { 39 | &:hover { 40 | text-decoration: underline; 41 | text-decoration-color: ${props=>props.color?Color(props.color):'#333'}; 42 | } 43 | } 44 | @media screen and (max-width: 767px) { 45 | text-align: left; 46 | } 47 | `; 48 | 49 | const MetaContainer = styled.div` 50 | display: flex; 51 | flex-direction: column; 52 | align-items: flex-end; 53 | @media screen and (max-width: 767px) { 54 | align-items: flex-start; 55 | } 56 | 57 | ` 58 | 59 | const PostImageContainer = styled.div` 60 | width: 80%; 61 | 62 | ` 63 | const PostImage = styled.img` 64 | object-fit: cover; 65 | width: calc(100% - 20px); 66 | max-height: 66px; 67 | border-radius: 2px; 68 | margin-right: 20px; 69 | ` 70 | 71 | const PostLink = styled(Link)` 72 | text-decoration: none; 73 | ` 74 | const KeepReading = styled(Link)` 75 | text-align: right; 76 | margin-top: 10px; 77 | text-decoration: none; 78 | color: #333; 79 | @media not all and (hover: none) { 80 | &:hover { 81 | text-decoration: underline; 82 | text-decoration-color: ${props=>props.color?Color(props.color):'#333'}; 83 | } 84 | } 85 | ` 86 | const Author = styled.h4` 87 | color: #999; 88 | text-align: right; 89 | margin-top: 10px; 90 | @media screen and (max-width: 767px) { 91 | text-align: left; 92 | } 93 | ` 94 | 95 | const Date = styled.h4` 96 | color: #999; 97 | margin-top: 5px; 98 | margin-bottom: 15px; 99 | text-align: right; 100 | margin-right: 20px; 101 | ` 102 | const BlogTitle = styled.div` 103 | display: flex; 104 | flex-direction: row; 105 | align-items: center; 106 | padding: 0 40px; 107 | @media screen and (max-width: 767px) { 108 | justify-content: flex-start; 109 | flex-direction: column; 110 | } 111 | ` 112 | const BlogDescription = styled.div` 113 | margin-left: 30px; 114 | margin-top: 40px; 115 | font-style: italic; 116 | @media screen and (max-width: 767px) { 117 | margin-top: 0; 118 | margin-left: 0; 119 | text-align: center; 120 | } 121 | 122 | ` 123 | const BlogName = styled.h1` 124 | font-size: 70px; 125 | letter-spacing: -4px; 126 | ` 127 | const Excerpt = styled.div` 128 | margin-top: 19px; 129 | margin-bottom: 20px; 130 | ` 131 | 132 | 133 | class IndexPage extends React.Component { 134 | render() { 135 | const { data } = this.props 136 | const { edges: posts } = data.allMarkdownRemark 137 | const allPosts = posts.map(({ node: post }) => { 138 | const {color, image, date, title, author} = post.frontmatter; 139 | return( 140 | 141 | 142 | 148 | 149 | {date} 150 | 151 | 152 | 153 | 154 | {title} 155 | 156 | 157 | By {author} 158 | 159 | 160 | 161 | 162 | 168 | 169 | {post.excerpt} 170 | 171 |

Keep Reading →

172 |
173 | 174 | 175 |
176 | 177 |
178 | ) 179 | } 180 | ); 181 | 182 | return ( 183 |
184 | 185 | 186 | Bloog. 187 | A twice-daily dose of the state of my succulents 188 | 189 | {allPosts} 190 |
191 | ) 192 | } 193 | } 194 | 195 | export default IndexPage; 196 | 197 | IndexPage.propTypes = { 198 | data: PropTypes.shape({ 199 | allMarkdownRemark: PropTypes.shape({ 200 | edges: PropTypes.array, 201 | }), 202 | }), 203 | } 204 | 205 | export const pageQuery = graphql` 206 | query IndexQuery { 207 | allMarkdownRemark( 208 | sort: { order: DESC, fields: [frontmatter___date] }, 209 | filter: { frontmatter: { templateKey: { eq: "blog-post" } }} 210 | ) { 211 | edges { 212 | node { 213 | excerpt(pruneLength: 300) 214 | id 215 | fields { 216 | slug 217 | } 218 | frontmatter { 219 | title 220 | author 221 | templateKey 222 | color 223 | image 224 | date(formatString: "MMMM DD, YYYY") 225 | } 226 | } 227 | } 228 | } 229 | } 230 | ` 231 | -------------------------------------------------------------------------------- /src/pages/its-night-and-theyre-sleeping.md: -------------------------------------------------------------------------------- 1 | --- 2 | templateKey: blog-post 3 | title: It's night and they're sleeping. 4 | description: What do you think they dream about 5 | author: Jimmy C 6 | image: /img/averie-woodard-181273-unsplash.jpg 7 | smTitle: Nighttimes is scary 8 | smDescription: My succulents go to sleep 9 | color: orange 10 | date: '2018-07-18T11:47:38-07:00' 11 | tags: 12 | - succulents 13 | - night 14 | - sleep 15 | - bob ross 16 | --- 17 | Let's do it again then, _what the heck_. It's hard to see things when you're too close. Take a step back and look. I thought today we would make a happy little stream that's just running through the woods here. You can do anything here. So don't worry about it. You have to allow the paint to break to make it beautiful. Trees live in your fan brush, but you have to scare them out. 18 | 19 | And I will hypnotize that just a little bit. And right there you got an almighty cloud. Now we'll take the almighty fan brush. We'll do another happy little painting. It's life. It's interesting. It's fun. You want your tree to have some character. Make it special. 20 | 21 | Let's give him a friend too. Everybody needs a friend. As trees get older they lose their chlorophyll. Have fun with it. There comes a nice little fluffer. Let all these little things happen. Don't fight them. Learn to use them. 22 | 23 | ![man standing](/img/averie-woodard-111831-unsplash.jpg) 24 | 25 | We'll have a super time. Let's put some highlights on these little trees. The sun wouldn't forget them. Let's have a happy little tree in here. 26 | 27 | Be careful. You can always add more - but you can't take it away. Remember how free clouds are. They just lay around in the sky all day long. Sometimes you learn more from your mistakes than you do from your masterpieces. And that's when it becomes fun - you don't have to spend your time thinking about what's happening - you just let it happen. Let's build an almighty mountain. We don't need any guidelines or formats. All we need to do is just let it flow right out of us. 28 | 29 | Don't kill all your dark areas - you need them to show the light. I get carried away with this brush cleaning. Maybe he has a little friend that lives right over here. Pretend you're water. Just floating without any effort. Having a good day. Don't fight it, use what happens. 30 | 31 | Maybe there's a happy little bush that lives right there. Every time you practice, you learn more. Do an almighty painting with us. Poor old tree. 32 | -------------------------------------------------------------------------------- /src/pages/my-succulents-are-doing-just-fine.md: -------------------------------------------------------------------------------- 1 | --- 2 | templateKey: blog-post 3 | title: My succulents are doing just fine 4 | description: I tell ya. 5 | author: Jimmy C 6 | image: /img/kace-rodriguez-82280-unsplash.jpg 7 | smTitle: What did I tell you about my succulents? 8 | smDescription: They're fine. 9 | color: purple 10 | date: '2018-07-17T11:47:38-07:00' 11 | tags: 12 | - succulents 13 | - design 14 | - fine 15 | - bob ross 16 | --- 17 | Let's do it again then, _what the heck_. It's hard to see things when you're too close. Take a step back and look. I thought today we would make a happy little stream that's just running through the woods here. You can do anything here. So don't worry about it. You have to allow the paint to break to make it beautiful. Trees live in your fan brush, but you have to scare them out. 18 | 19 | And I will hypnotize that just a little bit. And right there you got an almighty cloud. Now we'll take the almighty fan brush. We'll do another happy little painting. It's life. It's interesting. It's fun. You want your tree to have some character. Make it special. 20 | 21 | Let's give him a friend too. Everybody needs a friend. As trees get older they lose their chlorophyll. Have fun with it. There comes a nice little fluffer. Let all these little things happen. Don't fight them. Learn to use them. 22 | 23 | ![man standing](/img/averie-woodard-111823-unsplash.jpg) 24 | 25 | We'll have a super time. Let's put some highlights on these little trees. The sun wouldn't forget them. Let's have a happy little tree in here. 26 | 27 | Be careful. You can always add more - but you can't take it away. Remember how free clouds are. They just lay around in the sky all day long. Sometimes you learn more from your mistakes than you do from your masterpieces. And that's when it becomes fun - you don't have to spend your time thinking about what's happening - you just let it happen. Let's build an almighty mountain. We don't need any guidelines or formats. All we need to do is just let it flow right out of us. 28 | 29 | Don't kill all your dark areas - you need them to show the light. I get carried away with this brush cleaning. Maybe he has a little friend that lives right over here. Pretend you're water. Just floating without any effort. Having a good day. Don't fight it, use what happens. 30 | 31 | Maybe there's a happy little bush that lives right there. Every time you practice, you learn more. Do an almighty painting with us. Poor old tree. 32 | -------------------------------------------------------------------------------- /src/pages/so-many-shades-of-green.md: -------------------------------------------------------------------------------- 1 | --- 2 | templateKey: blog-post 3 | title: So many shades of green 4 | description: Like infinite. 5 | author: Jimmy C 6 | image: /img/averie-woodard-111831-unsplash.jpg 7 | smTitle: How many greens are in my succulents? 8 | smDescription: Click to find out 9 | color: teal 10 | date: '2018-07-17T11:47:38-07:00' 11 | tags: 12 | - night 13 | - design 14 | - green 15 | --- 16 | Let's do it again then, _what the heck_. It's hard to see things when you're too close. Take a step back and look. I thought today we would make a happy little stream that's just running through the woods here. You can do anything here. So don't worry about it. You have to allow the paint to break to make it beautiful. Trees live in your fan brush, but you have to scare them out. 17 | 18 | And I will hypnotize that just a little bit. And right there you got an almighty cloud. Now we'll take the almighty fan brush. We'll do another happy little painting. It's life. It's interesting. It's fun. You want your tree to have some character. Make it special. 19 | 20 | Let's give him a friend too. Everybody needs a friend. As trees get older they lose their chlorophyll. Have fun with it. There comes a nice little fluffer. Let all these little things happen. Don't fight them. Learn to use them. 21 | 22 | ![man standing](/img/averie-woodard-111823-unsplash.jpg) 23 | 24 | We'll have a super time. Let's put some highlights on these little trees. The sun wouldn't forget them. Let's have a happy little tree in here. 25 | 26 | Be careful. You can always add more - but you can't take it away. Remember how free clouds are. They just lay around in the sky all day long. Sometimes you learn more from your mistakes than you do from your masterpieces. And that's when it becomes fun - you don't have to spend your time thinking about what's happening - you just let it happen. Let's build an almighty mountain. We don't need any guidelines or formats. All we need to do is just let it flow right out of us. 27 | 28 | Don't kill all your dark areas - you need them to show the light. I get carried away with this brush cleaning. Maybe he has a little friend that lives right over here. Pretend you're water. Just floating without any effort. Having a good day. Don't fight it, use what happens. 29 | 30 | Maybe there's a happy little bush that lives right there. Every time you practice, you learn more. Do an almighty painting with us. Poor old tree. 31 | -------------------------------------------------------------------------------- /src/pages/some-are-spiky-some-are-smooth.md: -------------------------------------------------------------------------------- 1 | --- 2 | templateKey: blog-post 3 | title: Some are spiky, some are smooth 4 | description: It's like they have a personality 5 | author: Jimmy C 6 | image: /img/fletcher-clay-217243-unsplash.jpg 7 | smTitle: The textures of my succulents 8 | smDescription: Spiky and smooth. 9 | color: pink 10 | date: '2018-07-15T11:47:38-07:00' 11 | tags: 12 | - succulents 13 | - texture 14 | --- 15 | Let's do it again then, _what the heck_. It's hard to see things when you're too close. Take a step back and look. I thought today we would make a happy little stream that's just running through the woods here. You can do anything here. So don't worry about it. You have to allow the paint to break to make it beautiful. Trees live in your fan brush, but you have to scare them out. 16 | 17 | And I will hypnotize that just a little bit. And right there you got an almighty cloud. Now we'll take the almighty fan brush. We'll do another happy little painting. It's life. It's interesting. It's fun. You want your tree to have some character. Make it special. 18 | 19 | Let's give him a friend too. Everybody needs a friend. As trees get older they lose their chlorophyll. Have fun with it. There comes a nice little fluffer. Let all these little things happen. Don't fight them. Learn to use them. 20 | 21 | ![man standing](/img/averie-woodard-111829-unsplash.jpg) 22 | 23 | We'll have a super time. Let's put some highlights on these little trees. The sun wouldn't forget them. Let's have a happy little tree in here. 24 | 25 | Be careful. You can always add more - but you can't take it away. Remember how free clouds are. They just lay around in the sky all day long. Sometimes you learn more from your mistakes than you do from your masterpieces. And that's when it becomes fun - you don't have to spend your time thinking about what's happening - you just let it happen. Let's build an almighty mountain. We don't need any guidelines or formats. All we need to do is just let it flow right out of us. 26 | 27 | Don't kill all your dark areas - you need them to show the light. I get carried away with this brush cleaning. Maybe he has a little friend that lives right over here. Pretend you're water. Just floating without any effort. Having a good day. Don't fight it, use what happens. 28 | 29 | Maybe there's a happy little bush that lives right there. Every time you practice, you learn more. Do an almighty painting with us. Poor old tree. 30 | -------------------------------------------------------------------------------- /src/pages/wait-one-of-them-looks-wrinkly.md: -------------------------------------------------------------------------------- 1 | --- 2 | templateKey: blog-post 3 | title: Wait one of them looks wrinkly 4 | description: Is this the beginning of the end 5 | author: Jimmy C 6 | image: /img/averie-woodard-111829-unsplash.jpg 7 | smTitle: NOOOOOO 8 | smDescription: What's happening to my babies 9 | color: blue 10 | date: '2018-07-19T11:55:11-07:00' 11 | tags: 12 | - uhoh 13 | - help 14 | - succulents 15 | - tuesday 16 | --- 17 | Let's do it again then, what the heck. It's hard to see things when you're too close. Take a step back and look. I thought today we would make a happy little stream that's just running through the woods here. You can do anything here. So don't worry about it. You have to allow the paint to break to make it beautiful. Trees live in your fan brush, but you have to scare them out. 18 | 19 | And I will hypnotize that just a little bit. And right there you got an almighty cloud. Now we'll take the almighty fan brush. We'll do another happy little painting. It's life. It's interesting. It's fun. You want your tree to have some character. Make it special. 20 | 21 | ![man standing](/img/fletcher-clay-217243-unsplash.jpg) 22 | 23 | Let's give him a friend too. Everybody needs a friend. As trees get older they lose their chlorophyll. Have fun with it. There comes a nice little fluffer. Let all these little things happen. Don't fight them. Learn to use them. 24 | 25 | We'll have a super time. Let's put some highlights on these little trees. The sun wouldn't forget them. Let's have a happy little tree in here. 26 | 27 | Be careful. You can always add more - but you can't take it away. Remember how free clouds are. They just lay around in the sky all day long. Sometimes you learn more from your mistakes than you do from your masterpieces. And that's when it becomes fun - you don't have to spend your time thinking about what's happening - you just let it happen. Let's build an almighty mountain. We don't need any guidelines or formats. All we need to do is just let it flow right out of us. 28 | 29 | Don't kill all your dark areas - you need them to show the light. I get carried away with this brush cleaning. Maybe he has a little friend that lives right over here. Pretend you're water. Just floating without any effort. Having a good day. Don't fight it, use what happens. 30 | 31 | Maybe there's a happy little bush that lives right there. Every time you practice, you learn more. Do an almighty painting with us. Poor old tree. 32 | -------------------------------------------------------------------------------- /src/templates/blog-post.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Row, Col } from 'react-flexbox-grid'; 4 | import styled from 'styled-components' 5 | import Helmet from 'react-helmet' 6 | import Head from '../components/head' 7 | import {Spacer} from '../components/util' 8 | import Line from '../components/rounded-line' 9 | import Color from '../components/colors' 10 | import SocialMediaButtons from '../components/socialMediaButtons' 11 | import RelatedPosts from '../components/RelatedPostsSection' 12 | 13 | 14 | const FrontImage = styled.img` 15 | max-height: 200px; 16 | width: 100%; 17 | margin-top: 15px; 18 | object-fit: cover; 19 | object-position: 50% 50%; 20 | box-shadow: 0 2px 10px #eee; 21 | ` 22 | const TitleContainer = styled.div` 23 | @media screen and (max-width: 767px) { 24 | margin-left: 10px; 25 | } 26 | ` 27 | const Title = styled.h1` 28 | text-align: left; 29 | margin-bottom: 5px; 30 | ` 31 | const Subtitle = styled.p` 32 | font-style: italic; 33 | text-align: left; 34 | ` 35 | const AttributionText = styled.h4` 36 | color: #777; 37 | ` 38 | const Attribution = styled.div` 39 | margin-top: ${props=>props.offset}px; 40 | @media screen and (max-width: 767px) { 41 | margin-top: 0; 42 | margin-left: 10px; 43 | width: calc(100% - 20px); 44 | } 45 | ` 46 | 47 | /* 48 | There's two components in this file. 49 | BlogPost and BlogPostTemplate 50 | BlogPost receives the data returned from the graphql query 51 | if you console.log(props), you'll see there's a lot in there, 52 | including stuff from Gatsby's createPages, React-router, and the 53 | query results (`blogPostData` and `relatedPosts`) 54 | */ 55 | const BlogPost = (props) => { 56 | const thisPostsId = props.pathContext.id; 57 | const { relatedPosts, blogPostData: post } = props.data; 58 | let related = []; 59 | // remove self (this post) from related posts 60 | for( let i = 0; i < relatedPosts.edges.length; i++ ){ 61 | if (relatedPosts.edges[i].node.id !== thisPostsId) { 62 | related.push(relatedPosts.edges[i]); 63 | } 64 | } 65 | return ( 66 | 73 | ) 74 | } 75 | 76 | BlogPost.propTypes = { 77 | data: PropTypes.shape({ 78 | markdownRemark: PropTypes.object, 79 | }), 80 | } 81 | 82 | export default BlogPost 83 | 84 | //----------------------------------------- 85 | 86 | const HTMLContent = (props) => ( 87 |
88 | ) 89 | 90 | const Content = ({ content }) => ( 91 |
{content}
92 | ) 93 | //----------------------------------------- 94 | 95 | export class BlogPostTemplate extends React.Component { 96 | render() { 97 | const { content, contentComponent, frontmatter, slug, related} = this.props; 98 | const { description, tags, title, date, author, color, smTitle, smDescription, image} = frontmatter; 99 | const PostContent = contentComponent || Content; 100 | const SocialTitle = smTitle || title; 101 | const SocialDescription = smDescription || description; 102 | return ( 103 |
104 | 110 | 111 | 117 | 118 | 119 | {title} 120 | {description} 121 | 122 | 123 | 129 | 130 | 131 | 132 | By {author} 133 | {date} 134 | 135 | 136 | 137 |
138 | {image && } 139 | 140 | 141 | 142 | 143 |
144 | 149 | 150 |
151 | ) 152 | } 153 | } 154 | /* 155 | note: you'll notice I use className to add css rules here and there 156 | That's because Netlify CMS doesn't pick up styled-components, so anything that's important 157 | to Netlify CMS needs to be in layout/style.css 158 | */ 159 | 160 | BlogPostTemplate.propTypes = { 161 | content: PropTypes.string.isRequired, 162 | contentComponent: PropTypes.func, 163 | helmet: PropTypes.instanceOf(Helmet), 164 | } 165 | 166 | 167 | /* 168 | In Gatsby, the graphql pageQuery is automatically run when the page is compiled/created 169 | and the results are returned to the component in props.data. You can explore the format it 170 | returns via http://localhost:8000/___graphql 171 | (you wont be able to use variables, so replace them with strings that make sense) 172 | 173 | there's two queries in this, aliased by the names blogPostData and relatedPosts. It wasn't 174 | necessary to alias them, but if I didn't, the results would be under markdownRemark and allMarkdownRemark. 175 | That's not very helpful. 176 | 177 | blogPostData receives the parameter $id from the query in gatsby-node.js and passed via createPages() 178 | The graphql query finds the markdown file with that id and then returns all the information below it. 179 | (note: you can add more information by changing what's in the markdown files. The frontmatter is the top section. 180 | Be sure to restart gatsby if you change the schema. Can be the cause of endless frustration.) 181 | We'll need all this to hydrate the BlogPost component. 182 | 183 | relatedPosts finds the most recent 4 posts that share a tag with the current post. 184 | Remember, we're getting the param $tags from createPages, so we just query across all 185 | the markdown and find files that have a common tag. We then return just enough information 186 | so that we can link to them in the relatedPostsSection component. 187 | 188 | okay, hopefully that was helpful. 189 | */ 190 | export const pageQuery = graphql` 191 | query BlogPostByID($id: String!, $tags: [String!]!) { 192 | blogPostData: markdownRemark(id: { eq: $id }) { 193 | id 194 | html 195 | fields { 196 | slug 197 | } 198 | frontmatter { 199 | author 200 | date(formatString: "MMMM DD, YYYY") 201 | title 202 | description 203 | color 204 | smTitle 205 | smDescription 206 | tags 207 | image 208 | } 209 | } 210 | 211 | relatedPosts: allMarkdownRemark( 212 | limit: 4, 213 | sort: {fields: [frontmatter___date], order: DESC}, 214 | filter: {frontmatter: {tags: {in: $tags}}} 215 | ) { 216 | edges { 217 | node { 218 | id 219 | frontmatter { 220 | title 221 | description 222 | color 223 | } 224 | fields { 225 | slug 226 | } 227 | } 228 | } 229 | } 230 | } 231 | ` 232 | -------------------------------------------------------------------------------- /static/admin/config.yml: -------------------------------------------------------------------------------- 1 | backend: 2 | name: git-gateway 3 | branch: master 4 | 5 | media_folder: static/img 6 | public_folder: /img 7 | publish_mode: editorial_workflow 8 | # see https://www.netlifycms.org/docs/configuration-options/ 9 | 10 | collections: 11 | - name: "blog" 12 | label: "Blog" 13 | folder: "src/pages/" 14 | create: true 15 | slug: "{{slug}}" 16 | fields: 17 | - {label: "Template Key", name: "templateKey", widget: "hidden", default: "blog-post"} 18 | - {label: "Title", name: "title", widget: "string"} 19 | - {label: "Description", name: "description", widget: "string"} 20 | - {label: "Author", name: "author", widget: "string"} 21 | - {label: "Front Image", name: "image", widget: "image", required: false} 22 | - {label: "Social Media title", name: "smTitle", widget: "string", required: false} 23 | - {label: "Social Media description", name: "smDescription", widget: "string", required: false} 24 | - {label: "Color", name: "color", widget: "select", options: ["pink", "purple", "lightblue", "blue", "yellow", "tan", "orange"], default: "pink"} 25 | - {label: "Publish Date", name: "date", widget: "datetime"} 26 | - {label: "Body", name: "body", widget: "markdown"} 27 | - {label: "Tags", name: "tags", widget: "list"} 28 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/static/favicon.ico -------------------------------------------------------------------------------- /static/img/averie-woodard-111823-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/static/img/averie-woodard-111823-unsplash.jpg -------------------------------------------------------------------------------- /static/img/averie-woodard-111829-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/static/img/averie-woodard-111829-unsplash.jpg -------------------------------------------------------------------------------- /static/img/averie-woodard-111831-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/static/img/averie-woodard-111831-unsplash.jpg -------------------------------------------------------------------------------- /static/img/averie-woodard-181273-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/static/img/averie-woodard-181273-unsplash.jpg -------------------------------------------------------------------------------- /static/img/fletcher-clay-217243-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/static/img/fletcher-clay-217243-unsplash.jpg -------------------------------------------------------------------------------- /static/img/kace-rodriguez-82280-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/static/img/kace-rodriguez-82280-unsplash.jpg -------------------------------------------------------------------------------- /static/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjimmy/gatsby-netlify-cms-blog/e43f507c692db4bb870c57b81a7d9ce771f5cb0a/static/preview.png --------------------------------------------------------------------------------