aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKevin J Hoerr <kjhoerr@protonmail.com>2021-04-22 01:39:47 -0400
committerKevin J Hoerr <kjhoerr@protonmail.com>2021-04-22 01:39:47 -0400
commitd4f172ed203e6f3f6d1143f49c304cea8d45170e (patch)
tree2d1d611052df544df9603d2ebd9f17905a90ebea
parent9bfb5ce80ce8b5c2e7821b7ebea28a5f35848135 (diff)
downloadsubmelon.dev-d4f172ed203e6f3f6d1143f49c304cea8d45170e.tar.gz
submelon.dev-d4f172ed203e6f3f6d1143f49c304cea8d45170e.tar.bz2
submelon.dev-d4f172ed203e6f3f6d1143f49c304cea8d45170e.zip
Changes
-rw-r--r--.dockerignore3
-rw-r--r--.env.development1
-rw-r--r--.env.production1
-rw-r--r--Dockerfile4
-rw-r--r--gatsby-config.js13
-rw-r--r--package-lock.json128
-rw-r--r--package.json8
-rw-r--r--src/components/SEO.tsx76
-rw-r--r--src/images/favicon-70x70.pngbin0 -> 10940 bytes
-rw-r--r--src/images/icon.pngbin11189 -> 0 bytes
-rw-r--r--src/images/main.pngbin0 -> 75449 bytes
-rw-r--r--src/pages/404.tsx (renamed from src/pages/404.js)0
-rw-r--r--src/pages/index.js184
-rw-r--r--src/pages/index.tsx149
-rw-r--r--src/styles/main.css93
15 files changed, 460 insertions, 200 deletions
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..1b289ac
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,3 @@
+.cache/
+node_modules/
+public/ \ No newline at end of file
diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..6ef3730
--- /dev/null
+++ b/.env.development
@@ -0,0 +1 @@
+GATSBY_TIMESTAMP=1619067584 \ No newline at end of file
diff --git a/.env.production b/.env.production
new file mode 100644
index 0000000..6ef3730
--- /dev/null
+++ b/.env.production
@@ -0,0 +1 @@
+GATSBY_TIMESTAMP=1619067584 \ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..167a778
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,4 @@
+FROM gatsbyjs/gatsby:onbuild as build
+
+FROM gatsbyjs/gatsby
+COPY --from=build /pub /pub \ No newline at end of file
diff --git a/gatsby-config.js b/gatsby-config.js
index 8d57baf..ad3fc6d 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -1,15 +1,24 @@
module.exports = {
siteMetadata: {
title: "submelon.tech",
+ description: "The official website of Kevin Hoerr, the developer of websites.",
+ author: "Kevin J Hoerr",
},
plugins: [
+ "gatsby-plugin-typescript",
"gatsby-plugin-styled-components",
"gatsby-plugin-image",
"gatsby-plugin-react-helmet",
{
- resolve: "gatsby-plugin-manifest",
+ resolve: `gatsby-plugin-manifest`,
options: {
- icon: "src/images/icon.png",
+ name: `Kevin J Hoerr`,
+ short_name: `kjhoerr`,
+ start_url: `/`,
+ background_color: `#e1e1e1`,
+ theme_color: `#e1e1e1`,
+ display: `browser`,
+ icon: `src/images/favicon-70x70.png`,
},
},
"gatsby-plugin-mdx",
diff --git a/package-lock.json b/package-lock.json
index 9931d2c..7e99146 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,12 +17,20 @@
"gatsby-plugin-react-helmet": "^4.3.0",
"gatsby-plugin-sharp": "^3.3.1",
"gatsby-plugin-styled-components": "^4.3.0",
+ "gatsby-plugin-typescript": "^3.3.0",
"gatsby-source-filesystem": "^3.3.0",
"gatsby-transformer-sharp": "^3.3.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-helmet": "^6.1.0",
"styled-components": "^5.2.3"
+ },
+ "devDependencies": {
+ "@types/node": "^14.14.41",
+ "@types/react": "^17.0.3",
+ "@types/react-dom": "^17.0.3",
+ "@types/react-helmet": "^6.1.1",
+ "@types/styled-components": "^5.1.9"
}
},
"node_modules/@ardatan/aggregate-error": {
@@ -2946,6 +2954,16 @@
"@types/unist": "*"
}
},
+ "node_modules/@types/hoist-non-react-statics": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+ "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"node_modules/@types/http-cache-semantics": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
@@ -3082,6 +3100,24 @@
"csstype": "^3.0.2"
}
},
+ "node_modules/@types/react-dom": {
+ "version": "17.0.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.3.tgz",
+ "integrity": "sha512-4NnJbCeWE+8YBzupn/YrJxZ8VnjcJq5iR1laqQ1vkpQgBiA7bwk0Rp24fxsdNinzJY2U+HHS4dJJDPdoMjdJ7w==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-helmet": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.1.tgz",
+ "integrity": "sha512-VmSCMz6jp/06DABoY60vQa++h1YFt0PfAI23llxBJHbowqFgLUL0dhS1AQeVPNqYfRp9LAfokrfWACTNeobOrg==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/readable-stream": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.9.tgz",
@@ -3113,6 +3149,17 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz",
"integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA=="
},
+ "node_modules/@types/styled-components": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.9.tgz",
+ "integrity": "sha512-kbEG6YlwK8rucITpKEr6pA4Ho9KSQHUUOzZ9lY3va1mtcjvS3D0wDciFyHEiNHKLL/npZCKDQJqm0x44sPO9oA==",
+ "dev": true,
+ "dependencies": {
+ "@types/hoist-non-react-statics": "*",
+ "@types/react": "*",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/@types/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz",
@@ -29658,7 +29705,8 @@
"@mdx-js/react": {
"version": "1.6.22",
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz",
- "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg=="
+ "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==",
+ "requires": {}
},
"@mdx-js/util": {
"version": "2.0.0-next.8",
@@ -29902,6 +29950,16 @@
"@types/unist": "*"
}
},
+ "@types/hoist-non-react-statics": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+ "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"@types/http-cache-semantics": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
@@ -30038,6 +30096,24 @@
"csstype": "^3.0.2"
}
},
+ "@types/react-dom": {
+ "version": "17.0.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.3.tgz",
+ "integrity": "sha512-4NnJbCeWE+8YBzupn/YrJxZ8VnjcJq5iR1laqQ1vkpQgBiA7bwk0Rp24fxsdNinzJY2U+HHS4dJJDPdoMjdJ7w==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "@types/react-helmet": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.1.tgz",
+ "integrity": "sha512-VmSCMz6jp/06DABoY60vQa++h1YFt0PfAI23llxBJHbowqFgLUL0dhS1AQeVPNqYfRp9LAfokrfWACTNeobOrg==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/readable-stream": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.9.tgz",
@@ -30069,6 +30145,17 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz",
"integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA=="
},
+ "@types/styled-components": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.9.tgz",
+ "integrity": "sha512-kbEG6YlwK8rucITpKEr6pA4Ho9KSQHUUOzZ9lY3va1mtcjvS3D0wDciFyHEiNHKLL/npZCKDQJqm0x44sPO9oA==",
+ "dev": true,
+ "requires": {
+ "@types/hoist-non-react-statics": "*",
+ "@types/react": "*",
+ "csstype": "^3.0.2"
+ }
+ },
"@types/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz",
@@ -30409,7 +30496,8 @@
"acorn-jsx": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
- "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng=="
+ "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
+ "requires": {}
},
"address": {
"version": "1.1.2",
@@ -30439,12 +30527,14 @@
"ajv-errors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
- "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ=="
+ "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+ "requires": {}
},
"ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
- "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "requires": {}
},
"alphanum-sort": {
"version": "1.0.2",
@@ -30885,7 +30975,8 @@
"babel-plugin-remove-graphql-queries": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-3.3.0.tgz",
- "integrity": "sha512-G4FCVr8ex4Ck+wRTLsGBNnm7eWXKzpKrQI0u2zJ8KSsGbyWTarQZztSiJtV43dbbzmenjizHI5XrGA5rK9D4FQ=="
+ "integrity": "sha512-G4FCVr8ex4Ck+wRTLsGBNnm7eWXKzpKrQI0u2zJ8KSsGbyWTarQZztSiJtV43dbbzmenjizHI5XrGA5rK9D4FQ==",
+ "requires": {}
},
"babel-plugin-styled-components": {
"version": "1.12.0",
@@ -34710,7 +34801,8 @@
"eslint-plugin-react-hooks": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz",
- "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ=="
+ "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==",
+ "requires": {}
},
"eslint-scope": {
"version": "5.1.1",
@@ -37301,7 +37393,8 @@
"graphql-type-json": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.3.2.tgz",
- "integrity": "sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg=="
+ "integrity": "sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg==",
+ "requires": {}
},
"graphql-upload": {
"version": "11.0.0",
@@ -37337,7 +37430,8 @@
"graphql-ws": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.4.1.tgz",
- "integrity": "sha512-kHgDohfRQFDdzXzLqsV4wZM141sO1ukaXW/RSLlmIUsxT4N3r/4eQYTbkeLd4yRXaDkmv/rYf1EHL09Y5KO+Uw=="
+ "integrity": "sha512-kHgDohfRQFDdzXzLqsV4wZM141sO1ukaXW/RSLlmIUsxT4N3r/4eQYTbkeLd4yRXaDkmv/rYf1EHL09Y5KO+Uw==",
+ "requires": {}
},
"gray-matter": {
"version": "4.0.2",
@@ -37910,7 +38004,8 @@
"icss-utils": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
- "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA=="
+ "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "requires": {}
},
"ieee754": {
"version": "1.2.1",
@@ -38716,7 +38811,8 @@
"isomorphic-ws": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
- "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w=="
+ "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
+ "requires": {}
},
"isurl": {
"version": "1.0.0",
@@ -41981,7 +42077,8 @@
"postcss-flexbugs-fixes": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz",
- "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ=="
+ "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==",
+ "requires": {}
},
"postcss-loader": {
"version": "5.2.0",
@@ -42574,7 +42671,8 @@
"postcss-modules-extract-imports": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
- "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw=="
+ "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
+ "requires": {}
},
"postcss-modules-local-by-default": {
"version": "4.0.0",
@@ -44432,7 +44530,8 @@
"react-side-effect": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz",
- "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ=="
+ "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==",
+ "requires": {}
},
"read": {
"version": "1.0.7",
@@ -48772,7 +48871,8 @@
"ws": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
- "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
+ "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==",
+ "requires": {}
},
"x-is-string": {
"version": "0.1.0",
diff --git a/package.json b/package.json
index 5639973..ae2ead4 100644
--- a/package.json
+++ b/package.json
@@ -25,11 +25,19 @@
"gatsby-plugin-react-helmet": "^4.3.0",
"gatsby-plugin-sharp": "^3.3.1",
"gatsby-plugin-styled-components": "^4.3.0",
+ "gatsby-plugin-typescript": "^3.3.0",
"gatsby-source-filesystem": "^3.3.0",
"gatsby-transformer-sharp": "^3.3.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-helmet": "^6.1.0",
"styled-components": "^5.2.3"
+ },
+ "devDependencies": {
+ "@types/node": "^14.14.41",
+ "@types/react": "^17.0.3",
+ "@types/react-dom": "^17.0.3",
+ "@types/react-helmet": "^6.1.1",
+ "@types/styled-components": "^5.1.9"
}
}
diff --git a/src/components/SEO.tsx b/src/components/SEO.tsx
new file mode 100644
index 0000000..ce86222
--- /dev/null
+++ b/src/components/SEO.tsx
@@ -0,0 +1,76 @@
+import React from "react"
+import PropTypes from "prop-types"
+import { Helmet } from "react-helmet"
+import { useStaticQuery, graphql } from "gatsby"
+
+function SEO({ description, lang, meta, title }) {
+ const { site } = useStaticQuery(
+ graphql`
+ query {
+ site {
+ siteMetadata {
+ title
+ description
+ author
+ }
+ }
+ }
+ `
+ )
+ const metaDescription = description || site.siteMetadata.description
+ return (
+ <Helmet
+ htmlAttributes={{
+ lang,
+ }}
+ title={title}
+ titleTemplate={`%s | ${site.siteMetadata.title}`}
+ meta={[
+ {
+ name: `description`,
+ content: metaDescription,
+ },
+ {
+ property: `og:title`,
+ content: title,
+ },
+ {
+ property: `og:description`,
+ content: metaDescription,
+ },
+ {
+ property: `og:type`,
+ content: `website`,
+ },
+ {
+ name: `twitter:card`,
+ content: `summary`,
+ },
+ {
+ name: `twitter:creator`,
+ content: site.siteMetadata.author,
+ },
+ {
+ name: `twitter:title`,
+ content: title,
+ },
+ {
+ name: `twitter:description`,
+ content: metaDescription,
+ },
+ ].concat(meta)}
+ />
+ )
+}
+SEO.defaultProps = {
+ lang: `en`,
+ meta: [],
+ description: ``,
+}
+SEO.propTypes = {
+ description: PropTypes.string,
+ lang: PropTypes.string,
+ meta: PropTypes.arrayOf(PropTypes.object),
+ title: PropTypes.string.isRequired,
+}
+export default SEO \ No newline at end of file
diff --git a/src/images/favicon-70x70.png b/src/images/favicon-70x70.png
new file mode 100644
index 0000000..4d68d0f
--- /dev/null
+++ b/src/images/favicon-70x70.png
Binary files differ
diff --git a/src/images/icon.png b/src/images/icon.png
deleted file mode 100644
index 38b2fb0..0000000
--- a/src/images/icon.png
+++ /dev/null
Binary files differ
diff --git a/src/images/main.png b/src/images/main.png
new file mode 100644
index 0000000..a8557c9
--- /dev/null
+++ b/src/images/main.png
Binary files differ
diff --git a/src/pages/404.js b/src/pages/404.tsx
index 053ae0e..053ae0e 100644
--- a/src/pages/404.js
+++ b/src/pages/404.tsx
diff --git a/src/pages/index.js b/src/pages/index.js
deleted file mode 100644
index dbc0fb9..0000000
--- a/src/pages/index.js
+++ /dev/null
@@ -1,184 +0,0 @@
-import * as React from "react"
-
-// styles
-const pageStyles = {
- color: "#232129",
- padding: 96,
- fontFamily: "-apple-system, Roboto, sans-serif, serif",
-}
-const headingStyles = {
- marginTop: 0,
- marginBottom: 64,
- maxWidth: 320,
-}
-const headingAccentStyles = {
- color: "#663399",
-}
-const paragraphStyles = {
- marginBottom: 48,
-}
-const codeStyles = {
- color: "#8A6534",
- padding: 4,
- backgroundColor: "#FFF4DB",
- fontSize: "1.25rem",
- borderRadius: 4,
-}
-const listStyles = {
- marginBottom: 96,
- paddingLeft: 0,
-}
-const listItemStyles = {
- fontWeight: 300,
- fontSize: 24,
- maxWidth: 560,
- marginBottom: 30,
-}
-
-const linkStyle = {
- color: "#8954A8",
- fontWeight: "bold",
- fontSize: 16,
- verticalAlign: "5%",
-}
-
-const docLinkStyle = {
- ...linkStyle,
- listStyleType: "none",
- marginBottom: 24,
-}
-
-const descriptionStyle = {
- color: "#232129",
- fontSize: 14,
- marginTop: 10,
- marginBottom: 0,
- lineHeight: 1.25,
-}
-
-const docLink = {
- text: "Documentation",
- url: "https://www.gatsbyjs.com/docs/",
- color: "#8954A8",
-}
-
-const badgeStyle = {
- color: "#fff",
- backgroundColor: "#088413",
- border: "1px solid #088413",
- fontSize: 11,
- fontWeight: "bold",
- letterSpacing: 1,
- borderRadius: 4,
- padding: "4px 6px",
- display: "inline-block",
- position: "relative",
- top: -2,
- marginLeft: 10,
- lineHeight: 1,
-}
-
-// data
-const links = [
- {
- text: "Tutorial",
- url: "https://www.gatsbyjs.com/docs/tutorial/",
- description:
- "A great place to get started if you're new to web development. Designed to guide you through setting up your first Gatsby site.",
- color: "#E95800",
- },
- {
- text: "How to Guides",
- url: "https://www.gatsbyjs.com/docs/how-to/",
- description:
- "Practical step-by-step guides to help you achieve a specific goal. Most useful when you're trying to get something done.",
- color: "#1099A8",
- },
- {
- text: "Reference Guides",
- url: "https://www.gatsbyjs.com/docs/reference/",
- description:
- "Nitty-gritty technical descriptions of how Gatsby works. Most useful when you need detailed information about Gatsby's APIs.",
- color: "#BC027F",
- },
- {
- text: "Conceptual Guides",
- url: "https://www.gatsbyjs.com/docs/conceptual/",
- description:
- "Big-picture explanations of higher-level Gatsby concepts. Most useful for building understanding of a particular topic.",
- color: "#0D96F2",
- },
- {
- text: "Plugin Library",
- url: "https://www.gatsbyjs.com/plugins",
- description:
- "Add functionality and customize your Gatsby site or app with thousands of plugins built by our amazing developer community.",
- color: "#8EB814",
- },
- {
- text: "Build and Host",
- url: "https://www.gatsbyjs.com/cloud",
- badge: true,
- description:
- "Now you’re ready to show the world! Give your Gatsby site superpowers: Build and host on Gatsby Cloud. Get started for free!",
- color: "#663399",
- },
-]
-
-// markup
-const IndexPage = () => {
- return (
- <main style={pageStyles}>
- <title>Home Page</title>
- <h1 style={headingStyles}>
- Congratulations
- <br />
- <span style={headingAccentStyles}>β€” you just made a Gatsby site! </span>
- <span role="img" aria-label="Party popper emojis">
- πŸŽ‰πŸŽ‰πŸŽ‰
- </span>
- </h1>
- <p style={paragraphStyles}>
- Edit <code style={codeStyles}>src/pages/index.js</code> to see this page
- update in real-time.{" "}
- <span role="img" aria-label="Sunglasses smiley emoji">
- 😎
- </span>
- </p>
- <ul style={listStyles}>
- <li style={docLinkStyle}>
- <a
- style={linkStyle}
- href={`${docLink.url}?utm_source=starter&utm_medium=start-page&utm_campaign=minimal-starter`}
- >
- {docLink.text}
- </a>
- </li>
- {links.map(link => (
- <li key={link.url} style={{ ...listItemStyles, color: link.color }}>
- <span>
- <a
- style={linkStyle}
- href={`${link.url}?utm_source=starter&utm_medium=start-page&utm_campaign=minimal-starter`}
- >
- {link.text}
- </a>
- {link.badge && (
- <span style={badgeStyle} aria-label="New Badge">
- NEW!
- </span>
- )}
- <p style={descriptionStyle}>{link.description}</p>
- </span>
- </li>
- ))}
- </ul>
- <img
- alt="Gatsby G Logo"
- src="data:image/svg+xml,%3Csvg width='24' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 2a10 10 0 110 20 10 10 0 010-20zm0 2c-3.73 0-6.86 2.55-7.75 6L14 19.75c3.45-.89 6-4.02 6-7.75h-5.25v1.5h3.45a6.37 6.37 0 01-3.89 4.44L6.06 9.69C7 7.31 9.3 5.63 12 5.63c2.13 0 4 1.04 5.18 2.65l1.23-1.06A7.959 7.959 0 0012 4zm-8 8a8 8 0 008 8c.04 0 .09 0-8-8z' fill='%23639'/%3E%3C/svg%3E"
- />
- </main>
- )
-}
-
-export default IndexPage
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
new file mode 100644
index 0000000..884f829
--- /dev/null
+++ b/src/pages/index.tsx
@@ -0,0 +1,149 @@
+import React from "react";
+import styled from "styled-components";
+import { StaticImage } from "gatsby-plugin-image";
+import SEO from "../components/SEO";
+import "../styles/main.css";
+
+const SHORT_CHARS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
+
+function toShort (valu: number): string {
+ return valu.toString().match(/.{1,2}/g).map((s) => SHORT_CHARS[parseInt(s)]).join("");
+}
+
+function getTimestamp (seconds: number): string {
+ const date = new Date(seconds * 1000);
+ const year = date.getFullYear();
+ const month = date.getMonth();
+ const day = date.getDate();
+ const hour = date.getHours();
+ const minute = date.getMinutes();
+ return toShort(year)+'.'+toShort(month)+'.'+toShort(day)+'.'+toShort(hour)+'.'+toShort(minute)+"-0";
+}
+
+// styles
+const StyledContainer = styled.div`
+div#content {
+ width: 350px;
+ margin: 0px auto;
+ margin-bottom: 120px;
+ padding-top: 136px;
+}
+div#header {
+ text-align: center;
+ pointer-events: none;
+ user-select: none;
+}
+div#tagline {
+ background-color: #000;
+ width: 100%;
+ text-align: center;
+ color: #fff;
+ font-size: 36px;
+ font-weight: bold;
+}
+div#info {
+ padding: 4px 8px;
+}
+div#info a, div#info a:visited, div#info a:hover {
+ text-decoration: none;
+ color: #000;
+ padding: 1px 3px;
+}
+div#info a, div#info a:visited {
+ background-color: #e1e1e1;
+}
+div#info a:hover {
+ background-color: #d5d5d5;
+}
+div#info,div.link {
+ background-color: #fff;
+ border: 1px #000 solid;
+ border-left: 3px #000 solid;
+ border-right: 3px #000 solid;
+}
+div.link, div#tagline {
+ margin-top: 3px;
+}
+div.link > a, div.link > a:visited, div.link > a:active {
+ padding: 8px;
+ width: 328px;
+ display: block;
+ text-align: center;
+ text-decoration: none;
+ color: #666;
+}
+div.link:hover {
+ background-color: #ddd;
+ color: #222;
+}
+div.link:last-child, div.link:last-child > a {
+ border-bottom-left-radius: 18px;
+ border-bottom-right-radius: 18px;
+}
+q, p, em {
+ display: block;
+ text-indent: 28px;
+}
+
+p {
+ margin: 0px 0px 16px;
+}
+
+div#meta {
+ width: 100%;
+ position: fixed;
+ left: 0px;
+ bottom: 0px;
+ text-align: center;
+ font-size: 12px;
+}
+div#meta > span {
+ background-color: #e1e1e1;
+}
+`;
+
+// markup
+const IndexPage = () => {
+ return (
+ <main>
+ <SEO title="Kevin J Hoerr <kjhoerr@submelon.tech>" />
+ <StyledContainer>
+
+ <div id="content">
+ <div id="header">
+ <StaticImage
+ src="../images/main.png"
+ alt="Kevin J Hoerr"
+ placeholder="blurred"
+ layout="fixed"
+ width={340}
+ height={340}
+ />
+ </div>
+ <div id="tagline">Hello!</div>
+ <div id="info">
+ <p>I'm a computer science and math graduate from Millersville University. I work as an IT consultant and specialize in development operations and systems validation for web applications.</p>
+ <p>My most recent projects have been focused on full-stack development. I use Kubernetes for orchestration, NextJS for front-end, and rust+actix-web for my backend services with GraphQL serving as the public API.</p>
+ <p>This was recently rebuilt in GatsbyJS since the instance formerly holding this website broke during upgrades. Thank goodness for backups.</p>
+ <em>- Kevin H.</em>
+ </div>
+ <div id="links">
+ <div className="link">
+ <a href="https://cybr.es/@kjhoerr" rel="me">Mastodon: @kjhoerr@cybr.es</a>
+ </div>
+ <div className="link">
+ <a href="https://order.blackrockbrews.com">Black Rock Brewing (recent project)</a>
+ </div>
+ <div className="link">
+ <a href="https://git.submelon.dev">My Gitea instance for pet projects</a>
+ </div>
+ </div>
+ </div>
+
+ <div id="meta"><span>&copy;2021 kjhoerr@//submelon.tech/:{getTimestamp(parseInt(process.env.GATSBY_TIMESTAMP))}</span></div>
+ </StyledContainer>
+ </main>
+ );
+};
+
+export default IndexPage;
diff --git a/src/styles/main.css b/src/styles/main.css
new file mode 100644
index 0000000..fc55a61
--- /dev/null
+++ b/src/styles/main.css
@@ -0,0 +1,93 @@
+/** https://submelon.tech/res/css/main.css
+ * Main stylesheet for submelon.tech website.
+ * Author: Kevin J Hoerr
+ */
+body {
+ background-color: #e1e1e1; /* #e1e1e1, #d5d5d5 */
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAGXpUWHRUaXRsZQAACNdLSkzOTi/KL81LAQAWSwQhPQTyzQAAABt6VFh0QXV0aG9yAAAI1/NOLcvMU/DITy0qAgAYDwQeUS3vvwAAAZNJREFUeNrt3bFtQ0EMREH5x+q/M3Wi3IJaMGR7yHtbwCWDBY4J+fV4PL5vH879fr9tz/P5/JV3rzAcjI+DhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhAGBhDHglxXG/+RdjisMB2N1QyZirAWZirESZDLGOpDpGKtANmCsAdmCsQJkE8Z4kG0Yo0E2YowF2YoxEmQzxjiQ7RijQE7AGANyCsYIkJMweJDTMGiQEzFYkFMxSJCTMTiQ0zEokDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAgkDAGTuph/E3e5bjCcDBGNOS03fRXGA4GDXLq1YYrDAeDBDn9nskVhoNBgYQBgYQBgYQBgYQBgYQBgYQBgYQBgYQBgYQBgYQBgYQBgYQBgYTxs7wA+yQwVSKvoHMAAAAASUVORK5CYII=);
+ background-repeat: repeat;
+ font-family: Verdana;
+ font-size: 16px;
+}
+::selection {
+ background-color: #999;
+ color: #fff;
+}
+div#content {
+ width: 350px;
+ margin: 0px auto;
+ margin-bottom: 120px;
+ padding-top: 136px;
+}
+div#header {
+ text-align: center;
+ pointer-events: none;
+ user-select: none;
+}
+div#tagline {
+ background-color: #000;
+ width: 100%;
+ text-align: center;
+ color: #fff;
+ font-size: 36px;
+ font-weight: bold;
+}
+div#info {
+ padding: 4px 8px;
+}
+div#info a, div#info a:visited, div#info a:hover {
+ text-decoration: none;
+ color: #000;
+ padding: 1px 3px;
+}
+div#info a, div#info a:visited {
+ background-color: #e1e1e1;
+}
+div#info a:hover {
+ background-color: #d5d5d5;
+}
+div#info,div.link {
+ background-color: #fff;
+ border: 1px #000 solid;
+ border-left: 3px #000 solid;
+ border-right: 3px #000 solid;
+}
+div.link, div#tagline {
+ margin-top: 3px;
+}
+div.link > a, div.link > a:visited, div.link > a:active {
+ padding: 8px;
+ width: 328px;
+ display: block;
+ text-align: center;
+ text-decoration: none;
+ color: #666;
+}
+div.link:hover {
+ background-color: #ddd;
+ color: #222;
+}
+div.link:last-child, div.link:last-child > a {
+ border-bottom-left-radius: 18px;
+ border-bottom-right-radius: 18px;
+}
+q, p, em {
+ display: block;
+ text-indent: 28px;
+}
+
+p {
+ margin: 0px 0px 16px;
+}
+
+div#meta {
+ width: 100%;
+ position: fixed;
+ left: 0px;
+ bottom: 0px;
+ text-align: center;
+ font-size: 12px;
+}
+div#meta > span {
+ background-color: #e1e1e1;
+}