` containing the HTML for the post.
427 |
428 | To render the HTML, we'll use Gatsby's `dangerouslySetInnerHTML` and pass in `post.html`:
429 |
430 | ```jsx
431 |
432 | ```
433 |
434 | We will wrap all of the blog post JSX inside of the `
` component.
435 |
436 | Here is the completed `blogPost.js` template:
437 |
438 | ```jsx
439 |
440 | import React from 'react'
441 | import { Link, graphql } from 'gatsby'
442 |
443 | import Layout from '../components/layout'
444 |
445 | export default function Template({ data }) {
446 | const post = data.markdownRemark
447 |
448 | return (
449 |
450 |
461 | Back to blogs
462 |
463 |
468 | {post.frontmatter.title}
469 |
470 |
477 | Posted by {post.frontmatter.author} on {post.frontmatter.date}
478 |
479 |
480 |
481 | )
482 | }
483 |
484 | export const postQuery = graphql`
485 | query BlogPost($path: String!) {
486 | markdownRemark(frontmatter: { path: { eq: $path } }) {
487 | html
488 | frontmatter {
489 | date
490 | path
491 | title
492 | }
493 | }
494 | }
495 | `
496 | ```
497 |
498 | 22. There's one last thing we have to add to get our blog posts to render.
499 |
500 | We don't want to create individual pages for each blog post; that would be extremely time consuming!
501 |
502 | Instead, we want to use GraphQL to dynamically create each page.
503 |
504 | We can do this within `gatsby-node.js`.
505 |
506 | - Inside of `gatsby-node.js`, import `path`, a [node module](https://nodejs.org/api/path.html) for working with our file directories.
507 |
508 | ```jsx
509 | const path = require('path')
510 | ```
511 |
512 | - Let's use the node `exports.createPages` [API](https://www.gatsbyjs.org/docs/node-apis/) to dynamically generate our pages.
513 |
514 | I won't delve into the details of the `createPages` API in this post, but you can check out the documentation linked above.
515 |
516 | Add the follwing code underneath the `path` module import:
517 |
518 | ```jsx
519 | exports.createPages = ({ boundActionCreators, graphql }) => {
520 | const { createPage } = boundActionCreators
521 |
522 | const postTemplate = path.resolve('src/templates/blogPost.js')
523 | }
524 | ```
525 |
526 | - Now we want to return a GraphQL query to grab all of the blog posts:
527 |
528 | ```jsx
529 | return graphql(`
530 | {
531 | allMarkdownRemark {
532 | edges {
533 | node {
534 | html
535 | id
536 | frontmatter {
537 | path
538 | title
539 | date
540 | author
541 | }
542 | }
543 | }
544 | }
545 | }
546 | `)
547 | ```
548 |
549 | - Once we receive a response back from the query, we want to reject the promise if an error occurred, and otherwise create a page for each post.
550 |
551 | This will create a post at the designated path received from the query results, and will use the `postTemplate` we declared above (our `blogPost.js` template) to render each post.
552 |
553 | ```jsx
554 | ...
555 | `).then(res => {
556 | if (res.errors) {
557 | return Promise.reject(res.errors)
558 | }
559 |
560 | res.data.allMarkdownRemark.edges.forEach(({ node }) => {
561 | createPage({
562 | path: node.frontmatter.path,
563 | component: postTemplate,
564 | })
565 | })
566 | ```
567 |
568 | Here's the finalized code for `gatsby-node.js`:
569 |
570 | ```jsx
571 | const path = require('path')
572 |
573 | exports.createPages = ({ boundActionCreators, graphql }) => {
574 | const { createPage } = boundActionCreators
575 |
576 | const postTemplate = path.resolve('src/templates/blogPost.js')
577 |
578 | return graphql(`
579 | {
580 | allMarkdownRemark {
581 | edges {
582 | node {
583 | html
584 | id
585 | frontmatter {
586 | path
587 | title
588 | date
589 | author
590 | }
591 | }
592 | }
593 | }
594 | }
595 | `).then(res => {
596 | if (res.errors) {
597 | return Promise.reject(res.errors)
598 | }
599 |
600 | res.data.allMarkdownRemark.edges.forEach(({ node }) => {
601 | createPage({
602 | path: node.frontmatter.path,
603 | component: postTemplate,
604 | })
605 | })
606 | })
607 | }
608 | ```
609 |
610 | Now we're ready to see if it worked!
611 |
612 | 23. Re-start your development server, then head over to the browser and click one of the blog post "Read more" links:
613 |
614 | 
615 |
616 | _If you ever need to revert to this state, you can use `git checkout step-3` to check out the tag for this state._
617 |
618 |
619 |
620 | # Step 4: Adding Custom Google Fonts
621 | You may want to add some custom [Google fonts](https://fonts.google.com/) to your Gatsby site. Luckily there's a plug-in for that!
622 |
623 | 1. In the terminal run the following command to install the Google fonts plugin:
624 | ```
625 | yarn add gatsby-plugin-google-fonts
626 | ```
627 |
628 | 2. Inside `gatsby-config.js`, we need to configure the plug-in.
629 |
630 | I'm choosing to use the font PT Serif, but you can select whichever font you prefer.
631 |
632 | Add the following to the `plugins` array:
633 |
634 | ```jsx
635 | plugins: [
636 | ...
637 | {
638 | resolve: `gatsby-plugin-google-fonts`,
639 | options: {
640 | fonts: [
641 | `PT Serif`,
642 | ],
643 | display: 'swap'
644 | }
645 | },
646 | ]
647 | ```
648 |
649 | You can check out the full list of options for this plugin in the [Gatsby docs](https://www.gatsbyjs.org/packages/gatsby-plugin-google-fonts/).
650 |
651 | 3. Inside of `nav.css` update the `.nav__list` selector to use `font-family: 'PT Serif', sans-serif`;
652 |
653 | 4. Restart your development server to see the updated font family.
654 |
655 | 
656 |
657 | _If you ever need to revert to this state, you can use `git checkout step-4` to check out the tag for this state._
658 |
659 |
660 | # Step 5: Deploying To Netlify
661 | Gatsby makes it extremely easy to deploy your site using Netlify.
662 |
663 | 1. Head over to [Netlify](https://app.netlify.com/) and sign up for an account.
664 | 2. Click "New site from Git".
665 | 3. Select "GitHub" for continuous deployment, authenticate with your GitHub account, and select the repository.
666 | 4. Set your build options (I leave the defaults).
667 | 5. Click "Deploy site".
668 |
669 | 
670 |
671 | Now, every time I push to `master`, my site will deploy.
672 |
673 | Once your site deploys, head over to the URL to check it out! You can easily add [custom domains](https://www.netlify.com/docs/custom-domains/) with Netlify to personalize your portfolio.
674 |
675 |
676 |
--------------------------------------------------------------------------------
/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implement Gatsby's Browser APIs in this file.
3 | *
4 | * See: https://www.gatsbyjs.org/docs/browser-apis/
5 | */
6 |
7 | // You can delete this file if you're not using it
8 |
--------------------------------------------------------------------------------
/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteMetadata: {
3 | title: `My Blog`,
4 | description: `This is my blog`,
5 | author: `@me`,
6 | },
7 | plugins: [
8 | `gatsby-plugin-react-helmet`,
9 | `gatsby-transformer-sharp`,
10 | `gatsby-transformer-remark`,
11 | `gatsby-plugin-sharp`,
12 | {
13 | resolve: 'gatsby-source-filesystem',
14 | options: {
15 | path: `${__dirname}/src/pages`,
16 | name: 'pages',
17 | },
18 | },
19 | {
20 | resolve: `gatsby-plugin-google-fonts`,
21 | options: {
22 | fonts: [
23 | `PT Serif`,
24 | ],
25 | display: 'swap'
26 | }
27 | },
28 | {
29 | resolve: `gatsby-plugin-manifest`,
30 | options: {
31 | name: `gatsby-starter-default`,
32 | short_name: `starter`,
33 | start_url: `/`
34 | },
35 | },
36 | ],
37 | }
--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | exports.createPages = ({ boundActionCreators, graphql }) => {
4 | const { createPage } = boundActionCreators
5 |
6 | const postTemplate = path.resolve('src/templates/blogPost.js')
7 |
8 | return graphql(`
9 | {
10 | allMarkdownRemark {
11 | edges {
12 | node {
13 | html
14 | id
15 | frontmatter {
16 | path
17 | title
18 | date
19 | author
20 | }
21 | }
22 | }
23 | }
24 | }
25 | `).then(res => {
26 | if (res.errors) {
27 | return Promise.reject(res.errors)
28 | }
29 |
30 | res.data.allMarkdownRemark.edges.forEach(({ node }) => {
31 | createPage({
32 | path: node.frontmatter.path,
33 | component: postTemplate,
34 | })
35 | })
36 | })
37 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-starter-default",
3 | "private": true,
4 | "description": "A simple starter to get up and developing quickly with Gatsby",
5 | "version": "0.1.0",
6 | "author": "Kyle Mathews ",
7 | "dependencies": {
8 | "gatsby": "^2.13.73",
9 | "gatsby-image": "^2.2.10",
10 | "gatsby-plugin-google-fonts": "^1.0.1",
11 | "gatsby-plugin-manifest": "^2.2.6",
12 | "gatsby-plugin-offline": "^2.2.7",
13 | "gatsby-plugin-react-helmet": "^3.1.4",
14 | "gatsby-plugin-sharp": "^2.2.13",
15 | "gatsby-source-filesystem": "^2.1.11",
16 | "gatsby-transformer-remark": "^2.6.19",
17 | "gatsby-transformer-sharp": "^2.2.7",
18 | "prop-types": "^15.7.2",
19 | "react": "^16.9.0",
20 | "react-dom": "^16.9.0",
21 | "react-helmet": "^5.2.1"
22 | },
23 | "devDependencies": {
24 | "prettier": "^1.18.2"
25 | },
26 | "keywords": [
27 | "gatsby"
28 | ],
29 | "license": "MIT",
30 | "scripts": {
31 | "build": "gatsby build",
32 | "develop": "gatsby develop",
33 | "format": "prettier --write \"**/*.{js,jsx,json,md}\"",
34 | "start": "npm run develop",
35 | "serve": "gatsby serve",
36 | "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing \""
37 | },
38 | "repository": {
39 | "type": "git",
40 | "url": "https://github.com/gatsbyjs/gatsby-starter-default"
41 | },
42 | "bugs": {
43 | "url": "https://github.com/gatsbyjs/gatsby/issues"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/queries.md:
--------------------------------------------------------------------------------
1 | # All Blog Posts
2 | ## writing.js
3 |
4 | ```
5 | export const allBlogsQuery = graphql`
6 | query AllBlogPosts {
7 | allMarkdownRemark {
8 | edges {
9 | node {
10 | frontmatter {
11 | author
12 | date
13 | title
14 | path
15 | description
16 | }
17 | id
18 | }
19 | }
20 | }
21 | }
22 | `;
23 | ```
24 |
25 | # Single Blog Post
26 | # templates/blogPost.js
27 |
28 | ```
29 | export const postQuery = graphql`
30 | query BlogPost($path: String!) {
31 | markdownRemark(frontmatter: { path: { eq: $path } }) {
32 | html
33 | frontmatter {
34 | date
35 | path
36 | title
37 | }
38 | }
39 | }
40 | `
41 | ```
42 |
43 | # Dynamically Generate
44 | ## gatsby-node.js
45 |
46 | ```
47 | const path = require('path')
48 |
49 | exports.createPages = ({ boundActionCreators, graphql }) => {
50 | const { createPage } = boundActionCreators
51 |
52 | const postTemplate = path.resolve('src/templates/blogPost.js')
53 |
54 | return graphql(`
55 | {
56 | allMarkdownRemark {
57 | edges {
58 | node {
59 | html
60 | id
61 | frontmatter {
62 | path
63 | title
64 | date
65 | author
66 | }
67 | }
68 | }
69 | }
70 | }
71 | `).then(res => {
72 | if (res.errors) {
73 | return Promise.reject(res.errors)
74 | }
75 |
76 | res.data.allMarkdownRemark.edges.forEach(({ node }) => {
77 | createPage({
78 | path: node.frontmatter.path,
79 | component: postTemplate,
80 | })
81 | })
82 | })
83 | }
84 | ```
85 |
--------------------------------------------------------------------------------
/src/components/blogSquare.css:
--------------------------------------------------------------------------------
1 | .blogSquare {
2 | position: relative;
3 | display: flex;
4 | margin-bottom: 50px;
5 | flex-direction: column;
6 | padding-bottom: 50px;
7 | border-bottom: 1px solid #efefef;
8 | }
9 |
10 | .blogSquare__link {
11 | text-decoration: none;
12 | color: #4a4a4a;
13 | }
14 |
15 | .blogSquare__readMore {
16 | font-size: .8em;
17 | text-decoration: underline;
18 | }
19 |
20 | .blogSquare__title {
21 | font-weight: 200;
22 | margin: 0;
23 | font-size: 1.5em;
24 | }
25 |
26 | .blogSquare__date {
27 | margin: 5px 0px 15px;
28 | font-size: 0.8em;
29 | font-weight: bold;
30 | }
31 |
32 | .blogSquare__readMore {
33 | font-size: 0.8em;
34 | display: inline-block;
35 | }
--------------------------------------------------------------------------------
/src/components/blogSquare.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Link } from "gatsby";
4 | import './blogSquare.css'
5 |
6 | const BlogSquare = ({ title, date, path, description }) => (
7 |
8 |
12 | {title}
13 | {date}
14 | {description}
15 | Read more
16 |
17 |
18 | )
19 |
20 | export default BlogSquare
--------------------------------------------------------------------------------
/src/components/layout.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-family: sans-serif;
3 | -ms-text-size-adjust: 100%;
4 | -webkit-text-size-adjust: 100%;
5 | }
6 | body {
7 | margin: 0;
8 | -webkit-font-smoothing: antialiased;
9 | -moz-osx-font-smoothing: grayscale;
10 | }
11 | article,
12 | aside,
13 | details,
14 | figcaption,
15 | figure,
16 | footer,
17 | header,
18 | main,
19 | menu,
20 | nav,
21 | section,
22 | summary {
23 | display: block;
24 | }
25 | audio,
26 | canvas,
27 | progress,
28 | video {
29 | display: inline-block;
30 | }
31 | audio:not([controls]) {
32 | display: none;
33 | height: 0;
34 | }
35 | progress {
36 | vertical-align: baseline;
37 | }
38 | [hidden],
39 | template {
40 | display: none;
41 | }
42 | a {
43 | background-color: transparent;
44 | -webkit-text-decoration-skip: objects;
45 | }
46 | a:active,
47 | a:hover {
48 | outline-width: 0;
49 | }
50 | abbr[title] {
51 | border-bottom: none;
52 | text-decoration: underline;
53 | text-decoration: underline dotted;
54 | }
55 | b,
56 | strong {
57 | font-weight: inherit;
58 | font-weight: bolder;
59 | }
60 | dfn {
61 | font-style: italic;
62 | }
63 | h1 {
64 | font-size: 2em;
65 | margin: 0.67em 0;
66 | }
67 | mark {
68 | background-color: #ff0;
69 | color: #000;
70 | }
71 | small {
72 | font-size: 80%;
73 | }
74 | sub,
75 | sup {
76 | font-size: 75%;
77 | line-height: 0;
78 | position: relative;
79 | vertical-align: baseline;
80 | }
81 | sub {
82 | bottom: -0.25em;
83 | }
84 | sup {
85 | top: -0.5em;
86 | }
87 | img {
88 | border-style: none;
89 | }
90 | svg:not(:root) {
91 | overflow: hidden;
92 | }
93 | code,
94 | kbd,
95 | pre,
96 | samp {
97 | font-family: monospace, monospace;
98 | font-size: 1em;
99 | }
100 | figure {
101 | margin: 1em 40px;
102 | }
103 | hr {
104 | box-sizing: content-box;
105 | height: 0;
106 | overflow: visible;
107 | }
108 | button,
109 | input,
110 | optgroup,
111 | select,
112 | textarea {
113 | font: inherit;
114 | margin: 0;
115 | }
116 | optgroup {
117 | font-weight: 700;
118 | }
119 | button,
120 | input {
121 | overflow: visible;
122 | }
123 | button,
124 | select {
125 | text-transform: none;
126 | }
127 | [type="reset"],
128 | [type="submit"],
129 | button,
130 | html [type="button"] {
131 | -webkit-appearance: button;
132 | }
133 | [type="button"]::-moz-focus-inner,
134 | [type="reset"]::-moz-focus-inner,
135 | [type="submit"]::-moz-focus-inner,
136 | button::-moz-focus-inner {
137 | border-style: none;
138 | padding: 0;
139 | }
140 | [type="button"]:-moz-focusring,
141 | [type="reset"]:-moz-focusring,
142 | [type="submit"]:-moz-focusring,
143 | button:-moz-focusring {
144 | outline: 1px dotted ButtonText;
145 | }
146 | fieldset {
147 | border: 1px solid silver;
148 | margin: 0 2px;
149 | padding: 0.35em 0.625em 0.75em;
150 | }
151 | legend {
152 | box-sizing: border-box;
153 | color: inherit;
154 | display: table;
155 | max-width: 100%;
156 | padding: 0;
157 | white-space: normal;
158 | }
159 | textarea {
160 | overflow: auto;
161 | }
162 | [type="checkbox"],
163 | [type="radio"] {
164 | box-sizing: border-box;
165 | padding: 0;
166 | }
167 | [type="number"]::-webkit-inner-spin-button,
168 | [type="number"]::-webkit-outer-spin-button {
169 | height: auto;
170 | }
171 | [type="search"] {
172 | -webkit-appearance: textfield;
173 | outline-offset: -2px;
174 | }
175 | [type="search"]::-webkit-search-cancel-button,
176 | [type="search"]::-webkit-search-decoration {
177 | -webkit-appearance: none;
178 | }
179 | ::-webkit-input-placeholder {
180 | color: inherit;
181 | opacity: 0.54;
182 | }
183 | ::-webkit-file-upload-button {
184 | -webkit-appearance: button;
185 | font: inherit;
186 | }
187 | html {
188 | font: 112.5%/1.45em georgia, serif;
189 | box-sizing: border-box;
190 | overflow-y: scroll;
191 | }
192 | * {
193 | box-sizing: inherit;
194 | }
195 | *:before {
196 | box-sizing: inherit;
197 | }
198 | *:after {
199 | box-sizing: inherit;
200 | }
201 | body {
202 | color: hsla(0, 0%, 0%, 0.8);
203 | font-family: georgia, serif;
204 | font-weight: normal;
205 | word-wrap: break-word;
206 | font-kerning: normal;
207 | -moz-font-feature-settings: "kern", "liga", "clig", "calt";
208 | -ms-font-feature-settings: "kern", "liga", "clig", "calt";
209 | -webkit-font-feature-settings: "kern", "liga", "clig", "calt";
210 | font-feature-settings: "kern", "liga", "clig", "calt";
211 | }
212 | img {
213 | max-width: 100%;
214 | margin-left: 0;
215 | margin-right: 0;
216 | margin-top: 0;
217 | padding-bottom: 0;
218 | padding-left: 0;
219 | padding-right: 0;
220 | padding-top: 0;
221 | margin-bottom: 1.45rem;
222 | }
223 | h1 {
224 | margin-left: 0;
225 | margin-right: 0;
226 | margin-top: 0;
227 | padding-bottom: 0;
228 | padding-left: 0;
229 | padding-right: 0;
230 | padding-top: 0;
231 | margin-bottom: 1.45rem;
232 | color: inherit;
233 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
234 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
235 | font-weight: bold;
236 | text-rendering: optimizeLegibility;
237 | font-size: 2.25rem;
238 | line-height: 1.1;
239 | }
240 | h2 {
241 | margin-left: 0;
242 | margin-right: 0;
243 | margin-top: 0;
244 | padding-bottom: 0;
245 | padding-left: 0;
246 | padding-right: 0;
247 | padding-top: 0;
248 | margin-bottom: 1.45rem;
249 | color: inherit;
250 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
251 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
252 | font-weight: bold;
253 | text-rendering: optimizeLegibility;
254 | font-size: 1.62671rem;
255 | line-height: 1.1;
256 | }
257 | h3 {
258 | margin-left: 0;
259 | margin-right: 0;
260 | margin-top: 0;
261 | padding-bottom: 0;
262 | padding-left: 0;
263 | padding-right: 0;
264 | padding-top: 0;
265 | margin-bottom: 1.45rem;
266 | color: inherit;
267 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
268 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
269 | font-weight: bold;
270 | text-rendering: optimizeLegibility;
271 | font-size: 1.38316rem;
272 | line-height: 1.1;
273 | }
274 | h4 {
275 | margin-left: 0;
276 | margin-right: 0;
277 | margin-top: 0;
278 | padding-bottom: 0;
279 | padding-left: 0;
280 | padding-right: 0;
281 | padding-top: 0;
282 | margin-bottom: 1.45rem;
283 | color: inherit;
284 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
285 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
286 | font-weight: bold;
287 | text-rendering: optimizeLegibility;
288 | font-size: 1rem;
289 | line-height: 1.1;
290 | }
291 | h5 {
292 | margin-left: 0;
293 | margin-right: 0;
294 | margin-top: 0;
295 | padding-bottom: 0;
296 | padding-left: 0;
297 | padding-right: 0;
298 | padding-top: 0;
299 | margin-bottom: 1.45rem;
300 | color: inherit;
301 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
302 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
303 | font-weight: bold;
304 | text-rendering: optimizeLegibility;
305 | font-size: 0.85028rem;
306 | line-height: 1.1;
307 | }
308 | h6 {
309 | margin-left: 0;
310 | margin-right: 0;
311 | margin-top: 0;
312 | padding-bottom: 0;
313 | padding-left: 0;
314 | padding-right: 0;
315 | padding-top: 0;
316 | margin-bottom: 1.45rem;
317 | color: inherit;
318 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
319 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
320 | font-weight: bold;
321 | text-rendering: optimizeLegibility;
322 | font-size: 0.78405rem;
323 | line-height: 1.1;
324 | }
325 | hgroup {
326 | margin-left: 0;
327 | margin-right: 0;
328 | margin-top: 0;
329 | padding-bottom: 0;
330 | padding-left: 0;
331 | padding-right: 0;
332 | padding-top: 0;
333 | margin-bottom: 1.45rem;
334 | }
335 | ul {
336 | margin-left: 1.45rem;
337 | margin-right: 0;
338 | margin-top: 0;
339 | padding-bottom: 0;
340 | padding-left: 0;
341 | padding-right: 0;
342 | padding-top: 0;
343 | margin-bottom: 1.45rem;
344 | list-style-position: outside;
345 | list-style-image: none;
346 | }
347 | ol {
348 | margin-left: 1.45rem;
349 | margin-right: 0;
350 | margin-top: 0;
351 | padding-bottom: 0;
352 | padding-left: 0;
353 | padding-right: 0;
354 | padding-top: 0;
355 | margin-bottom: 1.45rem;
356 | list-style-position: outside;
357 | list-style-image: none;
358 | }
359 | dl {
360 | margin-left: 0;
361 | margin-right: 0;
362 | margin-top: 0;
363 | padding-bottom: 0;
364 | padding-left: 0;
365 | padding-right: 0;
366 | padding-top: 0;
367 | margin-bottom: 1.45rem;
368 | }
369 | dd {
370 | margin-left: 0;
371 | margin-right: 0;
372 | margin-top: 0;
373 | padding-bottom: 0;
374 | padding-left: 0;
375 | padding-right: 0;
376 | padding-top: 0;
377 | margin-bottom: 1.45rem;
378 | }
379 | p {
380 | margin-left: 0;
381 | margin-right: 0;
382 | margin-top: 0;
383 | padding-bottom: 0;
384 | padding-left: 0;
385 | padding-right: 0;
386 | padding-top: 0;
387 | margin-bottom: 1.45rem;
388 | }
389 | figure {
390 | margin-left: 0;
391 | margin-right: 0;
392 | margin-top: 0;
393 | padding-bottom: 0;
394 | padding-left: 0;
395 | padding-right: 0;
396 | padding-top: 0;
397 | margin-bottom: 1.45rem;
398 | }
399 | pre {
400 | margin-left: 0;
401 | margin-right: 0;
402 | margin-top: 0;
403 | margin-bottom: 1.45rem;
404 | font-size: 0.85rem;
405 | line-height: 1.42;
406 | background: hsla(0, 0%, 0%, 0.04);
407 | border-radius: 3px;
408 | overflow: auto;
409 | word-wrap: normal;
410 | padding: 1.45rem;
411 | }
412 | table {
413 | margin-left: 0;
414 | margin-right: 0;
415 | margin-top: 0;
416 | padding-bottom: 0;
417 | padding-left: 0;
418 | padding-right: 0;
419 | padding-top: 0;
420 | margin-bottom: 1.45rem;
421 | font-size: 1rem;
422 | line-height: 1.45rem;
423 | border-collapse: collapse;
424 | width: 100%;
425 | }
426 | fieldset {
427 | margin-left: 0;
428 | margin-right: 0;
429 | margin-top: 0;
430 | padding-bottom: 0;
431 | padding-left: 0;
432 | padding-right: 0;
433 | padding-top: 0;
434 | margin-bottom: 1.45rem;
435 | }
436 | blockquote {
437 | margin-left: 1.45rem;
438 | margin-right: 1.45rem;
439 | margin-top: 0;
440 | padding-bottom: 0;
441 | padding-left: 0;
442 | padding-right: 0;
443 | padding-top: 0;
444 | margin-bottom: 1.45rem;
445 | }
446 | form {
447 | margin-left: 0;
448 | margin-right: 0;
449 | margin-top: 0;
450 | padding-bottom: 0;
451 | padding-left: 0;
452 | padding-right: 0;
453 | padding-top: 0;
454 | margin-bottom: 1.45rem;
455 | }
456 | noscript {
457 | margin-left: 0;
458 | margin-right: 0;
459 | margin-top: 0;
460 | padding-bottom: 0;
461 | padding-left: 0;
462 | padding-right: 0;
463 | padding-top: 0;
464 | margin-bottom: 1.45rem;
465 | }
466 | iframe {
467 | margin-left: 0;
468 | margin-right: 0;
469 | margin-top: 0;
470 | padding-bottom: 0;
471 | padding-left: 0;
472 | padding-right: 0;
473 | padding-top: 0;
474 | margin-bottom: 1.45rem;
475 | }
476 | hr {
477 | margin-left: 0;
478 | margin-right: 0;
479 | margin-top: 0;
480 | padding-bottom: 0;
481 | padding-left: 0;
482 | padding-right: 0;
483 | padding-top: 0;
484 | margin-bottom: calc(1.45rem - 1px);
485 | background: hsla(0, 0%, 0%, 0.2);
486 | border: none;
487 | height: 1px;
488 | }
489 | address {
490 | margin-left: 0;
491 | margin-right: 0;
492 | margin-top: 0;
493 | padding-bottom: 0;
494 | padding-left: 0;
495 | padding-right: 0;
496 | padding-top: 0;
497 | margin-bottom: 1.45rem;
498 | }
499 | b {
500 | font-weight: bold;
501 | }
502 | strong {
503 | font-weight: bold;
504 | }
505 | dt {
506 | font-weight: bold;
507 | }
508 | th {
509 | font-weight: bold;
510 | }
511 | li {
512 | margin-bottom: calc(1.45rem / 2);
513 | }
514 | ol li {
515 | padding-left: 0;
516 | }
517 | ul li {
518 | padding-left: 0;
519 | }
520 | li > ol {
521 | margin-left: 1.45rem;
522 | margin-bottom: calc(1.45rem / 2);
523 | margin-top: calc(1.45rem / 2);
524 | }
525 | li > ul {
526 | margin-left: 1.45rem;
527 | margin-bottom: calc(1.45rem / 2);
528 | margin-top: calc(1.45rem / 2);
529 | }
530 | blockquote *:last-child {
531 | margin-bottom: 0;
532 | }
533 | li *:last-child {
534 | margin-bottom: 0;
535 | }
536 | p *:last-child {
537 | margin-bottom: 0;
538 | }
539 | li > p {
540 | margin-bottom: calc(1.45rem / 2);
541 | }
542 | code {
543 | font-size: 0.85rem;
544 | line-height: 1.45rem;
545 | }
546 | kbd {
547 | font-size: 0.85rem;
548 | line-height: 1.45rem;
549 | }
550 | samp {
551 | font-size: 0.85rem;
552 | line-height: 1.45rem;
553 | }
554 | abbr {
555 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
556 | cursor: help;
557 | }
558 | acronym {
559 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
560 | cursor: help;
561 | }
562 | abbr[title] {
563 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
564 | cursor: help;
565 | text-decoration: none;
566 | }
567 | thead {
568 | text-align: left;
569 | }
570 | td,
571 | th {
572 | text-align: left;
573 | border-bottom: 1px solid hsla(0, 0%, 0%, 0.12);
574 | font-feature-settings: "tnum";
575 | -moz-font-feature-settings: "tnum";
576 | -ms-font-feature-settings: "tnum";
577 | -webkit-font-feature-settings: "tnum";
578 | padding-left: 0.96667rem;
579 | padding-right: 0.96667rem;
580 | padding-top: 0.725rem;
581 | padding-bottom: calc(0.725rem - 1px);
582 | }
583 | th:first-child,
584 | td:first-child {
585 | padding-left: 0;
586 | }
587 | th:last-child,
588 | td:last-child {
589 | padding-right: 0;
590 | }
591 | tt,
592 | code {
593 | background-color: hsla(0, 0%, 0%, 0.04);
594 | border-radius: 3px;
595 | font-family: "SFMono-Regular", Consolas, "Roboto Mono", "Droid Sans Mono",
596 | "Liberation Mono", Menlo, Courier, monospace;
597 | padding: 0;
598 | padding-top: 0.2em;
599 | padding-bottom: 0.2em;
600 | }
601 | pre code {
602 | background: none;
603 | line-height: 1.42;
604 | }
605 | code:before,
606 | code:after,
607 | tt:before,
608 | tt:after {
609 | letter-spacing: -0.2em;
610 | content: " ";
611 | }
612 | pre code:before,
613 | pre code:after,
614 | pre tt:before,
615 | pre tt:after {
616 | content: "";
617 | }
618 | @media only screen and (max-width: 480px) {
619 | html {
620 | font-size: 100%;
621 | }
622 | }
623 |
624 | .layout {
625 | display: flex;
626 | }
627 |
628 | .main {
629 | width: 100%;
630 | height: 100vh;
631 | display: flex;
632 | flex-direction: column;
633 | padding: 80px 120px;
634 | }
--------------------------------------------------------------------------------
/src/components/layout.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import PropTypes from "prop-types"
3 | import { useStaticQuery, graphql } from "gatsby"
4 |
5 | import Nav from "../components/nav";
6 |
7 | import "./layout.css"
8 |
9 | const Layout = ({ children }) => {
10 | const data = useStaticQuery(graphql`
11 | query SiteTitleQuery {
12 | site {
13 | siteMetadata {
14 | title
15 | }
16 | }
17 | }
18 | `)
19 |
20 | return (
21 |
22 |
23 | {children}
24 |
25 | )
26 | }
27 |
28 | Layout.propTypes = {
29 | children: PropTypes.node.isRequired,
30 | }
31 |
32 | export default Layout
--------------------------------------------------------------------------------
/src/components/nav.css:
--------------------------------------------------------------------------------
1 | .nav {
2 | padding: 24px;
3 | width: 300px;
4 | }
5 |
6 | .nav__list {
7 | list-style: none;
8 | padding: 0;
9 | margin: 0;
10 | font-family: 'PT Serif', serif;
11 | }
12 |
13 | .nav__link {
14 | text-decoration: none;
15 | color: #4A4A4A;
16 | }
17 |
18 | .nav__link--active {
19 | font-weight: bold;
20 | }
--------------------------------------------------------------------------------
/src/components/nav.js:
--------------------------------------------------------------------------------
1 |
2 | import React from "react";
3 | import { Link } from "gatsby";
4 |
5 | import "./nav.css";
6 |
7 | const Nav = () => (
8 |
9 | My Portfolio
10 |
11 | Home
12 | Writing
13 | Speaking
14 | Podcasting
15 |
16 |
17 | )
18 |
19 | export default Nav;
--------------------------------------------------------------------------------
/src/components/seo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * SEO component that queries for data with
3 | * Gatsby's useStaticQuery React hook
4 | *
5 | * See: https://www.gatsbyjs.org/docs/use-static-query/
6 | */
7 |
8 | import React from "react"
9 | import PropTypes from "prop-types"
10 | import Helmet from "react-helmet"
11 | import { useStaticQuery, graphql } from "gatsby"
12 |
13 | function SEO({ description, lang, meta, title }) {
14 | const { site } = useStaticQuery(
15 | graphql`
16 | query {
17 | site {
18 | siteMetadata {
19 | title
20 | description
21 | author
22 | }
23 | }
24 | }
25 | `
26 | )
27 |
28 | const metaDescription = description || site.siteMetadata.description
29 |
30 | return (
31 |
72 | )
73 | }
74 |
75 | SEO.defaultProps = {
76 | lang: `en`,
77 | meta: [],
78 | description: ``,
79 | }
80 |
81 | SEO.propTypes = {
82 | description: PropTypes.string,
83 | lang: PropTypes.string,
84 | meta: PropTypes.arrayOf(PropTypes.object),
85 | title: PropTypes.string.isRequired,
86 | }
87 |
88 | export default SEO
89 |
--------------------------------------------------------------------------------
/src/pages/2019-08-24-cute-cats/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | path: '/cute-cats'
3 | date: '2019-08-24'
4 | title: 'Cute Cats'
5 | author: 'Me'
6 | description: 'Here are some cute cat photos!'
7 | ---
8 | 
9 |
10 |
--------------------------------------------------------------------------------
/src/pages/2019-08-26-five-tech-skills-to-master/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | path: '/five-tech-skills-to-master'
3 | date: '2019-08-26'
4 | title: 'Five Tech Skills To Master'
5 | author: 'Bob Ross'
6 | description: 'Here are the five tech skills you MUST master to get a job.'
7 | ---
8 |
9 | 
10 |
11 |
--------------------------------------------------------------------------------
/src/pages/2019-09-06-hello-graphql-day-bodensee/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | path: '/hello-graphql-day-bodensee'
3 | date: '2019-09-06'
4 | title: 'Hello GraphQL Day Bodensee'
5 | author: 'Emma Wedekind'
6 | description: 'We learning things about GraphQL! (hopefully).'
7 | ---
8 |
9 | 
10 |
11 |
--------------------------------------------------------------------------------
/src/pages/2019-09-13-hey-amsterdam/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | path: '/hey-amsterdam'
3 | date: '2019-09-13'
4 | title: 'Hello React Live'
5 | author: 'Emma Wedekind'
6 | description: 'We learning things about REACT! (hopefully).'
7 | ---
8 |
9 | # YAY
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Link } from "gatsby"
3 | import Layout from "../components/layout"
4 |
5 | const IndexPage = () => (
6 |
7 | This is my portfolio.
8 | I build cool things.
9 |
10 | )
11 |
12 | export default IndexPage
13 |
--------------------------------------------------------------------------------
/src/pages/podcasting.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Link } from "gatsby"
3 |
4 | import Layout from "../components/layout"
5 |
6 | import SEO from "../components/seo"
7 |
8 | const PodcastingPage = () => (
9 |
10 |
11 | I podcast about things
12 |
13 | )
14 |
15 | export default PodcastingPage
--------------------------------------------------------------------------------
/src/pages/speaking.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { Link } from "gatsby"
3 |
4 | import Layout from "../components/layout"
5 |
6 | import SEO from "../components/seo"
7 |
8 | const SpeakingPage = () => (
9 |
10 |
11 | I speak about things
12 |
13 | )
14 |
15 | export default SpeakingPage
--------------------------------------------------------------------------------
/src/pages/writing.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import Layout from "../components/layout"
4 | import BlogSquare from "../components/blogSquare"
5 |
6 | import SEO from "../components/seo"
7 |
8 | const WritingPage = ({ data }) => (
9 |
10 |
11 | I write about things
12 | {data.allMarkdownRemark.edges.map(post => (
13 |
20 | ))}
21 |
22 | )
23 |
24 | export const allBlogsQuery = graphql`
25 | query AllBlogPosts {
26 | allMarkdownRemark {
27 | edges {
28 | node {
29 | frontmatter {
30 | date
31 | description
32 | title
33 | path
34 | }
35 | id
36 | }
37 | }
38 | }
39 | }
40 | `
41 |
42 | export default WritingPage
--------------------------------------------------------------------------------
/src/templates/blogPost.css:
--------------------------------------------------------------------------------
1 | .blogPost__backToBlogs {
2 | display: flex;
3 | align-items: center;
4 | font-size: .8em;
5 | color: #4a4a4a;
6 | text-decoration: none;
7 | margin-top: 50px;
8 | }
9 |
10 | .blogPost__title {
11 | margin-top: 20px;
12 | }
13 |
14 | blogPost__postedByDate {
15 | font-size: .8em;
16 | color: #9fa7a7;
17 | font-weight: 400;
18 | }
--------------------------------------------------------------------------------
/src/templates/blogPost.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link, graphql } from 'gatsby'
3 | import Layout from '../components/layout'
4 | import './blogPost.css'
5 |
6 | export default function Template({ data }) {
7 | const post = data.markdownRemark
8 |
9 | return (
10 |
11 |
22 | Back to blogs
23 |
24 |
29 | {post.frontmatter.title}
30 |
31 |
38 | Posted by {post.frontmatter.author} on {post.frontmatter.date}
39 |
40 |
41 |
42 | )
43 | }
44 |
45 | export const postQuery = graphql`
46 | query BlogPost($path: String!) {
47 | markdownRemark(frontmatter: { path: { eq: $path } }) {
48 | html
49 | frontmatter {
50 | date
51 | path
52 | title
53 | }
54 | }
55 | }
56 | `
--------------------------------------------------------------------------------