diff --git a/.gitignore b/.gitignore
index ea226ea..133a6d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,4 +37,6 @@ yarn-error.log*
.swc
coverage
public/matomo.js
-public/favicon/
\ No newline at end of file
+# public/favicon*
+# public/apple-touch-icon*
+# public/manifest*
\ No newline at end of file
diff --git a/README.md b/README.md
index 6975465..7875afb 100644
--- a/README.md
+++ b/README.md
@@ -25,10 +25,12 @@
- [📇 Client-side vCard creation](#-client-side-vcard-creation)
- [💎 Importing SVG assets](#-importing-svg-assets)
- [🍬 Typekit component](#-typekit-component)
+- [🤓 Scripts](#-scripts)
+ - [🎈 Add a new project](#-add-a-new-project)
+ - [🌄 Favicon generation](#-favicon-generation)
- [✨ Development](#-development)
- [🔮 Linting](#-linting)
- [👩🔬 Testing](#-testing)
- - [🎈 Add a new project](#-add-a-new-project)
- [🚚 Deployment](#-deployment)
- [🏛 Licenses](#-licenses)
@@ -124,6 +126,38 @@ If you want to know how, have a look at the respective component:
- [`src/components/Typekit/index.tsx`](src/components/Typekit/index.tsx)
+## 🤓 Scripts
+
+### 🎈 Add a new project
+
+To add a new project, run the following command. This adds a new item to the top of the `projects.yml` file, creating the title & slug from the argument:
+
+```bash
+npm run new -- "Hello"
+```
+
+Then continue modifying the new entry in [`_content/projects.yml`](_content/projects.yml).
+
+Finally, add as many images as needed with the file name format and put into `public/images/`:
+
+```text
+SLUG-01.png
+SLUG-02.png
+SLUG-03.png
+...
+```
+
+### 🌄 Favicon generation
+
+This generates all required favcion sizes from:
+
+- `src/images/favicon-512.png`
+- `src/images/favicon.svg`
+
+```bash
+npm run favicon
+```
+
## ✨ Development
```bash
@@ -165,25 +199,6 @@ npm test
Most test files live beside the respective component. Testing setup, fixtures, and mocks can be found in the `tests/` folder.
-### 🎈 Add a new project
-
-To add a new project, run the following command. This adds a new item to the top of the `projects.yml` file, creating the title & slug from the argument:
-
-```bash
-npm run new -- "Hello"
-```
-
-Then continue modifying the new entry in [`_content/projects.yml`](_content/projects.yml).
-
-Finally, add as many images as needed with the file name format and put into `public/images/`:
-
-```text
-SLUG-01.png
-SLUG-02.png
-SLUG-03.png
-...
-```
-
## 🚚 Deployment
Every branch or Pull Request is automatically deployed by [Vercel](https://vercel.com) with their GitHub integration, where the `main` branch is automatically aliased to `matthiaskretschmann.com`. A link to a preview deployment will appear under each Pull Request.
diff --git a/_content/meta.json b/_content/meta.json
index 4112c63..bcfcaf2 100644
--- a/_content/meta.json
+++ b/_content/meta.json
@@ -7,7 +7,7 @@
"available": "👔 Available for new projects. Let’s talk!",
"unavailable": "Not available for new projects."
},
- "gpg": "https://kretschmann.io/pub.gpg",
+ "gpg": "/gpg.txt",
"addressbook": "/matthias-kretschmann.vcf",
"bugs": "https://github.com/kremalicious/portfolio/issues/new",
"matomoUrl": "https://analytics.kremalicious.com",
diff --git a/next.config.js b/next.config.js
index 13352d7..3ca45b8 100644
--- a/next.config.js
+++ b/next.config.js
@@ -23,7 +23,9 @@ const next = (phase, { defaultConfig }) => {
return typeof defaultConfig.webpack === 'function'
? defaultConfig.webpack(config, options)
: config
- }
+ },
+ // https://nextjs.org/docs/api-reference/next.config.js/react-strict-mode
+ reactStrictMode: true
}
return nextConfig
diff --git a/package-lock.json b/package-lock.json
index 8e466ce..fe01f0d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -42,6 +42,7 @@
"prepend": "^1.0.2",
"prettier": "^2.7.1",
"sharp": "^0.31.2",
+ "sharp-ico": "^0.1.5",
"slugify": "^1.6.5",
"stylelint": "^14.14.1",
"stylelint-config-prettier": "^9.0.4",
@@ -1933,6 +1934,12 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
+ "node_modules/@canvas/image-data": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@canvas/image-data/-/image-data-1.0.0.tgz",
+ "integrity": "sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==",
+ "dev": true
+ },
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@@ -5008,6 +5015,33 @@
"integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==",
"dev": true
},
+ "node_modules/decode-bmp": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/decode-bmp/-/decode-bmp-0.2.1.tgz",
+ "integrity": "sha512-NiOaGe+GN0KJqi2STf24hfMkFitDUaIoUU3eKvP/wAbLe8o6FuW5n/x7MHPR0HKvBokp6MQY/j7w8lewEeVCIA==",
+ "dev": true,
+ "dependencies": {
+ "@canvas/image-data": "^1.0.0",
+ "to-data-view": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/decode-ico": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/decode-ico/-/decode-ico-0.4.1.tgz",
+ "integrity": "sha512-69NZfbKIzux1vBOd31al3XnMnH+2mqDhEgLdpygErm4d60N+UwA5Sq5WFjmEDQzumgB9fElojGwWG0vybVfFmA==",
+ "dev": true,
+ "dependencies": {
+ "@canvas/image-data": "^1.0.0",
+ "decode-bmp": "^0.2.0",
+ "to-data-view": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
"node_modules/decode-named-character-reference": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
@@ -6797,6 +6831,12 @@
"node": ">=10.17.0"
}
},
+ "node_modules/ico-endec": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/ico-endec/-/ico-endec-0.1.6.tgz",
+ "integrity": "sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ==",
+ "dev": true
+ },
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -11406,6 +11446,17 @@
"url": "https://opencollective.com/libvips"
}
},
+ "node_modules/sharp-ico": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/sharp-ico/-/sharp-ico-0.1.5.tgz",
+ "integrity": "sha512-a3jODQl82NPp1d5OYb0wY+oFaPk7AvyxipIowCHk7pBsZCWgbe0yAkU2OOXdoH0ENyANhyOQbs9xkAiRHcF02Q==",
+ "dev": true,
+ "dependencies": {
+ "decode-ico": "*",
+ "ico-endec": "*",
+ "sharp": "*"
+ }
+ },
"node_modules/sharp/node_modules/semver": {
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
@@ -12202,6 +12253,12 @@
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
"dev": true
},
+ "node_modules/to-data-view": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/to-data-view/-/to-data-view-1.1.0.tgz",
+ "integrity": "sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==",
+ "dev": true
+ },
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
diff --git a/package.json b/package.json
index 7b95283..5db726e 100644
--- a/package.json
+++ b/package.json
@@ -8,10 +8,10 @@
"author": "Matthias Kretschmann ",
"type": "module",
"scripts": {
- "start": "npm run pregenerate && next",
- "build": "npm run pregenerate && next build",
+ "start": "next",
+ "build": "next build",
"preview": "npm run build && next start",
- "export": "npm run pregenerate && next export",
+ "export": "next export",
"typecheck": "tsc",
"lint:js": "next lint",
"lint:css": "stylelint ./src/**/*.css",
@@ -21,8 +21,7 @@
"test": "NODE_ENV=test npm run lint && npm run typecheck && npm run jest",
"deploy:s3": "./scripts/deploy-s3.sh",
"new": "ts-node-esm ./scripts/new.ts",
- "favicon": "ts-node-esm ./scripts/favicon.ts",
- "pregenerate": "npm run favicon"
+ "favicon": "ts-node-esm ./scripts/favicon.ts"
},
"dependencies": {
"@giphy/js-fetch-api": "^4.4.0",
diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png
new file mode 100644
index 0000000..1e64682
Binary files /dev/null and b/public/apple-touch-icon.png differ
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..5dbc536
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..227fa1a
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1,27 @@
+
diff --git a/public/gpg.txt b/public/gpg.txt
new file mode 100644
index 0000000..bce11da
--- /dev/null
+++ b/public/gpg.txt
@@ -0,0 +1,185 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+YXMgS3JldHNjaG1hbm4gPGtyZW1hQGpwYmVybGluLmRlPokCVAQTAQoAPgIbAwUL
+CQgHAwUVCgkICwUWAgMBAAIeAQIXgBYhBJrA95Du12SgGDjIKb08Hz7deDH8BQJj
+YcUIBQkUsaQDAAoJEL08Hz7deDH8PysP/jN45LeLMjiiGAr090sRtRNqmvljvWC4
+v1DlXxuLwCJFhB0U8cZMvx7uUU2PDJodecGiKunNBYAuz98WjMCTxMNnfT+cgkxv
+lSmfa2wjz3l/otHWaeyLTfn48SBbSd/RoFF8sH2OaqWIde8iJ6n6OvGVluNboL8g
++ZL+O28Du+tsnZ4v7WNNJGBHJBOupaB19/ConKR/fUC8WlFV8WTC/6egWB6TbNjU
+RUt/EPsc4hieU/bodJKmzYSVQcVoR/7x+wAnMNJrGS34tJN+2O1O9JobJVBCHsk5
+yMhs2XLrSIys5Xby1qFWk7zmdKP1NZaSeIh+4xXtbzURtO28Xqmry76s0tznqfss
+uy323+Hdp8ynXP6MuDE/nlPQVI2BMbfCvMQzSp3dSRhq2+siYg2qoL0QNAaXAPUU
+OxdH2ee84Hzx60s9kdnw5EHHQ6KZiCvuSABog0kldDAdIl8I2Kc8DtzZVBLPSWp3
+FPR7V4X8722tk6ZsZIMADJeslcSlnscRFPYh6gDrbzJCSj6IcG/wb8fHyUgMrLZb
+3E8rZceWXa5fiIPtb6z4LcPS3dRWD7iK52Uqg+8N6IuG/OvlmY9+6X6OseUm+qq6
+FZrXQONGC09mPGGvCKCfEZYKITO2m1Rd2SWPh5UXop8277q3botsOhMnjaTiVQVf
+xv37UFfGkdXBtC5NYXR0aGlhcyBLcmV0c2NobWFubiA8bWF0dGhpYXNAYmlnY2hh
+aW5kYi5jb20+iQJUBBMBCgA+AhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAFiEE
+msD3kO7XZKAYOMgpvTwfPt14MfwFAmNhxQgFCRSxpAMACgkQvTwfPt14Mfy2mQ//
+QQMn8nGbGNovHBpOq/AehCNeQlrtY4ln6k09kLZ+Xyy7BNB3xR6zLbyl6uLgbQKk
+d9q3/JFEPU8ZP/fH22AxR66R8vzcxWKeoow8EVpYLttyTl/JGFiFyv33xbhKynFC
+zhHfOpPdpMu4oJf78kAUT/OTuiRQFMlpIXGLKbhwkikGXWuejGuRXKF2v7Fxc+6y
+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
+=DXwP
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/public/manifest/favicon-192.png b/public/manifest/favicon-192.png
new file mode 100644
index 0000000..c09314f
Binary files /dev/null and b/public/manifest/favicon-192.png differ
diff --git a/public/manifest/favicon-512.png b/public/manifest/favicon-512.png
new file mode 100644
index 0000000..5b20a25
Binary files /dev/null and b/public/manifest/favicon-512.png differ
diff --git a/public/manifest/manifest.webmanifest b/public/manifest/manifest.webmanifest
new file mode 100644
index 0000000..4cf7846
--- /dev/null
+++ b/public/manifest/manifest.webmanifest
@@ -0,0 +1 @@
+{"name":"matthias kretschmann","shortName":"mk","icons":[{"src":"/manifest/favicon-192.png","type":"image/png","sizes":"192x192"},{"src":"/manifest/favicon-512.png","type":"image/png","sizes":"512x512"}]}
\ No newline at end of file
diff --git a/scripts/favicon.ts b/scripts/favicon.ts
index eea8640..02d8ba5 100644
--- a/scripts/favicon.ts
+++ b/scripts/favicon.ts
@@ -1,87 +1,83 @@
-import { favicons, FaviconImage } from 'favicons'
import path from 'path'
import fs from 'fs'
+import sharp from 'sharp'
+import ico from 'sharp-ico'
-const imagesDirectory = path.resolve(path.join(process.cwd(), 'src', 'images'))
-const source = `${imagesDirectory}/favicon.png`
-const output = path.resolve(path.join(process.cwd(), 'public', 'favicon'))
+const imagesSourcePath = path.resolve(path.join(process.cwd(), 'src', 'images'))
+const faviconSource = `${imagesSourcePath}/favicon-512.png`
+const faviconSourceSvg = `${imagesSourcePath}/favicon.svg`
-const configuration = {
- path: '/favicon', // Path for overriding default icons path. `string`
- appName: 'matthias kretschmann',
- appShortName: 'mk',
- appDescription: null,
- developerName: null,
- developerURL: null,
- dir: 'auto',
- lang: 'en-US',
- appleStatusBarStyle: 'black-translucent', // Style for Apple status bar: "black-translucent", "default", "black". `string`
- display: 'minimal-ui', // Preferred display mode: "fullscreen", "standalone", "minimal-ui" or "browser". `string`
- orientation: 'any', // Default orientation: "any", "natural", "portrait" or "landscape". `string`
- scope: '/', // set of URLs that the browser considers within your app
- start_url: '/?homescreen=1',
- background_color: '#e7eef4',
- theme_color: '#e7eef4',
- preferRelatedApplications: false,
- relatedApplications: undefined,
- version: '1.0',
- pixel_art: false, // Keeps pixels "sharp" when scaling up, for pixel art. Only supported in offline mode.
- loadManifestWithCredentials: false,
- manifestMaskable: `${imagesDirectory}/logo.svg`, // Maskable source image(s) for manifest.json. "true" to use default source. More information at https://web.dev/maskable-icon/. `boolean`, `string`, `buffer` or array of `string`
- icons: {
- android: true,
- appleIcon: true,
- appleStartup: false,
- favicons: true,
- windows: true,
- yandex: false
+const outputWebRoot = path.resolve(path.join(process.cwd(), 'public'))
+const outputManifest = path.resolve(
+ path.join(process.cwd(), 'public', 'manifest')
+)
+
+// All the sizes and meta we'll need
+// https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs
+const sizes = [32, 180, 192, 512]
+const outputMeta = `
+
+
+
+
+`
+
+function createManifest(iconsizes: number[]) {
+ const manifest = {
+ name: 'matthias kretschmann',
+ shortName: 'mk',
+ icons: iconsizes.map((size) => ({
+ src: `/manifest/favicon-${size}.png`,
+ type: 'image/png',
+ sizes: `${size}x${size}`
+ }))
}
+ fs.writeFileSync(
+ `${outputManifest}/manifest.webmanifest`,
+ JSON.stringify(manifest)
+ )
}
async function buildFavicons() {
try {
- const response = await favicons(source, configuration)
- const allFilesToWrite = response.images.concat(response.files as any)
+ // Nuke all & create output folder first
+ fs.rmSync(outputManifest, { recursive: true, force: true })
+ fs.rmSync(`${outputWebRoot}/apple-touch-icon.png`, { force: true })
+ fs.rmSync(`${outputWebRoot}/favicon.ico`, { force: true })
+ fs.mkdirSync(outputManifest, { recursive: true })
- fs.rmSync(output, { recursive: true, force: true })
+ // copy over the svg, as it's handcrafted
+ fs.copyFileSync(faviconSourceSvg, `${outputWebRoot}/favicon.svg`)
- allFilesToWrite.forEach((file) => {
- const { name, contents } = file
- const destination = `${output}/${name}`
+ // generate all the rest
+ await Promise.all(
+ sizes.map(async (size) => {
+ let destination = `${outputManifest}/favicon-${size}.png`
+ if (size === 180) destination = `${outputWebRoot}/apple-touch-icon.png`
- try {
- fs.readFileSync(destination, 'utf8')
- } catch (error) {
- // if there is no file, get data and write a fresh file
- if (error.code === 'ENOENT') {
- try {
- fs.mkdirSync(output, { recursive: true })
- fs.writeFileSync(destination, contents)
- } catch (error) {
- throw new Error("Couldn't write favicon file")
- }
- }
- }
- })
-
- const destinationHtml = `${output}/_meta.html`
-
- try {
- fs.readFileSync(destinationHtml, 'utf8')
- } catch (error) {
- // if there is no file, get data and write a fresh file
- if (error.code === 'ENOENT') {
- try {
- fs.mkdirSync(output, { recursive: true })
- fs.writeFileSync(
- destinationHtml,
- response.html.reverse().toString().replaceAll(',', '')
+ if (size === 32) {
+ await ico.sharpsToIco(
+ [sharp(faviconSource)],
+ `${outputWebRoot}/favicon.ico`,
+ { sizes: [32, 24, 16], resizeOptions: {} }
)
- } catch (error) {
- throw new Error("Couldn't write favicon file")
+ fs.rmSync(destination, { force: true })
+ } else {
+ await sharp(faviconSource).resize(size, size).toFile(destination)
}
- }
- }
+ })
+ )
+
+ // write out manifest
+ createManifest([192, 512])
+
+ console.log(`
+ -----------------------------
+ Favicon generation complete!
+ -----------------------------
+ Add this to src/components/Meta/Favicon.tsx:
+ ${outputMeta}
+ `)
} catch (error) {
console.error(error.message)
}
diff --git a/src/components/Meta/Favicon.tsx b/src/components/Meta/Favicon.tsx
index 2d487a5..19a2694 100644
--- a/src/components/Meta/Favicon.tsx
+++ b/src/components/Meta/Favicon.tsx
@@ -3,100 +3,14 @@ import Head from 'next/head'
const MetaFavicons = () => {
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* */}
-
+ {/*
+ Stop the favicon madness
+ https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs
+ */}
+
+
+
+
)
}
diff --git a/src/images/favicon-512.png b/src/images/favicon-512.png
new file mode 100644
index 0000000..669872c
Binary files /dev/null and b/src/images/favicon-512.png differ
diff --git a/src/images/favicon.png b/src/images/favicon.png
deleted file mode 100644
index 7eee420..0000000
Binary files a/src/images/favicon.png and /dev/null differ
diff --git a/src/images/favicon.svg b/src/images/favicon.svg
new file mode 100644
index 0000000..227fa1a
--- /dev/null
+++ b/src/images/favicon.svg
@@ -0,0 +1,27 @@
+
diff --git a/src/images/logo.svg b/src/images/logo.svg
index d218227..b289b46 100644
--- a/src/images/logo.svg
+++ b/src/images/logo.svg
@@ -1,3 +1,12 @@
-