├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.yml ├── .gitignore ├── .gitmodules ├── .node-version ├── .prettierignore ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── lint-staged.config.js ├── netlify.toml ├── package.json ├── prettier.config.js ├── src ├── components │ ├── audio.js │ ├── avatar.js │ ├── blog │ │ ├── blog-index.js │ │ ├── blog-layout.js │ │ ├── blog-list.js │ │ ├── blog-tag.js │ │ └── blog-tags.js │ ├── book-card.js │ ├── featured.js │ ├── global.css │ ├── latest-articles.js │ ├── layout │ │ ├── TextStyles.js │ │ ├── figure.js │ │ ├── footer.js │ │ ├── header-links.js │ │ ├── header.js │ │ ├── layout.js │ │ ├── links.js │ │ ├── linktip-preview.js │ │ ├── linktip.js │ │ ├── logo.js │ │ ├── mdx-components-with-previews.js │ │ ├── mdx-components.js │ │ ├── sidenote.js │ │ ├── tippyBox.css │ │ └── tooltip.js │ ├── notes │ │ ├── note-nav.js │ │ └── note.js │ ├── poems │ │ ├── poem-layout.js │ │ ├── poem-list.js │ │ ├── poem-tag.js │ │ └── poem-tags.js │ ├── posts │ │ ├── post-link.js │ │ ├── post-list.js │ │ ├── post-tag.js │ │ ├── post-tags.js │ │ └── post.js │ ├── prism.css │ ├── seo.js │ ├── similar-content.js │ └── tags │ │ ├── list-item.js │ │ ├── list.js │ │ ├── tag.js │ │ └── tags.js ├── content │ └── canon.yml ├── favicon.png ├── lib │ ├── breakpoints.js │ ├── colors.js │ └── dendron-parse-url.js └── pages │ ├── about.mdx │ ├── articles │ ├── all.js │ └── index.mdx │ ├── blog │ └── index.mdx │ ├── canon.js │ ├── index.mdx │ ├── mileage-metrics.mdx │ ├── notes │ ├── 404.mdx │ └── index.js │ ├── poetry │ ├── all.js │ ├── index.mdx │ └── seasons-of-thought.mdx │ └── privacy.mdx ├── static ├── 2021-06-03-07-35-43.png ├── 2021-06-05-14-49-38.png ├── 9a-XUSP.png ├── What-is-a-digital-garden.png ├── _redirects ├── create-github-project.png ├── create-wordpress-instance.png ├── dendron-frontmatter.png ├── evening-praise.mp3 ├── evergreen.mp3 ├── how-colorsdanielchapmandev-gets-its-content.png ├── little-women-pages.png ├── my-github-learning-project.png ├── org-roam-graph.png ├── to-the-green-rock.mp3 └── wordpress-instance-setup.png └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@emotion"], 3 | "presets": [["babel-preset-gatsby"]] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md*] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .cache 2 | 3 | dist 4 | public 5 | node_modules 6 | package.json 7 | *.md 8 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | root: true 2 | 3 | parserOptions: 4 | ecmaVersion: 10 5 | sourceType: module 6 | ecmaFeatures: 7 | jsx: true 8 | 9 | env: 10 | es6: true 11 | node: true 12 | 13 | plugins: 14 | - import 15 | - prettier 16 | - '@emotion' 17 | 18 | extends: 19 | - plugin:react/recommended 20 | - plugin:import/errors 21 | - prettier 22 | 23 | rules: 24 | eqeqeq: 0 25 | no-console: 0 26 | no-eq-null: 0 27 | no-negated-condition: 0 28 | no-unused-vars: 29 | - error 30 | - args: after-used 31 | argsIgnorePattern: '^_' 32 | no-var: error 33 | import/no-extraneous-dependencies: error 34 | prettier/prettier: error 35 | react/prop-types: off 36 | react/display-name: off 37 | react/no-children-prop: off 38 | react/react-in-jsx-scope: off 39 | '@emotion/pkg-renaming': error 40 | 41 | settings: 42 | react: 43 | version: 16.10.1 44 | 45 | overrides: 46 | - files: '*.md' 47 | extends: plugin:mdx/recommended 48 | 49 | - files: '*.mdx' 50 | extends: 51 | - plugin:mdx/recommended 52 | - plugin:mdx/overrides 53 | rules: 54 | import/no-extraneous-dependencies: 0 55 | 56 | - files: 57 | - '**/test/**/*.js' 58 | - '**/*.test.js' 59 | env: 60 | jest: true 61 | 62 | - files: 63 | - 'src/**/*js' 64 | env: 65 | browser: true 66 | rules: 67 | import/default: 0 68 | import/namespace: 0 69 | import/named: 0 70 | import/no-default-export: 0 71 | import/no-extraneous-dependencies: off 72 | import/no-unresolved: off 73 | no-unused-vars: 0 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | .cache/ 61 | public 62 | yarn-error.log 63 | 64 | \.vscode/settings\.json 65 | 66 | \.DS_Store 67 | content/notes/org-roam.db 68 | content/notes/.dir-locals.el 69 | content/notes/.obsidian/cache 70 | content/notes/.obsidian/config 71 | content/notes/.obsidian/workspace 72 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "My-notes"] 2 | path = My-notes 3 | url = git@github.com:dschapman/My-notes 4 | [submodule "dschapman-com-content"] 5 | path = dschapman-com-content 6 | url = git@github.com:dschapman/dschapman-com-content 7 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .cache 2 | 3 | dist 4 | public 5 | coverage 6 | yarn.lock 7 | node_modules 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschapman/dschapman-com/7b8c29a7f8afc3c7672b836460404aa34b35b932/.vscode/launch.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Daniel Chapman (D.S. Chapman). 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About this Website 2 | 3 | The website currently has three different types of posts - articles, poems, and notes. All three are implemented with gatsby-plugin-mdx and I curate links on /articles and /poetry respectively. On the backend I write my notes using [Dendron](https://dendron.so) 4 | 5 | ## 📝 Notes 6 | 7 | Dendron notes use wikilinks, are hierarchical, and context aware. Currently I use gatsby-remark-double-brackets-link to handle the wikilinks and gatsby-remark-copy-linked-files to display when these notes are referenced by another note. 8 | 9 | ## 📘 Articles 10 | 11 | Implemented in gatsby-plugin-mdx. The slug is drawn from slug in the frontmatter, title, and excerpt are also important values. A list of all tags used in articles can be viewed at /articles/tags. These tags will also list links to any note pages that are tagged as well. 12 | 13 | ## 📜 Poems 14 | 15 | Like Articles, all the poems on the site can be seen at /poetry/all. Much of the frontmatter for articles and poems are the same, the exception is the `recording` element which takes the location of an MP3 file and will generate a player at the top of the poem if one exists. Poems have the same tagging system as Articles. 16 | 17 | ## 📄 Pages 18 | 19 | Make sure to import a layout if you're creating an .mdx page in src/pages 20 | 21 | ## 📁 Directory Structure 22 | 23 | ``` 24 | . 25 | ├── LICENSE 26 | ├── README.md 27 | ├── content 28 | │   ├── assets 29 | │   ├── notes <-- Note .mdx or .md files go here 30 | │   ├── poems <-- Poem .mdx or .md files go here 31 | │   │   └── seasons-of-thought 32 | │   └── posts <-- Article .mdx or .md files go here 33 | ├── src 34 | │   ├── @aengusm / gatsby-theme-brain 35 | │   ├── components 36 | │ │ ├── layout <-- General Layout 37 | │ │ ├── notes <-- Modified layout for the notes 38 | │ │ ├── poems <-- Modified layout for poems 39 | │ │ ├── posts <-- Modified layout for articles 40 | │ │ └── tags <-- information on generating tags 41 | │   └── pages <-- Pages (often .mdx) go here 42 | ├── static <-- It might not be optimized but its sometimes easier to link to images stored here. 43 | ├── gatsby-config.js 44 | ├── gatsby-node.js 45 | └── yarn.lock 46 | ``` 47 | 48 | ## ↔️ Bidirectional Links / Link Previews 49 | 50 | Link Previews are implemented using Tippy JS Tooltips. gatsby-theme-brain provides native bidirectional linking and the information to easily implement link previews (with a slightly modified GraphQL query); however, in order to get this working across the site I added an MDX component that cycles through every single mdx page and puts the childMdx.body value inside of a tooltip if the slugs match. 51 | 52 | Link Previews only show up on larger screen size so make sure to check out the website on your desktop. 53 | 54 | ## 🕺🏼 Styling 55 | 56 | This site uses emotion for CSS in JS styling as well as some vanilla CSS. The CSS is inspired and adapted from [Tufte's CSS](https://github.com/edwardtufte/tufte-css). 57 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | require('./src/components/prism.css') 2 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | siteUrl: 'https://www.dschapman.com', 4 | title: 'D.S. Chapman', 5 | seoTitleAddition1: 'Website and Digital Home', 6 | seoTitleAddition2: 'Articles, Poetry, & Musings', 7 | twitter: '@ds_chapman', 8 | description: 9 | 'The website and digital home of writer and poet D.S. Chapman. Explore for articles, poetry, and other projects including my recent book - Seasons of Thought.', 10 | }, 11 | plugins: [ 12 | 'gatsby-plugin-sharp', 13 | 'gatsby-remark-images', 14 | 'gatsby-plugin-react-helmet', 15 | { 16 | resolve: `gatsby-plugin-mdx`, 17 | options: { 18 | extensions: [`.mdx`, `.md`], 19 | gatsbyRemarkPlugins: [ 20 | { 21 | resolve: `gatsby-remark-images`, 22 | options: { 23 | // should this be configurable by the end-user? 24 | maxWidth: 1380, 25 | }, 26 | }, 27 | { resolve: `gatsby-remark-copy-linked-files` }, 28 | { resolve: `gatsby-remark-smartypants` }, 29 | { resolve: `gatsby-remark-slug` }, 30 | { resolve: `gatsby-remark-mermaid` }, 31 | { 32 | resolve: `gatsby-remark-double-brackets-link`, 33 | options: { 34 | titleToURLPath: `${__dirname}/src/lib/dendron-parse-url.js`, 35 | }, 36 | }, 37 | { 38 | resolve: `gatsby-remark-prismjs`, 39 | options: { 40 | // Class prefix for
 tags containing syntax highlighting;
 41 |               // defaults to 'language-' (e.g. 
).
 42 |               // If your site loads Prism into the browser at runtime,
 43 |               // (e.g. for use with libraries like react-live),
 44 |               // you may use this to prevent Prism from re-processing syntax.
 45 |               // This is an uncommon use-case though;
 46 |               // If you're unsure, it's best to use the default value.
 47 |               classPrefix: 'language-',
 48 |               // This is used to allow setting a language for inline code
 49 |               // (i.e. single backticks) by creating a separator.
 50 |               // This separator is a string and will do no white-space
 51 |               // stripping.
 52 |               // A suggested value for English speakers is the non-ascii
 53 |               // character '›'.
 54 |               inlineCodeMarker: null,
 55 |               // This lets you set up language aliases.  For example,
 56 |               // setting this to '{ sh: "bash" }' will let you use
 57 |               // the language "sh" which will highlight using the
 58 |               // bash highlighter.
 59 |               aliases: {},
 60 |               // This toggles the display of line numbers globally alongside the code.
 61 |               // To use it, add the following line in gatsby-browser.js
 62 |               // right after importing the prism color scheme:
 63 |               //  require("prismjs/plugins/line-numbers/prism-line-numbers.css")
 64 |               // Defaults to false.
 65 |               // If you wish to only show line numbers on certain code blocks,
 66 |               // leave false and use the {numberLines: true} syntax below
 67 |               showLineNumbers: false,
 68 |               // If setting this to true, the parser won't handle and highlight inline
 69 |               // code used in markdown i.e. single backtick code like `this`.
 70 |               noInlineHighlight: false,
 71 |               // This adds a new language definition to Prism or extend an already
 72 |               // existing language definition. More details on this option can be
 73 |               // found under the header "Add new language definition or extend an
 74 |               // existing language" below.
 75 |               languageExtensions: [
 76 |                 {
 77 |                   language: 'superscript',
 78 |                   extend: 'javascript',
 79 |                   definition: {
 80 |                     superscript_types: /(SuperType)/,
 81 |                   },
 82 |                   insertBefore: {
 83 |                     function: {
 84 |                       superscript_keywords: /(superif|superelse)/,
 85 |                     },
 86 |                   },
 87 |                 },
 88 |               ],
 89 |               // Customize the prompt used in shell output
 90 |               // Values below are default
 91 |               prompt: {
 92 |                 user: 'root',
 93 |                 host: 'localhost',
 94 |                 global: false,
 95 |               },
 96 |               // By default the HTML entities <>&'" are escaped.
 97 |               // Add additional HTML escapes by providing a mapping
 98 |               // of HTML entities and their escape value IE: { '}': '{' }
 99 |               escapeEntities: {},
