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 @@ - - + +