1
0
mirror of https://github.com/kremalicious/portfolio.git synced 2024-12-22 09:13:19 +01:00

favicon & web manifest changes

This commit is contained in:
Matthias Kretschmann 2022-11-16 20:36:25 +00:00
parent 23b0f8dcc7
commit 88e5ef9502
Signed by: m
GPG Key ID: 606EEEF3C479A91F
11 changed files with 85 additions and 130 deletions

View File

@ -149,10 +149,12 @@ SLUG-03.png
### 🌄 Favicon generation
This generates all required favcion sizes from:
This generates all required favicon sizes from:
- `src/images/favicon-512.png`
- `src/images/favicon.svg`
- `src/images/favicon.svg` (handcrafted, adaptive based on OS theme)
Also creates a web manifest.
```bash
npm run favicon

77
package-lock.json generated
View File

@ -27,13 +27,12 @@
"@svgr/webpack": "^6.5.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@types/jest": "^29.2.2",
"@types/jest": "^29.2.3",
"@types/js-yaml": "^4.0.5",
"@types/sharp": "^0.31.0",
"chalk": "^5.1.2",
"eslint": "^8.27.0",
"eslint-config-next": "^13.0.3",
"favicons": "^7.0.2",
"jest": "^29.3.1",
"jest-canvas-mock": "^2.4.0",
"jest-environment-jsdom": "^29.3.1",
@ -44,11 +43,11 @@
"sharp": "^0.31.2",
"sharp-ico": "^0.1.5",
"slugify": "^1.6.5",
"stylelint": "^14.14.1",
"stylelint": "^14.15.0",
"stylelint-config-prettier": "^9.0.4",
"stylelint-prettier": "^2.0.0",
"ts-node": "^10.9.1",
"typescript": "^4.8.4"
"typescript": "^4.9.3"
},
"engines": {
"node": "16.x"
@ -3598,9 +3597,9 @@
}
},
"node_modules/@types/jest": {
"version": "29.2.2",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.2.tgz",
"integrity": "sha512-og1wAmdxKoS71K2ZwSVqWPX6OVn3ihZ6ZT2qvZvZQm90lJVDyXIjYcu4Khx2CNIeaFv12rOU/YObOsI3VOkzog==",
"version": "29.2.3",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.3.tgz",
"integrity": "sha512-6XwoEbmatfyoCjWRX7z0fKMmgYKe9+/HrviJ5k0X/tjJWHGAezZOfYaxqQKuzG/TvQyr+ktjm4jgbk0s4/oF2w==",
"dev": true,
"dependencies": {
"expect": "^29.0.0",
@ -5472,12 +5471,6 @@
"node": ">=6"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"dev": true
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@ -6204,20 +6197,6 @@
"reusify": "^1.0.4"
}
},
"node_modules/favicons": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/favicons/-/favicons-7.0.2.tgz",
"integrity": "sha512-M/qE3ERHOBu0+Op+61jx8CdvOnSKrrl2zxUPpoGgsNyfjuGqRsK80zYoA5Uwdxl8QM4egfhBWZp1j7KK3YnOMg==",
"dev": true,
"dependencies": {
"escape-html": "^1.0.3",
"sharp": "^0.31.1",
"xml2js": "^0.4.23"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/fb-watchman": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
@ -11388,12 +11367,6 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"node_modules/sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true
},
"node_modules/saxes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
@ -11924,15 +11897,15 @@
}
},
"node_modules/stylelint": {
"version": "14.14.1",
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.14.1.tgz",
"integrity": "sha512-Jnftu+lSD8cSpcV/+Z2nfgfgFpTIS1FcujezXPngtoIQ6wtwutL22MsNE0dJuMiM1h1790g2qIjAyUZCMrX4sw==",
"version": "14.15.0",
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.15.0.tgz",
"integrity": "sha512-JOgDAo5QRsqiOZPZO+B9rKJvBm64S0xasbuRPAbPs6/vQDgDCnZLIiw6XcAS6GQKk9k1sBWR6rmH3Mfj8OknKg==",
"dev": true,
"dependencies": {
"@csstools/selector-specificity": "^2.0.2",
"balanced-match": "^2.0.0",
"colord": "^2.9.3",
"cosmiconfig": "^7.0.1",
"cosmiconfig": "^7.1.0",
"css-functions-list": "^3.1.0",
"debug": "^4.3.4",
"fast-glob": "^3.2.12",
@ -11952,7 +11925,7 @@
"micromatch": "^4.0.5",
"normalize-path": "^3.0.0",
"picocolors": "^1.0.0",
"postcss": "^8.4.18",
"postcss": "^8.4.19",
"postcss-media-query-parser": "^0.2.3",
"postcss-resolve-nested-selector": "^0.1.1",
"postcss-safe-parser": "^6.0.0",
@ -12482,9 +12455,9 @@
}
},
"node_modules/typescript": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
"version": "4.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@ -13078,28 +13051,6 @@
"node": ">=12"
}
},
"node_modules/xml2js": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
"dev": true,
"dependencies": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
"dev": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",

View File

@ -42,13 +42,12 @@
"@svgr/webpack": "^6.5.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@types/jest": "^29.2.2",
"@types/jest": "^29.2.3",
"@types/js-yaml": "^4.0.5",
"@types/sharp": "^0.31.0",
"chalk": "^5.1.2",
"eslint": "^8.27.0",
"eslint-config-next": "^13.0.3",
"favicons": "^7.0.2",
"jest": "^29.3.1",
"jest-canvas-mock": "^2.4.0",
"jest-environment-jsdom": "^29.3.1",
@ -57,12 +56,13 @@
"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": "^14.15.0",
"stylelint-config-prettier": "^9.0.4",
"stylelint-prettier": "^2.0.0",
"ts-node": "^10.9.1",
"typescript": "^4.8.4"
"typescript": "^4.9.3"
},
"engines": {
"node": "16.x"

View File

@ -5,12 +5,6 @@
viewBox="0 0 512 512"
>
<style>
#background {
fill: #e7eef4;
}
#logomark {
fill: #6b7f88;
}
@media (prefers-color-scheme: dark) {
#background {
fill: #161a1b;
@ -18,9 +12,10 @@
}
</style>
<g fill="none" fill-rule="evenodd">
<rect id="background" width="512" height="512" />
<rect id="background" fill="#e7eef4" width="512" height="512" />
<path
id="logomark"
fill="#6b7f88"
d="M397,91 L421,115 L114,421 L91,398 L397,91 Z M397,182 L421,206 L205,421 L182,398 L397,182 Z M307,91 L330,115 L114,330 L91,307 L307,91 Z"
/>
</g>

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 541 B

View File

@ -1 +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"}]}
{"name":"matthias kretschmann","short_name":"mk","display":"standalone","start_url":"/?homescreen=1","icons":[{"src":"/manifest/favicon-192.png","type":"image/png","sizes":"192x192"},{"src":"/manifest/favicon-512.png","type":"image/png","sizes":"512x512"}]}

View File

@ -25,7 +25,9 @@ const outputMeta = `
function createManifest(iconsizes: number[]) {
const manifest = {
name: 'matthias kretschmann',
shortName: 'mk',
short_name: 'mk',
display: 'standalone',
start_url: '/?homescreen=1',
icons: iconsizes.map((size) => ({
src: `/manifest/favicon-${size}.png`,
type: 'image/png',
@ -38,13 +40,18 @@ function createManifest(iconsizes: number[]) {
)
}
function nuke() {
fs.rmSync(outputManifest, { recursive: true, force: true })
fs.rmSync(`${outputWebRoot}/apple-touch-icon.png`, { force: true })
fs.rmSync(`${outputWebRoot}/favicon.ico`, { force: true })
fs.rmSync(`${outputWebRoot}/favicon.svg`, { force: true })
fs.mkdirSync(outputManifest, { recursive: true })
}
async function buildFavicons() {
try {
// 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 })
nuke()
// copy over the svg, as it's handcrafted
fs.copyFileSync(faviconSourceSvg, `${outputWebRoot}/favicon.svg`)
@ -55,6 +62,7 @@ async function buildFavicons() {
let destination = `${outputManifest}/favicon-${size}.png`
if (size === 180) destination = `${outputWebRoot}/apple-touch-icon.png`
// 32px size only used for favicon.ico
if (size === 32) {
await ico.sharpsToIco(
[sharp(faviconSource)],

View File

@ -1,8 +1,7 @@
import React from 'react'
import Icon from '../Icon'
import styles from './index.module.css'
export const ThemeToggle = ({ dark }) => (
export const ThemeToggle = () => (
<span id="toggle" className={styles.checkboxContainer} aria-live="assertive">
<Icon name="Sun" />
<span className={styles.checkboxFake} />

View File

@ -1,17 +1,16 @@
import React from 'react'
import useDarkMode from '../../hooks/useDarkMode'
type Props = {
dark: boolean
toggleDark: () => void
export const ThemeToggleInput = () => {
const { isDarkMode, setIsDarkMode } = useDarkMode()
return (
<input
onChange={() => setIsDarkMode(!isDarkMode)}
type="checkbox"
name="toggle"
value="toggle"
aria-describedby="toggle"
checked={isDarkMode === true}
/>
)
}
export const ThemeToggleInput = ({ dark, toggleDark }: Props) => (
<input
onChange={() => toggleDark()}
type="checkbox"
name="toggle"
value="toggle"
aria-describedby="toggle"
checked={dark}
/>
)

View File

@ -5,7 +5,7 @@ import { ThemeToggle } from './ThemeToggle'
import { ThemeToggleInput } from './ThemeToggleInput'
export default function ThemeSwitch() {
const { value, toggle, themeColor } = useDarkMode()
const { themeColor } = useDarkMode()
return (
<>
@ -20,8 +20,8 @@ export default function ThemeSwitch() {
<aside className={styles.themeSwitch}>
<label className={styles.checkbox}>
<span className={styles.label}>Toggle Night Mode</span>
<ThemeToggleInput dark={value} toggleDark={toggle} />
<ThemeToggle dark={value} />
<ThemeToggleInput />
<ThemeToggle />
</label>
</aside>
</>

View File

@ -2,7 +2,13 @@
// adapted from
// https://github.com/daveschumaker/react-dark-mode-hook/blob/master/useDarkMode.js
//
import { useState, useEffect, useCallback } from 'react'
import {
useState,
useEffect,
useCallback,
Dispatch,
SetStateAction
} from 'react'
const isClient = typeof window === 'object'
@ -24,37 +30,37 @@ function getDarkMode() {
}
}
export default function useDarkMode() {
const [darkMode, setDarkMode] = useState<boolean>()
export type UseDarkMode = {
isDarkMode: boolean
themeColor: string
setIsDarkMode: Dispatch<SetStateAction<boolean>>
}
export default function useDarkMode(): UseDarkMode {
const [isDarkMode, setIsDarkMode] = useState<boolean>(getDarkMode())
const [themeColor, setThemeColor] = useState<string>()
const toggleDarkMode = useCallback(() => {
setDarkMode(!darkMode)
}, [darkMode])
const changeTheme = useCallback(() => {
if (isDarkMode) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
setThemeColor(isDarkMode === true ? '#1d2224' : '#e7eef4')
}, [isDarkMode])
//
// Init
//
useEffect(() => {
const prefersDark = getDarkMode()
setDarkMode(prefersDark)
}, [])
//
// Do things when darkMode changes
//
useEffect(() => {
const bodyClassList = document.querySelector('body').classList
bodyClassList.toggle('dark')
bodyClassList.toggle('light')
setThemeColor(darkMode === true ? '#1d2224' : '#e7eef4')
}, [darkMode])
changeTheme()
}, [changeTheme])
//
// Handle system theme change events
//
const handleChange = useCallback(() => {
setDarkMode(getDarkMode())
setIsDarkMode(getDarkMode())
}, [])
useEffect(() => {
@ -74,5 +80,5 @@ export default function useDarkMode() {
.removeEventListener('change', handleChange)
}, [handleChange])
return { value: darkMode, toggle: toggleDarkMode, themeColor }
return { isDarkMode, setIsDarkMode, themeColor }
}

View File

@ -5,12 +5,6 @@
viewBox="0 0 512 512"
>
<style>
#background {
fill: #e7eef4;
}
#logomark {
fill: #6b7f88;
}
@media (prefers-color-scheme: dark) {
#background {
fill: #161a1b;
@ -18,9 +12,10 @@
}
</style>
<g fill="none" fill-rule="evenodd">
<rect id="background" width="512" height="512" />
<rect id="background" fill="#e7eef4" width="512" height="512" />
<path
id="logomark"
fill="#6b7f88"
d="M397,91 L421,115 L114,421 L91,398 L397,91 Z M397,182 L421,206 L205,421 L182,398 L397,182 Z M307,91 L330,115 L114,330 L91,307 L307,91 Z"
/>
</g>

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 541 B