100 |             },
101 |           },
102 |         ],
103 |       },
104 |     },
105 |     {
106 |       resolve: 'gatsby-source-filesystem',
107 |       options: {
108 |         name: 'assets',
109 |         path: `${__dirname}/dschapman-com-content/assets/`,
110 |       },
111 |     },
112 |     {
113 |       resolve: 'gatsby-source-filesystem',
114 |       options: {
115 |         name: 'articles',
116 |         path: `${__dirname}/dschapman-com-content/posts/`,
117 |       },
118 |     },
119 |     {
120 |       resolve: 'gatsby-source-filesystem',
121 |       options: {
122 |         name: 'blogs',
123 |         path: `${__dirname}/dschapman-com-content/blog/`,
124 |       },
125 |     },
126 |     {
127 |       resolve: `gatsby-plugin-google-analytics`,
128 |       options: {
129 |         trackingId: 'UA-87782104-2',
130 |         // Puts tracking script in the head instead of the body
131 |         head: true,
132 |         // Setting this parameter is optional
133 |       },
134 |     },
135 |     {
136 |       resolve: 'gatsby-source-filesystem',
137 |       options: {
138 |         name: 'poetry',
139 |         path: `${__dirname}/dschapman-com-content/poems/`,
140 |       },
141 |     },
142 |     {
143 |       resolve: 'gatsby-source-filesystem',
144 |       options: {
145 |         name: 'pages',
146 |         path: `${__dirname}/src/pages/`,
147 |       },
148 |     },
149 |     {
150 |       resolve: 'gatsby-source-filesystem',
151 |       options: {
152 |         name: 'My-notes',
153 |         path: `${__dirname}/My-notes/`,
154 |       },
155 |     },
156 | 
157 |     'gatsby-plugin-sitemap',
158 |     'gatsby-plugin-twitter',
159 |     'gatsby-plugin-netlify',
160 |     {
161 |       resolve: `gatsby-transformer-markdown-references`,
162 |       options: {
163 |         types: ['Mdx'], // or ["MarkdownRemark"] (or both)
164 |       },
165 |     },
166 |     {
167 |       resolve: `gatsby-plugin-feed`,
168 |       options: {
169 |         query: `
170 |           {
171 |             site {
172 |               siteMetadata {
173 |                 title
174 |                 description
175 |                 siteUrl
176 |                 site_url: siteUrl
177 |               }
178 |             }
179 |           }
180 |         `,
181 |         feeds: [
182 |           {
183 |             serialize: ({ query: { site, allMdx } }) => {
184 |               {
185 |                 return allMdx.edges.map((edge) => {
186 |                   if (edge.node.frontmatter.slug != null) {
187 |                     return Object.assign({}, edge.node.frontmatter, {
188 |                       title: edge.node.frontmatter.title,
189 |                       description: edge.node.frontmatter.excerpt,
190 |                       date: edge.node.frontmatter.date,
191 |                       url:
192 |                         site.siteMetadata.siteUrl + edge.node.frontmatter.slug,
193 |                       guid:
194 |                         site.siteMetadata.siteUrl + edge.node.frontmatter.slug,
195 |                       custom_elements: [
196 |                         {
197 |                           'content:encoded': edge.node.html
198 |                             .replace(
199 |                               /(?<=\"|\s)\/static\//g,
200 |                               `${site.siteMetadata.siteUrl}\/static\/`
201 |                             )
202 |                             .replace(/\[\[(\w*)\|(\w*)\]\]/g, '$2'),
203 |                         },
204 |                       ],
205 |                     })
206 |                   } else {
207 |                     let year = edge.node.frontmatter.date.substring(0, 4)
208 |                     let month = edge.node.frontmatter.date.substring(5, 7)
209 |                     return Object.assign({}, edge.node.frontmatter, {
210 |                       title: edge.node.frontmatter.title,
211 |                       description: edge.node.excerpt,
212 |                       date: edge.node.frontmatter.date,
213 |                       url:
214 |                         site.siteMetadata.siteUrl +
215 |                         `/blog/${year}/${month}/${edge.node.slug}`,
216 |                       guid:
217 |                         site.siteMetadata.siteUrl +
218 |                         `/blog/${year}/${month}/${edge.node.slug}`,
219 |                       custom_elements: [
220 |                         {
221 |                           'content:encoded': edge.node.html
222 |                             .replace(
223 |                               /(?<=\"|\s)\/static\//g,
224 |                               `${site.siteMetadata.siteUrl}\/static\/`
225 |                             )
226 |                             .replace(/\[\[(\w*)\|(\w*)\]\]/g, '$2'),
227 |                         },
228 |                       ],
229 |                     })
230 |                   }
231 |                 })
232 |               }
233 |             },
234 |             query: `
235 |             {
236 |               allMdx(
237 |                 filter: {fileAbsolutePath: {regex: "/dschapman-com-content/(?:posts|blog)/"}}
238 |                 sort: {order: DESC, fields: frontmatter___date}
239 |               ) {
240 |                 edges {
241 |                   node {
242 |                     frontmatter {
243 |                       slug
244 |                       title
245 |                       date
246 |                       excerpt
247 |                     }
248 |                     html
249 |                     slug
250 |                     excerpt
251 |                   }
252 |                 }
253 |               }
254 |             }
255 |               
256 |             `,
257 |             output: '/rss.xml',
258 |             title: "D.S. Chapman's RSS Feed",
259 |           },
260 |         ],
261 |       },
262 |     },
263 |   ],
264 | }
265 | 


