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:
parent
23b0f8dcc7
commit
88e5ef9502
@ -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
77
package-lock.json
generated
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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 |
@ -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"}]}
|
@ -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)],
|
||||
|
@ -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} />
|
||||
|
@ -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}
|
||||
/>
|
||||
)
|
||||
|
@ -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>
|
||||
</>
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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 |
Loading…
Reference in New Issue
Block a user