--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
  1 | const path = require('path')
  2 | 
  3 | exports.createPages = async ({ graphql, actions, reporter }) => {
  4 |   // Destructure the createPage function from the actions object
  5 |   const { createPage } = actions
  6 | 
  7 |   const result = await graphql(`
  8 |     query {
  9 |       dendron: allMdx(
 10 |         filter: {
 11 |           fileAbsolutePath: { regex: "/My-notes/" }
 12 |           frontmatter: { published: { eq: true } }
 13 |         }
 14 |       ) {
 15 |         edges {
 16 |           node {
 17 |             frontmatter {
 18 |               id
 19 |               title
 20 |             }
 21 |             id
 22 |           }
 23 |           previous {
 24 |             id
 25 |           }
 26 |           next {
 27 |             id
 28 |           }
 29 |         }
 30 |       }
 31 |       poems: allMdx(
 32 |         filter: { fileAbsolutePath: { regex: "/dschapman-com-content/poems/" } }
 33 |       ) {
 34 |         edges {
 35 |           node {
 36 |             frontmatter {
 37 |               slug
 38 |               tags
 39 |             }
 40 |             id
 41 |           }
 42 |           previous {
 43 |             id
 44 |           }
 45 |           next {
 46 |             id
 47 |           }
 48 |         }
 49 |       }
 50 |       articles: allMdx(
 51 |         filter: { fileAbsolutePath: { regex: "/dschapman-com-content/posts/" } }
 52 |       ) {
 53 |         edges {
 54 |           node {
 55 |             frontmatter {
 56 |               slug
 57 |               tags
 58 |             }
 59 |             id
 60 |           }
 61 |           previous {
 62 |             id
 63 |           }
 64 |           next {
 65 |             id
 66 |           }
 67 |         }
 68 |       }
 69 |       blogs: allMdx(
 70 |         filter: { fileAbsolutePath: { regex: "/dschapman-com-content/blog/" } }
 71 |       ) {
 72 |         edges {
 73 |           node {
 74 |             frontmatter {
 75 |               title
 76 |               date
 77 |               tags
 78 |             }
 79 |             id
 80 |             slug
 81 |           }
 82 |           previous {
 83 |             id
 84 |           }
 85 |           next {
 86 |             id
 87 |           }
 88 |         }
 89 |       }
 90 |       articleTagsGroup: allMdx(
 91 |         filter: { fileAbsolutePath: { regex: "/dschapman-com-content/posts/" } }
 92 |       ) {
 93 |         group(field: frontmatter___tags) {
 94 |           fieldValue
 95 |           totalCount
 96 |         }
 97 |       }
 98 |       blogTagsGroup: allMdx(
 99 |         filter: { fileAbsolutePath: { regex: "/dschapman-com-content/blog/" } }
100 |       ) {
101 |         group(field: frontmatter___tags) {
102 |           fieldValue
103 |           totalCount
104 |         }
105 |       }
106 |       poemTagsGroup: allMdx(
107 |         filter: { fileAbsolutePath: { regex: "/dschapman-com-content/poems/" } }
108 |       ) {
109 |         group(field: frontmatter___tags) {
110 |           fieldValue
111 |           totalCount
112 |         }
113 |       }
114 |       allTagsGroup: allMdx {
115 |         group(field: frontmatter___tags) {
116 |           fieldValue
117 |           totalCount
118 |         }
119 |       }
120 |     }
121 |   `)
122 | 
123 |   if (result.errors) {
124 |     reporter.panicOnBuild('🚨  ERROR: Loading "createPages" query')
125 |   }
126 |   // Create blog post pages.
127 |   const poems = result.data.poems.edges
128 |   const dendron = result.data.dendron.edges
129 |   const articles = result.data.articles.edges
130 |   const blogs = result.data.blogs.edges
131 |   const articleTags = result.data.articleTagsGroup.group
132 |   const poemTags = result.data.poemTagsGroup.group
133 |   const blogTags = result.data.blogTagsGroup.group
134 |   const allTags = result.data.allTagsGroup.group
135 |   // you'll call `createPage` for each result
136 |   dendron.forEach(({ node }) => {
137 |     createPage({
138 |       path: `notes/${node.frontmatter.id}`,
139 |       component: path.resolve(`./src/components/notes/note.js`),
140 |       context: { id: node.id },
141 |     })
142 |   })
143 |   poems.forEach(({ node }) => {
144 |     createPage({
145 |       // This is the slug you created before
146 |       // (or `node.frontmatter.slug`)
147 |       path: node.frontmatter.slug,
148 |       // This component will wrap our MDX content
149 |       component: path.resolve(`./src/components/poems/poem-layout.js`),
150 |       // You can use the values in this context in
151 |       // our page layout component
152 |       context: { id: node.id },
153 |     })
154 |   })
155 |   blogs.forEach(({ node }) => {
156 |     let year = node.frontmatter.date.substring(0, 4)
157 |     let month = node.frontmatter.date.substring(5, 7)
158 |     createPage({
159 |       path: `blog/${year}/${month}/${node.slug}`,
160 |       component: path.resolve(`./src/components/blog/blog-layout.js`),
161 |       context: { id: node.id },
162 |     })
163 |   })
164 |   articles.forEach(({ node }) => {
165 |     createPage({
166 |       // This is the slug you created before
167 |       // (or `node.frontmatter.slug`)
168 |       path: node.frontmatter.slug,
169 |       // This component will wrap our MDX content
170 |       component: path.resolve(`./src/components/posts/post.js`),
171 |       // You can use the values in this context in
172 |       // our page layout component
173 |       context: { id: node.id },
174 |     })
175 |   })
176 |   articleTags.forEach((articleTag) => {
177 |     let tag = articleTag.fieldValue
178 |     let slug = tag
179 |       .toString()
180 |       .toLowerCase()
181 |       .replace(/\s+/g, '-') // Replace spaces with -
182 |       .replace(/[^\w\-]+/g, '') // Remove all non-word chars
183 |       .replace(/\-\-+/g, '-') // Replace multiple - with single -
184 |       .replace(/^-+/, '') // Trim - from start of text
185 |       .replace(/-+$/, '') // Trim - from end of text
186 | 
187 |     createPage({
188 |       path: `articles/tag/${slug}`,
189 |       component: path.resolve(`./src/components/posts/post-tag.js`),
190 |       context: { tag: tag },
191 |     })
192 |   })
193 |   blogTags.forEach((blogTag) => {
194 |     let tag = blogTag.fieldValue
195 |     let slug = tag
196 |       .toString()
197 |       .toLowerCase()
198 |       .replace(/\s+/g, '-') // Replace spaces with -
199 |       .replace(/[^\w\-]+/g, '') // Remove all non-word chars
200 |       .replace(/\-\-+/g, '-') // Replace multiple - with single -
201 |       .replace(/^-+/, '') // Trim - from start of text
202 |       .replace(/-+$/, '') // Trim - from end of text
203 | 
204 |     createPage({
205 |       path: `blog/tag/${slug}`,
206 |       component: path.resolve(`./src/components/blog/blog-tag.js`),
207 |       context: { tag: tag },
208 |     })
209 |   })
210 |   poemTags.forEach((poemTag) => {
211 |     let tag = poemTag.fieldValue
212 |     let slug = tag
213 |       .toString()
214 |       .toLowerCase()
215 |       .replace(/\s+/g, '-') // Replace spaces with -
216 |       .replace(/[^\w\-]+/g, '') // Remove all non-word chars
217 |       .replace(/\-\-+/g, '-') // Replace multiple - with single -
218 |       .replace(/^-+/, '') // Trim - from start of text
219 |       .replace(/-+$/, '') // Trim - from end of text
220 | 
221 |     createPage({
222 |       path: `poetry/tag/${slug}`,
223 |       component: path.resolve(`./src/components/poems/poem-tag.js`),
224 |       context: { tag: tag },
225 |     })
226 |   })
227 |   allTags.forEach((allTag) => {
228 |     let tag = allTag.fieldValue
229 |     let slug = tag
230 |       .toString()
231 |       .toLowerCase()
232 |       .replace(/\s+/g, '-') // Replace spaces with -
233 |       .replace(/[^\w\-]+/g, '') // Remove all non-word chars
234 |       .replace(/\-\-+/g, '-') // Replace multiple - with single -
235 |       .replace(/^-+/, '') // Trim - from start of text
236 |       .replace(/-+$/, '') // Trim - from end of text
237 | 
238 |     createPage({
239 |       path: `/tag/${slug}`,
240 |       component: path.resolve(`./src/components/tags/tag.js`),
241 |       context: { tag: tag },
242 |     })
243 |   })
244 | 
245 |   //Create article tags index
246 |   createPage({
247 |     path: 'poetry/tags',
248 |     component: path.resolve(`./src/components/poems/poem-tags.js`),
249 |     context: { tags: poemTags },
250 |   })
251 |   createPage({
252 |     path: 'articles/tags',
253 |     component: path.resolve(`./src/components/posts/post-tags.js`),
254 |     context: { tags: articleTags },
255 |   })
256 |   createPage({
257 |     path: 'blog/tags',
258 |     component: path.resolve(`./src/components/blog/blog-tags.js`),
259 |     context: { tags: blogTags },
260 |   })
261 |   createPage({
262 |     path: '/tags',
263 |     component: path.resolve(`./src/components/tags/tags.js`),
264 |     context: { tags: allTags },
265 |   })
266 | }
267 | 


--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |   '*.{js,json}': ['eslint --fix', 'prettier --write'],
3 | }
4 | 


--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 |   publish = "public"
3 | 
4 | [[plugins]]
5 |   package = "netlify-plugin-gatsby-cache"


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "private": true,
 3 |   "name": "dschapman-com",
 4 |   "version": "0.0.6",
 5 |   "author": "D.S. Chapman",
 6 |   "license": "MIT",
 7 |   "scripts": {
 8 |     "build": "GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true gatsby build --log-pages",
 9 |     "clean": "gatsby clean",
10 |     "develop": "gatsby develop",
11 |     "format": "prettier --write \"**/*.js{,on}\" \"**/*.md\"  \"**/*.mdx\"",
12 |     "lint": "eslint . --fix --ext .js,.md,.mdx",
13 |     "start": "gatsby develop",
14 |     "test": "jest"
15 |   },
16 |   "dependencies": {
17 |     "@emotion/core": "^11.0.0",
18 |     "@emotion/eslint-plugin": "^11.2.0",
19 |     "@emotion/react": "^11.4.1",
20 |     "@emotion/styled": "^11.3.0",
21 |     "@mdx-js/mdx": "^1.5.8",
22 |     "@mdx-js/react": "^1.5.8",
23 |     "@tippyjs/react": "^4.0.2",
24 |     "babel-plugin-emotion": "^11.0.0",
25 |     "gatsby": "^3.13.0",
26 |     "gatsby-cli": "^3.13.0",
27 |     "gatsby-image": "^3.11.0",
28 |     "gatsby-plugin-feed": "^3.0",
29 |     "gatsby-plugin-google-analytics": "^3.13.0",
30 |     "gatsby-plugin-mdx": "^2.13.0",
31 |     "gatsby-plugin-netlify": "^3.13.0",
32 |     "gatsby-plugin-react-helmet": "^4.13.0",
33 |     "gatsby-plugin-sharp": "^3.13.0",
34 |     "gatsby-plugin-sitemap": "^4.9.0",
35 |     "gatsby-plugin-twitter": "^3.13.0",
36 |     "gatsby-remark-copy-linked-files": "^4.10.0",
37 |     "gatsby-remark-double-brackets-link": "^0.1.8",
38 |     "gatsby-remark-images": "^5.10.0",
39 |     "gatsby-remark-mermaid": "^2.1.0",
40 |     "gatsby-remark-numbered-footnotes": "^1.0.1",
41 |     "gatsby-remark-prismjs": "^5.10.0",
42 |     "gatsby-remark-slug": "^0.1.0",
43 |     "gatsby-remark-smartypants": "^4.10.0",
44 |     "gatsby-source-filesystem": "^3.13.0",
45 |     "gatsby-transformer-markdown-references": "^0.1.5",
46 |     "gatsby-transformer-remark": "^5.4.0",
47 |     "gatsby-transformer-sharp": "^3.13.0",
48 |     "js-yaml": "^4.0.0",
49 |     "lodash": "^4.17.15",
50 |     "path": "^0.12.7",
51 |     "prismjs": "^1.20.0",
52 |     "prop-types": "^15.7.2",
53 |     "puppeteer": "^13.0.1",
54 |     "react": "^17.0.2",
55 |     "react-dom": "^17.0.2",
56 |     "react-helmet": "^6.0.0",
57 |     "remark-slug": "^7.0.0",
58 |     "sharp": "^0.31.3",
59 |     "slugify": "^1.4.0",
60 |     "yarn": "^1.22.4"
61 |   },
62 |   "devDependencies": {
63 |     "@iconify/icons-mdi": "^1.0.105",
64 |     "@iconify/react": "^3.0.1",
65 |     "@testing-library/react": "^12.0.0",
66 |     "eslint": "^7.0.0",
67 |     "eslint-config-prettier": "^8.3.0",
68 |     "eslint-plugin-import": "^2.20.2",
69 |     "eslint-plugin-mdx": "^1.6.8",
70 |     "eslint-plugin-prettier": "^3.1.2",
71 |     "eslint-plugin-react": "^7.20.0",
72 |     "husky": "^7.0.2",
73 |     "jest": "^27.0.6",
74 |     "lint-staged": "^11.1.2",
75 |     "prettier": "^2.0.2"
76 |   },
77 |   "husky": {
78 |     "hooks": {
79 |       "pre-commit": "lint-staged"
80 |     }
81 |   }
82 | }
83 | 


--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |   proseWrap: 'preserve',
3 |   singleQuote: true,
4 |   trailingComma: 'es5',
5 |   printWidth: 80,
6 |   semi: false,
7 |   jsxBracketSameLine: true,
8 | }
9 | 


--------------------------------------------------------------------------------
/src/components/audio.js:
--------------------------------------------------------------------------------
 1 | import React from 'react'
 2 | 
 3 | const Audio = ({ src }) => {
 4 |   return src ? (
 5 |     
 8 |   ) : (
 9 |     <>
10 |   )
11 | }
12 | 
13 | export default Audio
14 | 


--------------------------------------------------------------------------------
/src/components/avatar.js:
--------------------------------------------------------------------------------
 1 | /** @jsx jsx */
 2 | import React from 'react'
 3 | import { jsx, css } from '@emotion/react'
 4 | import avatar from '../../dschapman-com-content/assets/avatar.jpg'
 5 | 
 6 | const Avatar = () => {
 7 |   return (
 8 |     
9 | D.S. Chapman 22 |
23 | ) 24 | } 25 | 26 | export default Avatar 27 | -------------------------------------------------------------------------------- /src/components/blog/blog-index.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import React from 'react' 3 | import { css, jsx } from '@emotion/react' 4 | import styled from '@emotion/styled' 5 | import { graphql, useStaticQuery } from 'gatsby' 6 | import BlogList from './blog-list' 7 | 8 | export default function BlogIndex() { 9 | const data = useStaticQuery(graphql` 10 | query { 11 | allMdx( 12 | filter: { 13 | frontmatter: { published: { eq: true } } 14 | fileAbsolutePath: { regex: "/content/blog/" } 15 | } 16 | sort: { fields: frontmatter___date, order: DESC } 17 | limit: 1000 18 | ) { 19 | edges { 20 | node { 21 | id 22 | slug 23 | frontmatter { 24 | excerpt 25 | title 26 | date 27 | tags 28 | published 29 | } 30 | excerpt 31 | } 32 | } 33 | } 34 | } 35 | `) 36 | return 37 | } 38 | -------------------------------------------------------------------------------- /src/components/blog/blog-layout.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import React from 'react' 3 | import { css, jsx } from '@emotion/react' 4 | import { MDXRenderer } from 'gatsby-plugin-mdx' 5 | import SimilarContent from '../similar-content.js' 6 | import Layout from '../layout/layout' 7 | import { graphql } from 'gatsby' 8 | 9 | const Post = ({ data, pageContext, location }) => { 10 | return ( 11 | 17 |

22 | {data.mdx.frontmatter.title} 23 |

24 |

25 | 32 |

33 | {data.mdx.body} 34 | 38 |
39 | ) 40 | } 41 | 42 | export const pageQuery = graphql` 43 | query BlogQuery($id: String) { 44 | mdx(id: { eq: $id }) { 45 | id 46 | body 47 | frontmatter { 48 | title 49 | excerpt 50 | tags 51 | date 52 | } 53 | } 54 | } 55 | ` 56 | export default Post 57 | -------------------------------------------------------------------------------- /src/components/blog/blog-list.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import React from 'react' 3 | import styled from '@emotion/styled' 4 | import { jsx, css } from '@emotion/react' 5 | import { InternalLink } from '../layout/links' 6 | import { bpMaxLG, bpMaxSM } from '../../lib/breakpoints' 7 | import colors from '../../lib/colors' 8 | import { Link } from 'gatsby' 9 | 10 | export const BlogListStyled = styled.ul` 11 | list-style-type: none; 12 | padding: 4px 0px; 13 | margin: 0; 14 | ${bpMaxLG} { 15 | width: 100%; 16 | } 17 | ` 18 | 19 | export const BlogLinkStyled = styled.li` 20 | padding: 0.5rem 0.5rem 0.5rem 0; 21 | margin: 0 0rem 0.5rem 0rem; 22 | display: block; 23 | width: fit-content; 24 | height: fit-content; 25 | box-shadow: 0px 0px 1px 1px rgba(0, 0, 0, 0.01); 26 | transition: all 200ms ease-in-out; 27 | border-radius: 0 5px 5px 0; 28 | 29 | &:hover { 30 | box-shadow: 1px 4px 10px 3px rgba(28, 98, 103, 0.3); 31 | transform: scale(1.02); 32 | border-left: 4px solid ${colors.blue}; 33 | padding: 0.5rem 0.5rem 0.5rem 0.5rem; 34 | } 35 | 36 | ul { 37 | list-style-type: none; 38 | margin: 0; 39 | padding: 0; 40 | } 41 | li { 42 | display: inline; 43 | word-break: break-word; 44 | margin: 0; 45 | padding: 0; 46 | } 47 | ` 48 | 49 | const TagLink = styled(Link)` 50 | font-style: italic; 51 | font-size: 1rem; 52 | text-decoration: none; 53 | color: ${colors.gray}; 54 | border-radius: 5px; 55 | padding: 4px 0px; 56 | margin-right: 1rem; 57 | &:hover { 58 | color: ${colors.red}; 59 | } 60 | ` 61 | 62 | export const Description = styled.div` 63 | font-size: 1.4rem; 64 | padding: 0.5 0; 65 | color: ${colors.gray}; 66 | ` 67 | 68 | export const PubDate = styled.h3` 69 | font-size: 1.4rem; 70 | padding: 0; 71 | margin: 0.5rem 0; 72 | color: ${colors.text}; 73 | ` 74 | 75 | export default ({ posts, format }) => { 76 | return ( 77 | 89 | {posts.map(({ node: post }) => ( 90 | 91 | 96 | {post.frontmatter.title} 97 | 98 |
99 | 100 | {new Date(post.frontmatter.date).toDateString()} 101 | 102 |
    103 | {post.frontmatter.tags.map((tag) => ( 104 |
  • 105 | 114 | {tag} 115 | 116 |
  • 117 | ))} 118 |
119 |
120 | ))} 121 |
122 | ) 123 | } 124 | -------------------------------------------------------------------------------- /src/components/blog/blog-tag.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import React from 'react' 3 | import { jsx } from '@emotion/react' 4 | import { graphql } from 'gatsby' 5 | import { InternalLink, InternalNotesLink } from '../layout/links' 6 | import Layout from '../layout/layout' 7 | import BlogList from './blog-list' 8 | import { MDXProvider } from '@mdx-js/react' 9 | import MDXRenderer from 'gatsby-plugin-mdx/mdx-renderer' 10 | import components from '../layout/mdx-components' 11 | import LinktipPreview from '../layout/linktip-preview' 12 | 13 | const BlogTag = ({ data, pageContext, location }) => { 14 | const { tag } = pageContext 15 | const { edges, totalCount } = data.articles 16 | let note = data.notes 17 | if (note != null) { 18 | let references = [] 19 | let referenceBlock 20 | if (note.inboundReferenceNotes != null) { 21 | references = note.inboundReferenceNotes.map((ref) => ( 22 |
  • 23 | 24 | {ref.title} 25 | 26 |
  • 27 | )) 28 | } 29 | 30 | if (references.length > 0) { 31 | referenceBlock = ( 32 | <> 33 |

    Referenced in

    34 |
      {references}
    35 | 36 | ) 37 | } 38 | return ( 39 | 44 | 45 |
    46 | 50 |

    {note.title}

    51 | {note.childMdx.body} 52 | {referenceBlock} 53 | 54 | }> 55 | 56 | my notes on {tag} 57 | 58 |
    59 |
    60 | See all tags → 61 |
    62 | ) 63 | } else { 64 | return ( 65 | 70 | 71 |
    72 | See all tags → 73 |
    74 | ) 75 | } 76 | } 77 | 78 | export default BlogTag 79 | 80 | export const pageQuery = graphql` 81 | query BlogTag($tag: String) { 82 | articles: allMdx( 83 | limit: 2000 84 | sort: { fields: [frontmatter___date], order: DESC } 85 | filter: { 86 | frontmatter: { tags: { in: [$tag] } } 87 | fileAbsolutePath: { regex: "/content/blog/" } 88 | } 89 | ) { 90 | totalCount 91 | edges { 92 | node { 93 | slug 94 | frontmatter { 95 | title 96 | date 97 | excerpt 98 | tags 99 | } 100 | body 101 | } 102 | } 103 | } 104 | } 105 | ` 106 | -------------------------------------------------------------------------------- /src/components/blog/blog-tags.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Layout from '../layout/layout' 4 | import TagList from '../tags/list' 5 | 6 | const BlogTags = ({ pageContext, location }) => { 7 | const { tags } = pageContext 8 | return ( 9 | 14 | 15 | 16 | ) 17 | } 18 | 19 | export default BlogTags 20 | -------------------------------------------------------------------------------- /src/components/book-card.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import React from 'react' 3 | import { jsx, css } from '@emotion/react' 4 | import styled from '@emotion/styled' 5 | import book from '../../dschapman-com-content/assets/seasons-of-thought-cover.png' 6 | import { ExternalLink, InternalLink } from './layout/links' 7 | import colors from '../lib/colors' 8 | import { bpMaxLG } from '../lib/breakpoints' 9 | 10 | const Container = styled.div` 11 | display: flex; 12 | flex-wrap: wrap; 13 | width: 55%; 14 | ${bpMaxLG} { 15 | width: 100%; 16 | justify-content: center; 17 | } 18 | padding-bottom: 15px; 19 | ` 20 | 21 | const Links = styled.div` 22 | display: flex; 23 | flex-wrap: wrap; 24 | width: 100%; 25 | ` 26 | 27 | const Button = styled(ExternalLink)` 28 | padding: 0.66rem; 29 | border: solid 2px ${colors.red}; 30 | background-color: ${colors.background}; 31 | position: relative; 32 | overflow: hidden; 33 | color: ${colors.red}; 34 | font-family: 'proxima-nova'; 35 | border-radius: 30px; 36 | text-decoration: none; 37 | transition: 0.2s transform ease-in-out; 38 | margin: 0.5rem 2rem; 39 | white-space: nowrap; 40 | font-size: 1.25rem; 41 | 42 | &:after { 43 | background-color: ${colors.red}; 44 | border-radius: 30px; 45 | content: ''; 46 | display: block; 47 | height: 100%; 48 | width: 100%; 49 | position: absolute; 50 | left: 0; 51 | top: 0; 52 | transform: translate(-100%, 0) rotate(10deg); 53 | transform-origin: top left; 54 | transition: 0.2s transform ease-out; 55 | will-change: transform; 56 | z-index: -1; 57 | } 58 | 59 | &:hover { 60 | border: solid 2px transparent; 61 | color: ${colors.background}; 62 | transform: scale(1.05); 63 | will-change: transform; 64 | &:after { 65 | transform: translate(0, 0); 66 | } 67 | } 68 | ` 69 | 70 | const BookCard = ({ description }) => { 71 | return ( 72 | 73 | 74 | Cover for Seasons of Thought 87 | 88 |
    94 |

    95 | Seasons of Thought is a collection of poems about the beauty of human 96 | experience in everyday life. The poems in the collection use the 97 | passage of time as a canvas to explore nature, family, childhood, 98 | faith, and the changing seasons. 99 |

    100 | 101 | 104 | 107 | 108 |
    109 |
    110 | ) 111 | } 112 | 113 | export default BookCard 114 | -------------------------------------------------------------------------------- /src/components/featured.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import React from 'react' 3 | import { css, jsx } from '@emotion/react' 4 | import styled from '@emotion/styled' 5 | import { useStaticQuery, graphql, Link } from 'gatsby' 6 | import PostList from './posts/post-list' 7 | import BlogList from './blog/blog-list' 8 | 9 | export default function Featured() { 10 | const data = useStaticQuery(graphql` 11 | query { 12 | posts: allMdx( 13 | limit: 1000 14 | sort: { fields: frontmatter___date, order: DESC } 15 | filter: { 16 | frontmatter: { featured: { eq: true } } 17 | fileAbsolutePath: { regex: "/dschapman-com-content/posts/" } 18 | } 19 | ) { 20 | edges { 21 | node { 22 | frontmatter { 23 | title 24 | slug 25 | excerpt 26 | tags 27 | } 28 | } 29 | } 30 | } 31 | poems: allMdx( 32 | limit: 1000 33 | sort: { fields: frontmatter___title, order: DESC } 34 | filter: { 35 | frontmatter: { featured: { eq: true } } 36 | fileAbsolutePath: { regex: "/dschapman-com-content/poems/" } 37 | } 38 | ) { 39 | edges { 40 | node { 41 | frontmatter { 42 | title 43 | slug 44 | excerpt 45 | tags 46 | } 47 | } 48 | } 49 | } 50 | blogs: allMdx( 51 | limit: 3 52 | sort: { fields: frontmatter___date, order: DESC } 53 | filter: { 54 | frontmatter: { published: { eq: true } } 55 | fileAbsolutePath: { regex: "/dschapman-com-content/blog/" } 56 | } 57 | ) { 58 | edges { 59 | node { 60 | slug 61 | excerpt 62 | frontmatter { 63 | title 64 | date 65 | tags 66 | } 67 | } 68 | } 69 | } 70 | } 71 | `) 72 | return ( 73 | <> 74 |

    Latest Blog Posts

    75 | 76 |

    Featured Articles

    77 | 78 |

    Featured Poems

    79 | 80 | 81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /src/components/global.css: -------------------------------------------------------------------------------- 1 | @import url('https://p.typekit.net/p.css?s=1&k=osf8fyt&ht=tk&f=171.172.175.176.5474.5475.6768.6769.6770.6771.6772.6773&a=18424651&app=typekit&e=css'); 2 | 3 | *::selection { 4 | color: #ffffff; 5 | background-color: #75b9be; 6 | height: 100%; 7 | } 8 | 9 | html { 10 | font-size: 16px; 11 | font-family: 'adobe-garamond-pro'; 12 | text-rendering: optimizeLegibility; 13 | -webkit-font-smoothing: antialiased; 14 | } 15 | 16 | body { 17 | counter-reset: sidenote-counter; 18 | margin: 0; 19 | } 20 | 21 | header { 22 | display: flex; 23 | align-items: center; 24 | padding-top: 3rem; 25 | margin: 0 75px; 26 | justify-content: center; 27 | } 28 | 29 | header > div { 30 | display: flex; 31 | align-items: center; 32 | width: 85%; 33 | } 34 | 35 | footer { 36 | right: 0; 37 | margin-top: 5px; 38 | display: flex; 39 | flex-direction: column; 40 | flex-grow: 1; 41 | padding: 3rem; 42 | font-family: 'proxima-nova'; 43 | font-weight: 100; 44 | font-size: 2rem; 45 | } 46 | 47 | h1, 48 | h2, 49 | h3, 50 | h4, 51 | h5, 52 | h6 { 53 | font-family: 'proxima-nova'; 54 | font-weight: 100; 55 | } 56 | 57 | h1 { 58 | font-size: 3rem; 59 | line-height: 5rem; 60 | font-weight: 300; 61 | } 62 | 63 | h2 { 64 | display: inline-block; 65 | font-size: 2.25rem; 66 | font-weight: 300; 67 | padding-bottom: 5px; 68 | margin: 1rem 0; 69 | border-bottom: 2px solid #75b9be; 70 | } 71 | .post h2 { 72 | margin: 1.5rem 0; 73 | } 74 | 75 | h3 { 76 | font-size: 2.25rem; 77 | } 78 | 79 | p, 80 | dl, 81 | ol, 82 | ul, 83 | div { 84 | font-size: 1.4rem; 85 | line-height: 2rem; 86 | } 87 | 88 | p { 89 | margin-top: 1.4rem; 90 | margin-bottom: 1.4rem; 91 | padding-right: 0; 92 | vertical-align: baseline; 93 | } 94 | 95 | blockquote p { 96 | width: 55%; 97 | margin-right: 40px; 98 | } 99 | 100 | main { 101 | margin: 0 75px; 102 | display: flex; 103 | flex-grow: 1; 104 | flex-direction: row; 105 | justify-content: center; 106 | } 107 | 108 | article { 109 | width: 87.5%; 110 | } 111 | 112 | article > h1, 113 | article > h2, 114 | article > h3, 115 | article > h4, 116 | article > h5, 117 | article > h6 { 118 | max-width: 70%; 119 | } 120 | 121 | article > p, 122 | article > ol, 123 | article > footer, 124 | article > table, 125 | article > ul { 126 | width: 55%; 127 | } 128 | 129 | hr { 130 | border: 0; 131 | border-bottom: 1px solid; 132 | border-color: #f6f6f6; 133 | } 134 | 135 | @media (max-width: 1199px) { 136 | html { 137 | font-size: 16px; 138 | } 139 | 140 | main { 141 | margin: 0 40px; 142 | flex: 1; 143 | } 144 | 145 | header { 146 | margin: 0 40px; 147 | } 148 | 149 | article > h1, 150 | article > h2, 151 | article > h3, 152 | article > h4, 153 | article > h5, 154 | article > h6 { 155 | max-width: 100%; 156 | } 157 | 158 | hr, 159 | article > p, 160 | article > footer, 161 | article > table { 162 | width: 100%; 163 | } 164 | blockquote p { 165 | width: 100%; 166 | } 167 | } 168 | 169 | @media (max-width: 767px) { 170 | html { 171 | font-size: 15px; 172 | } 173 | } 174 | 175 | @media (max-width: 544px) { 176 | html { 177 | font-size: 14px; 178 | } 179 | article { 180 | width: 90%; 181 | } 182 | } 183 | 184 | @media (max-width: 400px) { 185 | article { 186 | width: 100%; 187 | } 188 | header { 189 | margin: 0 5px; 190 | } 191 | header > div { 192 | width: 100%; 193 | } 194 | main { 195 | margin: 0 5px; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/components/latest-articles.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import React from 'react' 3 | import { css, jsx } from '@emotion/react' 4 | import styled from '@emotion/styled' 5 | import { useStaticQuery, graphql, Link } from 'gatsby' 6 | import PostList from './posts/post-list' 7 | 8 | export default function LatestArticles() { 9 | const data = useStaticQuery(graphql` 10 | query { 11 | posts: allMdx( 12 | limit: 5 13 | sort: { fields: frontmatter___date, order: DESC } 14 | filter: { fileAbsolutePath: { regex: "/dschapman-com-content/posts/" } } 15 | ) { 16 | edges { 17 | node { 18 | frontmatter { 19 | title 20 | slug 21 | excerpt 22 | tags 23 | } 24 | } 25 | } 26 | } 27 | } 28 | `) 29 | return 30 | } 31 | -------------------------------------------------------------------------------- /src/components/layout/TextStyles.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from '@emotion/styled' 3 | import { bpMaxLG } from '../../lib/breakpoints' 4 | 5 | export const Callout = styled.blockquote` 6 | ::before { 7 | content: ''; 8 | display: block; 9 | margin: 0 auto; 10 | width: 3rem; 11 | border-left: none; 12 | border-top: 2px solid #75b9be; 13 | margin-bottom: 0.5em; 14 | } 15 | ::after { 16 | content: ''; 17 | display: block; 18 | margin: 0 auto; 19 | width: 3rem; 20 | border-top: 2px solid #75b9be; 21 | margin-top: 0.5em; 22 | } 23 | width: 55%; 24 | text-align: center; 25 | font-size: 2rem; 26 | lineheight: 2rem; 27 | display: block; 28 | ${bpMaxLG} { 29 | margin: 0 auto; 30 | } 31 | ` 32 | export const Blockquote = styled.blockquote` 33 | border-left: 10px solid #75b9be; 34 | margin: 1.5rem 10px; 35 | padding: 0 20px; 36 | font-size: 1.4rem; 37 | ` 38 | -------------------------------------------------------------------------------- /src/components/layout/figure.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import React from 'react' 3 | import { jsx, css } from '@emotion/react' 4 | import { bpMaxLG } from '../../lib/breakpoints' 5 | import colors from '../../lib/colors' 6 | 7 | function getCaption(caption) { 8 | if (caption == undefined) { 9 | return <> 10 | } else { 11 | return
    {caption}
    12 | } 13 | } 14 | 15 | const Figure = ({ children, caption, src, alt, fullwidth }) => { 16 | let Caption = getCaption(caption) 17 | const figureStyles = css` 18 | img { 19 | width: 100%; 20 | } 21 | 22 | figure { 23 | padding: 0; 24 | border: 0; 25 | font-size: 100%; 26 | font: inherit; 27 | vertical-align: baseline; 28 | max-width: 55%; 29 | -webkit-margin-start: 0; 30 | -webkit-margin-end: 0; 31 | margin: 0 0 3em 0; 32 | } 33 | 34 | figcaption { 35 | float: right; 36 | clear: right; 37 | margin-top: 0; 38 | margin-bottom: 0; 39 | font-size: 1.1rem; 40 | line-height: 1.4; 41 | vertical-align: baseline; 42 | position: relative; 43 | color: ${colors.gray}; 44 | } 45 | 46 | figure.fullwidth figcaption { 47 | margin-right: 24%; 48 | } 49 | ${bpMaxLG} { 50 | figure { 51 | max-width: 90%; 52 | } 53 | 54 | figcaption, 55 | figure.fullwidth figcaption { 56 | margin-right: 0%; 57 | max-width: none; 58 | } 59 | img { 60 | width: 100%; 61 | } 62 | } 63 | ` 64 | if (fullwidth == false) { 65 | return ( 66 | 67 |
    68 | {children} 69 | {alt} 70 | {Caption} 71 |
    72 |
    73 | ) 74 | } else { 75 | return ( 76 | 77 |
    78 | {children} 79 | {alt} 80 | {Caption} 81 |
    82 |
    83 | ) 84 | } 85 | } 86 | 87 | export { Figure } 88 | -------------------------------------------------------------------------------- /src/components/layout/footer.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import React from 'react' 3 | import styled from '@emotion/styled' 4 | import { css, jsx } from '@emotion/react' 5 | import colors from '../../lib/colors' 6 | 7 | const Footer = () => { 8 | const FooterContainer = styled.footer` 9 | background: ${colors.bluegreen}; 10 | ` 11 | return ( 12 | 13 | 14 |

    Contact

    15 | 32 |
    38 | Content is copyrighted 2019-2024 © D.S. Chapman. This site was built 39 | using GatsbyJS. Code for this website is open source and available on{' '} 40 | Github 41 |
    42 |
    43 |
    44 | ) 45 | } 46 | 47 | const FooterContent = styled.footer` 48 | margin: 0 auto; 49 | padding-right: 12.5%; 50 | a { 51 | text-decoration: underline; 52 | text-decoration-thickness: 1px; 53 | color: white; 54 | &:hover { 55 | text-decoration: none; 56 | } 57 | } 58 | ` 59 | 60 | export default Footer 61 | -------------------------------------------------------------------------------- /src/components/layout/header-links.js: -------------------------------------------------------------------------------- 1 | /**@jsx jsx */ 2 | import React from 'react' 3 | import { css, jsx } from '@emotion/react' 4 | import styled from '@emotion/styled' 5 | import { Link } from 'gatsby' 6 | import colors from '../../lib/colors' 7 | import { bpMaxLG, bpMaxMD, bpMaxSM, bpMaxXS } from '../../lib/breakpoints' 8 | const Nav = styled.nav` 9 | display: flex; 10 | width: 100%; 11 | ` 12 | 13 | const NavLink = styled(Link)` 14 | padding: 1rem; 15 | font-size: 1.4rem; 16 | color: ${colors.text}; 17 | text-decoration: none; 18 | text-transform: uppercase; 19 | letter-spacing: 3px; 20 | &:hover { 21 | text-decoration: underline; 22 | text-decoration-thickness: 1px; 23 | } 24 | 25 | ${bpMaxLG} { 26 | font-size: 1.4rem; 27 | padding: 0.75rem; 28 | } 29 | ${bpMaxMD} { 30 | font-size: 1rem; 31 | } 32 | ${bpMaxSM} { 33 | font-size: 0.75rem; 34 | } 35 | ` 36 | 37 | export default () => { 38 | return ( 39 | 81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /src/components/layout/header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import SEO from '../seo' 3 | import Logo from './logo' 4 | import HeaderLinks from './header-links' 5 | import { useStaticQuery, graphql } from 'gatsby' 6 | import styled from '@emotion/styled' 7 | 8 | import { bpMaxLG, bpMaxSM, bpMaxXS } from '../../lib/breakpoints' 9 | 10 | export const Header = styled.header` 11 | ${bpMaxLG} { 12 | padding-top: 2rem; 13 | } 14 | ${bpMaxSM} { 15 | padding-top: 1rem; 16 | } 17 | ${bpMaxXS} { 18 | padding-top: 0.5rem; 19 | } 20 | ` 21 | 22 | export default ({ 23 | title, 24 | description, 25 | seoTitle, 26 | seoTitleAddition1, 27 | seoTitleAddition2, 28 | type, 29 | location, 30 | }) => { 31 | const { 32 | site: { siteMetadata }, 33 | } = useStaticQuery(graphql` 34 | { 35 | site { 36 | siteMetadata { 37 | title 38 | } 39 | } 40 | } 41 | `) 42 | 43 | if (seoTitle) { 44 | title = seoTitle 45 | } else { 46 | title = title 47 | } 48 | return ( 49 |
    50 |
    51 | 52 | 53 | 61 |
    62 |
    63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /src/components/layout/layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import '../global.css' 3 | import { MDXProvider } from '@mdx-js/react' 4 | import components from './mdx-components-with-previews' 5 | import Header from './header' 6 | import Footer from './footer' 7 | import Tooltip from './tooltip' 8 | import { Footnote, Marginnote } from './sidenote' 9 | import { Figure } from './figure' 10 | import Linktip from './linktip' 11 | import { Callout } from './TextStyles' 12 | import { useStaticQuery, graphql } from 'gatsby' 13 | import styled from '@emotion/styled' 14 | import NoteNav from '../notes/note-nav' 15 | 16 | export const useAllMdx = () => { 17 | return useStaticQuery(graphql` 18 | query AllMdx { 19 | allMdx( 20 | filter: { 21 | fileAbsolutePath: { regex: "/dschapman-com-content|My-notes/" } 22 | } 23 | ) { 24 | nodes { 25 | frontmatter { 26 | slug 27 | title 28 | id 29 | published 30 | tags 31 | } 32 | slug 33 | body 34 | fileAbsolutePath 35 | } 36 | } 37 | } 38 | `) 39 | } 40 | 41 | export const Root = styled.root`` 42 | export const Main = styled.main` 43 | padding: 1rem 0rem; 44 | ` 45 | 46 | export default ({ 47 | children, 48 | title, 49 | description, 50 | seoTitle, 51 | seoTitleAddition1, 52 | seoTitleAddition2, 53 | type, 54 | location, 55 | className, 56 | }) => { 57 | const data = useAllMdx() 58 | const popups = {} 59 | const posts = data.allMdx.nodes 60 | posts.map((post) => { 61 | if (post) { 62 | popups[`${post.slug.substring(post.slug.lastIndexOf('/') + 1)}`] = { 63 | title: post.frontmatter.title, 64 | body: post.body, 65 | slug: post.frontmatter.slug, 66 | dendronId: post.frontmatter.id, 67 | published: post.frontmatter.published, 68 | } 69 | } 70 | }) 71 | const NoteBlock = () => { 72 | if (type === 'Note 📝') { 73 | return 74 | } else { 75 | return <> 76 | } 77 | } 78 | 79 | const AnchorTag = (props) => 80 | return ( 81 | 82 |
    91 | 92 |
    93 | 94 |
    95 |

    {title}

    96 | , 100 | Tooltip: (props) => , 101 | Linktip: (props) => , 102 | Callout: (props) => , 103 | Marginnote: (props) => , 104 | Figure: (props) =>
    , 105 | }}> 106 | {children} 107 | 108 |
    109 |
    110 |